1 /*
2 * Copyright (C) 2016-2019 Jan Grulich
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 */
19
20 #ifndef GNOME_HINTS_SETTINGS_H
21 #define GNOME_HINTS_SETTINGS_H
22
23 #include <QDBusVariant>
24 #include <QFont>
25 #include <QFlags>
26 #include <QObject>
27 #include <QPalette>
28 #include <QVariant>
29
30 #include <cmath>
31 #include <memory>
32
33 #undef signals
34 #include <gio/gio.h>
35 #include <gtk-3.0/gtk/gtk.h>
36 #include <gtk-3.0/gtk/gtksettings.h>
37 #define signals Q_SIGNALS
38
39 #include <qpa/qplatformtheme.h>
40
41 class GnomeHintsSettings : public QObject
42 {
43 Q_OBJECT
44 public:
45 enum TitlebarButtonsPlacement {
46 LeftPlacement = 0,
47 RightPlacement = 1
48 };
49
50 enum TitlebarButton {
51 CloseButton = 0x1,
52 MinimizeButton = 0x02,
53 MaximizeButton = 0x04
54 };
55 Q_DECLARE_FLAGS(TitlebarButtons, TitlebarButton);
56
57 explicit GnomeHintsSettings();
58 virtual ~GnomeHintsSettings();
59
60 // Borrowed from the KColorUtils code
61 static QColor mix(const QColor &c1, const QColor &c2, qreal bias = 0.5)
62 {
63 auto mixQreal = [](qreal a, qreal b, qreal bias) {
64 return a + (b - a) * bias;
65 };
66
67 if (bias <= 0.0)
68 return c1;
69 if (bias >= 1.0)
70 return c2;
71 if (std::isnan(bias))
72 return c1;
73
74 qreal r = mixQreal(c1.redF(), c2.redF(), bias);
75 qreal g = mixQreal(c1.greenF(), c2.greenF(), bias);
76 qreal b = mixQreal(c1.blueF(), c2.blueF(), bias);
77 qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias);
78
79 return QColor::fromRgbF(r, g, b, a);
80 }
81
82 static QColor lighten(const QColor &color, qreal amount = 0.1)
83 {
84 qreal h, s, l, a;
85 color.getHslF(&h, &s, &l, &a);
86
87 qreal lightness = l + amount;
88 if (lightness > 1)
89 lightness = 1;
90 return QColor::fromHslF(h, s, lightness, a);
91 }
92
93 static QColor darken(const QColor &color, qreal amount = 0.1)
94 {
95 qreal h, s, l, a;
96 color.getHslF(&h, &s, &l, &a);
97
98 qreal lightness = l - amount;
99 if (lightness < 0)
100 lightness = 0;
101
102 return QColor::fromHslF(h, s, lightness, a);
103 }
104
105 static QColor desaturate(const QColor &color, qreal amount = 0.1)
106 {
107 qreal h, s, l, a;
108 color.getHslF(&h, &s, &l, &a);
109
110 qreal saturation = s - amount;
111 if (saturation < 0)
112 saturation = 0;
113 return QColor::fromHslF(h, saturation, l, a);
114 }
115
116 static QColor transparentize(const QColor &color, qreal amount = 0.1)
117 {
118 qreal h, s, l, a;
119 color.getHslF(&h, &s, &l, &a);
120
121 qreal alpha = a - amount;
122 if (alpha < 0)
123 alpha = 0;
124 return QColor::fromHslF(h, s, l, alpha);
125 }
126
font(QPlatformTheme::Font type)127 inline QFont * font(QPlatformTheme::Font type) const
128 {
129 if (m_fonts.contains(type)) {
130 return m_fonts[type];
131 } else if (m_fonts.contains(QPlatformTheme::SystemFont)) {
132 return m_fonts[QPlatformTheme::SystemFont];
133 } else {
134 // GTK default font
135 return new QFont(QLatin1String("Sans"), 10);
136 }
137 }
138
palette()139 inline QPalette * palette() const
140 {
141 QPalette *palette = new QPalette;
142
143 if (m_gtkThemeDarkVariant) {
144 // Colors defined in GTK adwaita style in _colors.scss
145 QColor base_color = lighten(desaturate(QColor("#241f31"), 1.0), 0.02);
146 QColor text_color = QColor("white");
147 QColor bg_color = darken(desaturate(QColor("#3d3846"), 1.0), 0.04);
148 QColor fg_color = QColor("#eeeeec");
149 QColor selected_bg_color = darken(QColor("#3584e4"), 0.2);
150 QColor selected_fg_color = QColor("white");
151 QColor osd_text_color = QColor("white");
152 QColor osd_bg_color = QColor("black");
153 QColor shadow = transparentize(QColor("black"), 0.9);
154
155 QColor backdrop_fg_color = mix(fg_color, bg_color);
156 QColor backdrop_base_color = lighten(base_color, 0.01);
157 QColor backdrop_selected_fg_color = mix(text_color, backdrop_base_color, 0.2);
158
159 // This is the color we use as initial color for the gradient in normal state
160 // Defined in _drawing.scss button(normal)
161 QColor button_base_color = darken(bg_color, 0.01);
162
163 QColor link_color = lighten(selected_bg_color, 0.2);
164 QColor link_visited_color = lighten(selected_bg_color, 0.1);
165
166 palette->setColor(QPalette::All, QPalette::Window, bg_color);
167 palette->setColor(QPalette::All, QPalette::WindowText, fg_color);
168 palette->setColor(QPalette::All, QPalette::Base, base_color);
169 palette->setColor(QPalette::All, QPalette::AlternateBase, base_color);
170 palette->setColor(QPalette::All, QPalette::ToolTipBase, osd_bg_color);
171 palette->setColor(QPalette::All, QPalette::ToolTipText, osd_text_color);
172 palette->setColor(QPalette::All, QPalette::Text, fg_color);
173 palette->setColor(QPalette::All, QPalette::Button, button_base_color);
174 palette->setColor(QPalette::All, QPalette::ButtonText, fg_color);
175 palette->setColor(QPalette::All, QPalette::BrightText, text_color);
176
177 palette->setColor(QPalette::All, QPalette::Light, lighten(button_base_color));
178 palette->setColor(QPalette::All, QPalette::Midlight, mix(lighten(button_base_color), button_base_color));
179 palette->setColor(QPalette::All, QPalette::Mid, mix(darken(button_base_color), button_base_color));
180 palette->setColor(QPalette::All, QPalette::Dark, darken(button_base_color));
181 palette->setColor(QPalette::All, QPalette::Shadow, shadow);
182
183 palette->setColor(QPalette::All, QPalette::Highlight, selected_bg_color);
184 palette->setColor(QPalette::All, QPalette::HighlightedText, selected_fg_color);
185
186 palette->setColor(QPalette::All, QPalette::Link, link_color);
187 palette->setColor(QPalette::All, QPalette::LinkVisited, link_visited_color);
188
189
190 QColor insensitive_fg_color = mix(fg_color, bg_color);
191 QColor insensitive_bg_color = mix(bg_color, base_color, 0.4);
192
193 palette->setColor(QPalette::Disabled, QPalette::Window, insensitive_bg_color);
194 palette->setColor(QPalette::Disabled, QPalette::WindowText, insensitive_fg_color);
195 palette->setColor(QPalette::Disabled, QPalette::Base, base_color);
196 palette->setColor(QPalette::Disabled, QPalette::AlternateBase, base_color);
197 palette->setColor(QPalette::Disabled, QPalette::Text, insensitive_fg_color);
198 palette->setColor(QPalette::Disabled, QPalette::Button, insensitive_bg_color);
199 palette->setColor(QPalette::Disabled, QPalette::ButtonText, insensitive_fg_color);
200 palette->setColor(QPalette::Disabled, QPalette::BrightText, text_color);
201
202 palette->setColor(QPalette::Disabled, QPalette::Light, lighten(insensitive_bg_color));
203 palette->setColor(QPalette::Disabled, QPalette::Midlight, mix(lighten(insensitive_bg_color), insensitive_bg_color));
204 palette->setColor(QPalette::Disabled, QPalette::Mid, mix(darken(insensitive_bg_color), insensitive_bg_color));
205 palette->setColor(QPalette::Disabled, QPalette::Dark, darken(insensitive_bg_color));
206 palette->setColor(QPalette::Disabled, QPalette::Shadow, shadow);
207
208 palette->setColor(QPalette::Disabled, QPalette::Highlight, selected_bg_color);
209 palette->setColor(QPalette::Disabled, QPalette::HighlightedText, selected_fg_color);
210
211 palette->setColor(QPalette::Disabled, QPalette::Link, link_color);
212 palette->setColor(QPalette::Disabled, QPalette::LinkVisited, link_visited_color);
213
214
215 palette->setColor(QPalette::Inactive, QPalette::Window, bg_color);
216 palette->setColor(QPalette::Inactive, QPalette::WindowText, backdrop_fg_color);
217 palette->setColor(QPalette::Inactive, QPalette::Base, backdrop_base_color);
218 palette->setColor(QPalette::Inactive, QPalette::AlternateBase, backdrop_base_color);
219 palette->setColor(QPalette::Inactive, QPalette::Text, backdrop_fg_color);
220 palette->setColor(QPalette::Inactive, QPalette::Button, button_base_color);
221 palette->setColor(QPalette::Inactive, QPalette::ButtonText, backdrop_fg_color);
222 palette->setColor(QPalette::Inactive, QPalette::BrightText, text_color);
223
224 palette->setColor(QPalette::Inactive, QPalette::Light, lighten(insensitive_bg_color));
225 palette->setColor(QPalette::Inactive, QPalette::Midlight, mix(lighten(insensitive_bg_color), insensitive_bg_color));
226 palette->setColor(QPalette::Inactive, QPalette::Mid, mix(darken(insensitive_bg_color), insensitive_bg_color));
227 palette->setColor(QPalette::Inactive, QPalette::Dark, darken(insensitive_bg_color));
228 palette->setColor(QPalette::Inactive, QPalette::Shadow, shadow);
229
230 palette->setColor(QPalette::Inactive, QPalette::Highlight, selected_bg_color);
231 palette->setColor(QPalette::Inactive, QPalette::HighlightedText, backdrop_selected_fg_color);
232
233 palette->setColor(QPalette::Inactive, QPalette::Link, link_color);
234 palette->setColor(QPalette::Inactive, QPalette::LinkVisited, link_visited_color);
235 } else {
236 // Colors defined in GTK adwaita style in _colors.scss
237 QColor base_color = QColor("white");
238 QColor text_color = QColor("black");
239 QColor bg_color = QColor("#f6f5f4");
240 QColor fg_color = QColor("#2e3436");
241 QColor selected_bg_color = QColor("#3584e4");
242 QColor selected_fg_color = QColor("white");
243 QColor osd_text_color = QColor("white");
244 QColor osd_bg_color = QColor("black");
245 QColor shadow = transparentize(QColor("black"), 0.9);
246
247 QColor backdrop_fg_color = mix(fg_color, bg_color);
248 QColor backdrop_base_color = darken(base_color, 0.01);
249 QColor backdrop_selected_fg_color = backdrop_base_color;
250
251 // This is the color we use as initial color for the gradient in normal state
252 // Defined in _drawing.scss button(normal)
253 QColor button_base_color = darken(bg_color, 0.04);
254
255 QColor link_color = darken(selected_bg_color, 0.1);
256 QColor link_visited_color = darken(selected_bg_color, 0.2);
257
258 palette->setColor(QPalette::All, QPalette::Window, bg_color);
259 palette->setColor(QPalette::All, QPalette::WindowText, fg_color);
260 palette->setColor(QPalette::All, QPalette::Base, base_color);
261 palette->setColor(QPalette::All, QPalette::AlternateBase, base_color);
262 palette->setColor(QPalette::All, QPalette::ToolTipBase, osd_bg_color);
263 palette->setColor(QPalette::All, QPalette::ToolTipText, osd_text_color);
264 palette->setColor(QPalette::All, QPalette::Text, fg_color);
265 palette->setColor(QPalette::All, QPalette::Button, button_base_color);
266 palette->setColor(QPalette::All, QPalette::ButtonText, fg_color);
267 palette->setColor(QPalette::All, QPalette::BrightText, text_color);
268
269 palette->setColor(QPalette::All, QPalette::Light, lighten(button_base_color));
270 palette->setColor(QPalette::All, QPalette::Midlight, mix(lighten(button_base_color), button_base_color));
271 palette->setColor(QPalette::All, QPalette::Mid, mix(darken(button_base_color), button_base_color));
272 palette->setColor(QPalette::All, QPalette::Dark, darken(button_base_color));
273 palette->setColor(QPalette::All, QPalette::Shadow, shadow);
274
275 palette->setColor(QPalette::All, QPalette::Highlight, selected_bg_color);
276 palette->setColor(QPalette::All, QPalette::HighlightedText, selected_fg_color);
277
278 palette->setColor(QPalette::All, QPalette::Link, link_color);
279 palette->setColor(QPalette::All, QPalette::LinkVisited, link_visited_color);
280
281 QColor insensitive_fg_color = mix(fg_color, bg_color);
282 QColor insensitive_bg_color = mix(bg_color, base_color, 0.4);
283
284 palette->setColor(QPalette::Disabled, QPalette::Window, insensitive_bg_color);
285 palette->setColor(QPalette::Disabled, QPalette::WindowText, insensitive_fg_color);
286 palette->setColor(QPalette::Disabled, QPalette::Base, base_color);
287 palette->setColor(QPalette::Disabled, QPalette::AlternateBase, base_color);
288 palette->setColor(QPalette::Disabled, QPalette::Text, insensitive_fg_color);
289 palette->setColor(QPalette::Disabled, QPalette::Button, insensitive_bg_color);
290 palette->setColor(QPalette::Disabled, QPalette::ButtonText, insensitive_fg_color);
291 palette->setColor(QPalette::Disabled, QPalette::BrightText, text_color);
292
293 palette->setColor(QPalette::Disabled, QPalette::Light, lighten(insensitive_bg_color));
294 palette->setColor(QPalette::Disabled, QPalette::Midlight, mix(lighten(insensitive_bg_color), insensitive_bg_color));
295 palette->setColor(QPalette::Disabled, QPalette::Mid, mix(darken(insensitive_bg_color), insensitive_bg_color));
296 palette->setColor(QPalette::Disabled, QPalette::Dark, darken(insensitive_bg_color));
297 palette->setColor(QPalette::Disabled, QPalette::Shadow, shadow);
298
299 palette->setColor(QPalette::Disabled, QPalette::Highlight, selected_bg_color);
300 palette->setColor(QPalette::Disabled, QPalette::HighlightedText, selected_fg_color);
301
302 palette->setColor(QPalette::Disabled, QPalette::Link, link_color);
303 palette->setColor(QPalette::Disabled, QPalette::LinkVisited, link_visited_color);
304
305
306 palette->setColor(QPalette::Inactive, QPalette::Window, bg_color);
307 palette->setColor(QPalette::Inactive, QPalette::WindowText, backdrop_fg_color);
308 palette->setColor(QPalette::Inactive, QPalette::Base, backdrop_base_color);
309 palette->setColor(QPalette::Inactive, QPalette::AlternateBase, backdrop_base_color);
310 palette->setColor(QPalette::Inactive, QPalette::Text, backdrop_fg_color);
311 palette->setColor(QPalette::Inactive, QPalette::Button, button_base_color);
312 palette->setColor(QPalette::Inactive, QPalette::ButtonText, backdrop_fg_color);
313 palette->setColor(QPalette::Inactive, QPalette::BrightText, text_color);
314
315 palette->setColor(QPalette::Inactive, QPalette::Light, lighten(insensitive_bg_color));
316 palette->setColor(QPalette::Inactive, QPalette::Midlight, mix(lighten(insensitive_bg_color), insensitive_bg_color));
317 palette->setColor(QPalette::Inactive, QPalette::Mid, mix(darken(insensitive_bg_color), insensitive_bg_color));
318 palette->setColor(QPalette::Inactive, QPalette::Dark, darken(insensitive_bg_color));
319 palette->setColor(QPalette::Inactive, QPalette::Shadow, shadow);
320
321 palette->setColor(QPalette::Inactive, QPalette::Highlight, selected_bg_color);
322 palette->setColor(QPalette::Inactive, QPalette::HighlightedText, backdrop_selected_fg_color);
323
324 palette->setColor(QPalette::Inactive, QPalette::Link, link_color);
325 palette->setColor(QPalette::Inactive, QPalette::LinkVisited, link_visited_color);
326 }
327
328 return palette;
329 }
330
gtkThemeDarkVariant()331 inline bool gtkThemeDarkVariant() const
332 {
333 return m_gtkThemeDarkVariant;
334 }
335
gtkTheme()336 inline QString gtkTheme() const
337 {
338 return QString(m_gtkTheme);
339 }
340
hint(QPlatformTheme::ThemeHint hint)341 inline QVariant hint(QPlatformTheme::ThemeHint hint) const
342 {
343 return m_hints[hint];
344 }
345
titlebarButtons()346 inline TitlebarButtons titlebarButtons() const
347 {
348 return m_titlebarButtons;
349 }
350
titlebarButtonPlacement()351 inline TitlebarButtonsPlacement titlebarButtonPlacement() const
352 {
353 return m_titlebarButtonPlacement;
354 }
355
356 public Q_SLOTS:
357 void cursorBlinkTimeChanged();
358 void cursorSizeChanged();
359 void fontChanged();
360 void iconsChanged();
361 void themeChanged();
362
363 private Q_SLOTS:
364 void loadFonts();
365 void loadTheme();
366 void loadTitlebar();
367 void loadStaticHints();
368 void portalSettingChanged(const QString &group, const QString &key, const QDBusVariant &value);
369
370 protected:
371 static void gsettingPropertyChanged(GSettings *settings, gchar *key, GnomeHintsSettings *gnomeHintsSettings);
372
373 private:
374 template <typename T> T getSettingsProperty(GSettings *settings, const QString &property, bool *ok = nullptr) {
375 Q_UNUSED(settings); Q_UNUSED(property); Q_UNUSED(ok);
376 return {};
377 }
378 template <typename T>
379 T getSettingsProperty(const QString &property, bool *ok = nullptr) {
380 GSettings *settings = m_settings;
381
382 // In case of Cinnamon session, we most probably want to return the value from here if possible
383 if (m_cinnamonSettings) {
384 GSettingsSchema *schema;
385 g_object_get(G_OBJECT(m_cinnamonSettings), "settings-schema", &schema, NULL);
386
387 if (schema) {
388 if (g_settings_schema_has_key(schema, property.toStdString().c_str())) {
389 settings = m_cinnamonSettings;
390 }
391 }
392 }
393
394 // Use org.gnome.desktop.wm.preferences if the property is there, otherwise it would bail on
395 // non-existent property
396 GSettingsSchema *schema;
397 g_object_get(G_OBJECT(m_gnomeDesktopSettings), "settings-schema", &schema, NULL);
398
399 if (schema) {
400 if (g_settings_schema_has_key(schema, property.toStdString().c_str())) {
401 settings = m_gnomeDesktopSettings;
402 }
403 }
404
405 if (m_usePortal) {
406 QVariant value = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(property);
407 if (!value.isNull() && value.canConvert<T>())
408 return value.value<T>();
409 value = m_portalSettings.value(QStringLiteral("org.gnome.desktop.wm.preferences")).value(property);
410 if (!value.isNull() && value.canConvert<T>())
411 return value.value<T>();
412 }
413
414 return getSettingsProperty<T>(settings, property, ok);
415 }
416 QStringList xdgIconThemePaths() const;
417 QString kvantumThemeForGtkTheme() const;
418 void configureKvantum(const QString &theme) const;
419
420 bool m_usePortal;
421 bool m_gtkThemeDarkVariant = false;
422 TitlebarButtons m_titlebarButtons = TitlebarButton::CloseButton;
423 TitlebarButtonsPlacement m_titlebarButtonPlacement = TitlebarButtonsPlacement::RightPlacement;
424 QString m_gtkTheme = nullptr;
425 GSettings *m_cinnamonSettings = nullptr;
426 GSettings *m_gnomeDesktopSettings = nullptr;
427 GSettings *m_settings = nullptr;
428 QHash<QPlatformTheme::Font, QFont*> m_fonts;
429 QHash<QPlatformTheme::ThemeHint, QVariant> m_hints;
430 QMap<QString, QVariantMap> m_portalSettings;
431 };
432
getSettingsProperty(GSettings * settings,const QString & property,bool * ok)433 template <> inline int GnomeHintsSettings::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) {
434 if (ok)
435 *ok = true;
436 return g_settings_get_int(settings, property.toStdString().c_str());
437 }
438
getSettingsProperty(GSettings * settings,const QString & property,bool * ok)439 template <> inline QString GnomeHintsSettings::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) {
440 // be exception and resources safe
441 std::unique_ptr<gchar, void(*)(gpointer)> raw {g_settings_get_string(settings, property.toStdString().c_str()), g_free};
442 if (ok)
443 *ok = !!raw;
444 return QString{raw.get()};
445 }
446
getSettingsProperty(GSettings * settings,const QString & property,bool * ok)447 template <> inline qreal GnomeHintsSettings::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) {
448 if (ok)
449 *ok = true;
450 return g_settings_get_double(settings, property.toStdString().c_str());
451 }
452
453 Q_DECLARE_OPERATORS_FOR_FLAGS(GnomeHintsSettings::TitlebarButtons)
454
455 #endif // GNOME_HINTS_SETTINGS_H
456