KColorScheme

kcolorschememanager.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kcolorschememanager.h"
9#include "kcolorschememanager_p.h"
10
11#include "kcolorschememodel.h"
12
13#include <KConfigGroup>
14#include <KConfigGui>
15#include <KLocalizedString>
16#include <KSharedConfig>
17#include <kcolorscheme.h>
18
19#include <QDir>
20#include <QFileInfo>
21#include <QGuiApplication>
22#include <QIcon>
23#include <QPainter>
24#include <QPointer>
25#include <QStandardPaths>
26
27#include <private/qguiapplication_p.h>
28#include <qpa/qplatformtheme.h>
29
30// ensure we are linking KConfigGui, so QColor I/O from KConfig works
31KCONFIGGUI_EXPORT int initKConfigGroupGui();
32static int s_init = initKConfigGroupGui();
33
34constexpr int defaultSchemeRow = 0;
35
36static bool isKdePlatformTheme()
37{
38 if (!QGuiApplicationPrivate::platformTheme()) {
39 return false;
40 }
41
42 if (QGuiApplicationPrivate::platformTheme()->name() == QLatin1String("kde")) {
43 return true;
44 }
45
46 if (qgetenv("XDG_CURRENT_DESKTOP") == "KDE" && QGuiApplicationPrivate::platformTheme()->name() == QLatin1String("xdgdesktopportal")) {
47 return true;
48 }
49
50 return false;
51}
52
53void KColorSchemeManagerPrivate::activateSchemeInternal(const QString &colorSchemePath)
54{
55 // hint for plasma-integration to synchronize the color scheme with the window manager/compositor
56 // The property needs to be set before the palette change because is is checked upon the
57 // ApplicationPaletteChange event.
58 qApp->setProperty("KDE_COLOR_SCHEME_PATH", colorSchemePath);
59 if (colorSchemePath.isEmpty()) {
60 qApp->setPalette(QPalette());
61 } else {
62 qApp->setPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(colorSchemePath)));
63 }
64}
65
66// The meaning of the Default entry depends on the platform
67// On KDE we apply a default KColorScheme
68// On other platforms we automatically apply Breeze/Breeze Dark depending on the system preference
69QString KColorSchemeManagerPrivate::automaticColorSchemePath() const
70{
71 if (!m_colorSchemeWatcher) {
72 return QString();
73 }
74
75 const QString colorSchemeId = m_colorSchemeWatcher->systemPreference() == KColorSchemeWatcher::PreferDark ? getDarkColorScheme() : getLightColorScheme();
76 return indexForSchemeId(colorSchemeId).data(KColorSchemeModel::PathRole).toString();
77}
78
79QIcon KColorSchemeManagerPrivate::createPreview(const QString &path)
80{
81 KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
82 QIcon result;
83
84 KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig);
85 KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig);
86 KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig);
87 KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig);
88
89 auto pixmap = [&](int size) {
90 QPixmap pix(size, size);
91 pix.fill(Qt::black);
92 QPainter p;
93 p.begin(&pix);
94 const int itemSize = size / 2 - 1;
95 p.fillRect(1, 1, itemSize, itemSize, activeWindow.background());
96 p.fillRect(1 + itemSize, 1, itemSize, itemSize, activeButton.background());
97 p.fillRect(1, 1 + itemSize, itemSize, itemSize, activeView.background());
98 p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, activeSelection.background());
99 p.end();
100 result.addPixmap(pix);
101 };
102 // 16x16
103 pixmap(16);
104 // 24x24
105 pixmap(24);
106
107 return result;
108}
109
110KColorSchemeManagerPrivate::KColorSchemeManagerPrivate()
111 : model(new KColorSchemeModel())
112{
113}
114
115KColorSchemeManager::KColorSchemeManager(GuardApplicationConstructor, QGuiApplication *app)
116 : QObject(app)
117 , d(new KColorSchemeManagerPrivate)
118{
119 init();
120}
121
122#if KCOLORSCHEME_BUILD_DEPRECATED_SINCE(6, 6)
123KColorSchemeManager::KColorSchemeManager(QObject *parent)
124 : QObject(parent)
125 , d(new KColorSchemeManagerPrivate())
126{
127 init();
128}
129#endif
130
131KColorSchemeManager::~KColorSchemeManager()
132{
133}
134
135void KColorSchemeManager::init()
136{
137 QString platformThemeSchemePath = qApp->property("KDE_COLOR_SCHEME_PATH").toString();
138 if (!isKdePlatformTheme() && platformThemeSchemePath.isEmpty()) {
139 d->m_colorSchemeWatcher.emplace();
140 QObject::connect(&*d->m_colorSchemeWatcher, &KColorSchemeWatcher::systemPreferenceChanged, this, [this]() {
141 if (!d->m_activatedScheme.isEmpty()) {
142 // Don't override what has been manually set
143 return;
144 }
145
146 d->activateSchemeInternal(d->automaticColorSchemePath());
147 });
148 }
149
150 KSharedConfigPtr config = KSharedConfig::openConfig();
151 KConfigGroup cg(config, QStringLiteral("UiSettings"));
152 const QString scheme = cg.readEntry("ColorScheme", QString());
153
154 QString schemePath;
155
156 if (scheme.isEmpty() || scheme == QLatin1String("Default")) {
157 // Color scheme might be already set from a platform theme
158 // This is used for example by QGnomePlatform that can set color scheme
159 // matching GNOME settings. This avoids issues where QGnomePlatform sets
160 // QPalette for dark theme, but end up mixing it also with Breeze light
161 // that is going to be used as a fallback for apps using KColorScheme.
162 // BUG: 447029
163 if (platformThemeSchemePath.isEmpty()) {
164 schemePath = d->automaticColorSchemePath();
165 }
166 } else {
167 const auto index = indexForScheme(scheme);
168 schemePath = index.data(KColorSchemeModel::PathRole).toString();
169 d->m_activatedScheme = index.data(KColorSchemeModel::IdRole).toString();
170 }
171
172 if (!schemePath.isEmpty()) {
173 d->activateSchemeInternal(schemePath);
174 }
175}
176
178{
179 return d->model.get();
180}
181
182QModelIndex KColorSchemeManagerPrivate::indexForSchemeId(const QString &id) const
183{
184 for (int i = 1; i < model->rowCount(); ++i) {
185 QModelIndex index = model->index(i);
186 if (index.data(KColorSchemeModel::IdRole).toString() == id) {
187 return index;
188 }
189 }
190 return QModelIndex();
191}
192
194{
195 d->m_autosaveChanges = autosaveChanges;
196}
197
199{
200 // Empty string is mapped to "reset to the system scheme"
201 if (id.isEmpty()) {
202 return d->model->index(defaultSchemeRow);
203 }
204 return d->indexForSchemeId(id);
205}
206
208{
209 // Empty string is mapped to "reset to the system scheme"
210 if (name.isEmpty()) {
211 return d->model->index(defaultSchemeRow);
212 }
213 for (int i = 1; i < d->model->rowCount(); ++i) {
214 QModelIndex index = d->model->index(i);
215 if (index.data(KColorSchemeModel::NameRole).toString() == name) {
216 return index;
217 }
218 }
219 return QModelIndex();
220}
221
223{
224 const bool isDefaultEntry = index.data(KColorSchemeModel::PathRole).toString().isEmpty();
225
226 if (index.isValid() && index.model() == d->model.get() && !isDefaultEntry) {
227 d->activateSchemeInternal(index.data(KColorSchemeModel::PathRole).toString());
228 d->m_activatedScheme = index.data(KColorSchemeModel::IdRole).toString();
229 if (d->m_autosaveChanges) {
230 saveSchemeToConfigFile(index.data(KColorSchemeModel::NameRole).toString());
231 }
232 } else {
233 d->activateSchemeInternal(d->automaticColorSchemePath());
234 d->m_activatedScheme = QString();
235 if (d->m_autosaveChanges) {
237 }
238 }
239}
240
242{
243 KSharedConfigPtr config = KSharedConfig::openConfig();
244 KConfigGroup cg(config, QStringLiteral("UiSettings"));
245 cg.writeEntry("ColorScheme", KLocalizedString::removeAcceleratorMarker(schemeName));
246 cg.sync();
247}
248
250{
251 return d->m_activatedScheme;
252}
253
255{
256 return d->indexForSchemeId(d->m_activatedScheme).data(KColorSchemeModel::NameRole).toString();
257}
258
260{
261 Q_ASSERT(qApp);
262 static QPointer<KColorSchemeManager> manager;
263 if (!manager) {
264 manager = new KColorSchemeManager(GuardApplicationConstructor{}, qApp);
265 }
266 return manager;
267}
268
269#include "moc_kcolorschememanager.cpp"
A small helper to get access to all available color schemes and activating a scheme in the QApplicati...
QAbstractItemModel * model() const
A QAbstractItemModel of all available color schemes.
QString activeSchemeId() const
Returns the id of the currently active scheme or an empty string if the default scheme is active.
QModelIndex indexForScheme(const QString &name) const
Returns the model index for the scheme with the given name.
QModelIndex indexForSchemeId(const QString &id) const
Returns the model index for the scheme with the given id.
static KColorSchemeManager * instance()
Returns the manager for the current application instance.
void activateScheme(const QModelIndex &index)
Activates the KColorScheme identified by the provided index.
QString activeSchemeName() const
Returns the name of the currently active scheme or an empty string if the default scheme is active.
void saveSchemeToConfigFile(const QString &schemeName) const
Saves the color scheme to config file.
void setAutosaveChanges(bool autosaveChanges)
Sets color scheme autosaving.
A model listing the KColorSchemes available in the system.
void systemPreferenceChanged()
A set of methods used to work with colors.
static QPalette createApplicationPalette(const KSharedConfigPtr &config)
Used to obtain the QPalette that will be used to set the application palette from KDE Platform theme.
@ View
Views; for example, frames, input fields, etc.
@ Window
Non-editable window elements; for example, menus.
@ Selection
Selected items in views.
@ Button
Buttons and button-like controls.
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
bool sync() override
static QString removeAcceleratorMarker(const QString &label)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString name(StandardAction id)
QCA_EXPORT void init()
void addPixmap(const QPixmap &pixmap, Mode mode, State state)
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool begin(QPaintDevice *device)
bool end()
void fillRect(const QRect &rectangle, QGradient::Preset preset)
QChar * data()
bool isEmpty() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:05:51 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.