/****************************************************************************** * QSkinny - Copyright (C) 2016 Uwe Rathmann * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ #include "QskSkinHintTable.h" #include "QskAnimationHint.h" #include const QVariant QskSkinHintTable::invalidHint; inline const QVariant* qskResolvedHint( QskAspect aspect, const std::unordered_map< QskAspect, QVariant >& hints, QskAspect* resolvedAspect ) { const auto a = aspect; Q_FOREVER { auto it = hints.find( aspect ); if ( it != hints.cend() ) { if ( resolvedAspect ) *resolvedAspect = aspect; return &it->second; } #if 1 /* We intend to remove the obscure mechanism of resolving a hint by dropping the state bits ony by one in the future. Instead we will have methods in QskSkinHintTableEditor, that allow to set combinations of states in one call. */ if ( const auto topState = aspect.topState() ) { aspect.clearState( topState ); continue; } #else if ( aspect.hasState() ) { aspect.clearStates(); continue; } #endif if ( aspect.placement() ) { // clear the placement bits and restart aspect = a; aspect.setPlacement( QskAspect::NoPlacement ); continue; } return nullptr; } } QskSkinHintTable::QskSkinHintTable() { } QskSkinHintTable::QskSkinHintTable( const QskSkinHintTable& other ) : m_hints( nullptr ) , m_animatorCount( other.m_animatorCount ) , m_statefulCount( other.m_statefulCount ) { 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_statefulCount = other.m_statefulCount; 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 ) return *m_hints; static std::unordered_map< QskAspect, QVariant > dummyHints; return dummyHints; } #define QSK_ASSERT_COUNTER( x ) Q_ASSERT( x < std::numeric_limits< decltype( x ) >::max() ) bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint ) { if ( m_hints == nullptr ) m_hints = new HintMap(); auto it = m_hints->find( aspect ); if ( it == m_hints->end() ) { m_hints->emplace( aspect, skinHint ); if ( aspect.isAnimator() ) { m_animatorCount++; QSK_ASSERT_COUNTER( m_animatorCount ); } if ( aspect.hasStates() ) { m_statefulCount++; QSK_ASSERT_COUNTER( m_statefulCount ); } return true; } if ( it->second != skinHint ) { it->second = skinHint; return true; } return false; } #undef QSK_ASSERT_COUNTER bool QskSkinHintTable::removeHint( QskAspect aspect ) { if ( m_hints == nullptr ) return false; const bool erased = m_hints->erase( aspect ); if ( erased ) { if ( aspect.isAnimator() ) m_animatorCount--; if ( aspect.hasStates() ) m_statefulCount--; if ( m_hints->empty() ) { delete m_hints; m_hints = nullptr; } } return erased; } QVariant QskSkinHintTable::takeHint( QskAspect aspect ) { if ( m_hints ) { auto it = m_hints->find( aspect ); if ( it != m_hints->end() ) { const auto value = it->second; m_hints->erase( it ); if ( aspect.isAnimator() ) m_animatorCount--; if ( aspect.hasStates() ) m_statefulCount--; if ( m_hints->empty() ) { delete m_hints; m_hints = nullptr; } return value; } } return QVariant(); } void QskSkinHintTable::clear() { delete m_hints; m_hints = nullptr; m_animatorCount = 0; m_statefulCount = 0; } const QVariant* QskSkinHintTable::resolvedHint( QskAspect aspect, QskAspect* resolvedAspect ) const { if ( m_hints != nullptr ) return qskResolvedHint( aspect, *m_hints, resolvedAspect ); return nullptr; } QskAspect QskSkinHintTable::resolvedAspect( QskAspect aspect ) const { QskAspect a; if ( m_hints != nullptr ) qskResolvedHint( aspect, *m_hints, &a ); return a; } QskAspect QskSkinHintTable::resolvedAnimator( QskAspect aspect, QskAnimationHint& hint ) const { if ( m_hints && m_animatorCount > 0 ) { Q_FOREVER { auto it = m_hints->find( aspect ); if ( it != m_hints->cend() ) { hint = it->second.value< QskAnimationHint >(); return aspect; } if ( const auto topState = aspect.topState() ) aspect.clearState( topState ); else break; } } return QskAspect(); } QskAnimationHint QskSkinHintTable::animation( QskAspect aspect ) const { aspect.setAnimator( true ); return hint< QskAnimationHint >( aspect ); } bool QskSkinHintTable::setAnimation( QskAspect aspect, QskAnimationHint animation ) { aspect.setAnimator( true ); return setHint( aspect, animation ); } bool QskSkinHintTable::isResolutionMatching( QskAspect aspect1, QskAspect aspect2 ) const { if ( aspect1 == aspect2 ) return true; if ( aspect1.trunk() != aspect2.trunk() ) return false; if ( !hasStates() ) return false; const auto a1 = aspect1; const auto a2 = aspect2; Q_FOREVER { const auto s1 = aspect1.topState(); const auto s2 = aspect2.topState(); if ( s1 > s2 ) { if ( hasHint( aspect1 ) ) return false; aspect1.clearState( s1 ); } else if ( s2 > s1 ) { if ( hasHint( aspect2 ) ) return false; aspect2.clearState( s2 ); } else { if ( aspect1 == aspect2 ) { if ( hasHint( aspect1 ) ) return true; if ( s1 == 0 ) { if ( aspect1.placement() == QskAspect::NoPlacement ) return true; // clear the placement bits and restart with the initial state aspect1 = a1; aspect1.setPlacement( QskAspect::NoPlacement ); aspect2 = a2; aspect2.setPlacement( QskAspect::NoPlacement ); } } else { if ( hasHint( aspect1 ) || hasHint( aspect2 ) ) return false; } aspect1.clearState( s1 ); aspect2.clearState( s2 ); } } }