6#include "QskSkinTransition.h"
7#include "QskColorFilter.h"
10#include "QskAnimationHint.h"
11#include "QskHintAnimator.h"
13#include "QskSkinHintTable.h"
14#include "QskFontRole.h"
17#include <qglobalstatic.h>
18#include <qguiapplication.h>
33#include "QskRadioBox.h"
34#include "QskSegmentedBar.h"
35#include "QskListView.h"
39 if ( qobject_cast< const QskMenu* >( control ) )
40 return QskMenu::Selected;
42 if ( qobject_cast< const QskRadioBox* >( control ) )
43 return QskRadioBox::Selected;
45 if ( qobject_cast< const QskSegmentedBar* >( control ) )
46 return QskSegmentedBar::Selected;
48 if ( qobject_cast< const QskListView* >( control ) )
49 return QskListView::Selected;
58 return skin->hintTable().hints().isSharedWith( hintTable.hints() );
61static void qskSendStyleEventRecursive( QQuickItem* item )
63 QEvent event( QEvent::StyleChange );
64 QCoreApplication::sendEvent( item, &event );
66 const auto children = item->childItems();
67 for (
auto child : children )
68 qskSendStyleEventRecursive( child );
71static void qskAddCandidates(
const QskSkinTransition::Type mask,
72 const QHash< QskAspect, QVariant >& hints, QSet< QskAspect >& candidates )
74 for (
auto it = hints.constBegin(); it != hints.constEnd(); ++it )
76 const auto aspect = it.key().trunk();
78 if ( aspect.isAnimator() )
81 bool isCandidate =
false;
83 switch( aspect.type() )
85 case QskAspect::NoType:
89 isCandidate = mask & QskSkinTransition::Color;
93 isCandidate = mask & QskSkinTransition::Metric;
99 isCandidate = mask & QskSkinTransition::Color;
104 isCandidate = mask & QskSkinTransition::Metric;
110 candidates += aspect;
125 static inline bool compare(
const UpdateInfo& i1,
const UpdateInfo& i2 )
127 return i1.control.data() < i2.control.data();
130 QPointer< QskControl > control;
141 setStartValue( value1 );
142 setEndValue( value2 );
144 setDuration( hint.duration );
145 setEasingCurve( hint.type );
146 setUpdateFlags( hint.updateFlags );
148 setWindow( control->window() );
155 WindowAnimator( QQuickWindow* =
nullptr );
157 const QQuickWindow* window()
const;
160 bool isRunning()
const;
162 QVariant animatedHint(
QskAspect )
const;
163 QVariant animatedGraphicFilter(
int graphicRole )
const;
164 QVariant animatedFontSize(
const QskFontRole& )
const;
167 const QHash< int, QskColorFilter >&,
const QHash< int, QskColorFilter >& );
170 const QHash< QskFontRole, QFont >&,
const QHash< QskFontRole, QFont >& );
172 void addItemAspects( QQuickItem*,
181 const QVector< QskAspect::Subcontrol >&,
QskAspect )
const;
192 QQuickWindow* m_window;
194 QHash< QskAspect, HintAnimator > m_animatorMap;
195 QHash< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
196 QHash< QskFontRole, QskVariantAnimator > m_fontSizeAnimatorMap;
198 std::vector< UpdateInfo > m_updateInfos;
201 class ApplicationAnimator :
public QObject
206 ~ApplicationAnimator()
override;
208 WindowAnimator* windowAnimator(
const QQuickWindow* );
210 void add( WindowAnimator* );
214 bool isRunning()
const;
218 void notify( QQuickWindow* );
219 void cleanup( QQuickWindow* );
229 std::vector< WindowAnimator* > m_windowAnimators;
230 QMetaObject::Connection m_connections[2];
234Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator )
236WindowAnimator::WindowAnimator( QQuickWindow* window )
241inline const QQuickWindow* WindowAnimator::window()
const
246void WindowAnimator::start()
248 for (
auto& it : m_animatorMap )
251 for (
auto& it : m_graphicFilterAnimatorMap )
254 for (
auto& it : m_fontSizeAnimatorMap )
258bool WindowAnimator::isRunning()
const
260 if ( !m_animatorMap.empty() )
262 const auto& animator = m_animatorMap.constBegin().value();
263 if ( animator.isRunning() )
267 if ( !m_graphicFilterAnimatorMap.empty() )
269 const auto& animator = m_graphicFilterAnimatorMap.constBegin().value();
270 if ( animator.isRunning() )
274 if ( !m_fontSizeAnimatorMap.empty() )
276 const auto& animator = m_fontSizeAnimatorMap.constBegin().value();
277 if ( animator.isRunning() )
284inline QVariant WindowAnimator::animatedHint(
QskAspect aspect )
const
286 auto it = m_animatorMap.constFind( aspect );
287 if ( it != m_animatorMap.constEnd() )
289 const auto& animator = it.value();
290 if ( animator.isRunning() )
291 return animator.currentValue();
297inline QVariant WindowAnimator::animatedGraphicFilter(
int graphicRole )
const
299 auto it = m_graphicFilterAnimatorMap.constFind( graphicRole );
300 if ( it != m_graphicFilterAnimatorMap.constEnd() )
302 const auto& animator = it.value();
303 if ( animator.isRunning() )
304 return animator.currentValue();
310inline QVariant WindowAnimator::animatedFontSize(
const QskFontRole& fontRole )
const
312 auto it = m_fontSizeAnimatorMap.constFind( fontRole );
313 if ( it != m_fontSizeAnimatorMap.constEnd() )
315 const auto& animator = it.value();
316 if ( animator.isRunning() )
317 return animator.currentValue();
323void WindowAnimator::addGraphicFilterAnimators(
325 const QHash< int, QskColorFilter >& filters1,
326 const QHash< int, QskColorFilter >& filters2 )
330 for (
auto it2 = filters2.constBegin(); it2 != filters2.constEnd(); ++it2 )
332 auto it1 = filters1.constFind( it2.key() );
333 if ( it1 == filters1.constEnd() )
334 it1 = filters1.constFind( 0 );
336 const auto& f1 = ( it1 != filters1.constEnd() ) ? it1.value() : noFilter;
337 const auto& f2 = it2.value();
342 animator.setWindow( m_window );
343 animator.setDuration( animatorHint.duration );
344 animator.setEasingCurve( animatorHint.type );
345 animator.setStartValue( QVariant::fromValue( f1 ) );
346 animator.setEndValue( QVariant::fromValue( f2 ) );
348 m_graphicFilterAnimatorMap.insert( it2.key(), animator );
354extern QFont qskResolvedFont(
355 const QHash< QskFontRole, QFont >&,
const QskFontRole& );
357void WindowAnimator::addFontSizeAnimators(
359 const QHash< QskFontRole, QFont >& fonts1,
360 const QHash< QskFontRole, QFont >& fonts2 )
362 for (
int i = 0; i <= QskFontRole::Display; i++ )
364 for (
int j = 0; j <= QskFontRole::VeryHigh; j++ )
367 static_cast< QskFontRole::Category
>( i ),
368 static_cast< QskFontRole::Emphasis
>( j )
371 const auto size1 = qskResolvedFont( fonts1, fontRole ).pixelSize();
372 const auto size2 = qskResolvedFont( fonts2, fontRole ).pixelSize();
374 if ( ( size1 > 0 ) && ( size2 > 0 ) && ( size1 != size2 ) )
377 animator.setWindow( m_window );
378 animator.setDuration( animatorHint.duration );
379 animator.setEasingCurve( animatorHint.type );
380 animator.setStartValue( QVariant::fromValue( size1 ) );
381 animator.setEndValue( QVariant::fromValue( size2 ) );
383 m_fontSizeAnimatorMap.insert( fontRole, animator );
389void WindowAnimator::addItemAspects( QQuickItem* item,
393 if (
auto control = qskControlCast( (
const QQuickItem* )item ) )
400 const auto& localTable = control->
hintTable();
402 for (
auto aspect : candidates )
404 if ( isControlAffected( control, subControls, aspect ) )
407 aspect.
setStates( control->skinStates() );
408 aspect.setSection( control->section() );
410 if ( !localTable.resolvedHint( aspect ) )
411 addHint( control, animatorHint, aspect, table1, table2 );
413 if (
auto state = qskSelectedSampleState( control ) )
416 if ( !localTable.resolvedHint( aspect ) )
417 addHint( control, animatorHint, aspect, table1, table2 );
433 const auto children = item->childItems();
434 for (
auto child : children )
435 addItemAspects( child, animatorHint, candidates, table1, table2 );
438void WindowAnimator::update()
440 for (
auto& info : m_updateInfos )
442 if (
auto control = info.control )
444 if ( info.updateModes & UpdateInfo::Polish )
450 if ( info.updateModes & UpdateInfo::Update )
456void WindowAnimator::addHint(
const QskControl* control,
462 const auto v1 = table1.resolvedHint( aspect, &r1 );
463 const auto v2 = table2.resolvedHint( aspect, &r2 );
467 if ( r1.section() == r2.section() )
468 aspect.setSection( r2.section() );
476 bool accecptIdentity =
false;
477 if ( aspect != aspect.
trunk() )
485 accecptIdentity =
true;
488 if ( QskVariantAnimator::maybeInterpolate( *v1, *v2, accecptIdentity ) )
490 storeAnimator( control, aspect, *v1, *v2, animatorHint );
491 storeUpdateInfo( control, aspect );
499 storeAnimator( control, aspect, *v1, QVariant(), animatorHint );
500 storeUpdateInfo( control, aspect );
507 storeAnimator( control, aspect, QVariant(), *v2, animatorHint );
508 storeUpdateInfo( control, aspect );
512inline bool WindowAnimator::isControlAffected(
const QskControl* control,
513 const QVector< QskAspect::Subcontrol >& subControls,
const QskAspect aspect )
const
517 if ( !( control->flags() & QQuickItem::ItemHasContents ) )
534 if ( subControl != QskAspect::NoSubcontrol )
536 if ( !subControls.contains( subControl ) )
546inline void WindowAnimator::storeAnimator(
550 if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() )
552 m_animatorMap.insert( aspect,
553 HintAnimator( control, aspect, value1, value2, hint ) );
560 info.control =
const_cast< QskControl*
>( control );
562 info.updateModes = UpdateInfo::Update;
564 info.updateModes |= UpdateInfo::Polish;
566 auto it = std::lower_bound(
567 m_updateInfos.begin(), m_updateInfos.end(), info, UpdateInfo::compare );
569 if ( ( it != m_updateInfos.end() ) && ( it->control == info.control ) )
570 it->updateModes |= info.updateModes;
572 m_updateInfos.insert( it, info );
575ApplicationAnimator::~ApplicationAnimator()
580inline WindowAnimator* ApplicationAnimator::windowAnimator(
const QQuickWindow* window )
584 for (
auto animator : m_windowAnimators )
586 if ( animator->window() == window )
594void ApplicationAnimator::add( WindowAnimator* animator )
596 m_windowAnimators.push_back( animator );
599void ApplicationAnimator::start()
601 m_connections[0] = QskAnimator::addAdvanceHandler(
602 this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection );
604 m_connections[1] = QskAnimator::addCleanupHandler(
605 this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection );
607 for (
auto& animator : m_windowAnimators )
611void ApplicationAnimator::reset()
613 qDeleteAll( m_windowAnimators );
614 m_windowAnimators.clear();
616 disconnect( m_connections[0] );
617 disconnect( m_connections[1] );
620inline bool ApplicationAnimator::isRunning()
const
622 return !m_windowAnimators.empty();
625void ApplicationAnimator::notify( QQuickWindow* window )
627 for (
auto& animator : m_windowAnimators )
629 if ( animator->window() == window )
637void ApplicationAnimator::cleanup( QQuickWindow* window )
639 for (
auto it = m_windowAnimators.begin();
640 it != m_windowAnimators.end(); ++it )
643 if ( animator->window() == window )
645 if ( !animator->isRunning() )
649 m_windowAnimators.erase( it );
654 qskSendStyleEventRecursive( window->contentItem() );
660 if ( m_windowAnimators.empty() )
664class QskSkinTransition::PrivateData
670 QHash< int, QskColorFilter > graphicFilters;
671 QHash< QskFontRole, QFont > fontTable;
674 Type mask = QskSkinTransition::AllTypes;
677QskSkinTransition::QskSkinTransition()
678 : m_data( new PrivateData() )
682QskSkinTransition::~QskSkinTransition()
686void QskSkinTransition::setMask( Type type )
691QskSkinTransition::Type QskSkinTransition::mask()
const
696void QskSkinTransition::setSourceSkin(
const QskSkin* skin )
698 auto& tables = m_data->tables[ 0 ];
700 tables.hintTable = skin->hintTable();
701 tables.graphicFilters = skin->graphicFilters();
702 tables.fontTable = skin->fontTable();
705void QskSkinTransition::setTargetSkin(
const QskSkin* skin )
707 auto& tables = m_data->tables[ 1 ];
709 tables.hintTable = skin->hintTable();
710 tables.graphicFilters = skin->graphicFilters();
711 tables.fontTable = skin->fontTable();
716 qskApplicationAnimator->reset();
718 const auto& table1 = m_data->tables[ 0 ].hintTable;
719 const auto& table2 = m_data->tables[ 1 ].hintTable;
721 const auto& graphicFilters1 = m_data->tables[ 0 ].graphicFilters;
722 const auto& graphicFilters2 = m_data->tables[ 1 ].graphicFilters;
724 const auto& fontTable1 = m_data->tables[ 0 ].fontTable;
725 const auto& fontTable2 = m_data->tables[ 1 ].fontTable;
727 QSet< QskAspect > candidates;
729 if ( ( animationHint.duration > 0 ) && ( m_data->mask != 0 ) )
731 qskAddCandidates( m_data->mask, table1.hints(), candidates );
732 qskAddCandidates( m_data->mask, table2.hints(), candidates );
735 if ( !candidates.isEmpty() )
737 bool doGraphicFilter = m_data->mask & QskSkinTransition::Color;
738 bool doFont = m_data->mask & QskSkinTransition::Metric;
740 const auto windows = qGuiApp->topLevelWindows();
742 for (
const auto window : windows )
744 if (
auto w = qobject_cast< QQuickWindow* >( window ) )
746 if ( !w->isVisible() )
749 if ( !qskHasHintTable( qskEffectiveSkin( w ), table2 ) )
752 auto animator =
new WindowAnimator( w );
754 if ( doGraphicFilter )
756 animator->addGraphicFilterAnimators( animationHint,
757 graphicFilters1, graphicFilters2 );
759 doGraphicFilter =
false;
764 animator->addFontSizeAnimators( animationHint,
765 fontTable1, fontTable2 );
773 animator->addItemAspects( w->contentItem(),
774 animationHint, candidates, table1, table2 );
776 qskApplicationAnimator->add( animator );
780 qskApplicationAnimator->start();
784bool QskSkinTransition::isRunning()
786 if ( qskApplicationAnimator.exists() )
787 return qskApplicationAnimator->isRunning();
792QVariant QskSkinTransition::animatedHint(
793 const QQuickWindow* window,
QskAspect aspect )
795 if ( qskApplicationAnimator.exists() )
797 if (
const auto animator = qskApplicationAnimator->windowAnimator( window ) )
798 return animator->animatedHint( aspect );
804QVariant QskSkinTransition::animatedGraphicFilter(
805 const QQuickWindow* window,
int graphicRole )
807 if ( qskApplicationAnimator.exists() )
809 if (
const auto animator = qskApplicationAnimator->windowAnimator( window ) )
810 return animator->animatedGraphicFilter( graphicRole );
816QVariant QskSkinTransition::animatedFontSize(
817 const QQuickWindow* window,
const QskFontRole& fontRole )
819 if ( qskApplicationAnimator.exists() )
821 if (
const auto animator = qskApplicationAnimator->windowAnimator( window ) )
822 return animator->animatedFontSize( fontRole );
828#include "QskSkinTransition.moc"
Lookup key for a QskSkinHintTable.
void setStates(States) noexcept
constexpr bool isMetric() const noexcept
void addStates(States) noexcept
constexpr QskAspect trunk() const noexcept
constexpr Subcontrol subControl() const noexcept
constexpr Variation variation() const noexcept
constexpr States states() const noexcept
void setVariation(Variation) noexcept
Base class of all controls.
QVector< QskAspect::Subcontrol > subControls() const
bool isInitiallyPainted() const
const QskSkinHintTable & hintTable() const
Accessor for local skin hint table.
QskSkin * effectiveSkin() const
virtual QskAspect::Variation effectiveVariation() const
QskAspect::Subcontrol effectiveSubcontrol(QskAspect::Subcontrol) const