QskSkinTransition fixed to work with graphic roles

This commit is contained in:
Uwe Rathmann 2018-03-22 11:22:13 +01:00
parent c4325d5f45
commit 736a28d02f
3 changed files with 156 additions and 55 deletions

View File

@ -87,7 +87,7 @@ static QVector< AnimatorCandidate > qskAnimatorCandidates(
{ {
if ( !( mask & QskSkinTransition::Color ) ) if ( !( mask & QskSkinTransition::Color ) )
continue; continue;
int role1 = 0; int role1 = 0;
const auto value = oldTable.resolvedHint( aspect ); const auto value = oldTable.resolvedHint( aspect );
@ -96,19 +96,26 @@ static QVector< AnimatorCandidate > qskAnimatorCandidates(
const int role2 = entry.second.toInt(); const int role2 = entry.second.toInt();
const auto it1 = oldFilters.find( role1 ); /*
const auto it2 = newFilters.find( role2 ); When the role is the same we already have the animators
for the graphic filter table running
if ( it1 != oldFilters.end() || it2 != newFilters.end() ) */
if ( role1 != role2 )
{ {
const auto& f1 = ( it1 != oldFilters.end() ) ? it1->second : noFilter; const auto it1 = oldFilters.find( role1 );
const auto& f2 = ( it2 != newFilters.end() ) ? it2->second : noFilter; const auto it2 = newFilters.find( role2 );
if ( f1 != f2 ) if ( it1 != oldFilters.end() || it2 != newFilters.end() )
{ {
candidates += AnimatorCandidate( aspect, const auto& f1 = ( it1 != oldFilters.end() ) ? it1->second : noFilter;
QVariant::fromValue( f1 ), const auto& f2 = ( it2 != newFilters.end() ) ? it2->second : noFilter;
QVariant::fromValue( f2 ) );
if ( f1 != f2 )
{
candidates += AnimatorCandidate( aspect,
QVariant::fromValue( f1 ),
QVariant::fromValue( f2 ) );
}
} }
} }
break; break;
@ -154,40 +161,44 @@ static QVector< AnimatorCandidate > qskAnimatorCandidates(
namespace namespace
{ {
class AnimatorGroup final : public QObject class AnimatorGroup final : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
AnimatorGroup() AnimatorGroup()
{ {
} }
void start() void start()
{ {
m_notifyConnection = QskAnimator::addAdvanceHandler( this, m_notifyConnection = QskAnimator::addAdvanceHandler( this,
SLOT( notify( QQuickWindow* ) ) ); SLOT( notify( QQuickWindow* ) ) );
for ( auto& it : m_map ) for ( auto& it : m_hintAnimatorMap )
it.second.start();
for ( auto& it : m_graphicFilterAnimatorMap )
it.second.start(); it.second.start();
} }
bool isRunning() const bool isRunning() const
{ {
return !m_map.empty(); return !m_hintAnimatorMap.empty();
} }
void reset() void reset()
{ {
disconnect( m_notifyConnection ); disconnect( m_notifyConnection );
m_map.clear(); m_hintAnimatorMap.clear();
m_graphicFilterAnimatorMap.clear();
m_updateInfos.clear(); m_updateInfos.clear();
} }
inline QVariant animatedHint( QskAspect::Aspect aspect ) const inline QVariant animatedHint( QskAspect::Aspect aspect ) const
{ {
auto it = m_map.find( aspect ); auto it = m_hintAnimatorMap.find( aspect );
if ( it != m_map.cend() ) if ( it != m_hintAnimatorMap.cend() )
{ {
const auto& animator = it->second; const auto& animator = it->second;
if ( animator.isRunning() ) if ( animator.isRunning() )
@ -197,6 +208,49 @@ namespace
return QVariant(); return QVariant();
} }
inline QVariant animatedGraphicFilter( int graphicRole ) const
{
auto it = m_graphicFilterAnimatorMap.find( graphicRole );
if ( it != m_graphicFilterAnimatorMap.cend() )
{
const auto& animator = it->second;
if ( animator.isRunning() )
return animator.currentValue();
}
return QVariant();
}
void addGraphicFilterAnimators(
QQuickWindow* window, const QskAnimationHint& animatorHint,
const std::unordered_map< int, QskColorFilter >& oldFilters,
const std::unordered_map< int, QskColorFilter >& newFilters )
{
const QskColorFilter noFilter;
for ( auto it2 = newFilters.begin(); it2 != newFilters.end(); ++it2 )
{
auto it1 = oldFilters.find( it2->first );
if ( it1 == oldFilters.cend() )
it1 = oldFilters.find( 0 );
const auto& f1 = ( it1 != oldFilters.cend() ) ? it1->second : noFilter;
const auto& f2 = it2->second;
if ( f1 != f2 )
{
QskVariantAnimator animator;
animator.setWindow( window );
animator.setDuration( animatorHint.duration );
animator.setEasingCurve( animatorHint.type );
animator.setStartValue( QVariant::fromValue( f1 ) );
animator.setEndValue( QVariant::fromValue( f2 ) );
m_graphicFilterAnimatorMap.emplace( it2->first, animator );
}
}
}
void addAnimators( QQuickItem* item, void addAnimators( QQuickItem* item,
const QskAnimationHint& animatorHint, const QskAnimationHint& animatorHint,
const QVector< AnimatorCandidate >& candidates, QskSkin* skin ) const QVector< AnimatorCandidate >& candidates, QskSkin* skin )
@ -207,7 +261,17 @@ namespace
if ( auto control = qobject_cast< QskControl* >( item ) ) if ( auto control = qobject_cast< QskControl* >( item ) )
{ {
if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) ) if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) )
{
addControlAnimators( control, animatorHint, candidates ); addControlAnimators( control, animatorHint, candidates );
#if 1
/*
As it is hard to identify which controls depend on the animated
graphic filters we schedule an initial update and let the
controls do the rest: see QskSkinnable::effectiveGraphicFilter
*/
control->update();
#endif
}
} }
const auto children = item->childItems(); const auto children = item->childItems();
@ -237,9 +301,9 @@ namespace
} }
} }
if ( !m_map.empty() ) if ( !m_hintAnimatorMap.empty() )
{ {
if ( !m_map.begin()->second.isRunning() ) if ( !m_hintAnimatorMap.begin()->second.isRunning() )
reset(); reset();
} }
} }
@ -292,7 +356,7 @@ namespace
QskAspect::Aspect a = candidate.aspect; QskAspect::Aspect a = candidate.aspect;
a.clearStates(); a.clearStates();
a.addState( control->skinState() ); a.addState( control->skinState() );
const QskSkinHintStatus requestState = control->hintStatus( a ); const QskSkinHintStatus requestState = control->hintStatus( a );
if ( requestState.source != QskSkinHintStatus::Skin ) if ( requestState.source != QskSkinHintStatus::Skin )
@ -307,19 +371,19 @@ namespace
continue; continue;
} }
addAnimator( control, candidate, animatorHint ); addAnimator( control->window(), candidate, animatorHint );
storeUpdateInfo( control, candidate.aspect ); storeUpdateInfo( control, candidate.aspect );
} }
} }
void addAnimator( QskControl* control, void addAnimator( QQuickWindow* window,
const AnimatorCandidate& candidate, QskAnimationHint animationHint ) const AnimatorCandidate& candidate, QskAnimationHint animationHint )
{ {
auto it = m_map.find( candidate.aspect ); auto it = m_hintAnimatorMap.find( candidate.aspect );
if ( it != m_map.end() ) if ( it != m_hintAnimatorMap.end() )
return; // already there return; // already there
it = m_map.emplace( candidate.aspect, QskHintAnimator() ).first; it = m_hintAnimatorMap.emplace( candidate.aspect, QskHintAnimator() ).first;
auto& animator = it->second; auto& animator = it->second;
animator.setAspect( candidate.aspect ); animator.setAspect( candidate.aspect );
@ -330,7 +394,7 @@ namespace
animator.setEasingCurve( animationHint.type ); animator.setEasingCurve( animationHint.type );
animator.setControl( nullptr ); animator.setControl( nullptr );
animator.setWindow( control->window() ); animator.setWindow( window );
} }
inline void storeUpdateInfo( QskControl* control, QskAspect::Aspect aspect ) inline void storeUpdateInfo( QskControl* control, QskAspect::Aspect aspect )
@ -351,7 +415,8 @@ namespace
m_updateInfos.insert( it, info ); m_updateInfos.insert( it, info );
} }
std::map< QskAspect::Aspect, QskHintAnimator > m_map; std::unordered_map< QskAspect::Aspect, QskHintAnimator > m_hintAnimatorMap;
std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration
QMetaObject::Connection m_notifyConnection; QMetaObject::Connection m_notifyConnection;
@ -400,14 +465,14 @@ QskSkin* QskSkinTransition::targetSkin() const
return m_skins[1]; return m_skins[1];
} }
void QskSkinTransition::setAnimation( QskAnimationHint animation ) void QskSkinTransition::setAnimation( QskAnimationHint animationHint )
{ {
m_animation = animation; m_animationHint = animationHint;
} }
QskAnimationHint QskSkinTransition::animation() const QskAnimationHint QskSkinTransition::animation() const
{ {
return m_animation; return m_animationHint;
} }
void QskSkinTransition::updateSkin( QskSkin*, QskSkin* ) void QskSkinTransition::updateSkin( QskSkin*, QskSkin* )
@ -425,7 +490,7 @@ void QskSkinTransition::process()
qskSkinAnimator->reset(); qskSkinAnimator->reset();
if ( ( m_animation.duration <= 0 ) || ( m_mask == 0 ) ) if ( ( m_animationHint.duration <= 0 ) || ( m_mask == 0 ) )
{ {
// no animations, we can apply the changes // no animations, we can apply the changes
updateSkin( m_skins[0], m_skins[1] ); updateSkin( m_skins[0], m_skins[1] );
@ -433,13 +498,13 @@ void QskSkinTransition::process()
} }
QVector< AnimatorCandidate > candidates; QVector< AnimatorCandidate > candidates;
const auto oldFilters = m_skins[0]->graphicFilters();
{ {
// copy out all hints before updating the skin // copy out all hints before updating the skin
// - would be good to have Copy on Write here // - would be good to have Copy on Write here
const auto oldTable = m_skins[0]->hintTable(); const auto oldTable = m_skins[0]->hintTable();
const auto oldFilters = m_skins[0]->graphicFilters();
// apply the changes // apply the changes
updateSkin( m_skins[0], m_skins[1] ); updateSkin( m_skins[0], m_skins[1] );
@ -450,20 +515,29 @@ void QskSkinTransition::process()
if ( !candidates.isEmpty() ) if ( !candidates.isEmpty() )
{ {
/* bool firstWindow = true;
finally we schedule the animators the hard way by running
over the the item trees. for the moment o.k. but we should
find a way not to create lots of identical animator objects for
each object.
*/
const auto windows = qGuiApp->topLevelWindows(); const auto windows = qGuiApp->topLevelWindows();
for ( const auto window : windows ) for ( const auto window : windows )
{ {
if ( auto quickWindow = qobject_cast< const QQuickWindow* >( window ) ) if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) )
{ {
if ( firstWindow )
{
qskSkinAnimator->addGraphicFilterAnimators(
quickWindow, m_animationHint,
oldFilters, m_skins[1]->graphicFilters() );
firstWindow = false;
}
/*
finally we schedule the animators the hard way by running
over the the item trees.
*/
qskSkinAnimator->addAnimators( quickWindow->contentItem(), qskSkinAnimator->addAnimators( quickWindow->contentItem(),
m_animation, candidates, m_skins[1] ); m_animationHint, candidates, m_skins[1] );
} }
} }
@ -484,4 +558,12 @@ QVariant QskSkinTransition::animatedHint( QskAspect::Aspect aspect )
return qskSkinAnimator->animatedHint( aspect ); return qskSkinAnimator->animatedHint( aspect );
} }
QVariant QskSkinTransition::animatedGraphicFilter( int graphicRole )
{
if ( !qskSkinAnimator.exists() )
return QVariant();
return qskSkinAnimator->animatedGraphicFilter( graphicRole );
}
#include "QskSkinTransition.moc" #include "QskSkinTransition.moc"

