diff --git a/src/libkirigami/platformtheme.cpp b/src/libkirigami/platformtheme.cpp index bafc206d..8a9d2728 100644 --- a/src/libkirigami/platformtheme.cpp +++ b/src/libkirigami/platformtheme.cpp @@ -1,754 +1,755 @@ /* * Copyright (C) 2017 by Marco Martin * * This program 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, or * (at your option) any later version. * * This program 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 program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "platformtheme.h" #include "kirigamipluginfactory.h" #include "basictheme_p.h" #include #include #include #include #include #include #include #include #include namespace Kirigami { class PlatformThemePrivate { public: PlatformThemePrivate(PlatformTheme *q); ~PlatformThemePrivate(); inline void syncCustomPalette(); void emitCompressedColorChanged(); void findParentStyle(); static QColor tint(const QColor &c1, const QColor &c2, qreal ratio); PlatformTheme *q; PlatformTheme::ColorSet m_colorSet = PlatformTheme::Window; PlatformTheme::ColorGroup m_colorGroup = PlatformTheme::Active; QSet m_childThemes; QPointer m_parentTheme; //ordinary colors QColor textColor; QColor disabledTextColor; QColor highlightedTextColor; QColor activeTextColor; QColor linkColor; QColor visitedLinkColor; QColor negativeTextColor; QColor neutralTextColor; QColor positiveTextColor; QColor backgroundColor; QColor alternateBackgroundColor; QColor highlightColor; QColor focusColor; QColor hoverColor; QPalette palette; //custom colors QColor customTextColor; QColor customDisabledTextColor; QColor customHighlightedTextColor; QColor customActiveTextColor; QColor customLinkColor; QColor customVisitedLinkColor; QColor customNegativeTextColor; QColor customNeutralTextColor; QColor customPositiveTextColor; QColor customBackgroundColor; QColor customAlternateBackgroundColor; QColor customHighlightColor; QColor customFocusColor; QColor customHoverColor; QPalette customPalette; QFont font; bool m_inherit = true; bool m_init = true; bool m_supportsIconColoring = false; bool m_pendingColorChange = false; static KirigamiPluginFactory *s_pluginFactory; }; KirigamiPluginFactory *PlatformThemePrivate::s_pluginFactory = nullptr; PlatformThemePrivate::PlatformThemePrivate(PlatformTheme *q) : q(q) { } PlatformThemePrivate::~PlatformThemePrivate() {} void setPaletteColor(QPalette& customPalette, QPalette::ColorGroup cg, QPalette::ColorRole cr, const QColor &color) { if (customPalette.color(cg, cr) != color) { customPalette.setColor(cg, cr, color); } } void PlatformThemePrivate::syncCustomPalette() { for (auto state : { QPalette::Active, QPalette::Inactive, QPalette::Disabled }) { setPaletteColor(customPalette, state, QPalette::WindowText, q->textColor()); setPaletteColor(customPalette, state, QPalette::Window, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::Base, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::Text, q->textColor()); setPaletteColor(customPalette, state, QPalette::Button, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::ButtonText, q->textColor()); setPaletteColor(customPalette, state, QPalette::Highlight, q->highlightColor()); setPaletteColor(customPalette, state, QPalette::HighlightedText, q->highlightedTextColor()); setPaletteColor(customPalette, state, QPalette::ToolTipBase, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::ToolTipText, q->textColor()); setPaletteColor(customPalette, state, QPalette::Link, q->linkColor()); setPaletteColor(customPalette, state, QPalette::LinkVisited, q->visitedLinkColor()); } emit q->paletteChanged(customPalette); } void PlatformThemePrivate::findParentStyle() { if (m_parentTheme) { m_parentTheme->d->m_childThemes.remove(q); } QQuickItem *candidate = qobject_cast(q->parent()); while (candidate) { candidate = candidate->parentItem(); PlatformTheme *t = static_cast(qmlAttachedPropertiesObject(candidate, false)); if (t) { t->d->m_childThemes.insert(q); m_parentTheme = t; if (m_inherit) { q->setColorSet(t->colorSet()); q->setCustomTextColor(t->d->customTextColor); q->setCustomDisabledTextColor(t->d->customDisabledTextColor); q->setCustomHighlightedTextColor(t->d->customHighlightedTextColor); q->setCustomActiveTextColor(t->d->customActiveTextColor); q->setCustomLinkColor(t->d->customLinkColor); q->setCustomVisitedLinkColor(t->d->customVisitedLinkColor); q->setCustomNegativeTextColor(t->d->customNegativeTextColor); q->setCustomNeutralTextColor(t->d->customNeutralTextColor); q->setCustomPositiveTextColor(t->d->customPositiveTextColor); q->setCustomBackgroundColor(t->d->customBackgroundColor); q->setCustomAlternateBackgroundColor(t->d->customAlternateBackgroundColor); q->setCustomHighlightColor(t->d->customHighlightColor); q->setCustomFocusColor(t->d->customFocusColor); q->setCustomHoverColor(t->d->customHoverColor); } break; } } } QColor PlatformThemePrivate::tint(const QColor &c1, const QColor &c2, qreal ratio) { qreal r = c1.redF() + (c2.redF() - c1.redF()) * ratio; qreal g = c1.greenF() + (c2.greenF() - c1.greenF()) * ratio; qreal b = c1.blueF() + (c2.blueF() - c1.blueF()) * ratio; return QColor::fromRgbF(r, g, b, 1); } void PlatformThemePrivate::emitCompressedColorChanged() { if (m_pendingColorChange) { return; } m_pendingColorChange = true; QMetaObject::invokeMethod(q, [this]() { syncCustomPalette(); emit q->colorsChanged(); m_pendingColorChange = false; }, Qt::QueuedConnection); } PlatformTheme::PlatformTheme(QObject *parent) : QObject(parent), d(new PlatformThemePrivate(this)) { d->findParentStyle(); if (QQuickItem *item = qobject_cast(parent)) { connect(item, &QQuickItem::windowChanged, this, [this]() { d->findParentStyle(); }); connect(item, &QQuickItem::parentChanged, this, [this]() { d->findParentStyle(); }); } d->m_init = false; //TODO: needs https://codereview.qt-project.org/#/c/206889/ for font changes } PlatformTheme::~PlatformTheme() { if (d->m_parentTheme) { d->m_parentTheme->d->m_childThemes.remove(this); } delete d; } void PlatformTheme::setColorSet(PlatformTheme::ColorSet colorSet) { if (d->m_colorSet == colorSet) { return; } d->m_colorSet = colorSet; for (PlatformTheme *t : qAsConst(d->m_childThemes)) { if (t->inherit()) { t->setColorSet(colorSet); } } if (!d->m_init) { emit colorSetChanged(colorSet); d->emitCompressedColorChanged(); } } PlatformTheme::ColorSet PlatformTheme::colorSet() const { return d->m_colorSet; } void PlatformTheme::setColorGroup(PlatformTheme::ColorGroup colorGroup) { if (d->m_colorGroup == colorGroup) { return; } d->m_colorGroup = colorGroup; for (PlatformTheme *t : qAsConst(d->m_childThemes)) { if (t->inherit()) { t->setColorGroup(colorGroup); } } if (!d->m_init) { emit colorGroupChanged(colorGroup); d->emitCompressedColorChanged(); } } PlatformTheme::ColorGroup PlatformTheme::colorGroup() const { return d->m_colorGroup; } bool PlatformTheme::inherit() const { return d->m_inherit; } void PlatformTheme::setInherit(bool inherit) { if (d->m_inherit == inherit) { return; } d->m_inherit = inherit; if (inherit && d->m_parentTheme) { setColorSet(d->m_parentTheme->colorSet()); } emit inheritChanged(inherit); } QColor PlatformTheme::textColor() const { return d->customTextColor.isValid() ? d->customTextColor : d->textColor; } QColor PlatformTheme::disabledTextColor() const { return d->customDisabledTextColor.isValid() ? d->customDisabledTextColor : d->disabledTextColor; } QColor PlatformTheme::highlightColor() const { return d->customHighlightColor.isValid() ? d->customHighlightColor : d->highlightColor; } QColor PlatformTheme::highlightedTextColor() const { return d->customHighlightedTextColor.isValid() ? d->customHighlightedTextColor : d->highlightedTextColor; } QColor PlatformTheme::backgroundColor() const { return d->customBackgroundColor.isValid() ? d->customBackgroundColor : d->backgroundColor; } QColor PlatformTheme::alternateBackgroundColor() const { return d->customAlternateBackgroundColor.isValid() ? d->customAlternateBackgroundColor : d->alternateBackgroundColor; } QColor PlatformTheme::activeTextColor() const { return d->customActiveTextColor.isValid() ? d->customActiveTextColor : d->activeTextColor; } QColor PlatformTheme::linkColor() const { return d->customLinkColor.isValid() ? d->customLinkColor : d->linkColor; } QColor PlatformTheme::visitedLinkColor() const { return d->customVisitedLinkColor.isValid() ? d->customVisitedLinkColor : d->visitedLinkColor; } QColor PlatformTheme::negativeTextColor() const { return d->customNegativeTextColor.isValid() ? d->customNegativeTextColor : d->negativeTextColor; } QColor PlatformTheme::neutralTextColor() const { return d->customNeutralTextColor.isValid() ? d->customNeutralTextColor : d->neutralTextColor; } QColor PlatformTheme::positiveTextColor() const { return d->customPositiveTextColor.isValid() ? d->customPositiveTextColor : d->positiveTextColor; } QColor PlatformTheme::focusColor() const { return d->customFocusColor.isValid() ? d->customFocusColor : d->focusColor; } QColor PlatformTheme::hoverColor() const { return d->customHoverColor.isValid() ? d->customHoverColor : d->hoverColor; } //setters for theme implementations void PlatformTheme::setTextColor(const QColor &color) { if (d->textColor == color) { return; } d->textColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setDisabledTextColor(const QColor &color) { if (d->disabledTextColor == color) { return; } d->disabledTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setBackgroundColor(const QColor &color) { if (d->backgroundColor == color) { return; } d->backgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setAlternateBackgroundColor(const QColor &color) { if (d->alternateBackgroundColor == color) { return; } d->alternateBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setHighlightColor(const QColor &color) { if (d->highlightColor == color) { return; } d->highlightColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setHighlightedTextColor(const QColor &color) { if (d->highlightedTextColor == color) { return; } d->highlightedTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setActiveTextColor(const QColor &color) { if (d->activeTextColor == color) { return; } d->activeTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setLinkColor(const QColor &color) { if (d->linkColor == color) { return; } d->linkColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setVisitedLinkColor(const QColor &color) { if (d->visitedLinkColor == color) { return; } d->visitedLinkColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setNegativeTextColor(const QColor &color) { if (d->negativeTextColor == color) { return; } d->negativeTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setNeutralTextColor(const QColor &color) { if (d->neutralTextColor == color) { return; } d->neutralTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setPositiveTextColor(const QColor &color) { if (d->positiveTextColor == color) { return; } d->positiveTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setHoverColor(const QColor &color) { if (d->hoverColor == color) { return; } d->hoverColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setFocusColor(const QColor &color) { if (d->focusColor == color) { return; } d->focusColor = color; d->emitCompressedColorChanged(); } QFont PlatformTheme::defaultFont() const { return d->font; } void PlatformTheme::setDefaultFont(const QFont &font) { if (d->font == font) { return; } d->font = font; emit defaultFontChanged(font); } #define PROPAGATECUSTOMCOLOR(colorName, color)\ for (PlatformTheme *t : qAsConst(d->m_childThemes)) {\ if (t->inherit()) {\ t->set##colorName(color);\ }\ } //setters for QML clients void PlatformTheme::setCustomTextColor(const QColor &color) { if (d->customTextColor == color) { return; } d->customTextColor = color; PROPAGATECUSTOMCOLOR(CustomTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomDisabledTextColor(const QColor &color) { if (d->customDisabledTextColor == color) { return; } d->customDisabledTextColor = color; PROPAGATECUSTOMCOLOR(CustomDisabledTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomBackgroundColor(const QColor &color) { if (d->customBackgroundColor == color) { return; } d->customBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomAlternateBackgroundColor(const QColor &color) { if (d->customAlternateBackgroundColor == color) { return; } d->customAlternateBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomAlternateBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomHighlightColor(const QColor &color) { if (d->customHighlightColor == color) { return; } d->customHighlightColor = color; PROPAGATECUSTOMCOLOR(CustomHighlightColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomHighlightedTextColor(const QColor &color) { if (d->customHighlightedTextColor == color) { return; } d->customHighlightedTextColor = color; PROPAGATECUSTOMCOLOR(CustomHighlightedTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomActiveTextColor(const QColor &color) { if (d->customActiveTextColor == color) { return; } d->customActiveTextColor = color; PROPAGATECUSTOMCOLOR(CustomActiveTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomLinkColor(const QColor &color) { if (d->customLinkColor == color) { return; } d->customLinkColor = color; PROPAGATECUSTOMCOLOR(CustomLinkColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomVisitedLinkColor(const QColor &color) { if (d->customVisitedLinkColor == color) { return; } d->customVisitedLinkColor = color; PROPAGATECUSTOMCOLOR(CustomVisitedLinkColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomNegativeTextColor(const QColor &color) { if (d->customNegativeTextColor == color) { return; } d->customNegativeTextColor = color; PROPAGATECUSTOMCOLOR(CustomNegativeTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomNeutralTextColor(const QColor &color) { if (d->customNeutralTextColor == color) { return; } d->customNeutralTextColor = color; PROPAGATECUSTOMCOLOR(CustomNeutralTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomPositiveTextColor(const QColor &color) { if (d->customPositiveTextColor == color) { return; } d->customPositiveTextColor = color; PROPAGATECUSTOMCOLOR(CustomPositiveTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomHoverColor(const QColor &color) { if (d->customHoverColor == color) { return; } d->customHoverColor = color; PROPAGATECUSTOMCOLOR(CustomHoverColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomFocusColor(const QColor &color) { if (d->customFocusColor == color) { return; } d->customFocusColor = color; PROPAGATECUSTOMCOLOR(CustomFocusColor, color) d->emitCompressedColorChanged(); } QPalette PlatformTheme::palette() const { //check the most important custom colors to decide to return a custom palette return d->customTextColor.isValid() || d->customBackgroundColor.isValid() || d->customHighlightColor.isValid() ? d->customPalette : d->palette; } void PlatformTheme::setPalette(const QPalette &palette) { if (d->palette == palette) { return; } d->palette = palette; PROPAGATECUSTOMCOLOR(Palette, palette) emit paletteChanged(palette); } QIcon PlatformTheme::iconFromTheme(const QString &name, const QColor &customColor) { + Q_UNUSED(customColor); QIcon icon = QIcon::fromTheme(name); return icon; } bool PlatformTheme::supportsIconColoring() const { return d->m_supportsIconColoring; } void PlatformTheme::setSupportsIconColoring(bool support) { d->m_supportsIconColoring = support; } PlatformTheme *PlatformTheme::qmlAttachedProperties(QObject *object) { static bool s_factoryChecked = false; //check for the plugin only once: it's an heavy operation if (PlatformThemePrivate::s_pluginFactory) { return PlatformThemePrivate::s_pluginFactory->createPlatformTheme(object); } else if (!s_factoryChecked) { s_factoryChecked = true; #if QT_CONFIG(library) const auto libraryPaths = QCoreApplication::libraryPaths(); for (const QString &path : libraryPaths) { QDir dir(path + QStringLiteral("/kf5/kirigami")); const auto fileNames = dir.entryList(QDir::Files); for (const QString &fileName : fileNames) { //TODO: env variable? if (!QQuickStyle::name().isEmpty() && fileName.startsWith(QQuickStyle::name())) { QPluginLoader loader(dir.absoluteFilePath(fileName)); QObject *plugin = loader.instance(); //TODO: load actually a factory as plugin KirigamiPluginFactory *factory = qobject_cast(plugin); if (factory) { PlatformThemePrivate::s_pluginFactory = factory; return factory->createPlatformTheme(object); } } } } #endif } return new BasicTheme(object); } } #include "moc_platformtheme.cpp"