skins factories can be loaded as plugins

This commit is contained in:
Uwe Rathmann 2018-01-03 11:57:05 +01:00
parent bfd646c153
commit 6813d643d3
31 changed files with 887 additions and 185 deletions

View File

@ -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 \

View File

@ -2,6 +2,7 @@
#include "SkinFactory.h"
#include <QskShortcutMap.h>
#include <QskSkinManager.h>
#include <QskSetup.h>
#include <SkinnyShortcut.h>
@ -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 |

View File

@ -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
}
}

View File

@ -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}

View File

@ -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

View File

@ -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,

View File

@ -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"

View File

@ -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 <QskSkinFactory.h>
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

View File

@ -0,0 +1,13 @@
include( $${PWD}/../skins.pri )
TARGET = materialskin
HEADERS += \
QskMaterialSkin.h \
QskMaterialSkinFactory.h
SOURCES += \
QskMaterialSkin.cpp \
QskMaterialSkinFactory.cpp
OTHER_FILES += metadata.json

View File

@ -0,0 +1,4 @@
{
"FactoryId": "MaterialFactory",
"Skins": [ "material" ]
}

31
skins/skins.pri Normal file
View File

@ -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
}
}

7
skins/skins.pro Normal file
View File

@ -0,0 +1,7 @@
include( $${PWD}/../qskconfig.pri )
TEMPLATE = subdirs
SUBDIRS += \
squiek \
material

View File

@ -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

View File

@ -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"

View File

@ -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 <QskSkinFactory.h>
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

View File

@ -0,0 +1,4 @@
{
"FactoryId": "SquiekFactory",
"Skins": [ "squiek" ]
}

13
skins/squiek/squiek.pro Normal file
View File

@ -0,0 +1,13 @@
include( $${PWD}/../skins.pri )
TARGET = squiekskin
HEADERS += \
QskSquiekSkin.h \
QskSquiekSkinFactory.h
SOURCES += \
QskSquiekSkin.cpp \
QskSquiekSkinFactory.cpp
OTHER_FILES += metadata.json

View File

@ -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 <QList>
#include <QStringList>
#include <QMarginsF>
@ -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 );

View File

@ -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();
}

View File

@ -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* );

View File

@ -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 <QHash>
#include <QPointer>
#include <QGlobalStatic>
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"

View File

@ -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

View File

@ -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 <QGlobalStatic>
#include <QDir>
#include <QPluginLoader>
#include <QJsonObject>
#include <QJsonArray>
#include <QMap>
#include <QSet>
#include <QPointer>
/*
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"

View File

@ -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 <QObject>
#include <memory>
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

View File

@ -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 \

47
support/SkinnyPlugin.cpp Normal file
View File

@ -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 <QskSkinManager.h>
#include <squiek/QskSquiekSkinFactory.h>
#include <material/QskMaterialSkinFactory.h>
#include <QCoreApplication>
#include <QByteArray>
#include <QDir>
#include <iostream>
#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 )

16
support/SkinnyPlugin.h Normal file
View File

@ -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

View File

@ -1,8 +1,8 @@
#include "SkinnyShortcut.h"
#include <QskSkinFactory.h>
#include <QskShortcutMap.h>
#include <QskSetup.h>
#include <QskSkinManager.h>
#include <QskWindow.h>
#include <QskAspect.h>
#include <QskSkin.h>
@ -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;

View File

@ -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