Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgtk3storage.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4//
5// W A R N I N G
6// -------------
7//
8// This file is not part of the Qt API. It exists purely as an
9// implementation detail. This header file may change from version to
10// version without notice, or even be removed.
11//
12// We mean it.
13//
14
15#include "qgtk3json_p.h"
16#include "qgtk3storage_p.h"
17#include <qpa/qwindowsysteminterface.h>
18
20
22{
23 m_interface.reset(new QGtk3Interface(this));
24#if QT_CONFIG(dbus)
25 m_portalInterface.reset(new QGtk3PortalInterface(this));
26#endif
28}
29
50QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const
51{
52 switch (source.sourceType) {
53 case SourceType::Gtk:
54 return m_interface ? QBrush(m_interface->brush(source.gtk3.gtkWidgetType,
55 source.gtk3.source, source.gtk3.state))
56 : QBrush();
57
59 // don't loop through modified sources, break if modified source not found
60 Source recSource = brush(TargetBrush(source.rec.colorGroup, source.rec.colorRole,
61 source.rec.colorScheme), map);
62
63 if (!recSource.isValid() || (recSource.sourceType == SourceType::Modified))
64 return QBrush();
65
66 // Set brush and alter color
67 QBrush b = brush(recSource, map);
68 if (source.rec.width > 0 && source.rec.height > 0)
69 b.setTexture(QPixmap(source.rec.width, source.rec.height));
70 QColor c = b.color().lighter(source.rec.lighter);
71 c = QColor((c.red() + source.rec.deltaRed),
72 (c.green() + source.rec.deltaGreen),
73 (c.blue() + source.rec.deltaBlue));
74 b.setColor(c);
75 return b;
76 }
77
79 return source.fix.fixedBrush;
80
82 return QBrush();
83 }
84
85 // needed because of the scope after recursive
86 Q_UNREACHABLE();
87}
88
97QGtk3Storage::Source QGtk3Storage::brush(const TargetBrush &b, const BrushMap &map) const
98{
99#define FIND(brush) if (map.contains(brush))\
100 return map.value(brush)
101
102 // Return exact match
103 FIND(b);
104
105 // unknown color scheme can find anything
106 if (b.colorScheme == Qt::ColorScheme::Unknown) {
107 FIND(TargetBrush(b, Qt::ColorScheme::Dark));
108 FIND(TargetBrush(b, Qt::ColorScheme::Light));
109 }
110
111 // Color group All can always be found
112 if (b.colorGroup != QPalette::All)
113 return brush(TargetBrush(QPalette::All, b.colorRole, b.colorScheme), map);
114
115 // Brush not found
116 return Source();
117#undef FIND
118}
119
131{
132 QColor backgroundColor(0xd4, 0xd0, 0xc8);
133 QColor lightColor(backgroundColor.lighter());
134 QColor darkColor(backgroundColor.darker());
135 const QBrush darkBrush(darkColor);
136 QColor midColor(Qt::gray);
137 QPalette palette(Qt::black, backgroundColor, lightColor, darkColor,
138 midColor, Qt::black, Qt::white);
139 palette.setBrush(QPalette::Disabled, QPalette::WindowText, darkBrush);
140 palette.setBrush(QPalette::Disabled, QPalette::Text, darkBrush);
141 palette.setBrush(QPalette::Disabled, QPalette::ButtonText, darkBrush);
142 palette.setBrush(QPalette::Disabled, QPalette::Base, QBrush(backgroundColor));
143 return palette;
144}
145
154{
156 return nullptr;
157
158 if (m_paletteCache[type].has_value()) {
159 qCDebug(lcQGtk3Interface) << "Returning palette from cache:"
161
162 return &m_paletteCache[type].value();
163 }
164
165 // Read system palette as a baseline first
166 if (!m_paletteCache[QPlatformTheme::SystemPalette].has_value() && type != QPlatformTheme::SystemPalette)
167 palette();
168
169 // Fall back to system palette for unknown types
170 if (!m_palettes.contains(type) && type != QPlatformTheme::SystemPalette) {
171 qCDebug(lcQGtk3Interface) << "Returning system palette for unknown type"
173 return palette();
174 }
175
176 BrushMap brushes = m_palettes.value(type);
177
178 // Standard palette is base for system palette. System palette is base for all others.
180 : m_paletteCache[QPlatformTheme::SystemPalette].value());
181
182 qCDebug(lcQGtk3Interface) << "Creating palette:" << QGtk3Json::fromPalette(type);
183 for (auto i = brushes.begin(); i != brushes.end(); ++i) {
184 Source source = i.value();
185
186 // Brush is set if
187 // - theme and source color scheme match
188 // - or either of them is unknown
189 const auto appSource = i.key().colorScheme;
190 const auto appTheme = colorScheme();
191 const bool setBrush = (appSource == appTheme) ||
192 (appSource == Qt::ColorScheme::Unknown) ||
193 (appTheme == Qt::ColorScheme::Unknown);
194
195 if (setBrush) {
196 p.setBrush(i.key().colorGroup, i.key().colorRole, brush(source, brushes));
197 }
198 }
199
200 m_paletteCache[type].emplace(p);
202 qCDebug(lcQGtk3Interface) << "System Palette defined" << themeName() << colorScheme() << p;
203
204 return &m_paletteCache[type].value();
205}
206
214{
215 if (m_fontCache[type].has_value())
216 return &m_fontCache[type].value();
217
218 m_fontCache[type].emplace(m_interface->font(type));
219 return &m_fontCache[type].value();
220}
221
230 const QSizeF &size) const
231{
232 if (m_pixmapCache.contains(standardPixmap))
233 return QPixmap::fromImage(m_pixmapCache.object(standardPixmap)->scaled(size.toSize()));
234
235 if (!m_interface)
236 return QPixmap();
237
238 QImage image = m_interface->standardPixmap(standardPixmap);
239 if (image.isNull())
240 return QPixmap();
241
242 m_pixmapCache.insert(standardPixmap, new QImage(image));
243 return QPixmap::fromImage(image.scaled(size.toSize()));
244}
245
251{
252 return m_interface ? m_interface->fileIcon(fileInfo) : QIcon();
253}
254
259void QGtk3Storage::clear()
260{
261 m_colorScheme = Qt::ColorScheme::Unknown;
262 m_palettes.clear();
263 for (auto &cache : m_paletteCache)
264 cache.reset();
265
266 for (auto &cache : m_fontCache)
267 cache.reset();
268}
269
282
332{
333 static QString m_themeName;
334
335 // Distiguish initialization, theme change or call without theme change
337 const QString newThemeName = themeName();
338
339#if QT_CONFIG(dbus)
340 // Prefer color scheme we get from xdg-desktop-portal as this is what GNOME
341 // relies on these days
342 newColorScheme = m_portalInterface->colorScheme();
343#endif
344
345 if (newColorScheme == Qt::ColorScheme::Unknown) {
346 // Derive color scheme from theme name
347 newColorScheme = newThemeName.contains("dark"_L1, Qt::CaseInsensitive)
348 ? Qt::ColorScheme::Dark : m_interface->colorSchemeByColors();
349 }
350
351 if (m_themeName == newThemeName && m_colorScheme == newColorScheme)
352 return;
353
354 clear();
355
356 if (m_themeName.isEmpty()) {
357 qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << newColorScheme;
358 } else {
359 qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << newColorScheme;
360 }
361 m_colorScheme = newColorScheme;
362 m_themeName = newThemeName;
363
364 // create standard mapping or load from Json file?
365 const QString jsonInput = qEnvironmentVariable("QT_GUI_GTK_JSON");
366 if (!jsonInput.isEmpty()) {
367 if (load(jsonInput)) {
368 return;
369 } else {
370 qWarning() << "Falling back to standard GTK mapping.";
371 }
372 }
373
374 createMapping();
375
376 const QString jsonOutput = qEnvironmentVariable("QT_GUI_GTK_JSON_SAVE");
377 if (!jsonOutput.isEmpty() && !save(jsonOutput))
378 qWarning() << "File" << jsonOutput << "could not be saved.\n";
379}
380
390const QGtk3Storage::PaletteMap QGtk3Storage::savePalettes() const
391{
392 const QString hard = qEnvironmentVariable("QT_GUI_GTK_JSON_HARDCODED");
393 if (!hard.contains("true"_L1, Qt::CaseInsensitive))
394 return m_palettes;
395
396 // Json output is supposed to be readable without GTK connection
397 // convert palette map into hard coded brushes
398 PaletteMap map = m_palettes;
399 for (auto paletteIterator = map.begin(); paletteIterator != map.end();
400 ++paletteIterator) {
401 QGtk3Storage::BrushMap &bm = paletteIterator.value();
402 for (auto brushIterator = bm.begin(); brushIterator != bm.end();
403 ++brushIterator) {
404 QGtk3Storage::Source &s = brushIterator.value();
405 switch (s.sourceType) {
406
407 // Read the brush and convert it into a fixed brush
408 case SourceType::Gtk: {
409 const QBrush fixedBrush = brush(s, bm);
410 s.fix.fixedBrush = fixedBrush;
411 s.sourceType = SourceType::Fixed;
412 }
413 break;
417 break;
418 }
419 }
420 }
421 return map;
422}
423
432bool QGtk3Storage::save(const QString &filename, QJsonDocument::JsonFormat f) const
433{
434 return QGtk3Json::save(savePalettes(), filename, f);
435}
436
445QJsonDocument QGtk3Storage::save() const
446{
447 return QGtk3Json::save(savePalettes());
448}
449
456bool QGtk3Storage::load(const QString &filename)
457{
458 return QGtk3Json::load(m_palettes, filename);
459}
460
468void QGtk3Storage::createMapping()
469{
470 // Hard code standard mapping
472 Source source;
473
474 // Define a GTK source
475#define GTK(wtype, colorSource, state)\
476 source = Source(QGtk3Interface::QGtkWidget::gtk_ ##wtype,\
477 QGtk3Interface::QGtkColorSource::colorSource, GTK_STATE_FLAG_ ##state)
478
479 // Define a modified source
480#define LIGHTER(group, role, lighter)\
481 source = Source(QPalette::group, QPalette::role,\
482 Qt::ColorScheme::Unknown, lighter)
483#define MODIFY(group, role, red, green, blue)\
484 source = Source(QPalette::group, QPalette::role,\
485 Qt::ColorScheme::Unknown, red, green, blue)
486
487 // Define fixed source
488#define FIX(color) source = FixedSource(color);
489
490 // Add the source to a target brush
491 // Use default Qt::ColorScheme::Unknown, if no color scheme was specified
492#define ADD_2(group, role) map.insert(TargetBrush(QPalette::group, QPalette::role), source);
493#define ADD_3(group, role, app) map.insert(TargetBrush(QPalette::group, QPalette::role,\
494 Qt::ColorScheme::app), source);
495#define ADD_X(x, group, role, app, FUNC, ...) FUNC
496#define ADD(...) ADD_X(,##__VA_ARGS__, ADD_3(__VA_ARGS__), ADD_2(__VA_ARGS__))
497 // Save target brushes to a palette type
498#define SAVE(palette) m_palettes.insert(QPlatformTheme::palette, map)
499 // Clear brushes to start next palette
500#define CLEAR map.clear()
501
502 /*
503 Macro usage:
504
505 1. Define a source
506 GTK(QGtkWidget, QGtkColorSource, GTK_STATE_FLAG)
507 Fetch the color from a GtkWidget, related to a source and a state.
508
509 LIGHTER(ColorGroup, ColorROle, lighter)
510 Use a color of the same QPalette related to ColorGroup and ColorRole.
511 Make the color lighter (if lighter >100) or darker (if lighter < 100)
512
513 MODIFY(ColorGroup, ColorRole, red, green, blue)
514 Use a color of the same QPalette related to ColorGroup and ColorRole.
515 Modify it by adding red, green, blue.
516
517 FIX(const QBrush &)
518 Use a fixed brush without querying GTK
519
520 2. Define the target
521 Use ADD(ColorGroup, ColorRole) to use the defined source for the
522 color group / role in the current palette.
523
524 Use ADD(ColorGroup, ColorRole, ColorScheme) to use the defined source
525 only for a specific color scheme
526
527 3. Save mapping
528 Save the defined mappings for a specific palette.
529 If a mapping entry does not cover all color groups and roles of a palette,
530 the system palette will be used for the remaining values.
531 If the system palette does not have all combination of color groups and roles,
532 the remaining ones will be populated by a hard coded fusion-style like palette.
533
534 4. Clear mapping
535 Use CLEAR to clear the mapping and begin a new one.
536 */
537
538
539 // System palette
540 {
541 // background color and calculate derivates
542 GTK(Default, Background, INSENSITIVE);
543 ADD(All, Window);
544 ADD(All, Button);
545 ADD(All, Base);
546 LIGHTER(Normal, Window, 125);
547 ADD(Normal, Light);
548 ADD(Inactive, Light);
549 LIGHTER(Normal, Window, 70);
550 ADD(Normal, Shadow);
551 LIGHTER(Normal, Window, 80);
552 ADD(Normal, Dark);
553 ADD(Inactive, Dark)
554
555 GTK(button, Foreground, ACTIVE);
556 ADD(Inactive, WindowText);
557 LIGHTER(Normal, WindowText, 50);
558 ADD(Disabled, Text);
559 ADD(Disabled, WindowText);
560 ADD(Disabled, ButtonText);
561
562 GTK(button, Text, NORMAL);
563 ADD(Inactive, ButtonText);
564
565 // special background colors
566 GTK(Default, Background, SELECTED);
567 ADD(Disabled, Highlight);
568 ADD(Normal, Highlight);
569 ADD(Inactive, Highlight);
570
571 GTK(entry, Foreground, SELECTED);
572 ADD(Normal, HighlightedText);
573 ADD(Inactive, HighlightedText);
574
575 // text color and friends
576 GTK(entry, Text, NORMAL);
577 ADD(Normal, ButtonText);
578 ADD(Normal, WindowText);
579 ADD(Disabled, HighlightedText);
580
581 GTK(Default, Text, NORMAL);
582 ADD(Normal, Text);
583 ADD(Inactive, Text);
584 ADD(Normal, HighlightedText);
585 LIGHTER(Normal, Base, 93);
586 ADD(All, AlternateBase);
587
588 GTK(Default, Foreground, NORMAL);
589 MODIFY(Normal, Text, 100, 100, 100);
590 ADD(All, PlaceholderText, Light);
591 MODIFY(Normal, Text, -100, -100, -100);
592 ADD(All, PlaceholderText, Dark);
593
594 // Light, midlight, dark, mid, shadow colors
595 LIGHTER(Normal, Button, 125);
596 ADD(All, Light)
597 LIGHTER(Normal, Button, 113);
598 ADD(All, Midlight)
599 LIGHTER(Normal, Button, 113);
600 ADD(All, Mid)
601 LIGHTER(Normal, Button, 87);
602 ADD(All, Dark)
603 LIGHTER(Normal, Button, 5);
604 ADD(All, Shadow)
605
606 SAVE(SystemPalette);
607 CLEAR;
608 }
609
610 // Label and TabBar Palette
611 {
612 GTK(entry, Text, NORMAL);
613 ADD(Normal, WindowText);
614 ADD(Inactive, WindowText);
615
616 SAVE(LabelPalette);
617 SAVE(TabBarPalette);
618 CLEAR;
619 }
620
621 // Checkbox and RadioButton Palette
622 {
623 GTK(button, Text, ACTIVE);
624 ADD(Normal, Base, Dark);
625 ADD(Inactive, WindowText, Dark);
626
627 GTK(Default, Foreground, NORMAL);
628 ADD(All, Text);
629
630 GTK(Default, Background, NORMAL);
631 ADD(All, Base);
632
633 GTK(button, Text, NORMAL);
634 ADD(Normal, Base, Light);
635 ADD(Inactive, WindowText, Light);
636
637 SAVE(CheckBoxPalette);
638 SAVE(RadioButtonPalette);
639 CLEAR;
640 }
641
642 // ComboBox, GroupBox & Frame Palette
643 {
644 GTK(combo_box, Text, NORMAL);
645 ADD(Normal, ButtonText, Dark);
646 ADD(Normal, Text, Dark);
647 ADD(Inactive, WindowText, Dark);
648
649 GTK(combo_box, Text, ACTIVE);
650 ADD(Normal, ButtonText, Light);
651 ADD(Normal, Text, Light);
652 ADD(Inactive, WindowText, Light);
653
654 SAVE(ComboBoxPalette);
655 SAVE(GroupBoxPalette);
656 CLEAR;
657 }
658
659 // MenuBar Palette
660 {
661 GTK(Default, Text, ACTIVE);
662 ADD(Normal, ButtonText);
663 SAVE(MenuPalette);
664 CLEAR;
665 }
666
667 // LineEdit Palette
668 {
669 GTK(Default, Background, NORMAL);
670 ADD(All, Base);
671 SAVE(TextLineEditPalette);
672 CLEAR;
673 }
674
675#undef GTK
676#undef REC
677#undef FIX
678#undef ADD
679#undef ADD_2
680#undef ADD_3
681#undef ADD_X
682#undef SAVE
683#undef LOAD
684}
685
\inmodule QtGui
Definition qbrush.h:30
bool contains(const Key &key) const noexcept
Definition qcache.h:217
T * object(const Key &key) const noexcept
Definition qcache.h:209
bool insert(const Key &key, T *object, qsizetype cost=1)
Definition qcache.h:184
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
QColor darker(int f=200) const noexcept
Definition qcolor.cpp:2857
QColor lighter(int f=150) const noexcept
Definition qcolor.cpp:2812
iterator begin()
Definition qflatmap_p.h:769
iterator end()
Definition qflatmap_p.h:773
bool contains(const Key &key) const
Definition qflatmap_p.h:625
T value(const Key &key, const T &defaultValue) const
Definition qflatmap_p.h:636
void clear()
Definition qflatmap_p.h:591
\reentrant
Definition qfont.h:22
The QGtk3Interface class centralizes communication with the GTK3 library.
static bool save(const QGtk3Storage::PaletteMap &map, const QString &fileName, QJsonDocument::JsonFormat format=QJsonDocument::Indented)
static bool load(QGtk3Storage::PaletteMap &map, const QString &fileName)
static QLatin1String fromPalette(QPlatformTheme::Palette palette)
Definition qgtk3json.cpp:21
void handleThemeChange()
Handles a theme change at runtime.
const QPalette * palette(QPlatformTheme::Palette=QPlatformTheme::SystemPalette) const
Return a GTK styled QPalette.
QFlatMap< QPlatformTheme::Palette, BrushMap > PaletteMap
static QPalette standardPalette()
Returns a simple, hard coded base palette.
const QString themeName() const
Qt::ColorScheme colorScheme() const
QFlatMap< TargetBrush, Source > BrushMap
QIcon fileIcon(const QFileInfo &fileInfo) const
Returns a GTK styled file icon corresponding to.
void populateMap()
Populates a map with information about how to locate colors in GTK.
const QFont * font(QPlatformTheme::Font type) const
Return a GTK styled font.
QPixmap standardPixmap(QPlatformTheme::StandardPixmap standardPixmap, const QSizeF &size) const
Return a GTK styled standard pixmap if available.
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
\inmodule QtGui
Definition qimage.h:37
QImage scaled(int w, int h, Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode mode=Qt::FastTransformation) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qimage.h:209
\inmodule QtCore\reentrant
iterator begin()
Definition qmap.h:598
iterator end()
Definition qmap.h:602
The QPalette class contains color groups for each widget state.
Definition qpalette.h:19
@ Disabled
Definition qpalette.h:49
@ ButtonText
Definition qpalette.h:52
@ WindowText
Definition qpalette.h:51
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1437
\inmodule QtCore
Definition qsize.h:208
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static void handleThemeChange(QWindow *window=nullptr)
[Window class with invokable method]
Definition window.h:11
QMap< QString, QString > map
[6]
QPushButton * button
[2]
QCache< int, Employee > cache
[0]
Combined button and popup list for selecting options.
ColorScheme
Definition qnamespace.h:50
@ gray
Definition qnamespace.h:33
@ white
Definition qnamespace.h:31
@ black
Definition qnamespace.h:30
@ CaseInsensitive
Definition brush.cpp:5
Definition image.cpp:4
#define SAVE(src, state, prop, def)
#define FIND(brush)
#define GTK(wtype, colorSource, state)
#define CLEAR
#define ADD(...)
#define LIGHTER(group, role, lighter)
#define MODIFY(group, role, red, green, blue)
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
Button
GLboolean GLboolean GLboolean b
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat GLfloat f
GLenum type
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
GLboolean reset
const GLubyte * c
GLuint entry
GLfloat GLfloat p
[1]
static QT_BEGIN_NAMESPACE const uint Default
Definition qsplitter_p.h:27
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)