Qt 6.x
The Qt SDK
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));
25}
26
47QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const
48{
49 switch (source.sourceType) {
50 case SourceType::Gtk:
51 return m_interface ? QBrush(m_interface->brush(source.gtk3.gtkWidgetType,
52 source.gtk3.source, source.gtk3.state))
53 : QBrush();
54
56 // don't loop through modified sources, break if modified source not found
57 Source recSource = brush(TargetBrush(source.rec.colorGroup, source.rec.colorRole,
58 source.rec.colorScheme), map);
59
60 if (!recSource.isValid() || (recSource.sourceType == SourceType::Modified))
61 return QBrush();
62
63 // Set brush and alter color
64 QBrush b = brush(recSource, map);
65 if (source.rec.width > 0 && source.rec.height > 0)
66 b.setTexture(QPixmap(source.rec.width, source.rec.height));
67 QColor c = b.color().lighter(source.rec.lighter);
68 c = QColor((c.red() + source.rec.deltaRed),
69 (c.green() + source.rec.deltaGreen),
70 (c.blue() + source.rec.deltaBlue));
71 b.setColor(c);
72 return b;
73 }
74
76 return source.fix.fixedBrush;
77
79 return QBrush();
80 }
81
82 // needed because of the scope after recursive
83 Q_UNREACHABLE();
84}
85
94QGtk3Storage::Source QGtk3Storage::brush(const TargetBrush &b, const BrushMap &map) const
95{
96#define FIND(brush) if (map.contains(brush))\
97 return map.value(brush)
98
99 // Return exact match
100 FIND(b);
101
102 // unknown color scheme can find anything
103 if (b.colorScheme == Qt::ColorScheme::Unknown) {
104 FIND(TargetBrush(b, Qt::ColorScheme::Dark));
105 FIND(TargetBrush(b, Qt::ColorScheme::Light));
106 }
107
108 // Color group All can always be found
109 if (b.colorGroup != QPalette::All)
110 return brush(TargetBrush(QPalette::All, b.colorRole, b.colorScheme), map);
111
112 // Brush not found
113 return Source();
114#undef FIND
115}
116
128{
129 QColor backgroundColor(0xd4, 0xd0, 0xc8);
130 QColor lightColor(backgroundColor.lighter());
131 QColor darkColor(backgroundColor.darker());
132 const QBrush darkBrush(darkColor);
133 QColor midColor(Qt::gray);
134 QPalette palette(Qt::black, backgroundColor, lightColor, darkColor,
135 midColor, Qt::black, Qt::white);
136 palette.setBrush(QPalette::Disabled, QPalette::WindowText, darkBrush);
137 palette.setBrush(QPalette::Disabled, QPalette::Text, darkBrush);
138 palette.setBrush(QPalette::Disabled, QPalette::ButtonText, darkBrush);
139 palette.setBrush(QPalette::Disabled, QPalette::Base, QBrush(backgroundColor));
140 return palette;
141}
142
151{
153 return nullptr;
154
155 if (m_paletteCache[type].has_value()) {
156 qCDebug(lcQGtk3Interface) << "Returning palette from cache:"
158
159 return &m_paletteCache[type].value();
160 }
161
162 // Read system palette as a baseline first
163 if (!m_paletteCache[QPlatformTheme::SystemPalette].has_value() && type != QPlatformTheme::SystemPalette)
164 palette();
165
166 // Fall back to system palette for unknown types
167 if (!m_palettes.contains(type) && type != QPlatformTheme::SystemPalette) {
168 qCDebug(lcQGtk3Interface) << "Returning system palette for unknown type"
170 return palette();
171 }
172
173 BrushMap brushes = m_palettes.value(type);
174
175 // Standard palette is base for system palette. System palette is base for all others.
177 : m_paletteCache[QPlatformTheme::SystemPalette].value());
178
179 qCDebug(lcQGtk3Interface) << "Creating palette:" << QGtk3Json::fromPalette(type);
180 for (auto i = brushes.begin(); i != brushes.end(); ++i) {
181 Source source = i.value();
182
183 // Brush is set if
184 // - theme and source color scheme match
185 // - or either of them is unknown
186 const auto appSource = i.key().colorScheme;
187 const auto appTheme = colorScheme();
188 const bool setBrush = (appSource == appTheme) ||
189 (appSource == Qt::ColorScheme::Unknown) ||
190 (appTheme == Qt::ColorScheme::Unknown);
191
192 if (setBrush) {
193 p.setBrush(i.key().colorGroup, i.key().colorRole, brush(source, brushes));
194 }
195 }
196
197 m_paletteCache[type].emplace(p);
199 qCDebug(lcQGtk3Interface) << "System Palette defined" << themeName() << colorScheme() << p;
200
201 return &m_paletteCache[type].value();
202}
203
211{
212 if (m_fontCache[type].has_value())
213 return &m_fontCache[type].value();
214
215 m_fontCache[type].emplace(m_interface->font(type));
216 return &m_fontCache[type].value();
217}
218
227 const QSizeF &size) const
228{
229 if (m_pixmapCache.contains(standardPixmap))
230 return QPixmap::fromImage(m_pixmapCache.object(standardPixmap)->scaled(size.toSize()));
231
232 if (!m_interface)
233 return QPixmap();
234
235 QImage image = m_interface->standardPixmap(standardPixmap);
236 if (image.isNull())
237 return QPixmap();
238
239 m_pixmapCache.insert(standardPixmap, new QImage(image));
240 return QPixmap::fromImage(image.scaled(size.toSize()));
241}
242
248{
249 return m_interface ? m_interface->fileIcon(fileInfo) : QIcon();
250}
251
256void QGtk3Storage::clear()
257{
258 m_colorScheme = Qt::ColorScheme::Unknown;
259 m_palettes.clear();
260 for (auto &cache : m_paletteCache)
261 cache.reset();
262
263 for (auto &cache : m_fontCache)
264 cache.reset();
265}
266
275{
276 clear();
277 populateMap();
279}
280
330{
331 static QString m_themeName;
332
333 // Distiguish initialization, theme change or call without theme change
334 const QString newThemeName = themeName();
335 if (m_themeName == newThemeName)
336 return;
337
338 clear();
339
340 // Derive color scheme from theme name
341 m_colorScheme = newThemeName.contains("dark"_L1, Qt::CaseInsensitive)
342 ? Qt::ColorScheme::Dark : m_interface->colorSchemeByColors();
343
344 if (m_themeName.isEmpty()) {
345 qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << m_colorScheme;
346 } else {
347 qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << m_colorScheme;
348 }
349 m_themeName = newThemeName;
350
351 // create standard mapping or load from Json file?
352 const QString jsonInput = qEnvironmentVariable("QT_GUI_GTK_JSON");
353 if (!jsonInput.isEmpty()) {
354 if (load(jsonInput)) {
355 return;
356 } else {
357 qWarning() << "Falling back to standard GTK mapping.";
358 }
359 }
360
361 createMapping();
362
363 const QString jsonOutput = qEnvironmentVariable("QT_GUI_GTK_JSON_SAVE");
364 if (!jsonOutput.isEmpty() && !save(jsonOutput))
365 qWarning() << "File" << jsonOutput << "could not be saved.\n";
366}
367
377const QGtk3Storage::PaletteMap QGtk3Storage::savePalettes() const
378{
379 const QString hard = qEnvironmentVariable("QT_GUI_GTK_JSON_HARDCODED");
380 if (!hard.contains("true"_L1, Qt::CaseInsensitive))
381 return m_palettes;
382
383 // Json output is supposed to be readable without GTK connection
384 // convert palette map into hard coded brushes
385 PaletteMap map = m_palettes;
386 for (auto paletteIterator = map.begin(); paletteIterator != map.end();
387 ++paletteIterator) {
388 QGtk3Storage::BrushMap &bm = paletteIterator.value();
389 for (auto brushIterator = bm.begin(); brushIterator != bm.end();
390 ++brushIterator) {
391 QGtk3Storage::Source &s = brushIterator.value();
392 switch (s.sourceType) {
393
394 // Read the brush and convert it into a fixed brush
395 case SourceType::Gtk: {
396 const QBrush fixedBrush = brush(s, bm);
397 s.fix.fixedBrush = fixedBrush;
398 s.sourceType = SourceType::Fixed;
399 }
400 break;
404 break;
405 }
406 }
407 }
408 return map;
409}
410
419bool QGtk3Storage::save(const QString &filename, QJsonDocument::JsonFormat f) const
420{
421 return QGtk3Json::save(savePalettes(), filename, f);
422}
423
432QJsonDocument QGtk3Storage::save() const
433{
434 return QGtk3Json::save(savePalettes());
435}
436
443bool QGtk3Storage::load(const QString &filename)
444{
445 return QGtk3Json::load(m_palettes, filename);
446}
447
455void QGtk3Storage::createMapping()
456{
457 // Hard code standard mapping
459 Source source;
460
461 // Define a GTK source
462#define GTK(wtype, colorSource, state)\
463 source = Source(QGtk3Interface::QGtkWidget::gtk_ ##wtype,\
464 QGtk3Interface::QGtkColorSource::colorSource, GTK_STATE_FLAG_ ##state)
465
466 // Define a modified source
467#define LIGHTER(group, role, lighter)\
468 source = Source(QPalette::group, QPalette::role,\
469 Qt::ColorScheme::Unknown, lighter)
470#define MODIFY(group, role, red, green, blue)\
471 source = Source(QPalette::group, QPalette::role,\
472 Qt::ColorScheme::Unknown, red, green, blue)
473
474 // Define fixed source
475#define FIX(color) source = FixedSource(color);
476
477 // Add the source to a target brush
478 // Use default Qt::ColorScheme::Unknown, if no color scheme was specified
479#define ADD_2(group, role) map.insert(TargetBrush(QPalette::group, QPalette::role), source);
480#define ADD_3(group, role, app) map.insert(TargetBrush(QPalette::group, QPalette::role,\
481 Qt::ColorScheme::app), source);
482#define ADD_X(x, group, role, app, FUNC, ...) FUNC
483#define ADD(...) ADD_X(,##__VA_ARGS__, ADD_3(__VA_ARGS__), ADD_2(__VA_ARGS__))
484 // Save target brushes to a palette type
485#define SAVE(palette) m_palettes.insert(QPlatformTheme::palette, map)
486 // Clear brushes to start next palette
487#define CLEAR map.clear()
488
489 /*
490 Macro usage:
491
492 1. Define a source
493 GTK(QGtkWidget, QGtkColorSource, GTK_STATE_FLAG)
494 Fetch the color from a GtkWidget, related to a source and a state.
495
496 LIGHTER(ColorGroup, ColorROle, lighter)
497 Use a color of the same QPalette related to ColorGroup and ColorRole.
498 Make the color lighter (if lighter >100) or darker (if lighter < 100)
499
500 MODIFY(ColorGroup, ColorRole, red, green, blue)
501 Use a color of the same QPalette related to ColorGroup and ColorRole.
502 Modify it by adding red, green, blue.
503
504 FIX(const QBrush &)
505 Use a fixed brush without querying GTK
506
507 2. Define the target
508 Use ADD(ColorGroup, ColorRole) to use the defined source for the
509 color group / role in the current palette.
510
511 Use ADD(ColorGroup, ColorRole, ColorScheme) to use the defined source
512 only for a specific color scheme
513
514 3. Save mapping
515 Save the defined mappings for a specific palette.
516 If a mapping entry does not cover all color groups and roles of a palette,
517 the system palette will be used for the remaining values.
518 If the system palette does not have all combination of color groups and roles,
519 the remaining ones will be populated by a hard coded fusion-style like palette.
520
521 4. Clear mapping
522 Use CLEAR to clear the mapping and begin a new one.
523 */
524
525
526 // System palette
527 // background color and calculate derivates
528 GTK(Default, Background, INSENSITIVE);
529 ADD(Normal, Window);
530 ADD(Normal, Button);
531 ADD(Normal, Base);
532 ADD(Inactive, Base);
533 ADD(Inactive, Window);
534 LIGHTER(Normal, Window, 125);
535 ADD(Normal, Light);
536 LIGHTER(Normal, Window, 70);
537 ADD(Normal, Shadow);
538 LIGHTER(Normal, Window, 80);
539 ADD(Normal, Dark);
540 GTK(button, Foreground, ACTIVE);
541 ADD(Inactive, WindowText);
542 LIGHTER(Normal, WindowText, 50);
543 ADD(Disabled, Text);
544 ADD(Disabled, WindowText);
545 ADD(Inactive, ButtonText);
546 GTK(button, Text, NORMAL);
547 ADD(Disabled, ButtonText);
548 // special background colors
549 GTK(Default, Background, SELECTED);
550 ADD(Disabled, Highlight);
551 ADD(Normal, Highlight);
552 GTK(entry, Foreground, SELECTED);
553 ADD(Normal, HighlightedText);
554 GTK(entry, Background, ACTIVE);
555 ADD(Inactive, HighlightedText);
556 // text color and friends
557 GTK(entry, Text, NORMAL);
558 ADD(Normal, ButtonText);
559 ADD(Normal, WindowText);
560 ADD(Disabled, WindowText);
561 ADD(Disabled, HighlightedText);
562 GTK(Default, Text, NORMAL);
563 ADD(Normal, Text);
564 ADD(Normal, WindowText);
565 ADD(Inactive, Text);
566 ADD(Normal, HighlightedText);
567 LIGHTER(Normal, Base, 93);
568 ADD(All, AlternateBase);
569 GTK(Default, Foreground, NORMAL);
570 ADD(All, ToolTipText);
571 MODIFY(Normal, Text, 100, 100, 100);
572 ADD(All, PlaceholderText, Light);
573 MODIFY(Normal, Text, -100, -100, -100);
574 ADD(All, PlaceholderText, Dark);
575 SAVE(SystemPalette);
576 CLEAR;
577
578 // Checkbox and Radio Button
579 GTK(button, Text, ACTIVE);
580 ADD(Normal, Base, Dark);
581 GTK(Default, Background, NORMAL);
582 ADD(All, Base);
583 GTK(button, Text, NORMAL);
584 ADD(Normal, Base, Light);
585 SAVE(CheckBoxPalette);
586 SAVE(RadioButtonPalette);
587 CLEAR;
588
589 // ComboBox, GroupBox, Frame
590 GTK(combo_box, Text, NORMAL);
591 ADD(Normal, ButtonText, Dark);
592 ADD(Normal, Text, Dark);
593 GTK(combo_box, Text, ACTIVE);
594 ADD(Normal, ButtonText, Light);
595 ADD(Normal, Text, Light);
596 SAVE(ComboBoxPalette);
597 SAVE(GroupBoxPalette);
598 CLEAR;
599
600 // Menu bar
601 GTK(Default, Text, ACTIVE);
602 ADD(Normal, ButtonText);
603 SAVE(MenuPalette);
604 CLEAR;
605
606 // LineEdit
607 GTK(Default, Background, NORMAL);
608 ADD(All, Base);
609 SAVE(TextLineEditPalette);
610 CLEAR;
611
612#undef GTK
613#undef REC
614#undef FIX
615#undef ADD
616#undef ADD_2
617#undef ADD_3
618#undef ADD_X
619#undef SAVE
620#undef LOAD
621}
622
\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
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
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:20
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:208
\inmodule QtCore\reentrant
iterator begin()
Definition qmap.h:597
iterator end()
Definition qmap.h:601
The QPalette class contains color groups for each widget state.
Definition qpalette.h:19
@ Disabled
Definition qpalette.h:48
@ ButtonText
Definition qpalette.h:51
@ WindowText
Definition qpalette.h:50
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:1445
\inmodule QtCore
Definition qsize.h:207
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
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.
@ gray
Definition qnamespace.h:32
@ white
Definition qnamespace.h:30
@ black
Definition qnamespace.h:29
@ 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:162
#define qCDebug(category,...)
Button
GLboolean GLboolean GLboolean b
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat GLfloat f
GLenum type
GLsizei GLsizei GLchar * source
GLboolean reset
const GLubyte * c
GLuint entry
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
static QT_BEGIN_NAMESPACE const uint Default
Definition qsplitter_p.h:25
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)