diff --git a/src/controls/BasicListItem.qml b/src/controls/BasicListItem.qml index 34477653..9a55c5b1 100644 --- a/src/controls/BasicListItem.qml +++ b/src/controls/BasicListItem.qml @@ -1,116 +1,115 @@ /* * Copyright 2010 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 2.010-1301, USA. */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 /** * An item delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * It has a default icon and a label * */ AbstractListItem { id: listItem /** * string: bool * A single text label the list item will contain */ property alias label: listItem.text /** * icon: var * A single icon that will be displayed in the list item. * The icon can be a grouped property with name,size,color etc, as QtQuickControls2 icons are defined. * The icon can also be either a QIcon, a string name of a fdo compatible name, * or any url accepted by the Image element. */ property var icon /** * iconSize: int * The preferred size for the icon * @since 2.5 */ property alias iconSize: iconItem.size /** * iconColor: color * The color the icon should be colorized to. * By default it will be the text color. * If the icon shouldn't be colorized in any way, set it to "transparent" * @since 2.7 */ property alias iconColor: iconItem.color /** * reserveSpaceForIcon: bool * If true, even when there is no icon the space will be reserved for it * It's useful in layouts where only some entries have an icon, * having the text all horizontally aligned */ property alias reserveSpaceForIcon: iconItem.visible /** * reserveSpaceForLabel: bool * If false, the label will not try to be as wide as possible * It's useful in layouts containing entries without text */ property alias reserveSpaceForLabel: labelItem.visible default property alias _basicDefault: layout.children contentItem: RowLayout { id: layout spacing: LayoutMirroring.enabled ? listItem.rightPadding : listItem.leftPadding property bool indicateActiveFocus: listItem.pressed || Settings.tabletMode || listItem.activeFocus || (listItem.ListView.view ? listItem.ListView.view.activeFocus : false) Icon { id: iconItem source: { if (listItem.icon && listItem.icon.hasOwnProperty) { if (listItem.icon.hasOwnProperty("name") && listItem.icon.name !== "") return listItem.icon.name; if (listItem.icon.hasOwnProperty("source")) return listItem.icon.source; } return listItem.icon; } property int size: Units.iconSizes.smallMedium Layout.minimumHeight: size Layout.maximumHeight: size Layout.minimumWidth: size selected: layout.indicateActiveFocus && (listItem.highlighted || listItem.checked || (listItem.pressed && listItem.supportsMouseEvents)) - color: selected ? Theme.highlightedTextColor : listItem.Theme.textColor opacity: 1 } QQC2.Label { id: labelItem text: listItem.text Layout.fillWidth: true color: layout.indicateActiveFocus && (listItem.highlighted || listItem.checked || (listItem.pressed && listItem.supportsMouseEvents)) ? listItem.activeTextColor : listItem.textColor elide: Text.ElideRight font: listItem.font opacity: 1 } } } diff --git a/src/desktopicon.cpp b/src/desktopicon.cpp index d267f9be..49f1f743 100644 --- a/src/desktopicon.cpp +++ b/src/desktopicon.cpp @@ -1,532 +1,602 @@ /* * Copyright 2011 Marco Martin * Copyright 2014 Aleix Pol Gonzalez * * 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 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 "desktopicon.h" #include "libkirigami/platformtheme.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class ManagedTextureNode : public QSGSimpleTextureNode { Q_DISABLE_COPY(ManagedTextureNode) public: ManagedTextureNode(); void setTexture(QSharedPointer texture); private: QSharedPointer m_texture; }; ManagedTextureNode::ManagedTextureNode() {} void ManagedTextureNode::setTexture(QSharedPointer texture) { m_texture = texture; QSGSimpleTextureNode::setTexture(texture.data()); } typedef QHash > > TexturesCache; struct ImageTexturesCachePrivate { TexturesCache cache; }; class ImageTexturesCache { public: ImageTexturesCache(); ~ImageTexturesCache(); /** * @returns the texture for a given @p window and @p image. * * If an @p image id is the same as one already provided before, we won't create * a new texture and return a shared pointer to the existing texture. */ QSharedPointer loadTexture(QQuickWindow *window, const QImage &image, QQuickWindow::CreateTextureOptions options); QSharedPointer loadTexture(QQuickWindow *window, const QImage &image); private: QScopedPointer d; }; ImageTexturesCache::ImageTexturesCache() : d(new ImageTexturesCachePrivate) { } ImageTexturesCache::~ImageTexturesCache() { } QSharedPointer ImageTexturesCache::loadTexture(QQuickWindow *window, const QImage &image, QQuickWindow::CreateTextureOptions options) { qint64 id = image.cacheKey(); QSharedPointer texture = d->cache.value(id).value(window).toStrongRef(); if (!texture) { auto cleanAndDelete = [this, window, id](QSGTexture* texture) { QHash >& textures = (d->cache)[id]; textures.remove(window); if (textures.isEmpty()) d->cache.remove(id); delete texture; }; texture = QSharedPointer(window->createTextureFromImage(image, options), cleanAndDelete); (d->cache)[id][window] = texture.toWeakRef(); } //if we have a cache in an atlas but our request cannot use an atlassed texture //create a new texture and use that //don't use removedFromAtlas() as that requires keeping a reference to the non atlased version if (!(options & QQuickWindow::TextureCanUseAtlas) && texture->isAtlasTexture()) { texture = QSharedPointer(window->createTextureFromImage(image, options)); } return texture; } QSharedPointer ImageTexturesCache::loadTexture(QQuickWindow *window, const QImage &image) { return loadTexture(window, image, nullptr); } Q_GLOBAL_STATIC(ImageTexturesCache, s_iconImageCache) DesktopIcon::DesktopIcon(QQuickItem *parent) : QQuickItem(parent), m_smooth(false), m_changed(false), m_active(false), m_selected(false), m_isMask(false) { setFlag(ItemHasContents, true); //FIXME: not necessary anymore connect(qApp, &QGuiApplication::paletteChanged, this, [this]() { m_changed = true; update(); }); connect(this, &QQuickItem::enabledChanged, this, [this]() { m_changed = true; update(); }); } DesktopIcon::~DesktopIcon() { } void DesktopIcon::setSource(const QVariant &icon) { if (m_source == icon) { return; } m_source = icon; m_changed = true; + m_monochromeHeuristics.clear(); if (!m_theme) { m_theme = static_cast(qmlAttachedPropertiesObject(this, true)); Q_ASSERT(m_theme); connect(m_theme, &Kirigami::PlatformTheme::colorsChanged, this, [this]() { m_changed = true; update(); }); } + if (icon.type() == QVariant::String) { + const QString iconSource = icon.toString(); + m_isMaskHeuristic = (iconSource.endsWith(QStringLiteral("-symbolic")) + || iconSource.endsWith(QStringLiteral("-symbolic-rtl")) + || iconSource.endsWith(QStringLiteral("-symbolic-ltr"))); + emit isMaskChanged(); + } + if (m_networkReply) { //if there was a network query going on, interrupt it m_networkReply->close(); } m_loadedImage = QImage(); update(); emit sourceChanged(); } QVariant DesktopIcon::source() const { return m_source; } void DesktopIcon::setActive(const bool active) { if (active == m_active) { return; } m_active = active; m_changed = true; update(); emit activeChanged(); } bool DesktopIcon::active() const { return m_active; } bool DesktopIcon::valid() const { return !m_source.isNull(); } void DesktopIcon::setSelected(const bool selected) { if (selected == m_selected) { return; } m_selected = selected; m_changed = true; update(); emit selectedChanged(); } bool DesktopIcon::selected() const { return m_selected; } void DesktopIcon::setIsMask(bool mask) { if (m_isMask == mask) { return; } m_isMask = mask; + m_isMaskHeuristic = mask; m_changed = true; update(); emit isMaskChanged(); } bool DesktopIcon::isMask() const { - return m_isMask; + return m_isMask || m_isMaskHeuristic; } void DesktopIcon::setColor(const QColor &color) { if (m_color == color) { return; } m_color = color; m_changed = true; update(); emit colorChanged(); } QColor DesktopIcon::color() const { return m_color; } int DesktopIcon::implicitWidth() const { return 32; } int DesktopIcon::implicitHeight() const { return 32; } void DesktopIcon::setSmooth(const bool smooth) { if (smooth == m_smooth) { return; } m_smooth = smooth; m_changed = true; update(); emit smoothChanged(); } bool DesktopIcon::smooth() const { return m_smooth; } QSGNode* DesktopIcon::updatePaintNode(QSGNode* node, QQuickItem::UpdatePaintNodeData* /*data*/) { if (m_source.isNull()) { delete node; return Q_NULLPTR; } if (m_changed || node == nullptr) { QImage img; const QSize itemSize(width(), height()); QRect nodeRect(QPoint(0,0), itemSize); if (itemSize.width() != 0 && itemSize.height() != 0) { const auto multiplier = QCoreApplication::instance()->testAttribute(Qt::AA_UseHighDpiPixmaps) ? 1 : (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()); const QSize size = itemSize * multiplier; switch(m_source.type()){ case QVariant::Pixmap: img = m_source.value().toImage(); break; case QVariant::Image: img = m_source.value(); break; case QVariant::Bitmap: img = m_source.value().toImage(); break; case QVariant::Icon: img = m_source.value().pixmap(size, iconMode(), QIcon::On).toImage(); break; case QVariant::Url: case QVariant::String: img = findIcon(size); break; case QVariant::Brush: //todo: fill here too? case QVariant::Color: img = QImage(size, QImage::Format_Alpha8); img.fill(m_source.value()); break; default: break; } if (img.isNull()){ img = QImage(size, QImage::Format_Alpha8); img.fill(Qt::transparent); } - if (img.size() != size){ + if (img.size() != size) { // At this point, the image will already be scaled, but we need to output it in // the correct aspect ratio, painted centered in the viewport. So: QRect destination(QPoint(0, 0), img.size().scaled(itemSize, Qt::KeepAspectRatio)); destination.moveCenter(nodeRect.center()); nodeRect = destination; } + + const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; + + //TODO: initialize m_isMask with icon.isMask() + if (tintColor.alpha() > 0 && (isMask() || guessMonochrome(img))) { + QPainter p(&img); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(img.rect(), tintColor); + p.end(); + } } m_changed = false; ManagedTextureNode* mNode = dynamic_cast(node); if (!mNode) { delete node; mNode = new ManagedTextureNode; } mNode->setTexture(s_iconImageCache->loadTexture(window(), img)); mNode->setRect(nodeRect); node = mNode; if (m_smooth) { mNode->setFiltering(QSGTexture::Linear); } } return node; } void DesktopIcon::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (newGeometry.size() != oldGeometry.size()) { m_changed = true; update(); } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void DesktopIcon::handleFinished(QNetworkAccessManager* qnam, QNetworkReply* reply) { if (reply && reply->error() == QNetworkReply::NoError) { const QUrl possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (!possibleRedirectUrl.isEmpty()) { const QUrl redirectUrl = reply->url().resolved(possibleRedirectUrl); if (redirectUrl == reply->url()) { // no infinite redirections thank you very much reply->deleteLater(); return; } reply->deleteLater(); QNetworkRequest request(possibleRedirectUrl); request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); m_networkReply = qnam->get(request); connect(m_networkReply.data(), &QNetworkReply::readyRead, this, [this](){handleReadyRead(m_networkReply); }); connect(m_networkReply.data(), &QNetworkReply::finished, this, [this, qnam](){handleFinished(qnam, m_networkReply); }); return; } } } void DesktopIcon::handleReadyRead(QNetworkReply* reply) { if (reply && reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isNull()) { // We're handing the event loop back while doing network work, and it turns out // this fairly regularly results in things being deleted under us. So, just // handle that and crash less :) QPointer me(this); QPointer guardedReply(reply); QByteArray data; do { data.append(guardedReply->read(32768)); // Because we are in the main thread, this could be potentially very expensive, so let's not block qApp->processEvents(); if(!me || !guardedReply) { return; } } while(!guardedReply->atEnd()); m_loadedImage = QImage::fromData(data); if (m_loadedImage.isNull()) { // broken image from data, inform the user of this with some useful broken-image thing... const QSize size = QSize(width(), height()) * (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()); m_loadedImage = QIcon::fromTheme(m_fallback).pixmap(size, iconMode(), QIcon::On).toImage(); } m_changed = true; update(); } } QImage DesktopIcon::findIcon(const QSize &size) { QImage img; QString iconSource = m_source.toString(); if (iconSource.startsWith(QLatin1String("image://"))) { QUrl iconUrl(iconSource); QString iconProviderId = iconUrl.host(); QString iconId = iconUrl.path(); // QRC paths are not correctly handled by .path() if (iconId.size() >=2 && iconId.startsWith(QLatin1String("/:"))) { iconId = iconId.remove(0, 1); } QSize actualSize; QQuickImageProvider* imageProvider = dynamic_cast( qmlEngine(this)->imageProvider(iconProviderId)); if (!imageProvider) return img; switch(imageProvider->imageType()){ case QQmlImageProviderBase::Image: img = imageProvider->requestImage(iconId, &actualSize, size); break; case QQmlImageProviderBase::Pixmap: img = imageProvider->requestPixmap(iconId, &actualSize, size).toImage(); break; case QQmlImageProviderBase::Texture: case QQmlImageProviderBase::Invalid: case QQmlImageProviderBase::ImageResponse: //will have to investigate this more break; } } else if(iconSource.startsWith(QLatin1String("http://")) || iconSource.startsWith(QLatin1String("https://"))) { if(!m_loadedImage.isNull()) { return m_loadedImage.scaled(size, Qt::KeepAspectRatio, m_smooth ? Qt::SmoothTransformation : Qt::FastTransformation ); } const auto url = m_source.toUrl(); QQmlEngine* engine = qmlEngine(this); QNetworkAccessManager* qnam; if (engine && (qnam = engine->networkAccessManager()) && (!m_networkReply || m_networkReply->url() != url)) { QNetworkRequest request(url); request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); m_networkReply = qnam->get(request); connect(m_networkReply.data(), &QNetworkReply::readyRead, this, [this](){ handleReadyRead(m_networkReply); }); connect(m_networkReply.data(), &QNetworkReply::finished, this, [this, qnam](){ handleFinished(qnam, m_networkReply); }); } // Temporary icon while we wait for the real image to load... img = QIcon::fromTheme(QStringLiteral("image-x-icon")).pixmap(size, iconMode(), QIcon::On).toImage(); } else { if (iconSource.startsWith(QLatin1String("qrc:/"))) { iconSource = iconSource.mid(3); } else if (iconSource.startsWith(QLatin1String("file:/"))) { iconSource = QUrl(iconSource).path(); } QIcon icon; const bool isPath = iconSource.contains(QLatin1String("/")); if (isPath) { icon = QIcon(iconSource); } else { if (icon.isNull()) { icon = m_theme->iconFromTheme(iconSource, m_color); } } if (!icon.isNull()) { img = icon.pixmap(size, iconMode(), QIcon::On).toImage(); - qreal ratio = 1; - if (window() && window()->screen()) { - ratio = window()->screen()->devicePixelRatio(); - } - const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; + /*const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; - if (m_isMask || - //this is an heuristic to decide when to tint and when to just draw - //(fullcolor icons) in reality on basic styles the only colored icons should be -symbolic, this heuristic is the most compatible middle ground - icon.isMask() || - //if symbolic color based on tintColor - (iconSource.endsWith(QLatin1String("-symbolic")) && tintColor.isValid() && tintColor != Qt::transparent) || - //if path color based on m_color - (isPath && m_color.isValid() && m_color != Qt::transparent)) { + if (m_isMask || icon.isMask() || iconSource.endsWith(QStringLiteral("-symbolic")) || iconSource.endsWith(QStringLiteral("-symbolic-rtl")) || iconSource.endsWith(QStringLiteral("-symbolic-ltr")) || guessMonochrome(img)) { QPainter p(&img); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(img.rect(), tintColor); p.end(); - } + }*/ } } return img; } QIcon::Mode DesktopIcon::iconMode() const { if (!isEnabled()) { return QIcon::Disabled; } else if (m_selected) { return QIcon::Selected; } else if (m_active) { return QIcon::Active; } return QIcon::Normal; } +bool DesktopIcon::guessMonochrome(const QImage &img) +{ + //don't try for too big images + if (img.width() >= 256 || m_theme->supportsIconColoring()) { + return false; + } + // round size to a standard size. hardcode as we can't use KIconLoader + int stdSize; + if (img.width() <= 16) { + stdSize = 16; + } else if (img.width() <= 22) { + stdSize = 22; + } else if (img.width() <= 24) { + stdSize = 24; + } else if (img.width() <= 32) { + stdSize = 32; + } else if (img.width() <= 48) { + stdSize = 48; + } else if (img.width() <= 64) { + stdSize = 64; + } else { + stdSize = 128; + } + + auto findIt = m_monochromeHeuristics.constFind(stdSize); + if (findIt != m_monochromeHeuristics.constEnd()) { + return findIt.value(); + } + + QHash dist; + int transparentPixels = 0; + int saturatedPixels = 0; + for(int x=0; x < img.width(); x++) { + for(int y=0; y < img.height(); y++) { + QColor color = QColor::fromRgba(qUnpremultiply(img.pixel(x, y))); + if (color.alpha() < 100) { + ++transparentPixels; + continue; + } else if (color.saturation() > 84) { + ++saturatedPixels; + } + dist[qGray(color.rgb())]++; + } + } + + QMultiMap reverseDist; + auto it = dist.constBegin(); + std::vector probabilities(dist.size()); + qreal entropy = 0; + while (it != dist.constEnd()) { + reverseDist.insertMulti(it.value(), it.key()); + qreal probability = qreal(it.value()) / qreal(img.size().width() * img.size().height() - transparentPixels); + entropy -= probability * log(probability) / log(255); + ++it; + } + + // Arbitrarly low values of entropy and colored pixels + m_monochromeHeuristics[stdSize] = saturatedPixels <= (img.size().width()*img.size().height() - transparentPixels) * 0.3 && entropy <= 0.3; + return m_monochromeHeuristics[stdSize]; +} + QString DesktopIcon::fallback() const { return m_fallback; } void DesktopIcon::setFallback(const QString& fallback) { if (m_fallback != fallback) { m_fallback = fallback; Q_EMIT fallbackChanged(fallback); } } diff --git a/src/desktopicon.h b/src/desktopicon.h index 73a51af7..fd73db85 100644 --- a/src/desktopicon.h +++ b/src/desktopicon.h @@ -1,115 +1,118 @@ /* * Copyright 2011 Marco Martin * Copyright 2014 Aleix Pol Gonzalez * * 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 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. */ #ifndef DESKTOPICON_H #define DESKTOPICON_H #include #include #include #include class QNetworkAccessManager; class QNetworkReply; namespace Kirigami { class PlatformTheme; } class DesktopIcon : public QQuickItem { Q_OBJECT Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) Q_PROPERTY(int implicitWidth READ implicitWidth CONSTANT) Q_PROPERTY(int implicitHeight READ implicitHeight CONSTANT) Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(bool valid READ valid NOTIFY validChanged) Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged) Q_PROPERTY(bool isMask READ isMask WRITE setIsMask NOTIFY isMaskChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString fallback READ fallback WRITE setFallback NOTIFY fallbackChanged) public: DesktopIcon(QQuickItem *parent = nullptr); ~DesktopIcon(); void setSource(const QVariant &source); QVariant source() const; int implicitWidth() const; int implicitHeight() const; void setSmooth(const bool smooth); bool smooth() const; void setActive(bool active = true); bool active() const; bool valid() const; void setSelected(bool selected = true); bool selected() const; void setIsMask(bool mask); bool isMask() const; void setColor(const QColor &color); QColor color() const; QString fallback() const; void setFallback(const QString &fallback); QSGNode* updatePaintNode(QSGNode* node, UpdatePaintNodeData* data) override; Q_SIGNALS: void sourceChanged(); void smoothChanged(); void enabledChanged(); void activeChanged(); void validChanged(); void selectedChanged(); void isMaskChanged(); void colorChanged(); void fallbackChanged(const QString &fallback); protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; QImage findIcon(const QSize& size); void handleFinished(QNetworkAccessManager* qnam, QNetworkReply* reply); void handleReadyRead(QNetworkReply* reply); QIcon::Mode iconMode() const; + bool guessMonochrome(const QImage &img); private: Kirigami::PlatformTheme *m_theme = nullptr; QPointer m_networkReply; + QHash m_monochromeHeuristics; QVariant m_source; bool m_smooth; bool m_changed; bool m_active; bool m_selected; bool m_isMask; + bool m_isMaskHeuristic = false; QImage m_loadedImage; QColor m_color = Qt::transparent; QString m_fallback = QStringLiteral("unknown"); }; #endif diff --git a/src/libkirigami/platformtheme.cpp b/src/libkirigami/platformtheme.cpp index 4923c670..7ee57292 100644 --- a/src/libkirigami/platformtheme.cpp +++ b/src/libkirigami/platformtheme.cpp @@ -1,742 +1,749 @@ /* * 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 #include namespace Kirigami { class PlatformThemePrivate { public: PlatformThemePrivate(PlatformTheme *q); ~PlatformThemePrivate(); inline void syncCustomPalette(); void findParentStyle(); static QColor tint(const QColor &c1, const QColor &c2, qreal ratio); PlatformTheme *q; QTimer *setColorCompressTimer; 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; static KirigamiPluginFactory *s_pluginFactory; }; KirigamiPluginFactory *PlatformThemePrivate::s_pluginFactory = nullptr; PlatformThemePrivate::PlatformThemePrivate(PlatformTheme *q) : q(q) { setColorCompressTimer = new QTimer(q); setColorCompressTimer->setSingleShot(true); setColorCompressTimer->setInterval(0); } 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); } PlatformTheme::PlatformTheme(QObject *parent) : QObject(parent), d(new PlatformThemePrivate(this)) { connect(d->setColorCompressTimer, &QTimer::timeout, this, [this]() { d->syncCustomPalette(); emit colorsChanged(); }); 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->setColorCompressTimer->start(); } } 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->setColorCompressTimer->start(); } } 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->setColorCompressTimer->start(); } void PlatformTheme::setDisabledTextColor(const QColor &color) { if (d->disabledTextColor == color) { return; } d->disabledTextColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setBackgroundColor(const QColor &color) { if (d->backgroundColor == color) { return; } d->backgroundColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setAlternateBackgroundColor(const QColor &color) { if (d->alternateBackgroundColor == color) { return; } d->alternateBackgroundColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setHighlightColor(const QColor &color) { if (d->highlightColor == color) { return; } d->highlightColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setHighlightedTextColor(const QColor &color) { if (d->highlightedTextColor == color) { return; } d->highlightedTextColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setActiveTextColor(const QColor &color) { if (d->activeTextColor == color) { return; } d->activeTextColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setLinkColor(const QColor &color) { if (d->linkColor == color) { return; } d->linkColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setVisitedLinkColor(const QColor &color) { if (d->visitedLinkColor == color) { return; } d->visitedLinkColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setNegativeTextColor(const QColor &color) { if (d->negativeTextColor == color) { return; } d->negativeTextColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setNeutralTextColor(const QColor &color) { if (d->neutralTextColor == color) { return; } d->neutralTextColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setPositiveTextColor(const QColor &color) { if (d->positiveTextColor == color) { return; } d->positiveTextColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setHoverColor(const QColor &color) { if (d->hoverColor == color) { return; } d->hoverColor = color; d->setColorCompressTimer->start(); } void PlatformTheme::setFocusColor(const QColor &color) { if (d->focusColor == color) { return; } d->focusColor = color; d->setColorCompressTimer->start(); } 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->setColorCompressTimer->start(); } void PlatformTheme::setCustomDisabledTextColor(const QColor &color) { if (d->customDisabledTextColor == color) { return; } d->customDisabledTextColor = color; PROPAGATECUSTOMCOLOR(CustomDisabledTextColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomBackgroundColor(const QColor &color) { if (d->customBackgroundColor == color) { return; } d->customBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomBackgroundColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomAlternateBackgroundColor(const QColor &color) { if (d->customAlternateBackgroundColor == color) { return; } d->customAlternateBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomAlternateBackgroundColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomHighlightColor(const QColor &color) { if (d->customHighlightColor == color) { return; } d->customHighlightColor = color; PROPAGATECUSTOMCOLOR(CustomHighlightColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomHighlightedTextColor(const QColor &color) { if (d->customHighlightedTextColor == color) { return; } d->customHighlightedTextColor = color; PROPAGATECUSTOMCOLOR(CustomHighlightedTextColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomActiveTextColor(const QColor &color) { if (d->customActiveTextColor == color) { return; } d->customActiveTextColor = color; PROPAGATECUSTOMCOLOR(CustomActiveTextColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomLinkColor(const QColor &color) { if (d->customLinkColor == color) { return; } d->customLinkColor = color; PROPAGATECUSTOMCOLOR(CustomLinkColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomVisitedLinkColor(const QColor &color) { if (d->customVisitedLinkColor == color) { return; } d->customVisitedLinkColor = color; PROPAGATECUSTOMCOLOR(CustomVisitedLinkColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomNegativeTextColor(const QColor &color) { if (d->customNegativeTextColor == color) { return; } d->customNegativeTextColor = color; PROPAGATECUSTOMCOLOR(CustomNegativeTextColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomNeutralTextColor(const QColor &color) { if (d->customNeutralTextColor == color) { return; } d->customNeutralTextColor = color; PROPAGATECUSTOMCOLOR(CustomNeutralTextColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomPositiveTextColor(const QColor &color) { if (d->customPositiveTextColor == color) { return; } d->customPositiveTextColor = color; PROPAGATECUSTOMCOLOR(CustomPositiveTextColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomHoverColor(const QColor &color) { if (d->customHoverColor == color) { return; } d->customHoverColor = color; PROPAGATECUSTOMCOLOR(CustomHoverColor, color) d->setColorCompressTimer->start(); } void PlatformTheme::setCustomFocusColor(const QColor &color) { if (d->customFocusColor == color) { return; } d->customFocusColor = color; PROPAGATECUSTOMCOLOR(CustomFocusColor, color) d->setColorCompressTimer->start(); } 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) { QIcon icon = QIcon::fromTheme(name); - if (!icon.isNull() && (name.endsWith(QLatin1String("-symbolic")) || customColor != Qt::transparent)) { - icon.setIsMask(true); - } 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" diff --git a/src/libkirigami/platformtheme.h b/src/libkirigami/platformtheme.h index a9b20a81..fdf2a815 100644 --- a/src/libkirigami/platformtheme.h +++ b/src/libkirigami/platformtheme.h @@ -1,281 +1,284 @@ /* * 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. */ #ifndef PLATFORMTHEME_H #define PLATFORMTHEME_H #include #include #include #include #ifndef KIRIGAMI_BUILD_TYPE_STATIC #include #endif namespace Kirigami { class PlatformThemePrivate; /** * @class PlatformTheme platformtheme.h PlatformTheme * * This class is the base for color management in Kirigami, * different platforms can reimplement this class to integrate with * system platform colors of a given platform */ #ifdef KIRIGAMI_BUILD_TYPE_STATIC class PlatformTheme : public QObject #else class KIRIGAMI2_EXPORT PlatformTheme : public QObject #endif { Q_OBJECT /** * This enumeration describes the color set for which a color is being selected. * * Color sets define a color "environment", suitable for drawing all parts of a * given region. Colors from different sets should not be combined. */ Q_PROPERTY(ColorSet colorSet READ colorSet WRITE setColorSet NOTIFY colorSetChanged) /** * This enumeration describes the color group used to generate the colors. * The enum value is based upon QPalette::CpolorGroup and has the same values. * It's redefined here in order to make it work with QML * @since 4.43 */ Q_PROPERTY(ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * If true, the colorSet will be inherited from the colorset of a theme of one * of the ancestor items * default: true */ Q_PROPERTY(bool inherit READ inherit WRITE setInherit NOTIFY inheritChanged) // foreground colors /** * Color for normal foregrounds, usually text, but not limited to it, * anything that should be painted with a clear contrast should use this color */ Q_PROPERTY(QColor textColor READ textColor WRITE setCustomTextColor RESET setCustomTextColor NOTIFY colorsChanged) /** * Foreground color for disabled areas, usually a mid-gray */ Q_PROPERTY(QColor disabledTextColor READ disabledTextColor WRITE setCustomDisabledTextColor RESET setCustomDisabledTextColor NOTIFY colorsChanged) /** * Color for text that has been highlighted, often is a light color while normal text is dark */ Q_PROPERTY(QColor highlightedTextColor READ highlightedTextColor WRITE setCustomHighlightedTextColor RESET setCustomHighlightedTextColor NOTIFY colorsChanged) /** * Foreground for areas that are active or requesting attention */ Q_PROPERTY(QColor activeTextColor READ activeTextColor WRITE setCustomActiveTextColor RESET setCustomActiveTextColor NOTIFY colorsChanged) /** * Color for links */ Q_PROPERTY(QColor linkColor READ linkColor WRITE setCustomLinkColor RESET setCustomLinkColor NOTIFY colorsChanged) /** * Color for visited links, usually a bit darker than linkColor */ Q_PROPERTY(QColor visitedLinkColor READ visitedLinkColor WRITE setCustomVisitedLinkColor RESET setCustomVisitedLinkColor NOTIFY colorsChanged) /** * Foreground color for negative areas, such as critical error text */ Q_PROPERTY(QColor negativeTextColor READ negativeTextColor WRITE setCustomNegativeTextColor RESET setCustomNegativeTextColor NOTIFY colorsChanged) /** * Foreground color for neutral areas, such as warning texts (but not critical) */ Q_PROPERTY(QColor neutralTextColor READ neutralTextColor WRITE setCustomNeutralTextColor RESET setCustomNeutralTextColor NOTIFY colorsChanged) /** * Success messages, trusted content */ Q_PROPERTY(QColor positiveTextColor READ positiveTextColor WRITE setCustomPositiveTextColor RESET setCustomPositiveTextColor NOTIFY colorsChanged) //background colors /** * The generic background color */ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setCustomBackgroundColor RESET setCustomBackgroundColor NOTIFY colorsChanged) /** * The generic background color * Alternate background; for example, for use in lists. * This color may be the same as BackgroundNormal, * especially in sets other than View and Window. */ Q_PROPERTY(QColor alternateBackgroundColor READ alternateBackgroundColor WRITE setCustomAlternateBackgroundColor RESET setCustomAlternateBackgroundColor NOTIFY colorsChanged) /** * The background color for selected areas */ Q_PROPERTY(QColor highlightColor READ highlightColor WRITE setCustomHighlightColor RESET setCustomHighlightColor NOTIFY colorsChanged) //decoration colors /** * A decoration color that indicates active focus */ Q_PROPERTY(QColor focusColor READ focusColor WRITE setCustomFocusColor RESET setCustomFocusColor NOTIFY colorsChanged) /** * A decoration color that indicates mouse hovering */ Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setCustomHoverColor RESET setCustomHoverColor NOTIFY colorsChanged) // font and palette Q_PROPERTY(QFont defaultFont READ defaultFont NOTIFY defaultFontChanged) //Active palette Q_PROPERTY(QPalette palette READ palette NOTIFY paletteChanged) public: enum ColorSet { View = 0, /** Color set for item views, usually the lightest of all */ Window, /** Default Color set for windows and "chrome" areas */ Button, /** Color set used by buttons */ Selection, /** Color set used by selectged areas */ Tooltip, /** Color set used by tooltips */ Complementary /** Color set meant to be complementary to Window: usually is a dark theme for light themes */ }; Q_ENUM(ColorSet) enum ColorGroup { Disabled = QPalette::Disabled, Active = QPalette::Active, Inactive = QPalette::Inactive, Normal = QPalette::Normal }; Q_ENUM(ColorGroup) explicit PlatformTheme(QObject *parent = nullptr); ~PlatformTheme(); void setColorSet(PlatformTheme::ColorSet); PlatformTheme::ColorSet colorSet() const; void setColorGroup(PlatformTheme::ColorGroup); PlatformTheme::ColorGroup colorGroup() const; bool inherit() const; void setInherit(bool inherit); //foreground colors QColor textColor() const; QColor disabledTextColor() const; QColor highlightedTextColor() const; QColor activeTextColor() const; QColor linkColor() const; QColor visitedLinkColor() const; QColor negativeTextColor() const; QColor neutralTextColor() const; QColor positiveTextColor() const; //background colors QColor backgroundColor() const; QColor alternateBackgroundColor() const; QColor highlightColor() const; //TODO: add active/positive/neutral/negative backgrounds? //decoration colors QColor focusColor() const; QColor hoverColor() const; QFont defaultFont() const; //this may is used by the desktop QQC2 to set the styleoption palettes QPalette palette() const; //this will be used by desktopicon to fetch icons with KIconLoader virtual Q_INVOKABLE QIcon iconFromTheme(const QString &name, const QColor &customColor = Qt::transparent); + bool supportsIconColoring() const; + //foreground colors void setCustomTextColor(const QColor &color = QColor()); void setCustomDisabledTextColor(const QColor &color = QColor()); void setCustomHighlightedTextColor(const QColor &color = QColor()); void setCustomActiveTextColor(const QColor &color = QColor()); void setCustomLinkColor(const QColor &color = QColor()); void setCustomVisitedLinkColor(const QColor &color = QColor()); void setCustomNegativeTextColor(const QColor &color = QColor()); void setCustomNeutralTextColor(const QColor &color = QColor()); void setCustomPositiveTextColor(const QColor &color = QColor()); //background colors void setCustomBackgroundColor(const QColor &color = QColor()); void setCustomAlternateBackgroundColor(const QColor &color = QColor()); void setCustomHighlightColor(const QColor &color = QColor()); //decoration colors void setCustomFocusColor(const QColor &color = QColor()); void setCustomHoverColor(const QColor &color = QColor()); //QML attached property static PlatformTheme *qmlAttachedProperties(QObject *object); Q_SIGNALS: //TODO: parameters to signals as this is also a c++ api void colorsChanged(); void defaultFontChanged(const QFont &font); void colorSetChanged(Kirigami::PlatformTheme::ColorSet colorSet); void colorGroupChanged(Kirigami::PlatformTheme::ColorGroup colorGroup); void paletteChanged(const QPalette &pal); void inheritChanged(bool inherit); protected: //Setters, not accessible from QML but from implementations - + void setSupportsIconColoring(bool support); + //foreground colors void setTextColor(const QColor &color); void setDisabledTextColor(const QColor &color); void setHighlightedTextColor(const QColor &color); void setActiveTextColor(const QColor &color); void setLinkColor(const QColor &color); void setVisitedLinkColor(const QColor &color); void setNegativeTextColor(const QColor &color); void setNeutralTextColor(const QColor &color); void setPositiveTextColor(const QColor &color); //background colors void setBackgroundColor(const QColor &color); void setAlternateBackgroundColor(const QColor &color); void setHighlightColor(const QColor &color); //decoration colors void setFocusColor(const QColor &color); void setHoverColor(const QColor &color); void setDefaultFont(const QFont &defaultFont); void setPalette(const QPalette &palette); private: PlatformThemePrivate *d; friend class PlatformThemePrivate; }; } QML_DECLARE_TYPEINFO(Kirigami::PlatformTheme, QML_HAS_ATTACHED_PROPERTIES) #endif // PLATFORMTHEME_H