From 6813d643d3c0ba74dba7bf3f0f323b01772d3ffb Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 3 Jan 2018 11:57:05 +0100 Subject: [PATCH] skins factories can be loaded as plugins --- examples/automotive/automotive.pro | 10 + examples/automotive/main.cpp | 13 +- examples/examples.pri | 5 +- playground/playground.pri | 3 +- qskinny.pro | 8 +- .../material/QskMaterialSkin.cpp | 16 +- .../material/QskMaterialSkin.h | 0 skins/material/QskMaterialSkinFactory.cpp | 33 ++ skins/material/QskMaterialSkinFactory.h | 26 + skins/material/material.pro | 13 + skins/material/metadata.json | 4 + skins/skins.pri | 31 ++ skins/skins.pro | 7 + {src/skins => skins}/squiek/QskSquiekSkin.cpp | 14 +- {src/skins => skins}/squiek/QskSquiekSkin.h | 0 skins/squiek/QskSquiekSkinFactory.cpp | 33 ++ skins/squiek/QskSquiekSkinFactory.h | 26 + skins/squiek/metadata.json | 4 + skins/squiek/squiek.pro | 13 + src/common/QskModule.cpp | 11 +- src/controls/QskSetup.cpp | 30 +- src/controls/QskSetup.h | 1 - src/controls/QskSkinFactory.cpp | 107 +--- src/controls/QskSkinFactory.h | 10 +- src/controls/QskSkinManager.cpp | 505 ++++++++++++++++++ src/controls/QskSkinManager.h | 51 ++ src/src.pro | 18 +- support/SkinnyPlugin.cpp | 47 ++ support/SkinnyPlugin.h | 16 + support/SkinnyShortcut.cpp | 4 +- support/support.pro | 13 +- 31 files changed, 887 insertions(+), 185 deletions(-) rename {src/skins => skins}/material/QskMaterialSkin.cpp (99%) rename {src/skins => skins}/material/QskMaterialSkin.h (100%) create mode 100644 skins/material/QskMaterialSkinFactory.cpp create mode 100644 skins/material/QskMaterialSkinFactory.h create mode 100644 skins/material/material.pro create mode 100644 skins/material/metadata.json create mode 100644 skins/skins.pri create mode 100644 skins/skins.pro rename {src/skins => skins}/squiek/QskSquiekSkin.cpp (97%) rename {src/skins => skins}/squiek/QskSquiekSkin.h (100%) create mode 100644 skins/squiek/QskSquiekSkinFactory.cpp create mode 100644 skins/squiek/QskSquiekSkinFactory.h create mode 100644 skins/squiek/metadata.json create mode 100644 skins/squiek/squiek.pro create mode 100644 src/controls/QskSkinManager.cpp create mode 100644 src/controls/QskSkinManager.h create mode 100644 support/SkinnyPlugin.cpp create mode 100644 support/SkinnyPlugin.h diff --git a/examples/automotive/automotive.pro b/examples/automotive/automotive.pro index 9f29034f..cab1fdc9 100644 --- a/examples/automotive/automotive.pro +++ b/examples/automotive/automotive.pro @@ -2,6 +2,16 @@ include( $${PWD}/../examples.pri ) TARGET = automotive +# in this example we don't load skin factories as plugins +QSK_SKIN_DIR=$${QSK_ROOT}/skins + +INCLUDEPATH *= $${QSK_SKIN_DIR} +DEPENDPATH *= $${QSK_SKIN_DIR} + +QSK_PLUGIN_DIR = $${QSK_OUT_ROOT}/plugins +QMAKE_RPATHDIR *= $${QSK_PLUGIN_DIR}/skins +LIBS *= -L$${QSK_PLUGIN_DIR}/skins -lsquiekskin -lmaterialskin + HEADERS += \ ButtonBar.h \ SoundControl.h \ diff --git a/examples/automotive/main.cpp b/examples/automotive/main.cpp index 2a6606cb..7e95fafb 100644 --- a/examples/automotive/main.cpp +++ b/examples/automotive/main.cpp @@ -2,6 +2,7 @@ #include "SkinFactory.h" #include +#include #include #include @@ -12,10 +13,12 @@ using namespace std; int main( int argc, char** argv ) { - QGuiApplication app( argc, argv ); + auto skinFactory = new SkinFactory(); - SkinFactory skinFactory; - Qsk::registerSkinFactory( "SampleSkinFactory", &skinFactory ); + qskSkinManager->setPluginPaths( QStringList() ); // no plugins + qskSkinManager->registerFactory( "SampleSkinFactory", skinFactory ); + + QGuiApplication app( argc, argv ); /* When going over QPainter for the SVGs we prefer the raster paint @@ -36,10 +39,10 @@ int main( int argc, char** argv ) cout << "CTRL-T to change the color scheme, when the \"Default\" skin is active." << endl; QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ), - false, &skinFactory, SLOT(toggleScheme()) ); + false, skinFactory, SLOT(toggleScheme()) ); QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ), - false, &skinFactory, SLOT(rotateSkin()) ); + false, skinFactory, SLOT(rotateSkin()) ); // With CTRL-B you can rotate a couple of visual debug modes SkinnyShortcut::enable( SkinnyShortcut::DebugBackground | diff --git a/examples/examples.pri b/examples/examples.pri index a97dfa74..7ffb5d35 100644 --- a/examples/examples.pri +++ b/examples/examples.pri @@ -14,11 +14,10 @@ QSK_DIRS = \ $${QSK_ROOT}/src/controls \ $${QSK_ROOT}/src/layouts \ $${QSK_ROOT}/src/dialogs \ - $${QSK_ROOT}/src/skins \ $${QSK_ROOT}/support INCLUDEPATH *= $${QSK_DIRS} -DEPENDPATH += $${QSK_DIRS} +DEPENDPATH *= $${QSK_DIRS} DESTDIR = $${QSK_OUT_ROOT}/examples/bin @@ -27,7 +26,7 @@ LIBS *= -L$${QSK_OUT_ROOT}/lib -lqsktestsupport -lqskinny win32 { contains(QSK_CONFIG, QskDll) { - DEFINES += QT_DLL QSK_DLL QSK_DLL + DEFINES += QT_DLL QSK_DLL } } diff --git a/playground/playground.pri b/playground/playground.pri index e9d0889a..cf93dcc8 100644 --- a/playground/playground.pri +++ b/playground/playground.pri @@ -4,7 +4,7 @@ include( $${QSK_ROOT}/qskconfig.pri ) QSK_OUT_ROOT = $${OUT_PWD}/../.. QT += quick -QT += quick quick-private +QT += quick-private CONFIG += no_private_qt_headers_warning TEMPLATE = app @@ -16,7 +16,6 @@ QSK_DIRS = \ $${QSK_ROOT}/src/controls \ $${QSK_ROOT}/src/layouts \ $${QSK_ROOT}/src/dialogs \ - $${QSK_ROOT}/src/skins \ $${QSK_ROOT}/support INCLUDEPATH *= $${QSK_DIRS} diff --git a/qskinny.pro b/qskinny.pro index 9510ec87..7276264d 100644 --- a/qskinny.pro +++ b/qskinny.pro @@ -4,6 +4,7 @@ TEMPLATE = subdirs SUBDIRS = \ src \ + skins \ inputcontext \ tools \ support \ @@ -19,7 +20,8 @@ OTHER_FILES = \ TODO inputcontext.depends = src +skins.depends = src tools.depends = src -support.depends = src -examples.depends = tools support -playground.depends = tools support +support.depends = skins +examples.depends = tools support skins +playground.depends = tools support skins diff --git a/src/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp similarity index 99% rename from src/skins/material/QskMaterialSkin.cpp rename to skins/material/QskMaterialSkin.cpp index 8eb14d71..ae50589d 100644 --- a/src/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -36,7 +36,7 @@ #if 1 // should be defined in the public header, so that // application code can avoid conflicts -static const int ButtonFontRole = QskSkin::HugeFont + 77; +static const int ButtonFontRole = QskSkin::HugeFont + 77; #endif static const int qskDuration = 150; @@ -173,7 +173,7 @@ void QskMaterialSkin::initPopupHints() const QskGradient gradient( QskGradient::Vertical, qskShadedColor( pal.accentColor, 0.45 ), qskShadedColor( pal.accentColor, 0.7 ) ); - + setGradient( Q::Overlay, gradient ); } @@ -392,7 +392,7 @@ void QskMaterialSkin::initSliderHints() setBoxShape( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 0 ); setGradient( Q::Panel, QskGradient() ); - + setMargins( Q::Panel | Preserved | Padding, QskMargins( 0.5 * dim, 0 ) ); setMargins( Q::Panel | Transposed | Padding, QskMargins( 0, 0.5 * dim ) ); @@ -417,7 +417,7 @@ void QskMaterialSkin::initSliderHints() setBoxShape( Q::Handle, 100, Qt::RelativeSize ); setBoxBorderMetrics( Q::Handle, 4 ); - + // handle expanding, when being pressed setMetric( Q::Handle | Size, 0.6 * dim ); setMetric( Q::Handle | Size | Q::Pressed, dim ); @@ -567,13 +567,13 @@ void QskMaterialSkin::initScrollViewHints() setBoxBorderMetrics( Q::Viewport, 1 ); setGradient( Q::Viewport, QskRgbValue::White ); setBoxBorderColors( Q::Viewport, Qt::black ); - + for ( auto subControl : { Q::HorizontalScrollBar, Q::VerticalScrollBar } ) { setMetric( subControl | Size, 12 ); setMargins( subControl | Padding, 0 ); - } - + } + setMetric( Q::HorizontalScrollHandle | MinimumWidth, qskDpiScaled( 40.0 ) ); setMetric( Q::VerticalScrollHandle | MinimumHeight, qskDpiScaled( 40.0 ) ); @@ -585,7 +585,7 @@ void QskMaterialSkin::initScrollViewHints() setBoxBorderColors( subControl, QskRgbValue::White ); setAnimation( subControl | Color, qskDuration ); - } + } for ( auto subControl : { Q::HorizontalScrollHandle | Q::HorizontalHandlePressed, diff --git a/src/skins/material/QskMaterialSkin.h b/skins/material/QskMaterialSkin.h similarity index 100% rename from src/skins/material/QskMaterialSkin.h rename to skins/material/QskMaterialSkin.h diff --git a/skins/material/QskMaterialSkinFactory.cpp b/skins/material/QskMaterialSkinFactory.cpp new file mode 100644 index 00000000..e4325ae6 --- /dev/null +++ b/skins/material/QskMaterialSkinFactory.cpp @@ -0,0 +1,33 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "QskMaterialSkinFactory.h" +#include "QskMaterialSkin.h" + +static const QString materialSkinName = QStringLiteral( "material" ); + +QskMaterialSkinFactory::QskMaterialSkinFactory( QObject* parent ): + QskSkinFactory( parent ) +{ +} + +QskMaterialSkinFactory::~QskMaterialSkinFactory() +{ +} + +QStringList QskMaterialSkinFactory::skinNames() const +{ + return { materialSkinName }; +} + +QskSkin* QskMaterialSkinFactory::createSkin( const QString& skinName ) +{ + if ( skinName.toLower() == materialSkinName ) + return new QskMaterialSkin(); + + return nullptr; +} + +#include "moc_QskMaterialSkinFactory.cpp" diff --git a/skins/material/QskMaterialSkinFactory.h b/skins/material/QskMaterialSkinFactory.h new file mode 100644 index 00000000..8cf95314 --- /dev/null +++ b/skins/material/QskMaterialSkinFactory.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#ifndef QSK_MATERIAL_SKIN_FACTORY_H +#define QSK_MATERIAL_SKIN_FACTORY_H + +#include + +class QSK_EXPORT QskMaterialSkinFactory : public QskSkinFactory +{ + Q_OBJECT + + Q_PLUGIN_METADATA( IID QskSkinFactoryIID FILE "metadata.json" ) + Q_INTERFACES( QskSkinFactory ) + +public: + QskMaterialSkinFactory( QObject* parent = nullptr ); + virtual ~QskMaterialSkinFactory(); + + virtual QStringList skinNames() const override; + virtual QskSkin* createSkin( const QString& skinName ) override; +}; + +#endif diff --git a/skins/material/material.pro b/skins/material/material.pro new file mode 100644 index 00000000..26b5a6dd --- /dev/null +++ b/skins/material/material.pro @@ -0,0 +1,13 @@ +include( $${PWD}/../skins.pri ) + +TARGET = materialskin + +HEADERS += \ + QskMaterialSkin.h \ + QskMaterialSkinFactory.h + +SOURCES += \ + QskMaterialSkin.cpp \ + QskMaterialSkinFactory.cpp + +OTHER_FILES += metadata.json diff --git a/skins/material/metadata.json b/skins/material/metadata.json new file mode 100644 index 00000000..1812afd4 --- /dev/null +++ b/skins/material/metadata.json @@ -0,0 +1,4 @@ +{ + "FactoryId": "MaterialFactory", + "Skins": [ "material" ] +} diff --git a/skins/skins.pri b/skins/skins.pri new file mode 100644 index 00000000..ccc018f0 --- /dev/null +++ b/skins/skins.pri @@ -0,0 +1,31 @@ +QSK_ROOT = $${PWD}/.. +include( $${QSK_ROOT}/qskconfig.pri ) + +QSK_OUT_ROOT = $${OUT_PWD}/../.. + +CONFIG += plugin + +TEMPLATE = lib + +QSK_DIRS = \ + $${QSK_ROOT}/src/common \ + $${QSK_ROOT}/src/nodes \ + $${QSK_ROOT}/src/graphic \ + $${QSK_ROOT}/src/controls \ + $${QSK_ROOT}/src/layouts \ + $${QSK_ROOT}/src/dialogs + +INCLUDEPATH *= $${QSK_DIRS} +DEPENDPATH += $${QSK_DIRS} + +DESTDIR = $${QSK_OUT_ROOT}/plugins/skins + +QMAKE_RPATHDIR *= $${QSK_OUT_ROOT}/lib +LIBS *= -L$${QSK_OUT_ROOT}/lib -lqskinny + +win32 { + contains(QSK_CONFIG, QskDll) { + DEFINES += QT_DLL QSK_DLL + } +} + diff --git a/skins/skins.pro b/skins/skins.pro new file mode 100644 index 00000000..285fd4bc --- /dev/null +++ b/skins/skins.pro @@ -0,0 +1,7 @@ +include( $${PWD}/../qskconfig.pri ) + +TEMPLATE = subdirs + +SUBDIRS += \ + squiek \ + material diff --git a/src/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp similarity index 97% rename from src/skins/squiek/QskSquiekSkin.cpp rename to skins/squiek/QskSquiekSkin.cpp index edf0e847..7e0f7153 100644 --- a/src/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -326,7 +326,7 @@ void QskSquiekSkin::initPageIndicatorHints() setGradient( subControl, ( subControl == Q::Bullet ) ? pal.darker150 : pal.lighter150 ); - + } // no visible background panel @@ -432,13 +432,13 @@ void QskSquiekSkin::initTabButtonHints() const QskMargins padding( 10, 4 ); - for ( auto placement : { Preserved, Transposed } ) + for ( auto placement : { Preserved, Transposed } ) { const Aspect aspect = Q::Panel | placement; if ( placement == Preserved ) { - setMargins( aspect | Margin , QskMargins( -1, 2, -1, -2 ) ); + setMargins( aspect | Margin, QskMargins( -1, 2, -1, -2 ) ); for ( const auto state : { Q::Checked, Q::Checked | Q::Pressed } ) setMargins( aspect | Margin | state, QskMargins( -1, 0, -1, -3 ) ); @@ -448,7 +448,7 @@ void QskSquiekSkin::initTabButtonHints() } else { - setMargins( aspect | Margin , QskMargins( 2, -1, -2, -1 ) ); + setMargins( aspect | Margin, QskMargins( 2, -1, -2, -1 ) ); for ( const auto state : { Q::Checked, Q::Checked | Q::Pressed } ) setMargins( aspect | Margin | state, QskMargins( 0, -1, -3, 0 ) ); @@ -484,7 +484,7 @@ void QskSquiekSkin::initSliderHints() { const auto aspect = Q::Panel | placement; - setMetric( aspect | Size, dim ); + setMetric( aspect | Size, dim ); setBoxBorderMetrics( aspect, 0 ); setBoxShape( aspect, 0 ); setGradient( aspect, QskGradient() ); @@ -564,12 +564,12 @@ void QskSquiekSkin::initInputPanelHints() setButton( Q::KeyPanel, Raised ); setButton( Q::KeyPanel | Q::Pressed, Sunken ); - setAnimation( Q::KeyPanel | Color, qskDuration ); + setAnimation( Q::KeyPanel | Color, qskDuration ); #if 0 // crashes because animations are started from updateNode // TODO ... - setAnimation( Q::KeyPanel | Metric, qskDuration ); + setAnimation( Q::KeyPanel | Metric, qskDuration ); #endif // glyph diff --git a/src/skins/squiek/QskSquiekSkin.h b/skins/squiek/QskSquiekSkin.h similarity index 100% rename from src/skins/squiek/QskSquiekSkin.h rename to skins/squiek/QskSquiekSkin.h diff --git a/skins/squiek/QskSquiekSkinFactory.cpp b/skins/squiek/QskSquiekSkinFactory.cpp new file mode 100644 index 00000000..ef11fa06 --- /dev/null +++ b/skins/squiek/QskSquiekSkinFactory.cpp @@ -0,0 +1,33 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "QskSquiekSkinFactory.h" +#include "QskSquiekSkin.h" + +static const QString squiekSkinName = QStringLiteral( "squiek" ); + +QskSquiekSkinFactory::QskSquiekSkinFactory( QObject* parent ): + QskSkinFactory( parent ) +{ +} + +QskSquiekSkinFactory::~QskSquiekSkinFactory() +{ +} + +QStringList QskSquiekSkinFactory::skinNames() const +{ + return { squiekSkinName }; +} + +QskSkin* QskSquiekSkinFactory::createSkin( const QString& skinName ) +{ + if ( skinName.toLower() == squiekSkinName ) + return new QskSquiekSkin(); + + return nullptr; +} + +#include "moc_QskSquiekSkinFactory.cpp" diff --git a/skins/squiek/QskSquiekSkinFactory.h b/skins/squiek/QskSquiekSkinFactory.h new file mode 100644 index 00000000..baafdf15 --- /dev/null +++ b/skins/squiek/QskSquiekSkinFactory.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#ifndef QSK_SQUIEK_SKIN_FACTORY_H +#define QSK_SQUIEK_SKIN_FACTORY_H + +#include + +class QSK_EXPORT QskSquiekSkinFactory : public QskSkinFactory +{ + Q_OBJECT + + Q_PLUGIN_METADATA( IID QskSkinFactoryIID FILE "metadata.json" ) + Q_INTERFACES( QskSkinFactory ) + +public: + QskSquiekSkinFactory( QObject* parent = nullptr ); + virtual ~QskSquiekSkinFactory(); + + virtual QStringList skinNames() const override; + virtual QskSkin* createSkin( const QString& skinName ) override; +}; + +#endif diff --git a/skins/squiek/metadata.json b/skins/squiek/metadata.json new file mode 100644 index 00000000..02f9c7c8 --- /dev/null +++ b/skins/squiek/metadata.json @@ -0,0 +1,4 @@ +{ + "FactoryId": "SquiekFactory", + "Skins": [ "squiek" ] +} diff --git a/skins/squiek/squiek.pro b/skins/squiek/squiek.pro new file mode 100644 index 00000000..e6b1c74f --- /dev/null +++ b/skins/squiek/squiek.pro @@ -0,0 +1,13 @@ +include( $${PWD}/../skins.pri ) + +TARGET = squiekskin + +HEADERS += \ + QskSquiekSkin.h \ + QskSquiekSkinFactory.h + +SOURCES += \ + QskSquiekSkin.cpp \ + QskSquiekSkinFactory.cpp + +OTHER_FILES += metadata.json diff --git a/src/common/QskModule.cpp b/src/common/QskModule.cpp index f7f23919..6a0d5ca5 100644 --- a/src/common/QskModule.cpp +++ b/src/common/QskModule.cpp @@ -6,7 +6,8 @@ #include "QskModule.h" #include "QskSetup.h" -#include "QskSkinFactory.h" +#include "QskSkinManager.h" +#include "QskSkin.h" #include "QskPushButton.h" #include "QskCorner.h" #include "QskDialog.h" @@ -42,9 +43,6 @@ #include "QskSelectionWindow.h" #include "QskWindow.h" -#include "skins/squiek/QskSquiekSkin.h" -#include "skins/material/QskMaterialSkin.h" - #include #include #include @@ -187,7 +185,8 @@ public: QStringList skinList() const { - return Qsk::skinNames(); + auto manager = QskSkinManager::instance(); + return manager ? manager->skinNames() : QStringList(); } QQmlListProperty< QObject > data() @@ -277,8 +276,6 @@ void QskModule::registerTypes() QSK_REGISTER_SINGLETON( QskDialog, "Dialog", QskDialog::instance() ); qmlRegisterUncreatableType< QskSkin >( "Skinny.Skins", 1, 0, "Skin", QString() ); - qmlRegisterType< QskSquiekSkin >( "Skinny.Skins", 1, 0, "SquiekSkin" ); - qmlRegisterType< QskMaterialSkin >( "Skinny.Skins", 1, 0, "MaterialSkin" ); QSK_REGISTER_FLAGS( QskControl::Flag ); QSK_REGISTER_FLAGS( QskControl::Flags ); diff --git a/src/controls/QskSetup.cpp b/src/controls/QskSetup.cpp index a1481b62..f36fbe82 100644 --- a/src/controls/QskSetup.cpp +++ b/src/controls/QskSetup.cpp @@ -5,7 +5,7 @@ #include "QskSetup.h" #include "QskSkin.h" -#include "QskSkinFactory.h" +#include "QskSkinManager.h" #include "QskGraphicProviderMap.h" #include "QskControl.h" #include "QskInputPanel.h" @@ -203,7 +203,7 @@ QskSkin* QskSetup::setSkin( const QString& skinName ) if ( m_data->skin && ( skinName == m_data->skinName ) ) return m_data->skin; - QskSkin* skin = Qsk::createSkin( skinName ); + QskSkin* skin = QskSkinManager::instance()->createSkin( skinName ); if ( skin == nullptr ) return nullptr; @@ -231,33 +231,13 @@ QString QskSetup::skinName() const return m_data->skinName; } -void QskSetup::setSkin( QskSkin* skin, const QString& skinName ) -{ - if ( skin == m_data->skin ) - return; - - if ( skin && skin->parent() == nullptr ) - skin->setParent( this ); - - const QskSkin* oldSkin = m_data->skin; - - m_data->skin = skin; - m_data->skinName = skinName; - - if ( oldSkin ) - { - Q_EMIT skinChanged( skin ); - - if ( oldSkin->parent() == this ) - delete oldSkin; - } -} - QskSkin* QskSetup::skin() { if ( m_data->skin == nullptr ) { - m_data->skin = Qsk::createSkin( nullptr ); + m_data->skin = QskSkinManager::instance()->createSkin( QString::null ); + Q_ASSERT( m_data->skin ); + m_data->skin->setParent( this ); m_data->skinName = m_data->skin->objectName(); } diff --git a/src/controls/QskSetup.h b/src/controls/QskSetup.h index 891b5298..3ea12d9f 100644 --- a/src/controls/QskSetup.h +++ b/src/controls/QskSetup.h @@ -58,7 +58,6 @@ public: Q_INVOKABLE QskSkin* setSkin( const QString& ); Q_INVOKABLE QString skinName() const; - void setSkin( QskSkin*, const QString& = QString::null ); QskSkin* skin(); void setInputPanel( QskInputPanel* ); diff --git a/src/controls/QskSkinFactory.cpp b/src/controls/QskSkinFactory.cpp index 124daef9..33976081 100644 --- a/src/controls/QskSkinFactory.cpp +++ b/src/controls/QskSkinFactory.cpp @@ -1,112 +1,17 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + #include "QskSkinFactory.h" -#include "squiek/QskSquiekSkin.h" -#include "material/QskMaterialSkin.h" - -#include -#include -#include - -namespace -{ - static const char factoryId[] = "qskinny"; - static const char squiekSkinName[] = "Squiek"; - static const char materialSkinName[] = "Material"; - - class SkinFactory final : public QskSkinFactory - { - public: - SkinFactory( QObject* parent ): - QskSkinFactory( parent ) - { - } - - virtual QStringList skinNames() const override final - { - return { squiekSkinName, materialSkinName }; - } - - virtual QskSkin* createSkin( const QString& name ) override final - { - QskSkin* skin = nullptr; - - if ( name == squiekSkinName ) - skin = new QskSquiekSkin(); - - if ( name == materialSkinName ) - skin = new QskMaterialSkin(); - - if ( skin ) - skin->setObjectName( name ); - - return skin; - } - }; - - typedef QHash< QString, QPointer< QskSkinFactory > > FactoryTable; -} - -Q_GLOBAL_STATIC( FactoryTable, qskFactoryTable ) - -static FactoryTable& qskGetFactoryTable() -{ - if ( qskFactoryTable->isEmpty() ) - { - static SkinFactory dummySkinFactory ( nullptr ); - qskFactoryTable->insert( factoryId, &dummySkinFactory ); - } - - return *qskFactoryTable; -} - QskSkinFactory::QskSkinFactory( QObject* parent ): QObject( parent ) { } -QskSkinFactory::~QskSkinFactory() = default; - - -void Qsk::registerSkinFactory( const QString& id, QskSkinFactory* factory ) +QskSkinFactory::~QskSkinFactory() { - qskFactoryTable->insert( id.toLower(), factory ); -} - -QskSkinFactory* Qsk::skinFactory( const QString& id ) -{ - return qskGetFactoryTable().value( id.toLower() ).data(); -} - -QStringList Qsk::skinNames() -{ - QStringList names; - - const auto& factoryTable = qskGetFactoryTable(); - for ( const auto& factory : factoryTable ) - { - if ( factory ) - names += factory->skinNames(); - } - - return names; -} - -QskSkin* Qsk::createSkin( const QString& skinName ) -{ - if ( !skinName.isEmpty() ) - { - const auto& factoryTable = qskGetFactoryTable(); - for ( const auto& factory : factoryTable ) - { - QskSkin* skin = factory->createSkin( skinName ); - if ( skin ) - return skin; - - } - } - - QskSkinFactory* factory = skinFactory( factoryId ); - return factory->createSkin( squiekSkinName ); } #include "moc_QskSkinFactory.cpp" diff --git a/src/controls/QskSkinFactory.h b/src/controls/QskSkinFactory.h index 92e54265..6e9f25d1 100644 --- a/src/controls/QskSkinFactory.h +++ b/src/controls/QskSkinFactory.h @@ -24,13 +24,7 @@ public: virtual QskSkin* createSkin( const QString& skinName ) = 0; }; -namespace Qsk -{ - QSK_EXPORT void registerSkinFactory( const QString& id, QskSkinFactory* ); - QSK_EXPORT QskSkinFactory* skinFactory( const QString& id ); - - QSK_EXPORT QStringList skinNames(); - QSK_EXPORT QskSkin* createSkin( const QString& skinName ); -} +#define QskSkinFactoryIID "org.qskinny.Qsk.QskSkinFactory/1.0" +Q_DECLARE_INTERFACE( QskSkinFactory, QskSkinFactoryIID ) #endif diff --git a/src/controls/QskSkinManager.cpp b/src/controls/QskSkinManager.cpp new file mode 100644 index 00000000..60afa1b4 --- /dev/null +++ b/src/controls/QskSkinManager.cpp @@ -0,0 +1,505 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskSkinManager.h" +#include "QskSkinFactory.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + We could use QFactoryLoader, but as it is again a "private" class + and does a couple of hardcoded things we don't need ( like always resolving + from the application library path ) we prefer having our own code. + */ + +namespace { class SkinManager final : public QskSkinManager { }; } +Q_GLOBAL_STATIC( SkinManager, qskGlobalSkinManager ) + +static QStringList qskPathList( const char* envName ) +{ + const auto env = qgetenv( envName ); + if ( env.isEmpty() ) + return QStringList(); + + return QFile::decodeName( env ).split( + QDir::listSeparator(), QString::SkipEmptyParts ); +} + +static inline QString qskResolvedPath( const QString& path ) +{ + return QDir( path ).canonicalPath(); +} + +namespace +{ + class FactoryLoader final : public QPluginLoader + { + public: + FactoryLoader( QObject* parent = nullptr ): + QPluginLoader( parent ) + { + } + + bool setPlugin( const QString& fileName ) + { + QPluginLoader::setFileName( fileName ); + + /* + FactoryId and names of the skins can be found in the metadata + without having to load the plugin itself + */ + + constexpr QLatin1String TokenInterfaceId( "IID" ); + constexpr QLatin1String TokenData( "MetaData" ); + constexpr QLatin1String TokenFactoryId( "FactoryId" ); + constexpr QLatin1String TokenSkins( "Skins" ); + + constexpr QLatin1String InterfaceId( QskSkinFactoryIID ); + + const auto pluginData = metaData(); + if ( pluginData.value( TokenInterfaceId ) == InterfaceId ) + { + const auto factoryData = pluginData.value( TokenData ).toObject(); + + m_factoryId = factoryData.value( TokenFactoryId ).toString().toLower(); + if ( m_factoryId.isEmpty() ) + { + // Creating a dummy factory id + static int i = 0; + m_factoryId = QStringLiteral( "skin_factory_" ) + QString::number( i++ ); + } + + const auto skinNames = factoryData.value( TokenSkins ).toArray(); + + for ( const auto& name : skinNames ) + m_skinNames += name.toString().toLower(); + } + + return !m_skinNames.isEmpty(); + } + + inline QString factoryId() const + { + return m_factoryId; + } + + inline QskSkinFactory* factory() + { + auto factory = qobject_cast< QskSkinFactory* >( QPluginLoader::instance() ); + if ( factory ) + { + factory->setParent( nullptr ); + factory->setObjectName( m_factoryId ); + } + + return factory; + } + + inline QStringList skinNames() const + { + return m_skinNames; + } + + private: + void setFileName( const QString& ) = delete; + QObject* instance() = delete; + + QString m_factoryId; + QStringList m_skinNames; + }; + + class FactoryMap + { + private: + class Data + { + public: + Data(): + loader( nullptr ) + { + } + + ~Data() + { + reset(); + } + + void reset() + { + if ( factory && factory->parent() == nullptr ) + delete factory; + + factory = nullptr; + + delete loader; + loader = nullptr; + } + + FactoryLoader* loader; + QPointer< QskSkinFactory > factory; + }; + + public: + FactoryMap(): + m_isValid( false ) + { + } + + void reset() + { + m_skinNames.clear(); + m_skinMap.clear(); + m_factoryMap.clear(); + } + + QskSkinFactory* factory( const QString& skinName ) + { + if ( !m_isValid ) + rebuild(); + + const auto it = m_skinMap.constFind( skinName ); + if ( it != m_skinMap.constEnd() ) + { + auto it2 = m_factoryMap.find( it.value() ); + if ( it2 != m_factoryMap.end() ) + { + auto& data = it2.value(); + if ( ( data.factory == nullptr ) && data.loader != nullptr ) + data.factory = data.loader->factory(); + + return data.factory; + } + } + + return nullptr; + } + + QStringList skinNames() const + { + if ( !m_isValid ) + const_cast< FactoryMap* >( this )->rebuild(); + + return m_skinMap.keys(); + } + + QStringList skinNames( const QString& factoryId ) const + { + const auto it = m_factoryMap.constFind( factoryId ); + if ( it != m_factoryMap.constEnd() ) + { + const auto& data = it.value(); + + if ( data.factory ) + return data.factory->skinNames(); + + if ( data.loader ) + return data.loader->skinNames(); + } + + return QStringList(); + } + + void insertFactory( FactoryLoader* loader ) + { + auto& data = m_factoryMap[ loader->factoryId() ]; + + if ( data.loader != loader ) + { + data.reset(); + data.loader = loader; + + m_skinMap.clear(); + m_isValid = false; + } + } + + void insertFactory( const QString& factoryId, QskSkinFactory* factory ) + { + auto& data = m_factoryMap[ factoryId ]; + + if ( data.factory != factory ) + { + data.reset(); + data.factory = factory; + + m_skinMap.clear(); + m_skinNames.clear(); + m_isValid = false; + } + } + + void removeFactory( const QString& factoryId ) + { + const auto it = m_factoryMap.find( factoryId ); + if ( it == m_factoryMap.end() ) + return; + + m_factoryMap.erase( it ); + + if ( m_isValid ) + { + for ( auto it = m_skinMap.constBegin(); + it != m_skinMap.constEnd(); ++it ) + { + if ( it.key() == factoryId ) + { + m_isValid = false; + break; + } + } + + if ( !m_isValid ) + { + m_skinNames.clear(); + m_skinMap.clear(); + } + } + } + + inline bool hasFactory( const QString& factoryId ) const + { + return m_factoryMap.contains( factoryId ); + } + + private: + void rebuild() + { + m_skinMap.clear(); + + // first we try all factories, that have been added manually + for ( auto it = m_factoryMap.begin(); it != m_factoryMap.end(); ++it ) + { + const auto& data = it.value(); + + if ( data.loader == nullptr && data.factory ) + rebuild( it.key(), data.factory->skinNames() ); + } + + // all factories from plugins are following + for ( auto it = m_factoryMap.begin(); it != m_factoryMap.end(); ++it ) + { + const auto& data = it.value(); + if ( data.loader ) + rebuild( it.key(), data.loader->skinNames() ); + } + + m_skinNames = m_skinMap.keys(); + m_isValid = true; + } + + void rebuild( const QString& factoryId, const QStringList& skinNames ) + { + for ( const auto& name : skinNames ) + { + if ( !m_skinMap.contains( name ) ) + m_skinMap.insert( name, factoryId ); + } + } + + QMap< QString, Data > m_factoryMap; // factoryId -> data + QMap< QString, QString > m_skinMap; // skinName -> factoryId + QStringList m_skinNames; + + bool m_isValid; + }; +} + +class QskSkinManager::PrivateData +{ +public: + PrivateData(): + pluginsRegistered( false ) + { + } + + inline void ensurePlugins() + { + if ( !pluginsRegistered ) + { + for ( auto path : pluginPaths ) + registerPlugins( path + QStringLiteral( "/skins" ) ); + + pluginsRegistered = true; + } + } + + void registerPlugins( const QString& path ) + { + /* + We only detect plugins, but don't load them before being needed. + Static plugins are not supported as QskSkinmanager::registerFactory + offers a better solution for this use case. + */ + + QDir dir( path ); + + FactoryLoader* loader = nullptr; + + for ( auto fileName : dir.entryList( QDir::Files ) ) + { + if ( loader == nullptr ) + loader = new FactoryLoader(); + + bool ok = loader->setPlugin( dir.absoluteFilePath( fileName ) ); + if ( ok && !factoryMap.hasFactory( loader->factoryId() ) ) + { + factoryMap.insertFactory( loader ); + loader = nullptr; + } + } + + delete loader; + } + +public: + QStringList pluginPaths; + FactoryMap factoryMap; + + bool pluginsRegistered : 1; +}; + +QskSkinManager* QskSkinManager::instance() +{ + return qskGlobalSkinManager; +} + +QskSkinManager::QskSkinManager(): + m_data( new PrivateData() ) +{ + setPluginPaths( qskPathList( "QSK_PLUGIN_PATH" ) + + qskPathList( "QT_PLUGIN_PATH" ) ); +} + +QskSkinManager::~QskSkinManager() +{ +} + +void QskSkinManager::addPluginPath( const QString& path ) +{ + const auto pluginPath = qskResolvedPath( path ); + + if ( !pluginPath.isEmpty() && !pluginPath.contains( pluginPath ) ) + { + m_data->pluginPaths += pluginPath; + + if ( m_data->pluginsRegistered ) + m_data->registerPlugins( pluginPath ); + } +} + +void QskSkinManager::removePluginPath( const QString& path ) +{ + const auto pluginPath = qskResolvedPath( path ); + + if ( m_data->pluginPaths.removeOne( pluginPath ) ) + { + if ( m_data->pluginsRegistered ) + { + m_data->factoryMap.reset(); + m_data->pluginsRegistered = false; + } + } +} + +void QskSkinManager::setPluginPaths( const QStringList& paths ) +{ + m_data->pluginPaths.clear(); + + QSet< QString > pathSet; // checking for duplicates + QStringList pluginPaths; + + for ( auto path : paths ) + { + const auto pluginPath = qskResolvedPath( path ); + if ( !pluginPath.isEmpty() && !pathSet.contains( pluginPath ) ) + { + pathSet += pluginPath; + pluginPaths += pluginPath; + } + } + + if ( pluginPaths != m_data->pluginPaths ) + { + m_data->pluginPaths = pluginPaths; + m_data->factoryMap.reset(); + m_data->pluginsRegistered = false; + } +} + +QStringList QskSkinManager::pluginPaths() const +{ + return m_data->pluginPaths; +} + +void QskSkinManager::registerFactory( + const QString& factoryId, QskSkinFactory* factory ) +{ + if ( factoryId.isEmpty() || factory == nullptr ) + return; + + /* + Manually registered factories always come first, and we don't need + to check the plugins here. + */ + + m_data->factoryMap.insertFactory( factoryId.toLower(), factory ); +} + +void QskSkinManager::unregisterFactory( const QString& factoryId ) +{ + if ( factoryId.isEmpty() ) + return; + + /* + As this call might be about a factory from a plugin, we need + to know about them here. + */ + + m_data->ensurePlugins(); + m_data->factoryMap.removeFactory( factoryId.toLower() ); +} + +QStringList QskSkinManager::skinNames() const +{ + m_data->ensurePlugins(); + return m_data->factoryMap.skinNames(); +} + +QskSkin* QskSkinManager::createSkin( const QString& skinName ) const +{ + m_data->ensurePlugins(); + + auto& map = m_data->factoryMap; + + auto name = skinName; + + auto factory = map.factory( name ); + if ( factory == nullptr ) + { + /* + Once the Fusion skin has been implemented it will be used + as fallback. For the moment we implement + another stupid fallback. TODO ... + */ + + const auto names = map.skinNames(); + if ( !names.isEmpty() ) + { + name = names.last(); + factory = map.factory( name ); + } + } + + return factory ? factory->createSkin( name ) : nullptr; +} + +#include "moc_QskSkinManager.cpp" diff --git a/src/controls/QskSkinManager.h b/src/controls/QskSkinManager.h new file mode 100644 index 00000000..25592e9c --- /dev/null +++ b/src/controls/QskSkinManager.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_SKIN_MANAGER_H +#define QSK_SKIN_MANAGER_H + +#include "QskGlobal.h" +#include +#include + +class QskSkin; +class QskSkinFactory; +class QStringList; + +#if defined( qskSkinManager ) +#undef qskSkinManager +#endif + +#define qskSkinManager QskSkinManager::instance() + +class QSK_EXPORT QskSkinManager : public QObject +{ + Q_OBJECT + +public: + static QskSkinManager* instance(); + + void addPluginPath( const QString& ); + void removePluginPath( const QString& ); + + void setPluginPaths(const QStringList& paths); + QStringList pluginPaths() const; + + void registerFactory( const QString& factoryId, QskSkinFactory* ); + void unregisterFactory( const QString& factoryId ); + + QStringList skinNames() const; + + QskSkin* createSkin( const QString& skinName ) const; + +protected: + QskSkinManager(); + virtual ~QskSkinManager(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/src.pro b/src/src.pro index 0716bcf1..7fb47382 100644 --- a/src/src.pro +++ b/src/src.pro @@ -25,7 +25,7 @@ else { CONFIG += staticlib } -QSK_SUBDIRS = common graphic nodes controls layouts dialogs skins +QSK_SUBDIRS = common graphic nodes controls layouts dialogs INCLUDEPATH *= $${QSK_SUBDIRS} DEPENDPATH *= $${QSK_SUBDIRS} @@ -154,12 +154,13 @@ HEADERS += \ controls/QskShortcut.h \ controls/QskShortcutMap.h \ controls/QskSimpleListBox.h \ - controls/QskSkinFactory.h \ controls/QskSkin.h \ + controls/QskSkinFactory.h \ controls/QskSkinHintTable.h \ + controls/QskSkinManager.h \ + controls/QskSkinTransition.h \ controls/QskSkinlet.h \ controls/QskSkinnable.h \ - controls/QskSkinTransition.h \ controls/QskSlider.h \ controls/QskSliderSkinlet.h \ controls/QskStatusIndicator.h \ @@ -221,9 +222,10 @@ SOURCES += \ controls/QskSkin.cpp \ controls/QskSkinHintTable.cpp \ controls/QskSkinFactory.cpp \ + controls/QskSkinManager.cpp \ + controls/QskSkinTransition.cpp \ controls/QskSkinlet.cpp \ controls/QskSkinnable.cpp \ - controls/QskSkinTransition.cpp \ controls/QskSlider.cpp \ controls/QskSliderSkinlet.cpp \ controls/QskStatusIndicator.cpp \ @@ -242,14 +244,6 @@ SOURCES += \ controls/QskVariantAnimator.cpp \ controls/QskWindow.cpp -HEADERS += \ - skins/material/QskMaterialSkin.h \ - skins/squiek/QskSquiekSkin.h - -SOURCES += \ - skins/material/QskMaterialSkin.cpp \ - skins/squiek/QskSquiekSkin.cpp - HEADERS += \ layouts/QskGridBox.h \ layouts/QskIndexedLayoutBox.h \ diff --git a/support/SkinnyPlugin.cpp b/support/SkinnyPlugin.cpp new file mode 100644 index 00000000..9fe6b22d --- /dev/null +++ b/support/SkinnyPlugin.cpp @@ -0,0 +1,47 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +#define STRINGIFY(x) #x +#define STRING(x) STRINGIFY(x) + +static void initPlugins() +{ + const char env[] = "QT_PLUGIN_PATH"; + + QByteArray value = qgetenv( env ); + if ( !value.isEmpty() && QChar( value[ value.size() - 1 ] ) != QDir::listSeparator() ) + value += QDir::listSeparator(); + + value += STRING( PLUGIN_PATH ); + + qputenv( "QT_PLUGIN_PATH", value ); + + if ( qskSkinManager->skinNames().isEmpty() ) + { + using namespace std; + + /* + To avoid having problems with not finding the skin plugins + we manually add them here. + */ + + qskSkinManager->registerFactory( "SquiekFactory", new QskSquiekSkinFactory() ); + qskSkinManager->registerFactory( "MaterialFactory", new QskMaterialSkinFactory() ); + + cout << "Couldn't find skin plugins, adding them manually instead." << endl; + } +} + +Q_COREAPP_STARTUP_FUNCTION( initPlugins ) diff --git a/support/SkinnyPlugin.h b/support/SkinnyPlugin.h new file mode 100644 index 00000000..51cecb9a --- /dev/null +++ b/support/SkinnyPlugin.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#ifndef SKINNY_PLUGIN_H_ +#define SKINNY_PLUGIN_H_ + +#include "SkinnyGlobal.h" + +namespace SKINNY_EXPORT SkinnyPlugin +{ + void init(); +} + +#endif diff --git a/support/SkinnyShortcut.cpp b/support/SkinnyShortcut.cpp index 3fdc3744..759df28a 100644 --- a/support/SkinnyShortcut.cpp +++ b/support/SkinnyShortcut.cpp @@ -1,8 +1,8 @@ #include "SkinnyShortcut.h" -#include #include #include +#include #include #include #include @@ -63,7 +63,7 @@ void SkinnyShortcut::enable( Types types ) void SkinnyShortcut::rotateSkin() { - const QStringList names = Qsk::skinNames(); + const QStringList names = qskSkinManager->skinNames(); if ( names.size() <= 1 ) return; diff --git a/support/support.pro b/support/support.pro index b3550600..16648b77 100644 --- a/support/support.pro +++ b/support/support.pro @@ -1,5 +1,6 @@ QSK_ROOT = $${PWD}/.. QSK_OUT_ROOT = $${OUT_PWD}/.. +QSK_PLUGIN_DIR = $${QSK_OUT_ROOT}/plugins include( $${QSK_ROOT}/qskconfig.pri ) @@ -24,8 +25,13 @@ QSK_DIRS = \ $${QSK_ROOT}/src/controls \ $${QSK_ROOT}/src/graphic +QSK_DIRS += $${QSK_ROOT}/skins + +DEFINES += PLUGIN_PATH=$$clean_path( $$QSK_PLUGIN_DIR ) +QSK_DIRS += $${QSK_ROOT}/skins + INCLUDEPATH *= $${QSK_DIRS} -DEPENDPATH += $${QSK_DIRS} +DEPENDPATH *= $${QSK_DIRS} HEADERS += \ SkinnyGlobal.h \ @@ -36,9 +42,14 @@ HEADERS += \ SOURCES += \ SkinnyFont.cpp \ + SkinnyPlugin.cpp \ SkinnyShapeFactory.cpp \ SkinnyShapeProvider.cpp \ SkinnyShortcut.cpp RESOURCES += \ fonts.qrc + +QMAKE_RPATHDIR *= $${QSK_PLUGIN_DIR}/skins +LIBS *= -L$${QSK_PLUGIN_DIR}/skins -lsquiekskin -lmaterialskin +