moving away from std::unordered_map towards QHash. For transitions between

color schemes we need to copy out hash tables, what is way much easier
with the COW concept of the Qt containers. Also: according to
https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html QHash
seems to be faster in the most relevant category "Random full reads: execution time (integers)"
This commit is contained in:
Uwe Rathmann 2024-01-30 13:04:38 +01:00
parent 34e8dd9d65
commit 9ca02d7f1c
8 changed files with 178 additions and 188 deletions

View File

@ -102,22 +102,14 @@ class Window : public QskWindow
private: private:
void setAlternativeSkin( bool on ) void setAlternativeSkin( bool on )
{ {
auto oldSkin = qskSkinManager->skin();
if ( oldSkin->parent() == qskSkinManager )
oldSkin->setParent( nullptr ); // otherwise setSkin deletes it
auto newSkin = qskSkinManager->setSkin( alternativeSkin( on ) );
QskSkinTransition transition; QskSkinTransition transition;
transition.setSourceSkin( qskSkinManager->skin() );
transition.setSourceSkin( oldSkin ); if ( auto skin = qskSkinManager->setSkin( alternativeSkin( on ) ) )
transition.setTargetSkin( newSkin ); {
transition.setAnimation( QskAnimationHint( 600, QEasingCurve::Linear ) ); transition.setTargetSkin( skin );
transition.run( 600 );
transition.process(); }
if ( oldSkin->parent() == nullptr )
delete oldSkin;
} }
QString alternativeSkin( bool on ) const QString alternativeSkin( bool on ) const

View File

