skin transition heavily changed
This commit is contained in:
parent
27ee0fe423
commit
e2d6823927
|
@ -64,41 +64,11 @@ QskSkinHintTable::QskSkinHintTable()
|
|||
{
|
||||
}
|
||||
|
||||
QskSkinHintTable::QskSkinHintTable( const QskSkinHintTable& other )
|
||||
: m_hints( nullptr )
|
||||
, m_animatorCount( other.m_animatorCount )
|
||||
, m_states( other.m_states )
|
||||
{
|
||||
if ( other.m_hints )
|
||||
m_hints = new HintMap( *( other.m_hints ) );
|
||||
}
|
||||
|
||||
QskSkinHintTable::~QskSkinHintTable()
|
||||
{
|
||||
delete m_hints;
|
||||
}
|
||||
|
||||
QskSkinHintTable& QskSkinHintTable::operator=( const QskSkinHintTable& other )
|
||||
{
|
||||
m_animatorCount = other.m_animatorCount;
|
||||
m_states = other.m_states;
|
||||
|
||||
if ( other.m_hints )
|
||||
{
|
||||
if ( m_hints == nullptr )
|
||||
m_hints = new HintMap();
|
||||
|
||||
*m_hints = *other.m_hints;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete m_hints;
|
||||
m_hints = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const
|
||||
{
|
||||
if ( m_hints )
|
||||
|
|
|
@ -17,12 +17,8 @@ class QSK_EXPORT QskSkinHintTable
|
|||
{
|
||||
public:
|
||||
QskSkinHintTable();
|
||||
QskSkinHintTable( const QskSkinHintTable& other );
|
||||
|
||||
~QskSkinHintTable();
|
||||
|
||||
QskSkinHintTable& operator=( const QskSkinHintTable& );
|
||||
|
||||
bool setAnimation( QskAspect, QskAnimationHint );
|
||||
QskAnimationHint animation( QskAspect ) const;
|
||||
|
||||
|
@ -57,6 +53,8 @@ class QSK_EXPORT QskSkinHintTable
|
|||
bool isResolutionMatching( QskAspect, QskAspect ) const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY( QskSkinHintTable )
|
||||
|
||||
static const QVariant invalidHint;
|
||||
|
||||
typedef std::unordered_map< QskAspect, QVariant > HintMap;
|
||||
|
|
|
@ -20,6 +20,51 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
static void qskAddCandidates( const QskSkinTransition::Type mask,
|
||||
const QskSkin* skin, QSet< QskAspect >& candidates )
|
||||
{
|
||||
for ( const auto& entry : skin->hintTable().hints() )
|
||||
{
|
||||
const auto aspect = entry.first.trunk();
|
||||
|
||||
if ( aspect.isAnimator() )
|
||||
continue;
|
||||
|
||||
bool isCandidate = false;
|
||||
|
||||
switch( aspect.type() )
|
||||
{
|
||||
case QskAspect::Flag:
|
||||
{
|
||||
if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
|
||||
{
|
||||
isCandidate = mask & QskSkinTransition::Color;
|
||||
}
|
||||
#if 0
|
||||
else if ( aspect.flagPrimitive() == QskAspect::FontRole )
|
||||
{
|
||||
isCandidate = mask & QskSkinTransition::Metric;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case QskAspect::Color:
|
||||
{
|
||||
isCandidate = mask & QskSkinTransition::Color;
|
||||
break;
|
||||
}
|
||||
case QskAspect::Metric:
|
||||
{
|
||||
isCandidate = mask & QskSkinTransition::Metric;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isCandidate )
|
||||
candidates += aspect;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class UpdateInfo
|
||||
|
@ -40,157 +85,125 @@ namespace
|
|||
int updateModes;
|
||||
};
|
||||
|
||||
class AnimatorCandidate
|
||||
class HintAnimator : public QskHintAnimator
|
||||
{
|
||||
public:
|
||||
AnimatorCandidate() = default;
|
||||
|
||||
inline AnimatorCandidate( QskAspect aspect,
|
||||
const QVariant& from, const QVariant& to )
|
||||
: aspect( aspect )
|
||||
, from( from )
|
||||
, to( to )
|
||||
inline HintAnimator( const QskControl* control, const QskAspect aspect,
|
||||
const QVariant& value1, const QVariant& value2, QskAnimationHint hint )
|
||||
{
|
||||
}
|
||||
setAspect( aspect );
|
||||
setStartValue( value1 );
|
||||
setEndValue( value2 );
|
||||
|
||||
QskAspect aspect;
|
||||
QVariant from;
|
||||
QVariant to;
|
||||
setDuration( hint.duration );
|
||||
setEasingCurve( hint.type );
|
||||
setUpdateFlags( hint.updateFlags );
|
||||
|
||||
setWindow( control->window() );
|
||||
}
|
||||
};
|
||||
|
||||
class WindowAnimator
|
||||
{
|
||||
public:
|
||||
WindowAnimator( QQuickWindow* = nullptr );
|
||||
|
||||
const QQuickWindow* window() const;
|
||||
|
||||
void start();
|
||||
bool isRunning() const;
|
||||
|
||||
QVariant animatedHint( QskAspect ) const;
|
||||
QVariant animatedGraphicFilter( int graphicRole ) const;
|
||||
|
||||
void addGraphicFilterAnimators( const QskAnimationHint&,
|
||||
const QskSkin*, const QskSkin* );
|
||||
|
||||
void addItemAspects( QQuickItem*,
|
||||
const QskAnimationHint&, const QSet< QskAspect >&,
|
||||
const QskSkin*, const QskSkin* );
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
|
||||
bool isControlAffected( const QskControl*,
|
||||
const QVector< QskAspect::Subcontrol >&, QskAspect ) const;
|
||||
|
||||
void addHints( const QskControl*,
|
||||
const QskAnimationHint&, const QSet< QskAspect >& candidates,
|
||||
const QskSkin* skin1, const QskSkin* skin2 );
|
||||
|
||||
void storeAnimator( const QskControl*, const QskAspect,
|
||||
const QVariant&, const QVariant&, QskAnimationHint );
|
||||
|
||||
void storeUpdateInfo( const QskControl*, QskAspect );
|
||||
|
||||
QQuickWindow* m_window;
|
||||
std::unordered_map< QskAspect, HintAnimator > m_animatorMap;
|
||||
std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
|
||||
std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration
|
||||
};
|
||||
|
||||
class ApplicationAnimator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~ApplicationAnimator();
|
||||
|
||||
WindowAnimator* windowAnimator( const QQuickWindow* );
|
||||
|
||||
void add( WindowAnimator* );
|
||||
|
||||
void start();
|
||||
void reset();
|
||||
bool isRunning() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
// using functor slots ?
|
||||
void notify( QQuickWindow* );
|
||||
void cleanup( QQuickWindow* );
|
||||
|
||||
private:
|
||||
/*
|
||||
It should be possible to find an implementation, that interpolates
|
||||
a skin hint only once for all windows. But as our animtors are driven by
|
||||
QQuickWindow::afterAnimating the code will have to be somehow tricky.
|
||||
But as skin transitions are no operations, that happen often, we can accept
|
||||
the overhaed of the current implementation and do the finetuning later.
|
||||
*/
|
||||
std::vector< WindowAnimator* > m_windowAnimators;
|
||||
QMetaObject::Connection m_connections[2];
|
||||
};
|
||||
}
|
||||
|
||||
static QVector< AnimatorCandidate > qskAnimatorCandidates(
|
||||
QskSkinTransition::Type mask,
|
||||
const QskSkinHintTable& oldTable,
|
||||
const std::unordered_map< int, QskColorFilter >& oldFilters,
|
||||
const QskSkinHintTable& newTable,
|
||||
const std::unordered_map< int, QskColorFilter >& newFilters )
|
||||
Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator )
|
||||
|
||||
WindowAnimator::WindowAnimator( QQuickWindow* window )
|
||||
: m_window( window )
|
||||
{
|
||||
// building a list of candidates for animations by comparing
|
||||
// the old/new set of skin hints
|
||||
|
||||
const QskColorFilter noFilter;
|
||||
QVector< AnimatorCandidate > candidates;
|
||||
|
||||
if ( !oldTable.hasHints() )
|
||||
return candidates;
|
||||
|
||||
for ( const auto& entry : newTable.hints() )
|
||||
{
|
||||
const auto aspect = entry.first;
|
||||
|
||||
if ( aspect.isAnimator() )
|
||||
continue;
|
||||
|
||||
const auto type = aspect.type();
|
||||
|
||||
if ( type == QskAspect::Flag )
|
||||
{
|
||||
switch ( aspect.flagPrimitive() )
|
||||
{
|
||||
case QskAspect::GraphicRole:
|
||||
{
|
||||
if ( !( mask & QskSkinTransition::Color ) )
|
||||
continue;
|
||||
|
||||
int role1 = 0;
|
||||
|
||||
const auto value = oldTable.resolvedHint( aspect );
|
||||
if ( value )
|
||||
role1 = value->toInt();
|
||||
|
||||
const int role2 = entry.second.toInt();
|
||||
|
||||
/*
|
||||
When the role is the same we already have the animators
|
||||
for the graphic filter table running
|
||||
*/
|
||||
if ( role1 != role2 )
|
||||
{
|
||||
const auto it1 = oldFilters.find( role1 );
|
||||
const auto it2 = newFilters.find( role2 );
|
||||
|
||||
if ( it1 != oldFilters.end() || it2 != newFilters.end() )
|
||||
{
|
||||
const auto& f1 = ( it1 != oldFilters.end() ) ? it1->second : noFilter;
|
||||
const auto& f2 = ( it2 != newFilters.end() ) ? it2->second : noFilter;
|
||||
|
||||
if ( f1 != f2 )
|
||||
{
|
||||
candidates += AnimatorCandidate( aspect,
|
||||
QVariant::fromValue( f1 ), QVariant::fromValue( f2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QskAspect::FontRole:
|
||||
{
|
||||
if ( !( mask & QskSkinTransition::Metric ) )
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ( ( type == QskAspect::Color ) && ( mask & QskSkinTransition::Color ) ) ||
|
||||
( ( type == QskAspect::Metric ) && ( mask & QskSkinTransition::Metric ) ) )
|
||||
{
|
||||
auto value = oldTable.resolvedHint( aspect );
|
||||
if ( value == nullptr && aspect.subControl() != QskAspect::Control )
|
||||
{
|
||||
auto a = aspect;
|
||||
a.setSubControl( QskAspect::Control );
|
||||
a.clearStates();
|
||||
value = oldTable.resolvedHint( a );
|
||||
}
|
||||
|
||||
/*
|
||||
We are missing transitions, when a hint in newTable
|
||||
gets resolved from QskControl. TODO ...
|
||||
*/
|
||||
if ( value && *value != entry.second )
|
||||
candidates += AnimatorCandidate( aspect, *value, entry.second );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
namespace
|
||||
inline const QQuickWindow* WindowAnimator::window() const
|
||||
{
|
||||
class AnimatorGroup
|
||||
{
|
||||
public:
|
||||
AnimatorGroup( QQuickWindow* window = nullptr )
|
||||
: m_window( window )
|
||||
{
|
||||
}
|
||||
|
||||
inline const QQuickWindow* window() const
|
||||
{
|
||||
return m_window;
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
for ( auto& it : m_hintAnimatorMap )
|
||||
void WindowAnimator::start()
|
||||
{
|
||||
for ( auto& it : m_animatorMap )
|
||||
it.second.start();
|
||||
|
||||
for ( auto& it : m_graphicFilterAnimatorMap )
|
||||
it.second.start();
|
||||
}
|
||||
}
|
||||
|
||||
bool isRunning() const
|
||||
bool WindowAnimator::isRunning() const
|
||||
{
|
||||
if ( !m_animatorMap.empty() )
|
||||
{
|
||||
if ( !m_hintAnimatorMap.empty() )
|
||||
{
|
||||
const auto& animator = m_hintAnimatorMap.begin()->second;
|
||||
const auto& animator = m_animatorMap.begin()->second;
|
||||
if ( animator.isRunning() )
|
||||
return true;
|
||||
}
|
||||
|
@ -203,12 +216,12 @@ namespace
|
|||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline QVariant animatedHint( QskAspect aspect ) const
|
||||
{
|
||||
auto it = m_hintAnimatorMap.find( aspect );
|
||||
if ( it != m_hintAnimatorMap.cend() )
|
||||
inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const
|
||||
{
|
||||
auto it = m_animatorMap.find( aspect );
|
||||
if ( it != m_animatorMap.cend() )
|
||||
{
|
||||
const auto& animator = it->second;
|
||||
if ( animator.isRunning() )
|
||||
|
@ -216,10 +229,10 @@ namespace
|
|||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
inline QVariant animatedGraphicFilter( int graphicRole ) const
|
||||
{
|
||||
inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const
|
||||
{
|
||||
auto it = m_graphicFilterAnimatorMap.find( graphicRole );
|
||||
if ( it != m_graphicFilterAnimatorMap.cend() )
|
||||
{
|
||||
|
@ -229,22 +242,23 @@ namespace
|
|||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
void addGraphicFilterAnimators(
|
||||
const QskAnimationHint& animatorHint,
|
||||
const std::unordered_map< int, QskColorFilter >& oldFilters,
|
||||
const std::unordered_map< int, QskColorFilter >& newFilters )
|
||||
{
|
||||
void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint,
|
||||
const QskSkin* skin1, const QskSkin* skin2 )
|
||||
{
|
||||
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& filter1 = skin1->graphicFilters();
|
||||
const auto& filter2 = skin2->graphicFilters();
|
||||
|
||||
const auto& f1 = ( it1 != oldFilters.cend() ) ? it1->second : noFilter;
|
||||
for ( auto it2 = filter2.begin(); it2 != filter2.end(); ++it2 )
|
||||
{
|
||||
auto it1 = filter1.find( it2->first );
|
||||
if ( it1 == filter1.cend() )
|
||||
it1 = filter1.find( 0 );
|
||||
|
||||
const auto& f1 = ( it1 != filter1.cend() ) ? it1->second : noFilter;
|
||||
const auto& f2 = it2->second;
|
||||
|
||||
if ( f1 != f2 )
|
||||
|
@ -259,19 +273,20 @@ namespace
|
|||
m_graphicFilterAnimatorMap.emplace( it2->first, animator );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addAnimators( QQuickItem* item, const QskAnimationHint& animatorHint,
|
||||
const QVector< AnimatorCandidate >& candidates, QskSkin* skin )
|
||||
{
|
||||
void WindowAnimator::addItemAspects( QQuickItem* item,
|
||||
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
|
||||
const QskSkin* skin1, const QskSkin* skin2 )
|
||||
{
|
||||
if ( !item->isVisible() )
|
||||
return;
|
||||
|
||||
if ( auto control = qskControlCast( item ) )
|
||||
{
|
||||
if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) )
|
||||
if ( control->isInitiallyPainted() && ( control->effectiveSkin() == skin2 ) )
|
||||
{
|
||||
addControlAnimators( control, animatorHint, candidates );
|
||||
addHints( control, animatorHint, candidates, skin1, skin2 );
|
||||
#if 1
|
||||
/*
|
||||
As it is hard to identify which controls depend on the animated
|
||||
|
@ -285,11 +300,11 @@ namespace
|
|||
|
||||
const auto children = item->childItems();
|
||||
for ( auto child : children )
|
||||
addAnimators( child, animatorHint, candidates, skin );
|
||||
}
|
||||
addItemAspects( child, animatorHint, candidates, skin1, skin2 );
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
void WindowAnimator::update()
|
||||
{
|
||||
for ( auto& info : m_updateInfos )
|
||||
{
|
||||
if ( auto control = info.control )
|
||||
|
@ -304,26 +319,81 @@ namespace
|
|||
control->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void addControlAnimators( QskControl* control, const QskAnimationHint& animatorHint,
|
||||
const QVector< AnimatorCandidate >& candidates )
|
||||
{
|
||||
void WindowAnimator::addHints( const QskControl* control,
|
||||
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
|
||||
const QskSkin* skin1, const QskSkin* skin2 )
|
||||
{
|
||||
const auto subControls = control->subControls();
|
||||
|
||||
for ( const auto& candidate : candidates )
|
||||
{
|
||||
const auto aspect = candidate.aspect;
|
||||
const auto& localTable = control->hintTable();
|
||||
|
||||
const auto& table1 = skin1->hintTable();
|
||||
const auto& table2 = skin2->hintTable();
|
||||
|
||||
for ( auto aspect : candidates )
|
||||
{
|
||||
if ( !isControlAffected( control, subControls, aspect ) )
|
||||
continue;
|
||||
|
||||
aspect.setPlacement( control->effectivePlacement() );
|
||||
aspect.setStates( control->skinStates() );
|
||||
|
||||
if ( localTable.resolvedHint( aspect ) )
|
||||
{
|
||||
// value is not from the skin - ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
QskAspect r1, r2;
|
||||
|
||||
const auto v1 = table1.resolvedHint( aspect, &r1 );
|
||||
const auto v2 = table2.resolvedHint( aspect, &r2 );
|
||||
|
||||
if ( v1 && v2 )
|
||||
{
|
||||
if ( QskVariantAnimator::maybeInterpolate( *v1, *v2 ) )
|
||||
{
|
||||
if ( r1.placement() == r2.placement() )
|
||||
aspect.setPlacement( r2.placement() );
|
||||
|
||||
if ( r1.states() == r2.states() )
|
||||
aspect.setStates( r2.states() );
|
||||
|
||||
storeAnimator( control, aspect, *v1, *v2, animatorHint );
|
||||
storeUpdateInfo( control, aspect );
|
||||
}
|
||||
}
|
||||
else if ( v1 )
|
||||
{
|
||||
aspect.setPlacement( r1.placement() );
|
||||
aspect.setStates( r1.states() );
|
||||
|
||||
storeAnimator( control, aspect, *v1, QVariant(), animatorHint );
|
||||
storeUpdateInfo( control, aspect );
|
||||
}
|
||||
else if ( v2 )
|
||||
{
|
||||
aspect.setPlacement( r1.placement() );
|
||||
aspect.setStates( r1.states() );
|
||||
|
||||
storeAnimator( control, aspect, QVariant(), *v2, animatorHint );
|
||||
storeUpdateInfo( control, aspect );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool WindowAnimator::isControlAffected( const QskControl* control,
|
||||
const QVector< QskAspect::Subcontrol >& subControls, const QskAspect aspect ) const
|
||||
{
|
||||
if ( !aspect.isMetric() )
|
||||
{
|
||||
if ( !( control->flags() & QQuickItem::ItemHasContents ) )
|
||||
{
|
||||
// while metrics might have an effect on layouts, we
|
||||
// can safely ignore others for controls without content
|
||||
continue;
|
||||
// ignore all others for controls without content
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,76 +402,44 @@ namespace
|
|||
{
|
||||
// The control uses subcontrol redirection, so we can assume it
|
||||
// is not interested in this subcontrol.
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( subControl != QskAspect::Control )
|
||||
{
|
||||
if ( !subControls.contains( subControl ) )
|
||||
{
|
||||
// the control is not interested in the aspect
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( subControl == QskAspect::Control )
|
||||
{
|
||||
if ( !control->autoFillBackground() )
|
||||
{
|
||||
// no need to animate the background unless we show it
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto a = aspect;
|
||||
a.setStates( control->skinStates() );
|
||||
|
||||
const auto requestState = control->hintStatus( a );
|
||||
|
||||
if ( requestState.source != QskSkinHintStatus::Skin )
|
||||
else
|
||||
{
|
||||
// The control does not resolve the aspect from the skin.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( aspect != requestState.aspect )
|
||||
if ( !subControls.contains( subControl ) )
|
||||
{
|
||||
// the aspect was resolved to something else
|
||||
continue;
|
||||
}
|
||||
|
||||
addAnimator( control->window(), aspect,
|
||||
candidate.from, candidate.to, animatorHint );
|
||||
|
||||
storeUpdateInfo( control, aspect );
|
||||
// the control is not interested in the aspect
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void addAnimator( QQuickWindow* window, const QskAspect aspect,
|
||||
const QVariant& from, const QVariant& to, QskAnimationHint animationHint )
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void WindowAnimator::storeAnimator( const QskControl* control, const QskAspect aspect,
|
||||
const QVariant& value1, const QVariant& value2, QskAnimationHint hint )
|
||||
{
|
||||
if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() )
|
||||
{
|
||||
auto it = m_hintAnimatorMap.find( aspect );
|
||||
if ( it != m_hintAnimatorMap.end() )
|
||||
return; // already there
|
||||
|
||||
it = m_hintAnimatorMap.emplace( aspect, QskHintAnimator() ).first;
|
||||
auto& animator = it->second;
|
||||
|
||||
animator.setAspect( aspect );
|
||||
animator.setStartValue( from );
|
||||
animator.setEndValue( to );
|
||||
|
||||
animator.setDuration( animationHint.duration );
|
||||
animator.setEasingCurve( animationHint.type );
|
||||
animator.setUpdateFlags( animationHint.updateFlags );
|
||||
|
||||
animator.setControl( nullptr );
|
||||
animator.setWindow( window );
|
||||
m_animatorMap.emplace( aspect,
|
||||
HintAnimator( control, aspect, value1, value2, hint ) );
|
||||
}
|
||||
}
|
||||
|
||||
inline void storeUpdateInfo( QskControl* control, QskAspect aspect )
|
||||
{
|
||||
inline void WindowAnimator::storeUpdateInfo( const QskControl* control, QskAspect aspect )
|
||||
{
|
||||
UpdateInfo info;
|
||||
info.control = control;
|
||||
info.control = const_cast< QskControl* >( control );
|
||||
|
||||
info.updateModes = UpdateInfo::Update;
|
||||
if ( aspect.isMetric() )
|
||||
|
@ -414,121 +452,94 @@ namespace
|
|||
it->updateModes |= info.updateModes;
|
||||
else
|
||||
m_updateInfos.insert( it, info );
|
||||
}
|
||||
}
|
||||
|
||||
QQuickWindow* m_window;
|
||||
std::unordered_map< QskAspect, QskHintAnimator > m_hintAnimatorMap;
|
||||
std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
|
||||
std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration
|
||||
};
|
||||
|
||||
class AnimatorGroups : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~AnimatorGroups()
|
||||
{
|
||||
ApplicationAnimator::~ApplicationAnimator()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
inline AnimatorGroup* animatorGroup( const QQuickWindow* window )
|
||||
inline WindowAnimator* ApplicationAnimator::windowAnimator( const QQuickWindow* window )
|
||||
{
|
||||
if ( window )
|
||||
{
|
||||
if ( !m_animatorGroups.empty() && window )
|
||||
for ( auto animator : m_windowAnimators )
|
||||
{
|
||||
for ( auto group : m_animatorGroups )
|
||||
{
|
||||
if ( group->window() == window )
|
||||
return group;
|
||||
if ( animator->window() == window )
|
||||
return animator;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void add( AnimatorGroup* group )
|
||||
{
|
||||
m_animatorGroups.push_back( group );
|
||||
}
|
||||
void ApplicationAnimator::add( WindowAnimator* animator )
|
||||
{
|
||||
m_windowAnimators.push_back( animator );
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
void ApplicationAnimator::start()
|
||||
{
|
||||
m_connections[0] = QskAnimator::addAdvanceHandler(
|
||||
this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection );
|
||||
|
||||
m_connections[1] = QskAnimator::addCleanupHandler(
|
||||
this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection );
|
||||
|
||||
for ( auto& group : m_animatorGroups )
|
||||
group->start();
|
||||
}
|
||||
for ( auto& animator : m_windowAnimators )
|
||||
animator->start();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
qDeleteAll( m_animatorGroups );
|
||||
m_animatorGroups.clear();
|
||||
void ApplicationAnimator::reset()
|
||||
{
|
||||
qDeleteAll( m_windowAnimators );
|
||||
m_windowAnimators.clear();
|
||||
|
||||
disconnect( m_connections[0] );
|
||||
disconnect( m_connections[1] );
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isRunning() const
|
||||
{
|
||||
return !m_animatorGroups.empty();
|
||||
}
|
||||
inline bool ApplicationAnimator::isRunning() const
|
||||
{
|
||||
return !m_windowAnimators.empty();
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void notify( QQuickWindow* window )
|
||||
void ApplicationAnimator::notify( QQuickWindow* window )
|
||||
{
|
||||
for ( auto& animator : m_windowAnimators )
|
||||
{
|
||||
for ( auto& group : m_animatorGroups )
|
||||
if ( animator->window() == window )
|
||||
{
|
||||
if ( group->window() == window )
|
||||
{
|
||||
group->update();
|
||||
animator->update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup( QQuickWindow* window )
|
||||
void ApplicationAnimator::cleanup( QQuickWindow* window )
|
||||
{
|
||||
for ( auto it = m_windowAnimators.begin();
|
||||
it != m_windowAnimators.end(); ++it )
|
||||
{
|
||||
for ( auto it = m_animatorGroups.begin();
|
||||
it != m_animatorGroups.end(); ++it )
|
||||
auto animator = *it;
|
||||
if ( animator->window() == window )
|
||||
{
|
||||
auto group = *it;
|
||||
if ( group->window() == window )
|
||||
{
|
||||
if ( !group->isRunning() )
|
||||
if ( !animator->isRunning() )
|
||||
{
|
||||
// The notification might be for other animators
|
||||
|
||||
m_animatorGroups.erase( it );
|
||||
delete group;
|
||||
m_windowAnimators.erase( it );
|
||||
delete animator;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_animatorGroups.empty() )
|
||||
if ( m_windowAnimators.empty() )
|
||||
reset();
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
It should be possible to find an implementation, that interpolates
|
||||
a skin hint only once for all windows. But as our animtors are driven by
|
||||
QQuickWindow::afterAnimating the code will have to be somehow tricky.
|
||||
But as skin transitions are no operations, that happen often, we can accept
|
||||
the overhaed of the current implementation and do the finetuning later.
|
||||
*/
|
||||
std::vector< AnimatorGroup* > m_animatorGroups;
|
||||
QMetaObject::Connection m_connections[2];
|
||||
};
|
||||
}
|
||||
|
||||
Q_GLOBAL_STATIC( AnimatorGroups, qskSkinAnimator )
|
||||
|
||||
class QskSkinTransition::PrivateData
|
||||
{
|
||||
public:
|
||||
|
@ -593,38 +604,20 @@ void QskSkinTransition::updateSkin( QskSkin*, QskSkin* )
|
|||
|
||||
void QskSkinTransition::process()
|
||||
{
|
||||
auto skinFrom = m_data->skins[ 0 ];
|
||||
auto skinTo = m_data->skins[ 1 ];
|
||||
qskApplicationAnimator->reset();
|
||||
|
||||
if ( ( skinFrom == nullptr ) || ( skinTo == nullptr ) )
|
||||
auto skin1 = m_data->skins[ 0 ];
|
||||
auto skin2 = m_data->skins[ 1 ];
|
||||
|
||||
QSet< QskAspect > candidates;
|
||||
|
||||
if ( skin1 && skin2 )
|
||||
{
|
||||
// do nothing
|
||||
return;
|
||||
if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) )
|
||||
{
|
||||
qskAddCandidates( m_data->mask, skin1, candidates );
|
||||
qskAddCandidates( m_data->mask, skin2, candidates );
|
||||
}
|
||||
|
||||
qskSkinAnimator->reset();
|
||||
|
||||
if ( ( m_data->animationHint.duration <= 0 ) || ( m_data->mask == 0 ) )
|
||||
{
|
||||
// no animations, we can apply the changes
|
||||
updateSkin( skinFrom, skinTo );
|
||||
return;
|
||||
}
|
||||
|
||||
QVector< AnimatorCandidate > candidates;
|
||||
const auto oldFilters = skinFrom->graphicFilters();
|
||||
|
||||
{
|
||||
// copy out all hints before updating the skin
|
||||
// - would be good to have Copy on Write here
|
||||
|
||||
const auto oldTable = skinFrom->hintTable();
|
||||
|
||||
// apply the changes
|
||||
updateSkin( skinFrom, skinTo );
|
||||
|
||||
candidates = qskAnimatorCandidates( m_data->mask, oldTable, oldFilters,
|
||||
skinTo->hintTable(), skinTo->graphicFilters() );
|
||||
}
|
||||
|
||||
if ( !candidates.isEmpty() )
|
||||
|
@ -635,21 +628,19 @@ void QskSkinTransition::process()
|
|||
|
||||
for ( const auto window : windows )
|
||||
{
|
||||
if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) )
|
||||
if ( auto w = qobject_cast< QQuickWindow* >( window ) )
|
||||
{
|
||||
if ( !quickWindow->isVisible() ||
|
||||
( qskEffectiveSkin( quickWindow ) != skinTo ) )
|
||||
if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto group = new AnimatorGroup( quickWindow );
|
||||
auto animator = new WindowAnimator( w );
|
||||
|
||||
if ( doGraphicFilter )
|
||||
{
|
||||
group->addGraphicFilterAnimators(
|
||||
m_data->animationHint, oldFilters,
|
||||
skinTo->graphicFilters() );
|
||||
animator->addGraphicFilterAnimators(
|
||||
m_data->animationHint, skin1, skin2 );
|
||||
|
||||
doGraphicFilter = false;
|
||||
}
|
||||
|
@ -659,21 +650,24 @@ void QskSkinTransition::process()
|
|||
over the the item trees.
|
||||
*/
|
||||
|
||||
group->addAnimators( quickWindow->contentItem(),
|
||||
m_data->animationHint, candidates, skinTo );
|
||||
animator->addItemAspects( w->contentItem(),
|
||||
m_data->animationHint, candidates, skin1, skin2 );
|
||||
|
||||
qskSkinAnimator->add( group );
|
||||
qskApplicationAnimator->add( animator );
|
||||
}
|
||||
}
|
||||
|
||||
qskSkinAnimator->start();
|
||||
qskApplicationAnimator->start();
|
||||
}
|
||||
|
||||
// apply the changes
|
||||
updateSkin( skin1, skin2 );
|
||||
}
|
||||
|
||||
bool QskSkinTransition::isRunning()
|
||||
{
|
||||
if ( qskSkinAnimator.exists() )
|
||||
return qskSkinAnimator->isRunning();
|
||||
if ( qskApplicationAnimator.exists() )
|
||||
return qskApplicationAnimator->isRunning();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -681,10 +675,10 @@ bool QskSkinTransition::isRunning()
|
|||
QVariant QskSkinTransition::animatedHint(
|
||||
const QQuickWindow* window, QskAspect aspect )
|
||||
{
|
||||
if ( qskSkinAnimator.exists() )
|
||||
if ( qskApplicationAnimator.exists() )
|
||||
{
|
||||
if ( const auto group = qskSkinAnimator->animatorGroup( window ) )
|
||||
return group->animatedHint( aspect );
|
||||
if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
|
||||
return animator->animatedHint( aspect );
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -693,10 +687,10 @@ QVariant QskSkinTransition::animatedHint(
|
|||
QVariant QskSkinTransition::animatedGraphicFilter(
|
||||
const QQuickWindow* window, int graphicRole )
|
||||
{
|
||||
if ( qskSkinAnimator.exists() )
|
||||
if ( qskApplicationAnimator.exists() )
|
||||
{
|
||||
if ( const auto group = qskSkinAnimator->animatorGroup( window ) )
|
||||
return group->animatedGraphicFilter( graphicRole );
|
||||
if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
|
||||
return animator->animatedGraphicFilter( graphicRole );
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
|
|
@ -38,16 +38,6 @@ static inline bool qskIsControl( const QskSkinnable* skinnable )
|
|||
return skinnable->metaObject()->inherits( &QskControl::staticMetaObject );
|
||||
}
|
||||
|
||||
static inline QVariant qskTypedNullValue( const QVariant& value )
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
|
||||
return QVariant( value.metaType() );
|
||||
#else
|
||||
return QVariant( value.userType(), nullptr );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static inline bool qskSetFlag( QskSkinnable* skinnable,
|
||||
const QskAspect aspect, int flag )
|
||||
{
|
||||
|
@ -1155,27 +1145,11 @@ void QskSkinnable::startHintTransition( QskAspect aspect,
|
|||
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
|
||||
return;
|
||||
|
||||
/*
|
||||
We might be invalid for one of the values, when an aspect
|
||||
has not been defined for all states ( f.e. metrics are expected
|
||||
to fallback to 0.0 ). In this case we create a default one.
|
||||
*/
|
||||
|
||||
auto v1 = from;
|
||||
auto v2 = to;
|
||||
|
||||
if ( !v1.isValid() )
|
||||
{
|
||||
v1 = qskTypedNullValue( v2 );
|
||||
}
|
||||
else if ( !v2.isValid() )
|
||||
{
|
||||
v2 = qskTypedNullValue( v1 );
|
||||
}
|
||||
else if ( v1.userType() != v2.userType() )
|
||||
{
|
||||
if ( !QskVariantAnimator::convertValues( v1, v2 ) )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
|
||||
{
|
||||
|
|
|
@ -76,6 +76,31 @@ QSK_DECL_INSANE static inline QVariant qskInterpolate(
|
|||
return f( from.constData(), to.constData(), progress );
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||
|
||||
using QskMetaType = int;
|
||||
static inline QskMetaType qskMetaType( const QVariant& v ) { return v.userType(); }
|
||||
|
||||
#else
|
||||
|
||||
using QskMetaType = QMetaType;
|
||||
static inline QskMetaType qskMetaType( const QVariant& v ) { return v.metaType(); }
|
||||
|
||||
#endif
|
||||
|
||||
static inline QVariant qskDefaultVariant( QskMetaType type )
|
||||
{
|
||||
return QVariant( type, nullptr );
|
||||
}
|
||||
|
||||
static inline QVariant qskConvertedVariant( const QVariant& from, QskMetaType type )
|
||||
{
|
||||
auto v = from;
|
||||
v.convert( type );
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
QskVariantAnimator::QskVariantAnimator()
|
||||
: m_interpolator( nullptr )
|
||||
{
|
||||
|
@ -100,35 +125,60 @@ void QskVariantAnimator::setCurrentValue( const QVariant& value )
|
|||
m_currentValue = value;
|
||||
}
|
||||
|
||||
bool QskVariantAnimator::convertValues( QVariant& v1, QVariant& v2 )
|
||||
{
|
||||
if ( !v1.isValid() && !v2.isValid() )
|
||||
return false;
|
||||
|
||||
const auto type1 = qskMetaType( v1 );
|
||||
const auto type2 = qskMetaType( v2 );
|
||||
|
||||
if ( !v1.isValid() )
|
||||
{
|
||||
v1 = qskDefaultVariant( type2 );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !v2.isValid() )
|
||||
{
|
||||
v2 = qskDefaultVariant( type1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( type1 != type2 )
|
||||
{
|
||||
if ( v1.canConvert( type2 ) )
|
||||
{
|
||||
v1.convert( type2 );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( v2.canConvert( type1 ) )
|
||||
{
|
||||
v2.convert( type1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QskVariantAnimator::setup()
|
||||
{
|
||||
m_interpolator = nullptr;
|
||||
|
||||
if ( m_startValue.userType() != m_endValue.userType() )
|
||||
if ( convertValues( m_startValue, m_endValue ) )
|
||||
{
|
||||
/*
|
||||
Convert one value so that the types are matching.
|
||||
if ( m_startValue != m_endValue )
|
||||
{
|
||||
const auto id = m_startValue.userType();
|
||||
|
||||
As a side effect startValue()/endValue() won't return what had
|
||||
been set setStartValue()/setEndValue() !
|
||||
*/
|
||||
|
||||
if ( m_startValue.canConvert( m_endValue.userType() ) )
|
||||
{
|
||||
m_startValue.convert( m_endValue.userType() );
|
||||
}
|
||||
else if ( m_endValue.canConvert( m_startValue.userType() ) )
|
||||
{
|
||||
m_endValue.convert( m_startValue.userType() );
|
||||
}
|
||||
}
|
||||
|
||||
const auto type = m_startValue.userType();
|
||||
if ( type == m_endValue.userType() )
|
||||
{
|
||||
// all what has been registered by qRegisterAnimationInterpolator
|
||||
m_interpolator = reinterpret_cast< void ( * )() >(
|
||||
QVariantAnimationPrivate::getInterpolator( type ) );
|
||||
QVariantAnimationPrivate::getInterpolator( id ) );
|
||||
}
|
||||
}
|
||||
|
||||
m_currentValue = m_interpolator ? m_startValue : m_endValue;
|
||||
|
@ -150,3 +200,32 @@ void QskVariantAnimator::done()
|
|||
{
|
||||
m_interpolator = nullptr;
|
||||
}
|
||||
|
||||
bool QskVariantAnimator::maybeInterpolate(
|
||||
const QVariant& value1, const QVariant& value2 )
|
||||
{
|
||||
if ( !value1.isValid() && !value2.isValid() )
|
||||
return false;
|
||||
|
||||
const auto type1 = qskMetaType( value1 );
|
||||
const auto type2 = qskMetaType( value2 );
|
||||
|
||||
if ( !value1.isValid() )
|
||||
return value2 != qskDefaultVariant( type2 );
|
||||
|
||||
if ( !value2.isValid() )
|
||||
return value1 != qskDefaultVariant( type1 );
|
||||
|
||||
if ( type1 != type2 )
|
||||
{
|
||||
if ( value1.canConvert( type2 ) )
|
||||
return value2 != qskConvertedVariant( value1, type2 );
|
||||
|
||||
if ( value2.canConvert( type1 ) )
|
||||
return value1 != qskConvertedVariant( value2, type1 );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return value1 != value2;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ class QSK_EXPORT QskVariantAnimator : public QskAnimator
|
|||
void setEndValue( const QVariant& );
|
||||
QVariant endValue() const;
|
||||
|
||||
static bool maybeInterpolate( const QVariant&, const QVariant& );
|
||||
static bool convertValues( QVariant&, QVariant& );
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
void advance( qreal value ) override;
|
||||
|
|
Loading…
Reference in New Issue