diff --git a/autotests/kcolorschemetest.cpp b/autotests/kcolorschemetest.cpp index 890e232..f3df209 100644 --- a/autotests/kcolorschemetest.cpp +++ b/autotests/kcolorschemetest.cpp @@ -1,62 +1,62 @@ /* Copyright (c) 2019 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include "kcolorscheme.h" #include "kcolorschememanager.h" class KColorSchemeTest : public QObject { Q_OBJECT private Q_SLOTS: void benchConstruction_data() { KColorSchemeManager manager; - if (!manager.model()->rowCount()) { + if (manager.model()->rowCount() <= 1) { QSKIP("no scheme files found, cannot run benchmark"); } - const auto anyScheme = manager.model()->index(0, 0).data(Qt::UserRole).toString(); + const auto anyScheme = manager.model()->index(1, 0).data(Qt::UserRole).toString(); QVERIFY(QFile::exists(anyScheme)); QTest::addColumn("file"); QTest::newRow("default") << QString(); QTest::newRow("explicit") << anyScheme; } void benchConstruction() { QFETCH(QString, file); qApp->setProperty("KDE_COLOR_SCHEME_PATH", file); QBENCHMARK { KColorScheme scheme(QPalette::Active); } } }; QTEST_MAIN(KColorSchemeTest) #include "kcolorschemetest.moc" diff --git a/src/kcolorschememanager.cpp b/src/kcolorschememanager.cpp index 30942c5..a4d1635 100644 --- a/src/kcolorschememanager.cpp +++ b/src/kcolorschememanager.cpp @@ -1,219 +1,234 @@ /* This file is part of the KDE project * Copyright (C) 2013 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kcolorschememanager.h" #include "kcolorschememanager_p.h" #include #include #include +#include #include #include #include #include #include #include #include +#include + +constexpr int defaultSchemeRow = 0; KColorSchemeManagerPrivate::KColorSchemeManagerPrivate() : model(new KColorSchemeModel()) { } KColorSchemeModel::KColorSchemeModel(QObject *parent) : QAbstractListModel(parent) { init(); } KColorSchemeModel::~KColorSchemeModel() { } int KColorSchemeModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_data.count(); } QVariant KColorSchemeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || (index.row() >= m_data.count())) { return QVariant(); } switch (role) { case Qt::DisplayRole: return m_data.at(index.row()).name; case Qt::DecorationRole: { auto &item = m_data[index.row()]; if (item.preview.isNull()) { item.preview = createPreview(item.path); } return item.preview; } case Qt::UserRole: return m_data.at(index.row()).path; default: return QVariant(); } } void KColorSchemeModel::init() { beginResetModel(); m_data.clear(); const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); QStringList schemeFiles; for (const QString &dir : dirs) { const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.colors")); for (const QString &file : fileNames) { schemeFiles << dir + QDir::separator() + file; } } for (const QString &schemeFile : qAsConst(schemeFiles)) { KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig); KConfigGroup group(config, QStringLiteral("General")); const QString name = group.readEntry("Name", QFileInfo(schemeFile).baseName()); const KColorSchemeModelData data = {name, schemeFile, QIcon()}; m_data.append(data); } std::sort(m_data.begin(), m_data.end(), [](const KColorSchemeModelData & first, const KColorSchemeModelData & second) { return first.name < second.name; }); + m_data.insert(defaultSchemeRow, {i18n("Default"), QString(), QIcon::fromTheme("edit-undo")}); endResetModel(); } QIcon KColorSchemeModel::createPreview(const QString &path) const { KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path, KConfig::SimpleConfig); QIcon result; KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig); KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig); KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig); KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig); auto pixmap = [&](int size) { QPixmap pix(size, size); pix.fill(Qt::black); QPainter p; p.begin(&pix); const int itemSize = size / 2 - 1; p.fillRect(1, 1, itemSize, itemSize, activeWindow.background()); p.fillRect(1 + itemSize, 1, itemSize, itemSize, activeButton.background()); p.fillRect(1, 1 + itemSize, itemSize, itemSize, activeView.background()); p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, activeSelection.background()); p.end(); result.addPixmap(pix); }; // 16x16 pixmap(16); // 24x24 pixmap(24); return result; } KColorSchemeManager::KColorSchemeManager(QObject *parent) : QObject(parent) , d(new KColorSchemeManagerPrivate) { } KColorSchemeManager::~KColorSchemeManager() { } QAbstractItemModel *KColorSchemeManager::model() const { return d->model.data(); } QModelIndex KColorSchemeManager::indexForScheme(const QString &name) const { - for (int i = 0; i < d->model->rowCount(); ++i) { + // Empty string is mapped to "reset to the system scheme" + if (name.isEmpty()) { + return d->model->index(defaultSchemeRow); + } + for (int i = 1; i < d->model->rowCount(); ++i) { QModelIndex index = d->model->index(i); if (index.data().toString() == name) { return index; } } return QModelIndex(); } KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(const QIcon &icon, const QString &name, const QString &selectedSchemeName, QObject *parent) { KActionMenu *menu = new KActionMenu(icon, name, parent); QActionGroup *group = new QActionGroup(menu); - connect(group, &QActionGroup::triggered, [](QAction * action) { - // hint for the style to synchronize the color scheme with the window manager/compositor - qApp->setProperty("KDE_COLOR_SCHEME_PATH", action->data()); - qApp->setPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(action->data().toString()))); + connect(group, &QActionGroup::triggered, this, [this](QAction * action) { + activateScheme(d->model->index(action->data().toInt())); }); for (int i = 0; i < d->model->rowCount(); ++i) { QModelIndex index = d->model->index(i); QAction *action = new QAction(index.data(Qt::DisplayRole).toString(), menu); - action->setData(index.data(Qt::UserRole)); + action->setData(index.row()); action->setActionGroup(group); action->setCheckable(true); if (index.data().toString() == selectedSchemeName) { action->setChecked(true); } menu->addAction(action); } - + if (!group->checkedAction()) { + // If no (valid) color scheme has been selected we select the default one + group->actions()[defaultSchemeRow]->setChecked(true); + } connect(menu->menu(), &QMenu::aboutToShow, group, [this, group] { const auto actions = group->actions(); for (QAction *action : actions) { if (action->icon().isNull()) { - action->setIcon(d->model->createPreview(action->data().toString())); + action->setIcon(d->model->index(action->data().toInt()).data(Qt::DecorationRole).value()); } } }); return menu; } KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(const QString &text, const QString &selectedSchemeName, QObject *parent) { - return createSchemeSelectionMenu(QIcon(), text, selectedSchemeName, parent); + return createSchemeSelectionMenu(QIcon::fromTheme("preferences-desktop-color"), text, selectedSchemeName, parent); } KActionMenu *KColorSchemeManager::createSchemeSelectionMenu(const QString &selectedSchemeName, QObject *parent) { - return createSchemeSelectionMenu(QIcon(), QString(), selectedSchemeName, parent); + return createSchemeSelectionMenu(QIcon::fromTheme("preferences-desktop-color"), i18n("Color Scheme"), selectedSchemeName, parent); +} + +KActionMenu *KColorSchemeManager::createSchemeSelectionMenu (QObject *parent) +{ + return createSchemeSelectionMenu(QIcon::fromTheme("preferences-desktop-color"), i18n("Color Scheme"), QString(), parent); } void KColorSchemeManager::activateScheme(const QModelIndex &index) { - if (!index.isValid()) { - return; - } - if (index.model() != d->model.data()) { - return; - } - // hint for the style to synchronize the color scheme with the window manager/compositor + // hint for plasma-integration to synchronize the color scheme with the window manager/compositor + // The property needs to be set before the palette change because is is checked upon the + // ApplicationPaletteChange event. qApp->setProperty("KDE_COLOR_SCHEME_PATH", index.data(Qt::UserRole)); - qApp->setPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(index.data(Qt::UserRole).toString()))); + if (index.isValid() && index.model() == d->model.data() && index.row() != defaultSchemeRow) { + qApp->setPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(index.data(Qt::UserRole).toString()))); + } else { + qApp->setPalette(qApp->style()->standardPalette()); + } } diff --git a/src/kcolorschememanager.h b/src/kcolorschememanager.h index 381a106..e0e1d29 100644 --- a/src/kcolorschememanager.h +++ b/src/kcolorschememanager.h @@ -1,125 +1,134 @@ /* This file is part of the KDE project * Copyright (C) 2013 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KCOLORSCHEMEMANAGER_H #define KCOLORSCHEMEMANAGER_H #include #include class QAbstractItemModel; class QModelIndex; class QIcon; class KActionMenu; class KColorSchemeManagerPrivate; /** * @class KColorSchemeManager kcolorschememanager.h KColorSchemeManager * * A small helper to get access to all available color schemes and activating a scheme in the * QApplication. This is useful for applications which want to provide a selection of custom color * schemes to their user. For example it is very common for photo and painting applications to use - * a dark color scheme even if the default is a light scheme. + * a dark color scheme even if the default is a light scheme. Since version 5.67 it also allows + * going back to following the system color scheme. * * The KColorSchemeManager provides access to a QAbstractItemModel which holds all the available * schemes. A possible usage looks like the following: * * @code * KColorSchemeManager *schemes = new KColorSchemeManager(this); * QListView *view = new QListView(this); * view->setModel(schemes->model()); * connect(view, &QListView::activated, schemes, &KColorSchemeManager::activateScheme); * @endcode * * In addition the KColorSchemeManager also provides the possibility to create a KActionMenu populated * with all the available color schemes in an action group. If one of the actions is selected the * scheme is applied instantly: * * @code * QToolButton *button = new QToolButton(); * KColorSchemeManager *schemes = new KColorSchemeManager(this); * KActionMenu *menu = schemes->createSchemeSelectionMenu(QStringLiteral("Oxygen"), button); * button->setMenu(menu->menu()); * @endcode * * @since 5.0 */ class KCONFIGWIDGETS_EXPORT KColorSchemeManager : public QObject { Q_OBJECT public: explicit KColorSchemeManager(QObject *parent = nullptr); virtual ~KColorSchemeManager(); /** * A QAbstractItemModel of all available color schemes. * * The model provides the name of the scheme in Qt::DisplayRole, a preview - * in Qt::DelegateRole and the full path to the scheme file in Qt::UserRole. + * in Qt::DelegateRole and the full path to the scheme file in Qt::UserRole. The system theme + * has an empty Qt::UserRole. * * @return Model of all available color schemes. */ QAbstractItemModel *model() const; /** * Returns the model index for the scheme with the given @p name. If no such - * scheme exists an invalid index is returned. + * scheme exists an invalid index is returned. If you pass an empty + * string the index that is equivalent to going back to following the system scheme is returned + * for versions 5.67 and newer. * @see model */ QModelIndex indexForScheme(const QString &name) const; /** * Creates a KActionMenu populated with all the available color schemes. * All actions are in an action group and when one of the actions is triggered the scheme * referenced by this action is activated. * * The color scheme with the same name as @p selectedSchemeName will be checked. If none - * of the available color schemes has the same name, no action will be checked. + * of the available color schemes has the same name, the system theme entry will be checked. * * The KActionMenu will not be updated in case the installed color schemes change. It's the * task of the user of the KActionMenu to monitor for changes if required. * * @param icon The icon to use for the KActionMenu * @param text The text to use for the KActionMenu * @param selectedSchemeName The name of the color scheme to select * @param parent The parent of the KActionMenu * @return KActionMenu populated with all available color schemes. * @see activateScheme */ KActionMenu *createSchemeSelectionMenu(const QIcon &icon, const QString &text, const QString &selectedSchemeName, QObject *parent); KActionMenu *createSchemeSelectionMenu(const QString &text, const QString &selectedSchemeName, QObject *parent); KActionMenu *createSchemeSelectionMenu(const QString &selectedSchemeName, QObject *parent); + /** + * @since 5.67 + */ + KActionMenu *createSchemeSelectionMenu(QObject *parent); public Q_SLOTS: /** * @brief Activates the KColorScheme identified by the provided @p index. * * Installs the KColorScheme as the QApplication's QPalette. * * @param index The index for the KColorScheme to activate. - * The index must reference the QAbstractItemModel provided by @link model @endlink + * The index must reference the QAbstractItemModel provided by @link model @endlink. Since + * version 5.67 passing an invalid index activates the system scheme. * @see model() */ void activateScheme(const QModelIndex &index); private: QScopedPointer d; }; #endif