6#include "QskSkinManager.h"
7#include "QskSkinFactory.h"
9#include "QskSkinTransition.h"
10#include "QskAnimationHint.h"
13#include <qglobalstatic.h>
14#include <qjsonarray.h>
15#include <qjsonobject.h>
17#include <qpluginloader.h>
21#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
22#include <qguiapplication.h>
23#include <qstylehints.h>
34#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
35 connect( QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged,
36 this, &SkinManager::updateColorScheme );
41#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
42 void updateColorScheme( Qt::ColorScheme scheme )
44 if ( QGuiApplication::desktopSettingsAware() )
46 skin()->setColorScheme(
47 static_cast< QskSkin::ColorScheme
>( scheme ) );
54Q_GLOBAL_STATIC( SkinManager, qskGlobalSkinManager )
56static QStringList qskPathList(
const char* envName )
58 const auto env = qgetenv( envName );
62 const auto name = QFile::decodeName( env );
63 return name.split( QDir::listSeparator(), Qt::SkipEmptyParts );
66static inline QString qskResolvedPath(
const QString& path )
68 return QDir( path ).canonicalPath();
78 class FactoryLoader final :
public QPluginLoader
81 FactoryLoader( QObject* parent =
nullptr )
82 : QPluginLoader( parent )
86 bool setPlugin(
const QString& fileName )
88 QPluginLoader::setFileName( fileName );
95 const QLatin1String TokenInterfaceId(
"IID" );
96 const QLatin1String TokenData(
"MetaData" );
97 const QLatin1String TokenFactoryId(
"FactoryId" );
98 const QLatin1String TokenSkins(
"Skins" );
100 const QLatin1String InterfaceId( QskSkinFactoryIID );
102 const auto pluginData = metaData();
103 if ( pluginData.value( TokenInterfaceId ) == InterfaceId )
105 const auto factoryData = pluginData.value( TokenData ).toObject();
107 m_factoryId = factoryData.value( TokenFactoryId ).toString().toLower();
108 if ( m_factoryId.isEmpty() )
112 m_factoryId = QStringLiteral(
"skin_factory_" ) + QString::number( i++ );
115 const auto skins = factoryData.value( TokenSkins ).toArray();
117 for (
const auto& skin : skins )
118 m_skinNames += skin.toString();
121 return !m_skinNames.isEmpty();
124 inline QString factoryId()
const
131 auto factory = qobject_cast< QskSkinFactory* >( QPluginLoader::instance() );
134 factory->setParent(
nullptr );
135 factory->setObjectName( m_factoryId );
141 inline QStringList skinNames()
const
147 void setFileName(
const QString& ) =
delete;
148 QObject* instance() =
delete;
151 QStringList m_skinNames;
172 if ( factory && factory->parent() ==
nullptr )
181 FactoryLoader* loader;
182 QPointer< QskSkinFactory > factory;
195 m_factoryMap.clear();
203 const auto it = m_skinMap.constFind( skinName );
204 if ( it != m_skinMap.constEnd() )
206 auto it2 = m_factoryMap.find( it.value() );
207 if ( it2 != m_factoryMap.end() )
209 auto& data = it2.value();
210 if ( ( data.factory ==
nullptr ) && data.loader !=
nullptr )
211 data.factory = data.loader->factory();
220 QStringList skinNames()
const
223 const_cast< FactoryMap*
>( this )->rebuild();
228 void insertFactory( FactoryLoader* loader )
230 auto& data = m_factoryMap[ loader->factoryId() ];
232 if ( data.loader != loader )
235 data.loader = loader;
242 void insertFactory(
const QString& factoryId,
QskSkinFactory* factory )
244 auto& data = m_factoryMap[ factoryId ];
246 if ( data.factory != factory )
249 data.factory = factory;
257 void removeFactory(
const QString& factoryId )
259 const auto itFactory = m_factoryMap.find( factoryId );
260 if ( itFactory == m_factoryMap.end() )
263 m_factoryMap.erase( itFactory );
267 for (
auto it = m_skinMap.constBegin();
268 it != m_skinMap.constEnd(); ++it )
270 if ( it.key() == factoryId )
285 inline bool hasFactory(
const QString& factoryId )
const
287 return m_factoryMap.contains( factoryId );
297 for (
auto it = m_factoryMap.constBegin(); it != m_factoryMap.constEnd(); ++it )
299 const auto& data = it.value();
301 if ( data.loader ==
nullptr && data.factory )
302 rebuild( it.key(), data.factory->skinNames() );
306 for (
auto it = m_factoryMap.constBegin(); it != m_factoryMap.constEnd(); ++it )
308 const auto& data = it.value();
310 rebuild( it.key(), data.loader->skinNames() );
316 void rebuild(
const QString& factoryId,
const QStringList& skinNames )
318 for (
const auto& name : skinNames )
320 if ( !m_skinMap.contains( name ) )
322 m_skinMap.insert( name, factoryId );
328 QMap< QString, Data > m_factoryMap;
329 QMap< QString, QString > m_skinMap;
330 QStringList m_skinNames;
336class QskSkinManager::PrivateData
340 : pluginsRegistered( false )
344 inline void ensurePlugins()
346 if ( !pluginsRegistered )
348 for (
const auto& path : std::as_const( pluginPaths ) )
349 registerPlugins( path + QStringLiteral(
"/skins" ) );
351 pluginsRegistered =
true;
355 void registerPlugins(
const QString& path )
365 FactoryLoader* loader =
nullptr;
367 const auto dirEntries = dir.entryList( QDir::Files );
369 for (
const auto& fileName : dirEntries )
371 if ( loader ==
nullptr )
372 loader =
new FactoryLoader();
374 bool ok = loader->setPlugin( dir.absoluteFilePath( fileName ) );
375 if ( ok && !factoryMap.hasFactory( loader->factoryId() ) )
377 factoryMap.insertFactory( loader );
386 QStringList pluginPaths;
387 FactoryMap factoryMap;
389 QPointer< QskSkin > skin;
392 bool pluginsRegistered : 1;
397 return qskGlobalSkinManager;
400QskSkinManager::QskSkinManager()
401 : m_data( new PrivateData() )
403 setPluginPaths( qskPathList(
"QSK_PLUGIN_PATH" ) +
404 qskPathList(
"QT_PLUGIN_PATH" ) );
407QskSkinManager::~QskSkinManager()
411void QskSkinManager::addPluginPath(
const QString& path )
413 const auto pluginPath = qskResolvedPath( path );
415 if ( !pluginPath.isEmpty() && !pluginPath.contains( pluginPath ) )
417 m_data->pluginPaths += pluginPath;
419 if ( m_data->pluginsRegistered )
420 m_data->registerPlugins( pluginPath );
424void QskSkinManager::removePluginPath(
const QString& path )
426 const auto pluginPath = qskResolvedPath( path );
428 if ( m_data->pluginPaths.removeOne( pluginPath ) )
430 if ( m_data->pluginsRegistered )
432 m_data->factoryMap.reset();
433 m_data->pluginsRegistered =
false;
438void QskSkinManager::setPluginPaths(
const QStringList& paths )
440 m_data->pluginPaths.clear();
442 QSet< QString > pathSet;
443 QStringList pluginPaths;
445 for (
const auto& path : paths )
447 const auto pluginPath = qskResolvedPath( path );
448 if ( !pluginPath.isEmpty() && !pathSet.contains( pluginPath ) )
450 pathSet += pluginPath;
451 pluginPaths += pluginPath;
455 if ( pluginPaths != m_data->pluginPaths )
457 m_data->pluginPaths = pluginPaths;
458 m_data->factoryMap.reset();
459 m_data->pluginsRegistered =
false;
463QStringList QskSkinManager::pluginPaths()
const
465 return m_data->pluginPaths;
468void QskSkinManager::registerFactory(
471 if ( factoryId.isEmpty() || factory ==
nullptr )
479 m_data->factoryMap.insertFactory( factoryId.toLower(), factory );
482void QskSkinManager::unregisterFactory(
const QString& factoryId )
484 if ( factoryId.isEmpty() )
492 m_data->ensurePlugins();
493 m_data->factoryMap.removeFactory( factoryId.toLower() );
496void QskSkinManager::unregisterFactories()
498 m_data->factoryMap.reset();
501QStringList QskSkinManager::skinNames()
const
503 m_data->ensurePlugins();
504 return m_data->factoryMap.skinNames();
507QskSkin* QskSkinManager::createSkin(
508 const QString& skinName, QskSkin::ColorScheme colorScheme )
const
510 m_data->ensurePlugins();
512 auto& map = m_data->factoryMap;
514 auto name = skinName;
516 auto factory = map.factory( name );
517 if ( factory ==
nullptr )
519 const auto names = map.skinNames();
520 if ( !names.isEmpty() )
522 name = names.first();
523 factory = map.factory( name );
531 skin = factory->createSkin( name );
534 skin->setObjectName( name );
536 if ( colorScheme == QskSkin::UnknownScheme )
538#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
539 colorScheme =
static_cast< QskSkin::ColorScheme
>(
540 QGuiApplication::styleHints()->colorScheme() );
542 colorScheme = QskSkin::LightScheme;
546 skin->setColorScheme( colorScheme );
553void QskSkinManager::setSkin(
QskSkin* skin )
555 if ( m_data->skin == skin )
558 const auto oldSkin = m_data->skin;
563 if ( skin->parent() ==
nullptr )
564 skin->setParent(
this );
566 connect( skin, &QskSkin::colorSchemeChanged,
567 this, &QskSkinManager::colorSchemeChanged );
572 disconnect( oldSkin, &QskSkin::colorSchemeChanged,
573 this, &QskSkinManager::colorSchemeChanged );
576 Q_EMIT skinChanged( skin );
578 if ( skin && oldSkin && m_data->transitionHint.isValid() )
581 transition.setSourceSkin( oldSkin );
582 transition.setTargetSkin( skin );
583 transition.run( m_data->transitionHint );
586 if ( oldSkin && oldSkin->parent() ==
this )
590QskSkin* QskSkinManager::setSkin(
const QString& name )
592 if ( !( m_data->skin && ( m_data->skin->objectName() == name ) ) )
594 auto colorScheme = QskSkin::UnknownScheme;
596 colorScheme = m_data->skin->colorScheme();
598 auto skin = createSkin( name, colorScheme );
606QString QskSkinManager::skinName()
const
609 return m_data->skin->objectName();
616 if ( m_data->skin ==
nullptr )
618 m_data->skin = createSkin( QString() );
619 Q_ASSERT( m_data->skin );
621 m_data->skin->setParent(
this );
629 m_data->transitionHint = hint;
634 return m_data->transitionHint;
637#include "moc_QskSkinManager.cpp"