View File

@ -39,13 +39,14 @@ public:
static bool isRunning(); static bool isRunning();
static QVariant animatedHint( QskAspect::Aspect ); static QVariant animatedHint( QskAspect::Aspect );
static QVariant animatedGraphicFilter( int graphicRole );
protected: protected:
virtual void updateSkin( QskSkin*, QskSkin* ); virtual void updateSkin( QskSkin*, QskSkin* );
private: private:
QskSkin* m_skins[2]; QskSkin* m_skins[2];
QskAnimationHint m_animation; QskAnimationHint m_animationHint;
Type m_mask : 2; Type m_mask : 2;
}; };

View File

@ -84,10 +84,10 @@ static inline bool qskCompareResolvedStates(
// clear the placement bit and restart with the initial state // clear the placement bit and restart with the initial state
aspect1 = a1; aspect1 = a1;
aspect1.setPlacement( static_cast< QskAspect::Placement>( 0 ) ); aspect1.setPlacement( static_cast< QskAspect::Placement >( 0 ) );
aspect2 = a2; aspect2 = a2;
aspect2.setPlacement( static_cast< QskAspect::Placement>( 0 ) ); aspect2.setPlacement( static_cast< QskAspect::Placement >( 0 ) );
} }
} }
else else
@ -176,12 +176,12 @@ const QskSkinlet* QskSkinnable::effectiveSkinlet() const
return m_data->skinlet; return m_data->skinlet;
} }
QskSkinHintTable &QskSkinnable::hintTable() QskSkinHintTable& QskSkinnable::hintTable()
{ {
return m_data->hintTable; return m_data->hintTable;
} }
const QskSkinHintTable &QskSkinnable::hintTable() const const QskSkinHintTable& QskSkinnable::hintTable() const
{ {
return m_data->hintTable; return m_data->hintTable;
} }
@ -288,7 +288,7 @@ void QskSkinnable::setBoxBorderColorsHint(
QskBoxBorderColors QskSkinnable::boxBorderColorsHint( QskBoxBorderColors QskSkinnable::boxBorderColorsHint(
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
{ {
using namespace QskAspect; using namespace QskAspect;
return effectiveHint( aspect | Color | Border, status ).value< QskBoxBorderColors >(); return effectiveHint( aspect | Color | Border, status ).value< QskBoxBorderColors >();
} }
@ -325,7 +325,6 @@ QskColorFilter QskSkinnable::effectiveGraphicFilter(
aspect.setPlacement( effectivePlacement() ); aspect.setPlacement( effectivePlacement() );
aspect = aspect | QskAspect::GraphicRole; aspect = aspect | QskAspect::GraphicRole;
#if 1
QskSkinHintStatus status; QskSkinHintStatus status;
const QVariant hint = storedHint( aspect | skinState(), &status ); const QVariant hint = storedHint( aspect | skinState(), &status );
@ -336,13 +335,32 @@ QskColorFilter QskSkinnable::effectiveGraphicFilter(
aspect.setSubControl( status.aspect.subControl() ); aspect.setSubControl( status.aspect.subControl() );
} }
#endif
if ( !aspect.isAnimator() ) if ( !aspect.isAnimator() )
{ {
const QVariant v = animatedValue( aspect, nullptr ); QVariant v = animatedValue( aspect, nullptr );
if ( v.canConvert< QskColorFilter >() ) if ( v.canConvert< QskColorFilter >() )
return v.value< QskColorFilter >(); return v.value< QskColorFilter >();
if ( QskSkinTransition::isRunning() )
{
v = QskSkinTransition::animatedGraphicFilter( hint.toInt() );
if ( v.canConvert< QskColorFilter >() )
{
if ( owningControl() )
{
/*
As it is hard to find out which controls depend
on the animated graphic filters we reschedule
our updates here.
*/
owningControl()->update();
}
return v.value< QskColorFilter >();
}
}
} }
return effectiveSkin()->graphicFilter( hint.toInt() ); return effectiveSkin()->graphicFilter( hint.toInt() );
@ -362,7 +380,7 @@ QskAnimationHint QskSkinnable::animation(
} }
QskAnimationHint QskSkinnable::effectiveAnimation( QskAnimationHint QskSkinnable::effectiveAnimation(
QskAspect::Type type, QskAspect::Subcontrol subControl, QskAspect::Type type, QskAspect::Subcontrol subControl,
QskAspect::State state, QskSkinHintStatus* status ) const QskAspect::State state, QskSkinHintStatus* status ) const
{ {
QskAspect::Aspect aspect = subControl | type | state; QskAspect::Aspect aspect = subControl | type | state;
@ -526,7 +544,7 @@ const QVariant& QskSkinnable::storedHint(
{ {
status->source = QskSkinHintStatus::Skin; status->source = QskSkinHintStatus::Skin;
status->aspect = resolvedAspect; status->aspect = resolvedAspect;
} }
return *value; return *value;
} }
@ -732,7 +750,7 @@ void QskSkinnable::setSkinStateFlag( QskAspect::State state, bool on )
QskControl* control = owningControl(); QskControl* control = owningControl();
#if DEBUG_STATE #if DEBUG_STATE
qDebug() << control->className() << ":" qDebug() << control->className() << ":"
<< skinStateAsPrintable( m_data->skinState ) << "->" << skinStateAsPrintable( m_data->skinState ) << "->"
<< skinStateAsPrintable( newState ); << skinStateAsPrintable( newState );
#endif #endif
@ -780,7 +798,7 @@ void QskSkinnable::setSkinStateFlag( QskAspect::State state, bool on )
we can stop as soon as the aspects have the same state bits. we can stop as soon as the aspects have the same state bits.
This way we can reduce the number of lookups significantly This way we can reduce the number of lookups significantly
for skinnables with many state bits. for skinnables with many state bits.
*/ */
doTransition = !qskCompareResolvedStates( a1, a2, skinTable ); doTransition = !qskCompareResolvedStates( a1, a2, skinTable );
} }