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()
|
QskSkinHintTable::~QskSkinHintTable()
|
||||||
{
|
{
|
||||||
delete m_hints;
|
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
|
const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const
|
||||||
{
|
{
|
||||||
if ( m_hints )
|
if ( m_hints )
|
||||||
|
|
|
@ -17,12 +17,8 @@ class QSK_EXPORT QskSkinHintTable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QskSkinHintTable();
|
QskSkinHintTable();
|
||||||
QskSkinHintTable( const QskSkinHintTable& other );
|
|
||||||
|
|
||||||
~QskSkinHintTable();
|
~QskSkinHintTable();
|
||||||
|
|
||||||
QskSkinHintTable& operator=( const QskSkinHintTable& );
|
|
||||||
|
|
||||||
bool setAnimation( QskAspect, QskAnimationHint );
|
bool setAnimation( QskAspect, QskAnimationHint );
|
||||||
QskAnimationHint animation( QskAspect ) const;
|
QskAnimationHint animation( QskAspect ) const;
|
||||||
|
|
||||||
|
@ -57,6 +53,8 @@ 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;
|
typedef std::unordered_map< QskAspect, QVariant > HintMap;
|
||||||
|
|
|
@ -20,6 +20,51 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#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
|
namespace
|
||||||
{
|
{
|
||||||
class UpdateInfo
|
class UpdateInfo
|
||||||
|
@ -40,157 +85,125 @@ namespace
|
||||||
int updateModes;
|
int updateModes;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimatorCandidate
|
class HintAnimator : public QskHintAnimator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AnimatorCandidate() = default;
|
inline HintAnimator( const QskControl* control, const QskAspect aspect,
|
||||||
|
const QVariant& value1, const QVariant& value2, QskAnimationHint hint )
|
||||||
inline AnimatorCandidate( QskAspect aspect,
|
|
||||||
const QVariant& from, const QVariant& to )
|
|
||||||
: aspect( aspect )
|
|
||||||
, from( from )
|
|
||||||
, to( to )
|
|
||||||
{
|
{
|
||||||
}
|
setAspect( aspect );
|
||||||
|
setStartValue( value1 );
|
||||||
|
setEndValue( value2 );
|
||||||
|
|
||||||
QskAspect aspect;
|
setDuration( hint.duration );
|
||||||
QVariant from;
|
setEasingCurve( hint.type );
|
||||||
QVariant to;
|
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(
|
Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator )
|
||||||
QskSkinTransition::Type mask,
|
|
||||||
const QskSkinHintTable& oldTable,
|
WindowAnimator::WindowAnimator( QQuickWindow* window )
|
||||||
const std::unordered_map< int, QskColorFilter >& oldFilters,
|
: m_window( window )
|
||||||
const QskSkinHintTable& newTable,
|
|
||||||
const std::unordered_map< int, QskColorFilter >& newFilters )
|
|
||||||
{
|
{
|
||||||
// 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;
|
return m_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start()
|
void WindowAnimator::start()
|
||||||
{
|
{
|
||||||
for ( auto& it : m_hintAnimatorMap )
|
for ( auto& it : m_animatorMap )
|
||||||
it.second.start();
|
it.second.start();
|
||||||
|
|
||||||
for ( auto& it : m_graphicFilterAnimatorMap )
|
for ( auto& it : m_graphicFilterAnimatorMap )
|
||||||
it.second.start();
|
it.second.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRunning() const
|
bool WindowAnimator::isRunning() const
|
||||||
|
{
|
||||||
|
if ( !m_animatorMap.empty() )
|
||||||
{
|
{
|
||||||
if ( !m_hintAnimatorMap.empty() )
|
const auto& animator = m_animatorMap.begin()->second;
|
||||||
{
|
|
||||||
const auto& animator = m_hintAnimatorMap.begin()->second;
|
|
||||||
if ( animator.isRunning() )
|
if ( animator.isRunning() )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -203,12 +216,12 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QVariant animatedHint( QskAspect aspect ) const
|
inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const
|
||||||
{
|
{
|
||||||
auto it = m_hintAnimatorMap.find( aspect );
|
auto it = m_animatorMap.find( aspect );
|
||||||
if ( it != m_hintAnimatorMap.cend() )
|
if ( it != m_animatorMap.cend() )
|
||||||
{
|
{
|
||||||
const auto& animator = it->second;
|
const auto& animator = it->second;
|
||||||
if ( animator.isRunning() )
|
if ( animator.isRunning() )
|
||||||
|
@ -216,10 +229,10 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QVariant animatedGraphicFilter( int graphicRole ) const
|
inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const
|
||||||
{
|
{
|
||||||
auto it = m_graphicFilterAnimatorMap.find( graphicRole );
|
auto it = m_graphicFilterAnimatorMap.find( graphicRole );
|
||||||
if ( it != m_graphicFilterAnimatorMap.cend() )
|
if ( it != m_graphicFilterAnimatorMap.cend() )
|
||||||
{
|
{
|
||||||
|
@ -229,22 +242,23 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addGraphicFilterAnimators(
|
void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint,
|
||||||
const QskAnimationHint& animatorHint,
|
const QskSkin* skin1, const QskSkin* skin2 )
|
||||||
const std::unordered_map< int, QskColorFilter >& oldFilters,
|
{
|
||||||
const std::unordered_map< int, QskColorFilter >& newFilters )
|
|
||||||
{
|
|
||||||
const QskColorFilter noFilter;
|
const QskColorFilter noFilter;
|
||||||
|
|
||||||
for ( auto it2 = newFilters.begin(); it2 != newFilters.end(); ++it2 )
|
const auto& filter1 = skin1->graphicFilters();
|
||||||
{
|
const auto& filter2 = skin2->graphicFilters();
|
||||||
auto it1 = oldFilters.find( it2->first );
|
|
||||||
if ( it1 == oldFilters.cend() )
|
|
||||||
it1 = oldFilters.find( 0 );
|
|
||||||
|
|
||||||
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;
|
const auto& f2 = it2->second;
|
||||||
|
|
||||||
if ( f1 != f2 )
|
if ( f1 != f2 )
|
||||||
|
@ -259,19 +273,20 @@ namespace
|
||||||
m_graphicFilterAnimatorMap.emplace( it2->first, animator );
|
m_graphicFilterAnimatorMap.emplace( it2->first, animator );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addAnimators( QQuickItem* item, const QskAnimationHint& animatorHint,
|
void WindowAnimator::addItemAspects( QQuickItem* item,
|
||||||
const QVector< AnimatorCandidate >& candidates, QskSkin* skin )
|
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
|
||||||
{
|
const QskSkin* skin1, const QskSkin* skin2 )
|
||||||
|
{
|
||||||
if ( !item->isVisible() )
|
if ( !item->isVisible() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( auto control = qskControlCast( item ) )
|
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
|
#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
|
||||||
|
@ -285,11 +300,11 @@ namespace
|
||||||
|
|
||||||
const auto children = item->childItems();
|
const auto children = item->childItems();
|
||||||
for ( auto child : children )
|
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 )
|
for ( auto& info : m_updateInfos )
|
||||||
{
|
{
|
||||||
if ( auto control = info.control )
|
if ( auto control = info.control )
|
||||||
|
@ -304,26 +319,81 @@ namespace
|
||||||
control->update();
|
control->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void WindowAnimator::addHints( const QskControl* control,
|
||||||
|
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
|
||||||
void addControlAnimators( QskControl* control, const QskAnimationHint& animatorHint,
|
const QskSkin* skin1, const QskSkin* skin2 )
|
||||||
const QVector< AnimatorCandidate >& candidates )
|
{
|
||||||
{
|
|
||||||
const auto subControls = control->subControls();
|
const auto subControls = control->subControls();
|
||||||
|
|
||||||
for ( const auto& candidate : candidates )
|
const auto& localTable = control->hintTable();
|
||||||
{
|
|
||||||
const auto aspect = candidate.aspect;
|
|
||||||
|
|
||||||
|
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 ( !aspect.isMetric() )
|
||||||
{
|
{
|
||||||
if ( !( control->flags() & QQuickItem::ItemHasContents ) )
|
if ( !( control->flags() & QQuickItem::ItemHasContents ) )
|
||||||
{
|
{
|
||||||
// while metrics might have an effect on layouts, we
|
// while metrics might have an effect on layouts, we
|
||||||
// can safely ignore others for controls without content
|
// ignore all others for controls without content
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,76 +402,44 @@ namespace
|
||||||
{
|
{
|
||||||
// The control uses subcontrol redirection, so we can assume it
|
// The control uses subcontrol redirection, so we can assume it
|
||||||
// is not interested in this subcontrol.
|
// is not interested in this subcontrol.
|
||||||
continue;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( subControl != QskAspect::Control )
|
if ( subControl == QskAspect::Control )
|
||||||
{
|
|
||||||
if ( !subControls.contains( subControl ) )
|
|
||||||
{
|
|
||||||
// the control is not interested in the aspect
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if ( !control->autoFillBackground() )
|
if ( !control->autoFillBackground() )
|
||||||
{
|
{
|
||||||
// no need to animate the background unless we show it
|
// no need to animate the background unless we show it
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
auto a = aspect;
|
|
||||||
a.setStates( control->skinStates() );
|
|
||||||
|
|
||||||
const auto requestState = control->hintStatus( a );
|
|
||||||
|
|
||||||
if ( requestState.source != QskSkinHintStatus::Skin )
|
|
||||||
{
|
{
|
||||||
// The control does not resolve the aspect from the skin.
|
if ( !subControls.contains( subControl ) )
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( aspect != requestState.aspect )
|
|
||||||
{
|
{
|
||||||
// the aspect was resolved to something else
|
// the control is not interested in the aspect
|
||||||
continue;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
addAnimator( control->window(), aspect,
|
|
||||||
candidate.from, candidate.to, animatorHint );
|
|
||||||
|
|
||||||
storeUpdateInfo( control, aspect );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addAnimator( QQuickWindow* window, const QskAspect aspect,
|
return true;
|
||||||
const QVariant& from, const QVariant& to, QskAnimationHint animationHint )
|
}
|
||||||
|
|
||||||
|
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 );
|
m_animatorMap.emplace( aspect,
|
||||||
if ( it != m_hintAnimatorMap.end() )
|
HintAnimator( control, aspect, value1, value2, hint ) );
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void storeUpdateInfo( QskControl* control, QskAspect aspect )
|
inline void WindowAnimator::storeUpdateInfo( const QskControl* control, QskAspect aspect )
|
||||||
{
|
{
|
||||||
UpdateInfo info;
|
UpdateInfo info;
|
||||||
info.control = control;
|
info.control = const_cast< QskControl* >( control );
|
||||||
|
|
||||||
info.updateModes = UpdateInfo::Update;
|
info.updateModes = UpdateInfo::Update;
|
||||||
if ( aspect.isMetric() )
|
if ( aspect.isMetric() )
|
||||||
|
@ -414,121 +452,94 @@ namespace
|
||||||
it->updateModes |= info.updateModes;
|
it->updateModes |= info.updateModes;
|
||||||
else
|
else
|
||||||
m_updateInfos.insert( it, info );
|
m_updateInfos.insert( it, info );
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickWindow* m_window;
|
ApplicationAnimator::~ApplicationAnimator()
|
||||||
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()
|
|
||||||
{
|
|
||||||
reset();
|
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 ( animator->window() == window )
|
||||||
{
|
return animator;
|
||||||
if ( group->window() == window )
|
|
||||||
return group;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add( AnimatorGroup* group )
|
void ApplicationAnimator::add( WindowAnimator* animator )
|
||||||
{
|
{
|
||||||
m_animatorGroups.push_back( group );
|
m_windowAnimators.push_back( animator );
|
||||||
}
|
}
|
||||||
|
|
||||||
void start()
|
void ApplicationAnimator::start()
|
||||||
{
|
{
|
||||||
m_connections[0] = QskAnimator::addAdvanceHandler(
|
m_connections[0] = QskAnimator::addAdvanceHandler(
|
||||||
this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection );
|
this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection );
|
||||||
|
|
||||||
m_connections[1] = QskAnimator::addCleanupHandler(
|
m_connections[1] = QskAnimator::addCleanupHandler(
|
||||||
this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection );
|
this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection );
|
||||||
|
|
||||||
for ( auto& group : m_animatorGroups )
|
for ( auto& animator : m_windowAnimators )
|
||||||
group->start();
|
animator->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset()
|
void ApplicationAnimator::reset()
|
||||||
{
|
{
|
||||||
qDeleteAll( m_animatorGroups );
|
qDeleteAll( m_windowAnimators );
|
||||||
m_animatorGroups.clear();
|
m_windowAnimators.clear();
|
||||||
|
|
||||||
disconnect( m_connections[0] );
|
disconnect( m_connections[0] );
|
||||||
disconnect( m_connections[1] );
|
disconnect( m_connections[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isRunning() const
|
inline bool ApplicationAnimator::isRunning() const
|
||||||
{
|
{
|
||||||
return !m_animatorGroups.empty();
|
return !m_windowAnimators.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Q_SLOTS:
|
void ApplicationAnimator::notify( QQuickWindow* window )
|
||||||
void notify( QQuickWindow* window )
|
{
|
||||||
|
for ( auto& animator : m_windowAnimators )
|
||||||
{
|
{
|
||||||
for ( auto& group : m_animatorGroups )
|
if ( animator->window() == window )
|
||||||
{
|
{
|
||||||
if ( group->window() == window )
|
animator->update();
|
||||||
{
|
|
||||||
group->update();
|
|
||||||
return;
|
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();
|
auto animator = *it;
|
||||||
it != m_animatorGroups.end(); ++it )
|
if ( animator->window() == window )
|
||||||
{
|
{
|
||||||
auto group = *it;
|
if ( !animator->isRunning() )
|
||||||
if ( group->window() == window )
|
|
||||||
{
|
|
||||||
if ( !group->isRunning() )
|
|
||||||
{
|
{
|
||||||
// The notification might be for other animators
|
// The notification might be for other animators
|
||||||
|
|
||||||
m_animatorGroups.erase( it );
|
m_windowAnimators.erase( it );
|
||||||
delete group;
|
delete animator;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( m_animatorGroups.empty() )
|
if ( m_windowAnimators.empty() )
|
||||||
reset();
|
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
|
class QskSkinTransition::PrivateData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -593,38 +604,20 @@ void QskSkinTransition::updateSkin( QskSkin*, QskSkin* )
|
||||||
|
|
||||||
void QskSkinTransition::process()
|
void QskSkinTransition::process()
|
||||||
{
|
{
|
||||||
auto skinFrom = m_data->skins[ 0 ];
|
qskApplicationAnimator->reset();
|
||||||
auto skinTo = m_data->skins[ 1 ];
|
|
||||||
|
|
||||||
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
|
if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) )
|
||||||
return;
|
{
|
||||||
|
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() )
|
if ( !candidates.isEmpty() )
|
||||||
|
@ -635,21 +628,19 @@ void QskSkinTransition::process()
|
||||||
|
|
||||||
for ( const auto window : windows )
|
for ( const auto window : windows )
|
||||||
{
|
{
|
||||||
if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) )
|
if ( auto w = qobject_cast< QQuickWindow* >( window ) )
|
||||||
{
|
{
|
||||||
if ( !quickWindow->isVisible() ||
|
if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) )
|
||||||
( qskEffectiveSkin( quickWindow ) != skinTo ) )
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto group = new AnimatorGroup( quickWindow );
|
auto animator = new WindowAnimator( w );
|
||||||
|
|
||||||
if ( doGraphicFilter )
|
if ( doGraphicFilter )
|
||||||
{
|
{
|
||||||
group->addGraphicFilterAnimators(
|
animator->addGraphicFilterAnimators(
|
||||||
m_data->animationHint, oldFilters,
|
m_data->animationHint, skin1, skin2 );
|
||||||
skinTo->graphicFilters() );
|
|
||||||
|
|
||||||
doGraphicFilter = false;
|
doGraphicFilter = false;
|
||||||
}
|
}
|
||||||
|
@ -659,21 +650,24 @@ void QskSkinTransition::process()
|
||||||
over the the item trees.
|
over the the item trees.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
group->addAnimators( quickWindow->contentItem(),
|
animator->addItemAspects( w->contentItem(),
|
||||||
m_data->animationHint, candidates, skinTo );
|
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()
|
bool QskSkinTransition::isRunning()
|
||||||
{
|
{
|
||||||
if ( qskSkinAnimator.exists() )
|
if ( qskApplicationAnimator.exists() )
|
||||||
return qskSkinAnimator->isRunning();
|
return qskApplicationAnimator->isRunning();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -681,10 +675,10 @@ bool QskSkinTransition::isRunning()
|
||||||
QVariant QskSkinTransition::animatedHint(
|
QVariant QskSkinTransition::animatedHint(
|
||||||
const QQuickWindow* window, QskAspect aspect )
|
const QQuickWindow* window, QskAspect aspect )
|
||||||
{
|
{
|
||||||
if ( qskSkinAnimator.exists() )
|
if ( qskApplicationAnimator.exists() )
|
||||||
{
|
{
|
||||||
if ( const auto group = qskSkinAnimator->animatorGroup( window ) )
|
if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
|
||||||
return group->animatedHint( aspect );
|
return animator->animatedHint( aspect );
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
@ -693,10 +687,10 @@ QVariant QskSkinTransition::animatedHint(
|
||||||
QVariant QskSkinTransition::animatedGraphicFilter(
|
QVariant QskSkinTransition::animatedGraphicFilter(
|
||||||
const QQuickWindow* window, int graphicRole )
|
const QQuickWindow* window, int graphicRole )
|
||||||
{
|
{
|
||||||
if ( qskSkinAnimator.exists() )
|
if ( qskApplicationAnimator.exists() )
|
||||||
{
|
{
|
||||||
if ( const auto group = qskSkinAnimator->animatorGroup( window ) )
|
if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
|
||||||
return group->animatedGraphicFilter( graphicRole );
|
return animator->animatedGraphicFilter( graphicRole );
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
|
@ -38,16 +38,6 @@ static inline bool qskIsControl( const QskSkinnable* skinnable )
|
||||||
return skinnable->metaObject()->inherits( &QskControl::staticMetaObject );
|
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,
|
static inline bool qskSetFlag( QskSkinnable* skinnable,
|
||||||
const QskAspect aspect, int flag )
|
const QskAspect aspect, int flag )
|
||||||
{
|
{
|
||||||
|
@ -1155,27 +1145,11 @@ void QskSkinnable::startHintTransition( QskAspect aspect,
|
||||||
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
|
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
|
||||||
return;
|
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 v1 = from;
|
||||||
auto v2 = to;
|
auto v2 = to;
|
||||||
|
|
||||||
if ( !v1.isValid() )
|
if ( !QskVariantAnimator::convertValues( v1, v2 ) )
|
||||||
{
|
|
||||||
v1 = qskTypedNullValue( v2 );
|
|
||||||
}
|
|
||||||
else if ( !v2.isValid() )
|
|
||||||
{
|
|
||||||
v2 = qskTypedNullValue( v1 );
|
|
||||||
}
|
|
||||||
else if ( v1.userType() != v2.userType() )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
|
if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,6 +76,31 @@ QSK_DECL_INSANE static inline QVariant qskInterpolate(
|
||||||
return f( from.constData(), to.constData(), progress );
|
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()
|
QskVariantAnimator::QskVariantAnimator()
|
||||||
: m_interpolator( nullptr )
|
: m_interpolator( nullptr )
|
||||||
{
|
{
|
||||||
|
@ -100,35 +125,60 @@ void QskVariantAnimator::setCurrentValue( const QVariant& value )
|
||||||
m_currentValue = 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()
|
void QskVariantAnimator::setup()
|
||||||
{
|
{
|
||||||
m_interpolator = nullptr;
|
m_interpolator = nullptr;
|
||||||
|
|
||||||
if ( m_startValue.userType() != m_endValue.userType() )
|
if ( convertValues( m_startValue, m_endValue ) )
|
||||||
{
|
{
|
||||||
/*
|
if ( m_startValue != m_endValue )
|
||||||
Convert one value so that the types are matching.
|
{
|
||||||
|
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
|
// all what has been registered by qRegisterAnimationInterpolator
|
||||||
m_interpolator = reinterpret_cast< void ( * )() >(
|
m_interpolator = reinterpret_cast< void ( * )() >(
|
||||||
QVariantAnimationPrivate::getInterpolator( type ) );
|
QVariantAnimationPrivate::getInterpolator( id ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_currentValue = m_interpolator ? m_startValue : m_endValue;
|
m_currentValue = m_interpolator ? m_startValue : m_endValue;
|
||||||
|
@ -150,3 +200,32 @@ void QskVariantAnimator::done()
|
||||||
{
|
{
|
||||||
m_interpolator = nullptr;
|
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& );
|
void setEndValue( const QVariant& );
|
||||||
QVariant endValue() const;
|
QVariant endValue() const;
|
||||||
|
|
||||||
|
static bool maybeInterpolate( const QVariant&, const QVariant& );
|
||||||
|
static bool convertValues( QVariant&, QVariant& );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void advance( qreal value ) override;
|
void advance( qreal value ) override;
|
||||||
|
|
Loading…
Reference in New Issue