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 | #ifndef QGTK3STORAGE_P_H |
5 | #define QGTK3STORAGE_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include "qgtk3interface_p.h" |
19 | #if QT_CONFIG(dbus) |
20 | #include "qgtk3portalinterface_p.h" |
21 | #endif |
22 | |
23 | #include <QtCore/QJsonDocument> |
24 | #include <QtCore/QCache> |
25 | #include <QtCore/QString> |
26 | #include <QtGui/QGuiApplication> |
27 | #include <QtGui/QPalette> |
28 | |
29 | #include <qpa/qplatformtheme.h> |
30 | #include <private/qflatmap_p.h> |
31 | |
32 | QT_BEGIN_NAMESPACE |
33 | class QGtk3Storage |
34 | { |
35 | Q_GADGET |
36 | public: |
37 | QGtk3Storage(); |
38 | |
39 | // Enum documented in cpp file. Please keep it in line with updates made here. |
40 | enum class SourceType { |
41 | Gtk, |
42 | Fixed, |
43 | Modified, |
44 | Mixed, |
45 | Invalid |
46 | }; |
47 | Q_ENUM(SourceType) |
48 | |
49 | // Standard GTK source: Populate a brush from GTK |
50 | struct Gtk3Source { |
51 | QGtk3Interface::QGtkWidget gtkWidgetType; |
52 | QGtk3Interface::QGtkColorSource source; |
53 | GtkStateFlags state; |
54 | int width = -1; |
55 | int height = -1; |
56 | QDebug operator<<(QDebug dbg) |
57 | { |
58 | return dbg << "QGtkStorage::Gtk3Source(gtkwidgetType=" << gtkWidgetType << ", source=" |
59 | << source << ", state=" << state << ", width=" << width << ", height=" |
60 | << height << ")" ; |
61 | } |
62 | }; |
63 | |
64 | // Recursive source: Populate a brush by altering another source |
65 | struct RecursiveSource { |
66 | QPalette::ColorGroup colorGroup; |
67 | QPalette::ColorRole colorRole; |
68 | Qt::ColorScheme colorScheme; |
69 | int lighter = 100; |
70 | int deltaRed = 0; |
71 | int deltaGreen = 0; |
72 | int deltaBlue = 0; |
73 | int width = -1; |
74 | int height = -1; |
75 | QDebug operator<<(QDebug dbg) |
76 | { |
77 | return dbg << "QGtkStorage::RecursiceSource(colorGroup=" << colorGroup << ", colorRole=" |
78 | << colorRole << ", colorScheme=" << colorScheme << ", lighter=" << lighter |
79 | << ", deltaRed=" << deltaRed << "deltaBlue =" << deltaBlue << "deltaGreen=" |
80 | << deltaGreen << ", width=" << width << ", height=" << height << ")" ; |
81 | } |
82 | }; |
83 | |
84 | // Mixed source: Populate a brush by mixing two brushes. |
85 | // Useful for creating disabled color by mixing, |
86 | // for example the background and foreground colors. |
87 | struct MixSources { |
88 | QPalette::ColorGroup sourceGroup; // source group of the mixing color roles |
89 | QPalette::ColorRole colorRole1; |
90 | QPalette::ColorRole colorRole2; |
91 | QDebug operator<<(QDebug dbg) |
92 | { |
93 | return dbg << "QGtkStorage::MixSources(sourceGroup=" << sourceGroup |
94 | << ", colorRole1=" << colorRole1 |
95 | << ", colorRole2=" << colorRole2 << ")" ; |
96 | } |
97 | static inline QColor mixColors(const QColor &color1, const QColor &color2) |
98 | { |
99 | return QColor{ (color1.red() + color2.red()) / 2, |
100 | (color1.green() + color2.green()) / 2, |
101 | (color1.blue() + color2.blue()) / 2 }; |
102 | } |
103 | }; |
104 | |
105 | // Fixed source: Populate a brush with fixed values rather than reading GTK |
106 | struct FixedSource { |
107 | QBrush fixedBrush; |
108 | QDebug operator<<(QDebug dbg) |
109 | { |
110 | return dbg << "QGtkStorage::FixedSource(" << fixedBrush << ")" ; |
111 | } |
112 | }; |
113 | |
114 | // Data source for brushes |
115 | struct Source { |
116 | SourceType sourceType = SourceType::Invalid; |
117 | Gtk3Source gtk3; |
118 | RecursiveSource rec; |
119 | FixedSource fix; |
120 | MixSources mix; |
121 | |
122 | // GTK constructor |
123 | Source(QGtk3Interface::QGtkWidget wtype, QGtk3Interface::QGtkColorSource csource, |
124 | GtkStateFlags cstate, int bwidth = -1, int bheight = -1) : sourceType(SourceType::Gtk) |
125 | { |
126 | gtk3.gtkWidgetType = wtype; |
127 | gtk3.source = csource; |
128 | gtk3.state = cstate; |
129 | gtk3.width = bwidth; |
130 | gtk3.height = bheight; |
131 | } |
132 | |
133 | // Recursive constructor for darker/lighter colors |
134 | Source(QPalette::ColorGroup group, QPalette::ColorRole role, |
135 | Qt::ColorScheme scheme, int p_lighter = 100) |
136 | : sourceType(SourceType::Modified) |
137 | { |
138 | rec.colorGroup = group; |
139 | rec.colorRole = role; |
140 | rec.colorScheme = scheme; |
141 | rec.lighter = p_lighter; |
142 | } |
143 | |
144 | // Recursive constructor for color modification |
145 | Source(QPalette::ColorGroup group, QPalette::ColorRole role, |
146 | Qt::ColorScheme scheme, int p_red, int p_green, int p_blue) |
147 | : sourceType(SourceType::Modified) |
148 | { |
149 | rec.colorGroup = group; |
150 | rec.colorRole = role; |
151 | rec.colorScheme = scheme; |
152 | rec.deltaRed = p_red; |
153 | rec.deltaGreen = p_green; |
154 | rec.deltaBlue = p_blue; |
155 | } |
156 | |
157 | // Recursive constructor for all: color modification and darker/lighter |
158 | Source(QPalette::ColorGroup group, QPalette::ColorRole role, |
159 | Qt::ColorScheme scheme, int p_lighter, |
160 | int p_red, int p_green, int p_blue) : sourceType(SourceType::Modified) |
161 | { |
162 | rec.colorGroup = group; |
163 | rec.colorRole = role; |
164 | rec.colorScheme = scheme; |
165 | rec.lighter = p_lighter; |
166 | rec.deltaRed = p_red; |
167 | rec.deltaGreen = p_green; |
168 | rec.deltaBlue = p_blue; |
169 | } |
170 | |
171 | // Mixed constructor for color modification |
172 | Source(QPalette::ColorGroup sourceGroup, |
173 | QPalette::ColorRole role1, QPalette::ColorRole role2) |
174 | : sourceType(SourceType::Mixed) |
175 | { |
176 | mix.sourceGroup = sourceGroup; |
177 | mix.colorRole1 = role1; |
178 | mix.colorRole2 = role2; |
179 | } |
180 | |
181 | // Fixed Source constructor |
182 | Source(const QBrush &brush) : sourceType(SourceType::Fixed) |
183 | { |
184 | fix.fixedBrush = brush; |
185 | }; |
186 | |
187 | // Invalid constructor and getter |
188 | Source() : sourceType(SourceType::Invalid) {}; |
189 | bool isValid() const { return sourceType != SourceType::Invalid; } |
190 | |
191 | // Debug |
192 | QDebug operator<<(QDebug dbg) |
193 | { |
194 | return dbg << "QGtk3Storage::Source(sourceType=" << sourceType << ")" ; |
195 | } |
196 | }; |
197 | |
198 | // Struct with key attributes to identify a brush: color group, color role and color scheme |
199 | struct TargetBrush { |
200 | QPalette::ColorGroup colorGroup; |
201 | QPalette::ColorRole colorRole; |
202 | Qt::ColorScheme colorScheme; |
203 | |
204 | // Generic constructor |
205 | TargetBrush(QPalette::ColorGroup group, QPalette::ColorRole role, |
206 | Qt::ColorScheme scheme = Qt::ColorScheme::Unknown) : |
207 | colorGroup(group), colorRole(role), colorScheme(scheme) {}; |
208 | |
209 | // Copy constructor with color scheme modifier for dark/light aware search |
210 | TargetBrush(const TargetBrush &other, Qt::ColorScheme scheme) : |
211 | colorGroup(other.colorGroup), colorRole(other.colorRole), colorScheme(scheme) {}; |
212 | |
213 | // struct becomes key of a map, so operator< is needed |
214 | bool operator<(const TargetBrush& other) const { |
215 | return std::tie(args: colorGroup, args: colorRole, args: colorScheme) < |
216 | std::tie(args: other.colorGroup, args: other.colorRole, args: other.colorScheme); |
217 | } |
218 | }; |
219 | |
220 | // Mapping a palette's brushes to their GTK sources |
221 | typedef QFlatMap<TargetBrush, Source> BrushMap; |
222 | |
223 | // Storage of palettes and their GTK sources |
224 | typedef QFlatMap<QPlatformTheme::Palette, BrushMap> PaletteMap; |
225 | |
226 | // Public getters |
227 | const QPalette *palette(QPlatformTheme::Palette = QPlatformTheme::SystemPalette) const; |
228 | QPixmap standardPixmap(QPlatformTheme::StandardPixmap standardPixmap, const QSizeF &size) const; |
229 | Qt::ColorScheme colorScheme() const { return m_colorScheme; }; |
230 | static QPalette standardPalette(); |
231 | const QString themeName() const { return m_interface ? m_interface->themeName() : QString(); }; |
232 | const QFont *font(QPlatformTheme::Font type) const; |
233 | QIcon fileIcon(const QFileInfo &fileInfo) const; |
234 | |
235 | // Initialization |
236 | void populateMap(); |
237 | void handleThemeChange(); |
238 | |
239 | private: |
240 | // Storage for palettes and their brushes |
241 | PaletteMap m_palettes; |
242 | |
243 | std::unique_ptr<QGtk3Interface> m_interface; |
244 | #if QT_CONFIG(dbus) |
245 | std::unique_ptr<QGtk3PortalInterface> m_portalInterface; |
246 | #endif |
247 | |
248 | Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; |
249 | |
250 | // Caches for Pixmaps, fonts and palettes |
251 | mutable QCache<QPlatformTheme::StandardPixmap, QImage> m_pixmapCache; |
252 | mutable std::array<std::optional<QPalette>, QPlatformTheme::Palette::NPalettes> m_paletteCache; |
253 | mutable std::array<std::optional<QFont>, QPlatformTheme::NFonts> m_fontCache; |
254 | |
255 | // Search brush with a given GTK3 source |
256 | QBrush brush(const Source &source, const BrushMap &map) const; |
257 | |
258 | // Get GTK3 source for a target brush |
259 | Source brush (const TargetBrush &brush, const BrushMap &map) const; |
260 | |
261 | // clear cache, palettes and color scheme |
262 | void clear(); |
263 | |
264 | // Data creation, import & export |
265 | void createMapping (); |
266 | const PaletteMap savePalettes() const; |
267 | bool save(const QString &filename, const QJsonDocument::JsonFormat f = QJsonDocument::Indented) const; |
268 | QJsonDocument save() const; |
269 | bool load(const QString &filename); |
270 | }; |
271 | |
272 | QT_END_NAMESPACE |
273 | #endif // QGTK3STORAGE_H |
274 | |