/****************************************************************************** * QSkinny - Copyright (C) 2016 Uwe Rathmann * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ #include "QskAspect.h" #include #include #include #include #include #include #include #include static_assert( sizeof( QskAspect::Aspect ) == sizeof( quint64 ), "QskAspect::Aspect has to match quint64" ); namespace { using namespace QskAspect; using namespace std; struct StateInfo { State state; QByteArray name; }; struct AspectRegistry { QVector< QByteArray > subControlNames; unordered_map< const QMetaObject*, QVector< Subcontrol > > subControlTable; unordered_map< const QMetaObject*, QVector< StateInfo > > stateTable; }; } Q_GLOBAL_STATIC( AspectRegistry, qskAspectRegistry ) QskAspect::State QskAspect::registerState( const QMetaObject* metaObject, State state, const char* name ) { StateInfo info; info.state = state; info.name = name; auto& stateTable = qskAspectRegistry->stateTable; stateTable[ metaObject ] += info; return state; } QskAspect::Subcontrol QskAspect::nextSubcontrol( const QMetaObject* metaObject, const char* name ) { auto& names = qskAspectRegistry->subControlNames; auto& hashTable = qskAspectRegistry->subControlTable; Q_ASSERT_X( names.size() <= LastSubcontrol, "QskAspect", "There are no free subcontrol aspects; please modify your" " application to declare fewer aspects, or increase the mask size of" " QskAspect::Subcontrol in QskAspect.h." ); names += name; // 0 is QskAspect::Control, so we have to start with 1 const auto subControl = static_cast< Subcontrol >( names.size() ); hashTable[ metaObject ] += subControl; return subControl; } QByteArray QskAspect::subControlName( Subcontrol subControl ) { const auto& names = qskAspectRegistry->subControlNames; const int index = subControl; if ( index > 0 && index < names.size() ) return names[ index - 1 ]; return QByteArray(); } QVector< QByteArray > QskAspect::subControlNames( const QMetaObject* metaObject ) { const auto& names = qskAspectRegistry->subControlNames; if ( metaObject == nullptr ) return names; const auto subControls = QskAspect::subControls( metaObject ); QVector< QByteArray > subControlNames; subControlNames.reserve( subControls.size() ); for ( auto subControl : subControls ) subControlNames += names[ subControl ]; return subControlNames; } QVector< QskAspect::Subcontrol > QskAspect::subControls( const QMetaObject* metaObject ) { if ( metaObject ) { const auto& hashTable = qskAspectRegistry->subControlTable; auto it = hashTable.find( metaObject ); if ( it != hashTable.end() ) return it->second; } return QVector< Subcontrol >(); } #ifndef QT_NO_DEBUG_STREAM static QByteArray qskEnumString( const char* name, int value ) { const QMetaObject& mo = QskAspect::staticMetaObject; const QMetaEnum metaEnum = mo.enumerator( mo.indexOfEnumerator( name ) ); const char* key = metaEnum.valueToKey( value ); return key ? QByteArray( key ) : QString::number( value ).toLatin1(); } static QByteArray qskStateKey( const QMetaObject* metaObject, quint16 state ) { const auto& stateTable = qskAspectRegistry->stateTable; for ( auto mo = metaObject; mo != nullptr; mo = mo->superClass() ) { const auto it = stateTable.find( mo ); if ( it != stateTable.end() ) { for ( const auto& info : it->second ) { if ( info.state == state ) return info.name; } } } return QByteArray(); } static QByteArray qskStateString( const QMetaObject* metaObject, QskAspect::State state ) { if ( state == 0 ) { return "NoState"; } if ( metaObject == nullptr ) { const std::bitset< 16 > stateBits( state ); return stateBits.to_string().c_str(); } // not the fastest implementation, but as it is for debugging only QByteArray stateString; bool first = true; for ( int i = 0; i < 16; i++ ) { const quint16 mask = 1 << i; if ( state & mask ) { if ( first ) first = false; else stateString += ", "; const auto key = qskStateKey( metaObject, mask ); if ( key.isEmpty() ) { const std::bitset< 16 > stateBits( state ); stateString += stateBits.to_string().c_str(); } else { stateString += key; } } } return stateString; } static inline QDebug qskDebugEnum( QDebug debug, const char* name, int value ) { qt_QMetaEnum_debugOperator( debug, value, &QskAspect::staticMetaObject, name ); return debug; } QDebug operator<<( QDebug debug, const QskAspect::Type& type ) { return qskDebugEnum( debug, "Type", type ); } QDebug operator<<( QDebug debug, const QskAspect::FlagPrimitive& primitive ) { return qskDebugEnum( debug, "FlagPrimitive", primitive ); } QDebug operator<<( QDebug debug, const QskAspect::ColorPrimitive& primitive ) { return qskDebugEnum( debug, "ColorPrimitive", primitive ); } QDebug operator<<( QDebug debug, const QskAspect::MetricPrimitive& primitive ) { return qskDebugEnum( debug, "MetricPrimitive", primitive ); } QDebug operator<<( QDebug debug, const QskAspect::Subcontrol& subControl ) { QDebugStateSaver saver( debug ); debug.nospace(); debug << "QskAspect::Subcontrol" << '('; debug << subControlName( subControl ); debug << ')'; return debug; } QDebug operator<<( QDebug debug, const QskAspect::Placement& placement ) { qskDebugEnum( debug, "Placement", placement ); return debug; } QDebug operator<<( QDebug debug, const QskAspect::State& state ) { qskDebugState( debug, nullptr, state ); return debug; } QDebug operator<<( QDebug debug, const QskAspect::Aspect& aspect ) { qskDebugAspect( debug, nullptr, aspect ); return debug; } void qskDebugState( QDebug debug, const QMetaObject* metaObject, QskAspect::State state ) { QDebugStateSaver saver(debug); debug.resetFormat(); debug.noquote(); debug.nospace(); debug << "QskAspect::State( " << qskStateString( metaObject, state ) << " )"; } void qskDebugAspect( QDebug debug, const QMetaObject* metaObject, QskAspect::Aspect aspect ) { using namespace QskAspect; QDebugStateSaver saver(debug); debug.resetFormat(); debug.noquote(); debug.nospace(); debug << "QskAspect( "; const auto subControlName = QskAspect::subControlName( aspect.subControl() ); if ( subControlName.isEmpty() ) debug << QByteArrayLiteral( "NoSubcontrol" ); else debug << subControlName; debug << ", " << qskEnumString( "Type", aspect.type() ); if ( aspect.isAnimator() ) debug << "(A)"; switch( aspect.type() ) { case Color: { if ( aspect.colorPrimitive() != 0 ) debug << ", " << qskEnumString( "ColorPrimitive", aspect.colorPrimitive() ); break; } case Metric: { if ( aspect.metricPrimitive() != 0 ) debug << ", " << qskEnumString( "MetricPrimitive", aspect.metricPrimitive() ); break; } default: { if ( aspect.flagPrimitive() != 0 ) debug << ", " << qskEnumString( "FlagPrimitive", aspect.flagPrimitive() ); } } if ( aspect.placement() != QskAspect::Preserved ) debug << ", " << qskEnumString( "Placement", aspect.placement() ); if ( aspect.state() ) debug << ", " << qskStateString( metaObject, aspect.state() ); debug << " )"; } #endif const char* QskAspect::Aspect::toPrintable() const { QString tmp; QDebug debug( &tmp ); debug << *this; // we should find a better impementation static QByteArray bytes[10]; static int counter = 0; counter = ( counter + 1 ) % 10; bytes[counter] = tmp.toUtf8(); return bytes[counter].constData(); } QskAspect::State QskAspect::Aspect::topState() const { if ( m_bits.states == NoState ) return NoState; /* Before Qt 5.8 qCountLeadingZeroBits does not use _BitScanReverse - we can live with this. */ const auto n = qCountLeadingZeroBits( static_cast< quint16 >( m_bits.states ) ); return static_cast< QskAspect::State >( 1 << ( 15 - n ) ); } #include "moc_QskAspect.cpp"