Warning, file /frameworks/qqc2-desktop-style/kirigami-plasmadesktop-integration/plasmadesktoptheme.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "plasmadesktoptheme.h"
0008 #include <KIconLoader>
0009 #include <QDebug>
0010 #include <QGuiApplication>
0011 #include <QPalette>
0012 #include <QQmlContext>
0013 #include <QQmlEngine>
0014 #include <QQuickRenderControl>
0015 #include <QQuickWindow>
0016 #include <QScopeGuard>
0017 
0018 #include <KColorScheme>
0019 #include <KConfigGroup>
0020 #include <KIconColors>
0021 #include <QDBusConnection>
0022 
0023 class StyleSingleton : public QObject
0024 {
0025     Q_OBJECT
0026 
0027 public:
0028     struct Colors {
0029         QPalette palette;
0030         KColorScheme selectionScheme;
0031         KColorScheme scheme;
0032     };
0033 
0034     explicit StyleSingleton()
0035         : QObject()
0036         , buttonScheme(QPalette::Active, KColorScheme::ColorSet::Button)
0037         , viewScheme(QPalette::Active, KColorScheme::ColorSet::View)
0038     {
0039         connect(qGuiApp, &QGuiApplication::paletteChanged, this, &StyleSingleton::refresh);
0040 
0041         // Use DBus in order to listen for settings changes directly, as the
0042         // QApplication doesn't expose the font variants we're looking for,
0043         // namely smallFont.
0044         QDBusConnection::sessionBus().connect(QString(),
0045                                               QStringLiteral("/KDEPlatformTheme"),
0046                                               QStringLiteral("org.kde.KDEPlatformTheme"),
0047                                               QStringLiteral("refreshFonts"),
0048                                               this,
0049                                               SLOT(notifyWatchersConfigurationChange()));
0050 
0051         connect(qGuiApp, &QGuiApplication::fontDatabaseChanged, this, &StyleSingleton::notifyWatchersConfigurationChange);
0052         connect(qGuiApp, &QGuiApplication::fontChanged, this, &StyleSingleton::notifyWatchersConfigurationChange);
0053 
0054         // Use NativeTextRendering as the default text rendering type when the scale factor is an integer.
0055         // NativeTextRendering is still distorted sometimes with fractional scale factors,
0056         // despite https://bugreports.qt.io/browse/QTBUG-67007 being closed.
0057         qreal devicePixelRatio = qGuiApp->devicePixelRatio();
0058         QQuickWindow::TextRenderType defaultTextRenderType =
0059             int(devicePixelRatio) == devicePixelRatio ? QQuickWindow::NativeTextRendering : QQuickWindow::QtTextRendering;
0060         QQuickWindow::setTextRenderType(defaultTextRenderType);
0061 
0062         smallFont = loadSmallFont();
0063     }
0064 
0065     QFont loadSmallFont() const
0066     {
0067         KSharedConfigPtr ptr = KSharedConfig::openConfig();
0068         KConfigGroup general(ptr->group("general"));
0069 
0070         return general.readEntry("smallestReadableFont", []() {
0071             auto smallFont = qApp->font();
0072 #ifndef Q_OS_WIN
0073             if (smallFont.pixelSize() != -1) {
0074                 smallFont.setPixelSize(smallFont.pixelSize() - 2);
0075             } else {
0076                 smallFont.setPointSize(smallFont.pointSize() - 2);
0077             }
0078 #endif
0079             return smallFont;
0080         }());
0081     }
0082 
0083     void refresh()
0084     {
0085         m_cache.clear();
0086         buttonScheme = KColorScheme(QPalette::Active, KColorScheme::ColorSet::Button);
0087         viewScheme = KColorScheme(QPalette::Active, KColorScheme::ColorSet::View);
0088 
0089         notifyWatchersPaletteChange();
0090     }
0091 
0092     Colors loadColors(Kirigami::PlatformTheme::ColorSet cs, QPalette::ColorGroup group)
0093     {
0094         const auto key = qMakePair(cs, group);
0095         auto it = m_cache.constFind(key);
0096         if (it != m_cache.constEnd()) {
0097             return *it;
0098         }
0099 
0100         using Kirigami::PlatformTheme;
0101 
0102         KColorScheme::ColorSet set;
0103 
0104         switch (cs) {
0105         case PlatformTheme::Button:
0106             set = KColorScheme::ColorSet::Button;
0107             break;
0108         case PlatformTheme::Selection:
0109             set = KColorScheme::ColorSet::Selection;
0110             break;
0111         case PlatformTheme::Tooltip:
0112             set = KColorScheme::ColorSet::Tooltip;
0113             break;
0114         case PlatformTheme::View:
0115             set = KColorScheme::ColorSet::View;
0116             break;
0117         case PlatformTheme::Complementary:
0118             set = KColorScheme::ColorSet::Complementary;
0119             break;
0120         case PlatformTheme::Header:
0121             set = KColorScheme::ColorSet::Header;
0122             break;
0123         case PlatformTheme::Window:
0124         default:
0125             set = KColorScheme::ColorSet::Window;
0126         }
0127 
0128         Colors ret = {{}, KColorScheme(group, KColorScheme::ColorSet::Selection), KColorScheme(group, set)};
0129 
0130         QPalette pal;
0131         for (auto state : {QPalette::Active, QPalette::Inactive, QPalette::Disabled}) {
0132             pal.setBrush(state, QPalette::WindowText, ret.scheme.foreground());
0133             pal.setBrush(state, QPalette::Window, ret.scheme.background());
0134             pal.setBrush(state, QPalette::Base, ret.scheme.background());
0135             pal.setBrush(state, QPalette::Text, ret.scheme.foreground());
0136             pal.setBrush(state, QPalette::Button, ret.scheme.background());
0137             pal.setBrush(state, QPalette::ButtonText, ret.scheme.foreground());
0138             pal.setBrush(state, QPalette::Highlight, ret.selectionScheme.background());
0139             pal.setBrush(state, QPalette::HighlightedText, ret.selectionScheme.foreground());
0140             pal.setBrush(state, QPalette::ToolTipBase, ret.scheme.background());
0141             pal.setBrush(state, QPalette::ToolTipText, ret.scheme.foreground());
0142 
0143             pal.setColor(state, QPalette::Light, ret.scheme.shade(KColorScheme::LightShade));
0144             pal.setColor(state, QPalette::Midlight, ret.scheme.shade(KColorScheme::MidlightShade));
0145             pal.setColor(state, QPalette::Mid, ret.scheme.shade(KColorScheme::MidShade));
0146             pal.setColor(state, QPalette::Dark, ret.scheme.shade(KColorScheme::DarkShade));
0147             pal.setColor(state, QPalette::Shadow, ret.scheme.shade(KColorScheme::ShadowShade));
0148 
0149             pal.setBrush(state, QPalette::AlternateBase, ret.scheme.background(KColorScheme::AlternateBackground));
0150             pal.setBrush(state, QPalette::Link, ret.scheme.foreground(KColorScheme::LinkText));
0151             pal.setBrush(state, QPalette::LinkVisited, ret.scheme.foreground(KColorScheme::VisitedText));
0152         }
0153         ret.palette = pal;
0154         m_cache.insert(key, ret);
0155         return ret;
0156     }
0157 
0158     void notifyWatchersPaletteChange()
0159     {
0160         for (auto watcher : std::as_const(watchers)) {
0161             watcher->syncColors();
0162         }
0163     }
0164 
0165     Q_SLOT void notifyWatchersConfigurationChange()
0166     {
0167         smallFont = loadSmallFont();
0168         for (auto watcher : std::as_const(watchers)) {
0169             watcher->setSmallFont(smallFont);
0170             watcher->setDefaultFont(qApp->font());
0171         }
0172     }
0173 
0174     KColorScheme buttonScheme;
0175     KColorScheme viewScheme;
0176     QFont smallFont;
0177 
0178     QVector<PlasmaDesktopTheme *> watchers;
0179 
0180 private:
0181     QHash<QPair<Kirigami::PlatformTheme::ColorSet, QPalette::ColorGroup>, Colors> m_cache;
0182 };
0183 
0184 Q_GLOBAL_STATIC(StyleSingleton, s_style);
0185 
0186 PlasmaDesktopTheme::PlasmaDesktopTheme(QObject *parent)
0187     : PlatformTheme(parent)
0188 {
0189     setSupportsIconColoring(true);
0190 
0191     auto parentItem = qobject_cast<QQuickItem *>(parent);
0192     if (parentItem) {
0193         connect(parentItem, &QQuickItem::enabledChanged, this, &PlasmaDesktopTheme::syncColors);
0194         connect(parentItem, &QQuickItem::visibleChanged, this, &PlasmaDesktopTheme::syncColors);
0195         connect(parentItem, &QQuickItem::windowChanged, this, &PlasmaDesktopTheme::syncWindow);
0196     }
0197 
0198     s_style->watchers.append(this);
0199 
0200     setDefaultFont(qGuiApp->font());
0201     setSmallFont(s_style->smallFont);
0202 
0203     syncWindow();
0204     if (!m_window) {
0205         syncColors();
0206     }
0207 }
0208 
0209 PlasmaDesktopTheme::~PlasmaDesktopTheme()
0210 {
0211     s_style->watchers.removeOne(this);
0212 }
0213 
0214 void PlasmaDesktopTheme::syncWindow()
0215 {
0216     if (m_window) {
0217         disconnect(m_window.data(), &QWindow::activeChanged, this, &PlasmaDesktopTheme::syncColors);
0218     }
0219 
0220     QWindow *window = nullptr;
0221 
0222     auto parentItem = qobject_cast<QQuickItem *>(parent());
0223     if (parentItem) {
0224         QQuickWindow *qw = parentItem->window();
0225 
0226         window = QQuickRenderControl::renderWindowFor(qw);
0227         if (!window) {
0228             window = qw;
0229         }
0230         if (qw) {
0231             connect(qw, &QQuickWindow::sceneGraphInitialized, this, &PlasmaDesktopTheme::syncWindow, Qt::UniqueConnection);
0232         }
0233     }
0234     m_window = window;
0235 
0236     if (window) {
0237         connect(m_window.data(), &QWindow::activeChanged, this, &PlasmaDesktopTheme::syncColors);
0238         syncColors();
0239     }
0240 }
0241 
0242 QIcon PlasmaDesktopTheme::iconFromTheme(const QString &name, const QColor &customColor)
0243 {
0244     if (customColor != Qt::transparent) {
0245         KIconColors colors;
0246         colors.setText(customColor);
0247         return KDE::icon(name, colors);
0248     } else {
0249         return KDE::icon(name);
0250     }
0251 }
0252 
0253 void PlasmaDesktopTheme::syncColors()
0254 {
0255     if (QCoreApplication::closingDown()) {
0256         return;
0257     }
0258 
0259     QPalette::ColorGroup group = (QPalette::ColorGroup)colorGroup();
0260     auto parentItem = qobject_cast<QQuickItem *>(parent());
0261     if (parentItem) {
0262         if (!parentItem->isVisible()) {
0263             return;
0264         }
0265         if (!parentItem->isEnabled()) {
0266             group = QPalette::Disabled;
0267             // Why also checking the window is exposed?
0268             // in the case of QQuickWidget the window() will never be active
0269             // and the widgets will always have the inactive palette.
0270             // better to always show it active than always show it inactive
0271         } else if (m_window && !m_window->isActive() && m_window->isExposed()) {
0272             group = QPalette::Inactive;
0273         }
0274     }
0275 
0276     const auto colors = s_style->loadColors(colorSet(), group);
0277 
0278     // foreground
0279     setTextColor(colors.scheme.foreground(KColorScheme::NormalText).color());
0280     setDisabledTextColor(colors.scheme.foreground(KColorScheme::InactiveText).color());
0281     setHighlightedTextColor(colors.selectionScheme.foreground(KColorScheme::NormalText).color());
0282     setActiveTextColor(colors.scheme.foreground(KColorScheme::ActiveText).color());
0283     setLinkColor(colors.scheme.foreground(KColorScheme::LinkText).color());
0284     setVisitedLinkColor(colors.scheme.foreground(KColorScheme::VisitedText).color());
0285     setNegativeTextColor(colors.scheme.foreground(KColorScheme::NegativeText).color());
0286     setNeutralTextColor(colors.scheme.foreground(KColorScheme::NeutralText).color());
0287     setPositiveTextColor(colors.scheme.foreground(KColorScheme::PositiveText).color());
0288 
0289     // background
0290     setBackgroundColor(colors.scheme.background(KColorScheme::NormalBackground).color());
0291     setAlternateBackgroundColor(colors.scheme.background(KColorScheme::AlternateBackground).color());
0292     setHighlightColor(colors.selectionScheme.background(KColorScheme::NormalBackground).color());
0293     setActiveBackgroundColor(colors.scheme.background(KColorScheme::ActiveBackground).color());
0294     setLinkBackgroundColor(colors.scheme.background(KColorScheme::LinkBackground).color());
0295     setVisitedLinkBackgroundColor(colors.scheme.background(KColorScheme::VisitedBackground).color());
0296     setNegativeBackgroundColor(colors.scheme.background(KColorScheme::NegativeBackground).color());
0297     setNeutralBackgroundColor(colors.scheme.background(KColorScheme::NeutralBackground).color());
0298     setPositiveBackgroundColor(colors.scheme.background(KColorScheme::PositiveBackground).color());
0299 
0300     // decoration
0301     setHoverColor(colors.scheme.decoration(KColorScheme::HoverColor).color());
0302     setFocusColor(colors.scheme.decoration(KColorScheme::FocusColor).color());
0303 }
0304 
0305 bool PlasmaDesktopTheme::event(QEvent *event)
0306 {
0307     if (event->type() == Kirigami::PlatformThemeEvents::DataChangedEvent::type) {
0308         syncColors();
0309     }
0310 
0311     if (event->type() == Kirigami::PlatformThemeEvents::ColorSetChangedEvent::type) {
0312         syncColors();
0313     }
0314 
0315     if (event->type() == Kirigami::PlatformThemeEvents::ColorGroupChangedEvent::type) {
0316         syncColors();
0317     }
0318 
0319     return PlatformTheme::event(event);
0320 }
0321 
0322 #include "moc_plasmadesktoptheme.cpp"
0323 #include "plasmadesktoptheme.moc"