@ -21,7 +21,6 @@
#include <qpa/qplatformtheme.h> #include <qpa/qplatformtheme.h>
#include <cmath> #include <cmath>
#include <unordered_map>
#include "QskBox.h" #include "QskBox.h"
#include "QskBoxSkinlet.h" #include "QskBoxSkinlet.h"
@ -104,6 +103,8 @@
#include "QskStatusIndicator.h" #include "QskStatusIndicator.h"
#include "QskStatusIndicatorSkinlet.h" #include "QskStatusIndicatorSkinlet.h"
#include <qhash.h>
static inline QskSkinlet* qskNewSkinlet( const QMetaObject* metaObject, QskSkin* skin ) static inline QskSkinlet* qskNewSkinlet( const QMetaObject* metaObject, QskSkin* skin )
{ {
const QByteArray signature = metaObject->className() + QByteArrayLiteral( "(QskSkin*)" ); const QByteArray signature = metaObject->className() + QByteArrayLiteral( "(QskSkin*)" );
@ -137,19 +138,19 @@ namespace
} }
const QMetaObject* metaObject; const QMetaObject* metaObject;
QskSkinlet* skinlet; QskSkinlet* skinlet; // mutable ???
}; };
} }
class QskSkin::PrivateData class QskSkin::PrivateData
{ {
public: public:
std::unordered_map< const QMetaObject*, SkinletData > skinletMap; QHash< const QMetaObject*, SkinletData > skinletMap;
QskSkinHintTable hintTable; QskSkinHintTable hintTable;
std::unordered_map< int, QFont > fonts; QHash< int, QFont > fonts;
std::unordered_map< int, QskColorFilter > graphicFilters; QHash< int, QskColorFilter > graphicFilters;
QskGraphicProviderMap graphicProviders; QskGraphicProviderMap graphicProviders;
}; };
@ -229,9 +230,9 @@ void QskSkin::declareSkinlet( const QMetaObject* metaObject,
const auto it = m_data->skinletMap.find( metaObject ); const auto it = m_data->skinletMap.find( metaObject );
if ( it != m_data->skinletMap.cend() ) if ( it != m_data->skinletMap.end() )
{ {
auto& entry = it->second; auto& entry = it.value();
if ( entry.metaObject != skinletMetaObject ) if ( entry.metaObject != skinletMetaObject )
{ {
entry.metaObject = skinletMetaObject; entry.metaObject = skinletMetaObject;
@ -276,18 +277,18 @@ void QskSkin::setFont( int fontRole, const QFont& font )
void QskSkin::resetFont( int fontRole ) void QskSkin::resetFont( int fontRole )
{ {
m_data->fonts.erase( fontRole ); m_data->fonts.remove( fontRole );
} }
QFont QskSkin::font( int fontRole ) const QFont QskSkin::font( int fontRole ) const
{ {
auto it = m_data->fonts.find( fontRole ); auto it = m_data->fonts.constFind( fontRole );
if ( it != m_data->fonts.cend() ) if ( it != m_data->fonts.constEnd() )
return it->second; return it.value();
it = m_data->fonts.find( QskSkin::DefaultFont ); it = m_data->fonts.constFind( QskSkin::DefaultFont );
if ( it != m_data->fonts.cend() ) if ( it != m_data->fonts.constEnd() )
return it->second; return it.value();
return QGuiApplication::font(); return QGuiApplication::font();
} }
@ -299,16 +300,12 @@ void QskSkin::setGraphicFilter( int graphicRole, const QskColorFilter& colorFilt
void QskSkin::resetGraphicFilter( int graphicRole ) void QskSkin::resetGraphicFilter( int graphicRole )
{ {
m_data->graphicFilters.erase( graphicRole ); m_data->graphicFilters.remove( graphicRole );
} }
QskColorFilter QskSkin::graphicFilter( int graphicRole ) const QskColorFilter QskSkin::graphicFilter( int graphicRole ) const
{ {
auto it = m_data->graphicFilters.find( graphicRole ); return m_data->graphicFilters.value( graphicRole );
if ( it != m_data->graphicFilters.cend() )
return it->second;
return QskColorFilter();
} }
const QskSkinHintTable& QskSkin::hintTable() const const QskSkinHintTable& QskSkin::hintTable() const
@ -321,12 +318,12 @@ QskSkinHintTable& QskSkin::hintTable()
return m_data->hintTable; return m_data->hintTable;
} }
const std::unordered_map< int, QFont >& QskSkin::fonts() const const QHash< int, QFont >& QskSkin::fonts() const
{ {
return m_data->fonts; return m_data->fonts;
} }
const std::unordered_map< int, QskColorFilter >& QskSkin::graphicFilters() const const QHash< int, QskColorFilter >& QskSkin::graphicFilters() const
{ {
return m_data->graphicFilters; return m_data->graphicFilters;
} }
@ -375,9 +372,9 @@ const QMetaObject* QskSkin::skinletMetaObject( const QMetaObject* metaObject ) c
{ {
while ( metaObject ) while ( metaObject )
{ {
auto it = m_data->skinletMap.find( metaObject ); auto it = m_data->skinletMap.constFind( metaObject );
if ( it != m_data->skinletMap.cend() ) if ( it != m_data->skinletMap.constEnd() )
return it->second.metaObject; return it.value().metaObject;
metaObject = metaObject->superClass(); metaObject = metaObject->superClass();
} }
@ -390,9 +387,9 @@ QskSkinlet* QskSkin::skinlet( const QMetaObject* metaObject )
while ( metaObject ) while ( metaObject )
{ {
auto it = m_data->skinletMap.find( metaObject ); auto it = m_data->skinletMap.find( metaObject );
if ( it != m_data->skinletMap.cend() ) if ( it != m_data->skinletMap.end() )
{ {
auto& entry = it->second; auto& entry = it.value();
if ( entry.skinlet == nullptr ) if ( entry.skinlet == nullptr )
entry.skinlet = qskNewSkinlet( entry.metaObject, this ); entry.skinlet = qskNewSkinlet( entry.metaObject, this );

View File

@ -13,7 +13,6 @@
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <unordered_map>
class QskSkinnable; class QskSkinnable;
class QskSkinlet; class QskSkinlet;
@ -25,6 +24,7 @@ class QskGraphicProvider;
class QskSkinHintTable; class QskSkinHintTable;
class QVariant; class QVariant;
template< typename Key, typename T > class QHash;
class QSK_EXPORT QskSkin : public QObject class QSK_EXPORT QskSkin : public QObject
{ {
@ -91,8 +91,8 @@ class QSK_EXPORT QskSkin : public QObject
const QskSkinHintTable& hintTable() const; const QskSkinHintTable& hintTable() const;
QskSkinHintTable& hintTable(); QskSkinHintTable& hintTable();
const std::unordered_map< int, QFont >& fonts() const; const QHash< int, QFont >& fonts() const;
const std::unordered_map< int, QskColorFilter >& graphicFilters() const; const QHash< int, QskColorFilter >& graphicFilters() const;
private: private:
void declareSkinlet( const QMetaObject* metaObject, void declareSkinlet( const QMetaObject* metaObject,

View File

@ -11,20 +11,19 @@
const QVariant QskSkinHintTable::invalidHint; const QVariant QskSkinHintTable::invalidHint;
inline const QVariant* qskResolvedHint( QskAspect aspect, inline const QVariant* qskResolvedHint( QskAspect aspect,
const std::unordered_map< QskAspect, QVariant >& hints, const QHash< QskAspect, QVariant >& hints, QskAspect* resolvedAspect )
QskAspect* resolvedAspect )
{ {
auto a = aspect; auto a = aspect;
Q_FOREVER Q_FOREVER
{ {
auto it = hints.find( aspect ); auto it = hints.constFind( aspect );
if ( it != hints.cend() ) if ( it != hints.constEnd() )
{ {
if ( resolvedAspect ) if ( resolvedAspect )
*resolvedAspect = aspect; *resolvedAspect = aspect;
return &it->second; return &it.value();
} }
#if 1 #if 1
@ -74,17 +73,53 @@ QskSkinHintTable::QskSkinHintTable()
{ {
} }
QskSkinHintTable::QskSkinHintTable( const QskSkinHintTable& other )
: m_animatorCount( other.m_animatorCount )
, m_states( other.m_states )
{
if ( other.m_hints )
{
/*
A previous implementation was using STL containers - however:
according to https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html
QHash does slightly faster lookups than std::unordered_map in the category
"Random full reads: execution time (integers)", that is the most relevant one
in our use case.
Considering, that the "copy on write" strategy of QHash makes the implementation
of copy/assignment operators much easier ( needed in QskSkinTransition ) we prefer
using the Qt container over the STL counterparts.
*/
m_hints = new QHash< QskAspect, QVariant >( *other.m_hints );
}
}
QskSkinHintTable::~QskSkinHintTable() QskSkinHintTable::~QskSkinHintTable()
{ {
delete m_hints; delete m_hints;
} }
const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const QskSkinHintTable& QskSkinHintTable::operator=( const QskSkinHintTable& other )
{
m_animatorCount = ( other.m_animatorCount );
m_states = other.m_states;
delete m_hints;
m_hints = nullptr;
if ( other.m_hints )
m_hints = new QHash< QskAspect, QVariant >( *other.m_hints );
return *this;
}
const QHash< QskAspect, QVariant >& QskSkinHintTable::hints() const
{ {
if ( m_hints ) if ( m_hints )
return *m_hints; return *m_hints;
static std::unordered_map< QskAspect, QVariant > dummyHints; static QHash< QskAspect, QVariant > dummyHints;
return dummyHints; return dummyHints;
} }
@ -93,7 +128,7 @@ const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const
bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint ) bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint )
{ {
if ( m_hints == nullptr ) if ( m_hints == nullptr )
m_hints = new HintMap(); m_hints = new QHash< QskAspect, QVariant >();
auto it = m_hints->find( aspect ); auto it = m_hints->find( aspect );
if ( it == m_hints->end() ) if ( it == m_hints->end() )
@ -111,9 +146,9 @@ bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint )
return true; return true;
} }
if ( it->second != skinHint ) if ( it.value() != skinHint )
{ {
it->second = skinHint; it.value() = skinHint;
return true; return true;
} }
@ -127,7 +162,7 @@ bool QskSkinHintTable::removeHint( QskAspect aspect )
if ( m_hints == nullptr ) if ( m_hints == nullptr )
return false; return false;
const bool erased = m_hints->erase( aspect ); const bool erased = m_hints->remove( aspect );
if ( erased ) if ( erased )
{ {
@ -153,7 +188,7 @@ QVariant QskSkinHintTable::takeHint( QskAspect aspect )
auto it = m_hints->find( aspect ); auto it = m_hints->find( aspect );
if ( it != m_hints->end() ) if ( it != m_hints->end() )
{ {
const auto value = it->second; const auto value = it.value();
m_hints->erase( it ); m_hints->erase( it );
if ( aspect.isAnimator() ) if ( aspect.isAnimator() )
@ -214,7 +249,7 @@ QskAspect QskSkinHintTable::resolvedAnimator(
auto it = m_hints->find( aspect ); auto it = m_hints->find( aspect );
if ( it != m_hints->cend() ) if ( it != m_hints->cend() )
{ {
hint = it->second.value< QskAnimationHint >(); hint = it.value().value< QskAnimationHint >();
return aspect; return aspect;
} }

View File

@ -9,7 +9,7 @@
#include "QskAspect.h" #include "QskAspect.h"
#include <qvariant.h> #include <qvariant.h>
#include <unordered_map> #include <qhash.h>
class QskAnimationHint; class QskAnimationHint;
@ -17,8 +17,11 @@ class QSK_EXPORT QskSkinHintTable
{ {
public: public:
QskSkinHintTable(); QskSkinHintTable();
QskSkinHintTable( const QskSkinHintTable& );
~QskSkinHintTable(); ~QskSkinHintTable();
QskSkinHintTable& operator=( const QskSkinHintTable& );
bool setAnimation( QskAspect, QskAnimationHint ); bool setAnimation( QskAspect, QskAnimationHint );
QskAnimationHint animation( QskAspect ) const; QskAnimationHint animation( QskAspect ) const;
@ -33,7 +36,7 @@ class QSK_EXPORT QskSkinHintTable
bool hasHint( QskAspect ) const; bool hasHint( QskAspect ) const;
const std::unordered_map< QskAspect, QVariant >& hints() const; const QHash< QskAspect, QVariant >& hints() const;
bool hasAnimators() const; bool hasAnimators() const;
bool hasHints() const; bool hasHints() const;
@ -53,12 +56,10 @@ class QSK_EXPORT QskSkinHintTable
bool isResolutionMatching( QskAspect, QskAspect ) const; bool isResolutionMatching( QskAspect, QskAspect ) const;
private: private:
Q_DISABLE_COPY( QskSkinHintTable )
static const QVariant invalidHint; static const QVariant invalidHint;
typedef std::unordered_map< QskAspect, QVariant > HintMap; QHash< QskAspect, QVariant >* m_hints = nullptr;
HintMap* m_hints = nullptr;
unsigned short m_animatorCount = 0; unsigned short m_animatorCount = 0;
QskAspect::States m_states; QskAspect::States m_states;
@ -81,19 +82,16 @@ inline bool QskSkinHintTable::hasAnimators() const
inline bool QskSkinHintTable::hasHint( QskAspect aspect ) const inline bool QskSkinHintTable::hasHint( QskAspect aspect ) const
{ {
if ( m_hints != nullptr ) return m_hints && m_hints->contains( aspect );
return m_hints->find( aspect ) != m_hints->cend();
return false;
} }
inline const QVariant& QskSkinHintTable::hint( QskAspect aspect ) const inline const QVariant& QskSkinHintTable::hint( QskAspect aspect ) const
{ {
if ( m_hints != nullptr ) if ( m_hints != nullptr )
{ {
auto it = m_hints->find( aspect ); auto it = m_hints->constFind( aspect );
if ( it != m_hints->cend() ) if ( it != m_hints->constEnd() )
return it->second; return it.value();
} }
return invalidHint; return invalidHint;

View File

@ -16,10 +16,15 @@
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qobject.h> #include <qobject.h>
#include <qvector.h> #include <qvector.h>
#include <qhash.h>
#include <unordered_map>
#include <vector> #include <vector>
static bool qskHasHintTable( const QskSkin* skin, const QskSkinHintTable& hintTable )
{
return skin->hintTable().hints().isSharedWith( hintTable.hints() );
}
static void qskSendStyleEventRecursive( QQuickItem* item ) static void qskSendStyleEventRecursive( QQuickItem* item )
{ {
QEvent event( QEvent::StyleChange ); QEvent event( QEvent::StyleChange );
@ -31,11 +36,11 @@ static void qskSendStyleEventRecursive( QQuickItem* item )
} }
static void qskAddCandidates( const QskSkinTransition::Type mask, static void qskAddCandidates( const QskSkinTransition::Type mask,
const QskSkin* skin, QSet< QskAspect >& candidates ) const QHash< QskAspect, QVariant >& hints, QSet< QskAspect >& candidates )
{ {
for ( const auto& entry : skin->hintTable().hints() ) for ( auto it = hints.constBegin(); it != hints.constEnd(); ++it )
{ {
const auto aspect = entry.first.trunk(); const auto aspect = it.key().trunk();
if ( aspect.isAnimator() ) if ( aspect.isAnimator() )
continue; continue;
@ -127,11 +132,11 @@ namespace
QVariant animatedGraphicFilter( int graphicRole ) const; QVariant animatedGraphicFilter( int graphicRole ) const;
void addGraphicFilterAnimators( const QskAnimationHint&, void addGraphicFilterAnimators( const QskAnimationHint&,
const QskSkin*, const QskSkin* ); const QHash< int, QskColorFilter >&, const QHash< int, QskColorFilter >& );
void addItemAspects( QQuickItem*, void addItemAspects( QQuickItem*,
const QskAnimationHint&, const QSet< QskAspect >&, const QskAnimationHint&, const QSet< QskAspect >&,
const QskSkin*, const QskSkin* ); const QskSkinHintTable&, const QskSkinHintTable& );
void update(); void update();
@ -142,7 +147,7 @@ namespace
void addHints( const QskControl*, void addHints( const QskControl*,
const QskAnimationHint&, const QSet< QskAspect >& candidates, const QskAnimationHint&, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 ); const QskSkinHintTable&, const QskSkinHintTable& );
void storeAnimator( const QskControl*, const QskAspect, void storeAnimator( const QskControl*, const QskAspect,
const QVariant&, const QVariant&, QskAnimationHint ); const QVariant&, const QVariant&, QskAnimationHint );
@ -150,8 +155,8 @@ namespace
void storeUpdateInfo( const QskControl*, QskAspect ); void storeUpdateInfo( const QskControl*, QskAspect );
QQuickWindow* m_window; QQuickWindow* m_window;
std::unordered_map< QskAspect, HintAnimator > m_animatorMap; QHash< QskAspect, HintAnimator > m_animatorMap;
std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap; QHash< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration
}; };
@ -203,24 +208,24 @@ inline const QQuickWindow* WindowAnimator::window() const
void WindowAnimator::start() void WindowAnimator::start()
{ {
for ( auto& it : m_animatorMap ) for ( auto& it : m_animatorMap )
it.second.start(); it.start();
for ( auto& it : m_graphicFilterAnimatorMap ) for ( auto& it : m_graphicFilterAnimatorMap )
it.second.start(); it.start();
} }
bool WindowAnimator::isRunning() const bool WindowAnimator::isRunning() const
{ {
if ( !m_animatorMap.empty() ) if ( !m_animatorMap.empty() )
{ {
const auto& animator = m_animatorMap.begin()->second; const auto& animator = m_animatorMap.constBegin().value();
if ( animator.isRunning() ) if ( animator.isRunning() )
return true; return true;
} }
if ( !m_graphicFilterAnimatorMap.empty() ) if ( !m_graphicFilterAnimatorMap.empty() )
{ {
const auto& animator = m_graphicFilterAnimatorMap.begin()->second; const auto& animator = m_graphicFilterAnimatorMap.constBegin().value();
if ( animator.isRunning() ) if ( animator.isRunning() )
return true; return true;
} }
@ -230,10 +235,10 @@ bool WindowAnimator::isRunning() const
inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const
{ {
auto it = m_animatorMap.find( aspect ); auto it = m_animatorMap.constFind( aspect );
if ( it != m_animatorMap.cend() ) if ( it != m_animatorMap.constEnd() )
{ {
const auto& animator = it->second; const auto& animator = it.value();
if ( animator.isRunning() ) if ( animator.isRunning() )
return animator.currentValue(); return animator.currentValue();
} }
@ -243,10 +248,10 @@ inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const
inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const
{ {
auto it = m_graphicFilterAnimatorMap.find( graphicRole ); auto it = m_graphicFilterAnimatorMap.constFind( graphicRole );
if ( it != m_graphicFilterAnimatorMap.cend() ) if ( it != m_graphicFilterAnimatorMap.constEnd() )
{ {
const auto& animator = it->second; const auto& animator = it.value();
if ( animator.isRunning() ) if ( animator.isRunning() )
return animator.currentValue(); return animator.currentValue();
} }
@ -254,22 +259,21 @@ inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const
return QVariant(); return QVariant();
} }
void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint, void WindowAnimator::addGraphicFilterAnimators(
const QskSkin* skin1, const QskSkin* skin2 ) const QskAnimationHint& animatorHint,
const QHash< int, QskColorFilter >& filters1,
const QHash< int, QskColorFilter >& filters2 )
{ {
const QskColorFilter noFilter; const QskColorFilter noFilter;
const auto& filter1 = skin1->graphicFilters(); for ( auto it2 = filters2.constBegin(); it2 != filters2.constEnd(); ++it2 )
const auto& filter2 = skin2->graphicFilters();
for ( auto it2 = filter2.begin(); it2 != filter2.end(); ++it2 )
{ {
auto it1 = filter1.find( it2->first ); auto it1 = filters1.constFind( it2.key() );
if ( it1 == filter1.cend() ) if ( it1 == filters1.constEnd() )
it1 = filter1.find( 0 ); it1 = filters1.constFind( 0 );
const auto& f1 = ( it1 != filter1.cend() ) ? it1->second : noFilter; const auto& f1 = ( it1 != filters1.constEnd() ) ? it1.value() : noFilter;
const auto& f2 = it2->second; const auto& f2 = it2.value();
if ( f1 != f2 ) if ( f1 != f2 )
{ {
@ -280,23 +284,24 @@ void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animator
animator.setStartValue( QVariant::fromValue( f1 ) ); animator.setStartValue( QVariant::fromValue( f1 ) );
animator.setEndValue( QVariant::fromValue( f2 ) ); animator.setEndValue( QVariant::fromValue( f2 ) );
m_graphicFilterAnimatorMap.emplace( it2->first, animator ); m_graphicFilterAnimatorMap.emplace( it2.key(), animator );
} }
} }
} }
void WindowAnimator::addItemAspects( QQuickItem* item, void WindowAnimator::addItemAspects( QQuickItem* item,
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 ) const QskSkinHintTable& table1, const QskSkinHintTable& table2 )
{ {
if ( !item->isVisible() ) if ( !item->isVisible() )
return; return;
if ( auto control = qskControlCast( item ) ) if ( auto control = qskControlCast( item ) )
{ {
if ( control->isInitiallyPainted() && ( control->effectiveSkin() == skin2 ) ) if ( control->isInitiallyPainted() &&
qskHasHintTable( control->effectiveSkin(), table2 ) )
{ {
addHints( control, animatorHint, candidates, skin1, skin2 ); addHints( control, animatorHint, candidates, table1, table2 );
#if 1 #if 1
/* /*
As it is hard to identify which controls depend on the animated As it is hard to identify which controls depend on the animated
@ -310,7 +315,7 @@ void WindowAnimator::addItemAspects( QQuickItem* item,
const auto children = item->childItems(); const auto children = item->childItems();
for ( auto child : children ) for ( auto child : children )
addItemAspects( child, animatorHint, candidates, skin1, skin2 ); addItemAspects( child, animatorHint, candidates, table1, table2 );
} }
void WindowAnimator::update() void WindowAnimator::update()
@ -333,15 +338,12 @@ void WindowAnimator::update()
void WindowAnimator::addHints( const QskControl* control, void WindowAnimator::addHints( const QskControl* control,
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 ) const QskSkinHintTable& table1, const QskSkinHintTable& table2 )
{ {
const auto subControls = control->subControls(); const auto subControls = control->subControls();
const auto& localTable = control->hintTable(); const auto& localTable = control->hintTable();
const auto& table1 = skin1->hintTable();
const auto& table2 = skin2->hintTable();
for ( auto aspect : candidates ) for ( auto aspect : candidates )
{ {
if ( !isControlAffected( control, subControls, aspect ) ) if ( !isControlAffected( control, subControls, aspect ) )
@ -548,8 +550,12 @@ void ApplicationAnimator::cleanup( QQuickWindow* window )
class QskSkinTransition::PrivateData class QskSkinTransition::PrivateData
{ {
public: public:
QskSkin* skins[ 2 ] = {}; struct
QskAnimationHint animationHint; {
QskSkinHintTable hintTable;
QHash< int, QskColorFilter > graphicFilters;
} tables[ 2 ];
Type mask = QskSkinTransition::AllTypes; Type mask = QskSkinTransition::AllTypes;
}; };
@ -572,57 +578,38 @@ QskSkinTransition::Type QskSkinTransition::mask() const
return m_data->mask; return m_data->mask;
} }
void QskSkinTransition::setSourceSkin( QskSkin* skin ) void QskSkinTransition::setSourceSkin( const QskSkin* skin )
{ {
m_data->skins[ 0 ] = skin; auto& tables = m_data->tables[ 0 ];
tables.hintTable = skin->hintTable();
tables.graphicFilters = skin->graphicFilters();
} }
QskSkin* QskSkinTransition::sourceSkin() const void QskSkinTransition::setTargetSkin( const QskSkin* skin )
{ {
return m_data->skins[ 0 ]; auto& tables = m_data->tables[ 1 ];
tables.hintTable = skin->hintTable();
tables.graphicFilters = skin->graphicFilters();
} }
void QskSkinTransition::setTargetSkin( QskSkin* skin ) void QskSkinTransition::run( const QskAnimationHint& animationHint )
{
m_data->skins[ 1 ] = skin;
}
QskSkin* QskSkinTransition::targetSkin() const
{
return m_data->skins[ 1 ];
}
void QskSkinTransition::setAnimation( QskAnimationHint animationHint )
{
m_data->animationHint = animationHint;
}
QskAnimationHint QskSkinTransition::animation() const
{
return m_data->animationHint;
}
void QskSkinTransition::updateSkin( QskSkin*, QskSkin* )
{
// nop
}
void QskSkinTransition::process()
{ {
qskApplicationAnimator->reset(); qskApplicationAnimator->reset();
auto skin1 = m_data->skins[ 0 ]; const auto& table1 = m_data->tables[ 0 ].hintTable;
auto skin2 = m_data->skins[ 1 ]; const auto& table2 = m_data->tables[ 1 ].hintTable;
const auto& graphicFilters1 = m_data->tables[ 0 ].graphicFilters;
const auto& graphicFilters2 = m_data->tables[ 1 ].graphicFilters;
QSet< QskAspect > candidates; QSet< QskAspect > candidates;
if ( skin1 && skin2 ) if ( ( animationHint.duration > 0 ) && ( m_data->mask != 0 ) )
{ {
if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) ) qskAddCandidates( m_data->mask, table1.hints(), candidates );
{ qskAddCandidates( m_data->mask, table2.hints(), candidates );
qskAddCandidates( m_data->mask, skin1, candidates );
qskAddCandidates( m_data->mask, skin2, candidates );
}
} }
if ( !candidates.isEmpty() ) if ( !candidates.isEmpty() )
@ -635,17 +622,18 @@ void QskSkinTransition::process()
{ {
if ( auto w = qobject_cast< QQuickWindow* >( window ) ) if ( auto w = qobject_cast< QQuickWindow* >( window ) )
{ {
if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) ) if ( !w->isVisible() )
{ continue;
if ( !qskHasHintTable( qskEffectiveSkin( w ), table2 ) )
continue; continue;
}
auto animator = new WindowAnimator( w ); auto animator = new WindowAnimator( w );
if ( doGraphicFilter ) if ( doGraphicFilter )
{ {
animator->addGraphicFilterAnimators( animator->addGraphicFilterAnimators( animationHint,
m_data->animationHint, skin1, skin2 ); graphicFilters1, graphicFilters2 );
doGraphicFilter = false; doGraphicFilter = false;
} }
@ -656,7 +644,7 @@ void QskSkinTransition::process()
*/ */
animator->addItemAspects( w->contentItem(), animator->addItemAspects( w->contentItem(),
m_data->animationHint, candidates, skin1, skin2 ); animationHint, candidates, table1, table2 );
qskApplicationAnimator->add( animator ); qskApplicationAnimator->add( animator );
} }
@ -664,9 +652,6 @@ void QskSkinTransition::process()
qskApplicationAnimator->start(); qskApplicationAnimator->start();
} }
// apply the changes
updateSkin( skin1, skin2 );
} }
bool QskSkinTransition::isRunning() bool QskSkinTransition::isRunning()

View File

@ -9,10 +9,12 @@
#include "QskAspect.h" #include "QskAspect.h"
#include <memory> #include <memory>
class QskSkin;
class QskAnimationHint; class QskAnimationHint;
class QskSkin;
class QQuickWindow; class QQuickWindow;
class QVariant; class QVariant;
template< typename Key, typename T > class QHash;
class QSK_EXPORT QskSkinTransition class QSK_EXPORT QskSkinTransition
{ {
@ -28,27 +30,18 @@ class QSK_EXPORT QskSkinTransition
QskSkinTransition(); QskSkinTransition();
virtual ~QskSkinTransition(); virtual ~QskSkinTransition();
void setSourceSkin( QskSkin* ); void setSourceSkin( const QskSkin* );
QskSkin* sourceSkin() const; void setTargetSkin( const QskSkin* );
void setTargetSkin( QskSkin* );
QskSkin* targetSkin() const;
void setAnimation( QskAnimationHint );
QskAnimationHint animation() const;
void setMask( Type ); void setMask( Type );
Type mask() const; Type mask() const;
void process(); void run( const QskAnimationHint& );
static bool isRunning(); static bool isRunning();
static QVariant animatedHint( const QQuickWindow*, QskAspect ); static QVariant animatedHint( const QQuickWindow*, QskAspect );
static QVariant animatedGraphicFilter( const QQuickWindow*, int graphicRole ); static QVariant animatedGraphicFilter( const QQuickWindow*, int graphicRole );
protected:
virtual void updateSkin( QskSkin*, QskSkin* );
private: private:
Q_DISABLE_COPY( QskSkinTransition ) Q_DISABLE_COPY( QskSkinTransition )

View File

@ -152,23 +152,13 @@ void Skinny::setSkin( int index, QskAnimationHint hint )
if ( index == names.indexOf( qskSkinManager->skinName() ) ) if ( index == names.indexOf( qskSkinManager->skinName() ) )
return; return;
auto oldSkin = qskSkinManager->skin();
if ( oldSkin->parent() == qskSkinManager )
oldSkin->setParent( nullptr ); // otherwise setSkin deletes it
if ( auto newSkin = qskSkinManager->setSkin( names[ index ] ) )
{
QskSkinTransition transition; QskSkinTransition transition;
transition.setSourceSkin( qskSkinManager->skin() );
//transition.setMask( QskAspect::Color ); // Metrics are flickering -> TODO if ( auto skin = qskSkinManager->setSkin( names[ index ] ) )
transition.setSourceSkin( oldSkin ); {
transition.setTargetSkin( newSkin ); transition.setTargetSkin( skin );
transition.setAnimation( hint ); transition.run( hint );
transition.process();
if ( oldSkin->parent() == nullptr )
delete oldSkin;
} }
} }
@ -178,10 +168,10 @@ void Skinny::changeFonts( int increment )
const auto fonts = skin->fonts(); const auto fonts = skin->fonts();
for ( auto it = fonts.begin(); it != fonts.end(); ++it ) for ( auto it = fonts.constBegin(); it != fonts.constEnd(); ++it )
{ {
auto role = it->first; auto role = it.key();
auto font = it->second; auto font = it.value();
if ( font.pixelSize() > 0 ) if ( font.pixelSize() > 0 )
{ {