Wireshark 5.1.0
The Wireshark network protocol analyzer
Loading...
Searching...
No Matches
theme_manager.h
Go to the documentation of this file.
1
9
10#pragma once
11
12#include <QColor>
13#include <QFont>
14#include <QHash>
15#include <QMutex>
16#include <QObject>
17#include <QPalette>
18#include <QList>
19#include <QString>
20#include <QStringList>
21
23struct ThemeDraft;
24
26enum class ThemeContrast { Standard, High };
27
31struct ThemeInfo {
32 QString name;
33 QString internalName;
34 int version = 0;
35 QString description;
36 QString author;
37 ThemeContrast contrast = ThemeContrast::Standard;
38 bool builtIn = false;
39 QString filePath;
40};
41
43 bool required;
44 QStringList tokens;
45};
46
48 QColor light;
49 QColor dark;
50
52 QColor forMode(bool isDarkMode) const { return isDarkMode ? dark : light; }
54 void setForMode(bool isDarkMode, const QColor &color) { (isDarkMode ? dark : light) = color; }
55
57 static bool sameColor(const QColor &a, const QColor &b) {
58 return a.isValid() == b.isValid() && (!a.isValid() || a.rgba() == b.rgba());
59 }
60
61 bool isValid() const { return light.isValid() && dark.isValid(); }
62 bool operator==(const ThemeColorPair &other) const {
63 return sameColor(light, other.light) && sameColor(dark, other.dark);
64 }
65 bool operator!=(const ThemeColorPair &other) const { return !(*this == other); }
66};
67
73 QString family;
74 int pointSize = 0;
75
77 bool isSet() const { return !family.isEmpty() || pointSize > 0; }
78 bool operator==(const ThemeFontHint &other) const {
79 return family == other.family && pointSize == other.pointSize;
80 }
81 bool operator!=(const ThemeFontHint &other) const { return !(*this == other); }
82};
83
90 QStringList messages;
96};
97
127class ThemeManager : public QObject {
128 Q_OBJECT
129public:
130
140 enum class ThemeMode {
141 System,
142 Dark,
143 Light
144 };
145 Q_ENUM(ThemeMode)
146
147
162 enum class PreviewScheme {
163 Default,
164 PreferLight,
165 PreferDark
166 };
167 Q_ENUM(PreviewScheme)
168
169 enum ThemeToken {
170 // Brand
171 BrandPrimary,
172
173 // Accent
174 AccentSuccess,
175 AccentWarning,
176 AccentError,
177 AccentInfo,
178
179 // Expert
180 ExpertNone,
181 ExpertComment,
182 ExpertChat,
183 ExpertNote,
184 ExpertWarn,
185 ExpertError,
186 ExpertForeground,
187
188 // Packets — for each state, <Name> is the row background tint and
189 // <Name>Text is the foreground. Hidden is text-only by design (it
190 // dims an item that otherwise renders with the normal row bg).
191 // When a theme omits a Text token, ThemeTokenHandler derives it
192 // from contrastingText(<Name>) or PaletteText as appropriate.
193 PacketsSelection,
194 PacketsSelectionText,
195 PacketsInactive,
196 PacketsInactiveText,
197 PacketsMarked,
198 PacketsMarkedText,
199 PacketsIgnored,
200 PacketsIgnoredText,
201 PacketsHidden,
202
203 // Conversation — same fg/bg convention as Packets.
204 ConversationClient,
205 ConversationClientText,
206 ConversationServer,
207 ConversationServerText,
208
209 // Filter state (single color per state — use as bg tint; pair
210 // with QPalette::Text for foreground). Derived from accent
211 // when no explicit theme.filter override is set; the JSONC
212 // "filter" section lets a theme pin saturated/historical values
213 // that alpha-tinting the accent can't reach. FilterBusy and
214 // FilterBusyText carry the "I'm working" look used while a
215 // capture filter compiles off-thread.
216 FilterValid,
217 FilterInvalid,
218 FilterDeprecated,
219 FilterBusy,
220 FilterBusyText,
221
222 // Inline filter-affordance glyph colours (icon fills, not bg tints):
223 // the bookmark / clear / apply buttons inside the filter line. Each
224 // derives from the matching accent when a theme omits it (so every
225 // theme has them defined), and theme.filter.<name> overrides per-token.
226 // Display and capture bookmarks differ on purpose (blue vs green); the
227 // match colour marks a filter already saved.
228 FilterBookmarkDisplay, // display-filter bookmark (← AccentInfo, blue)
229 FilterBookmarkCapture, // capture-filter bookmark (← AccentSuccess, green)
230 FilterBookmarkMatch, // current filter is saved (← AccentWarning, yellow)
231 FilterClear, // clear affordance (active) (← AccentError, red)
232 FilterApply, // apply affordance (← AccentInfo, blue)
233 FilterHistory, // recent-filters arrow, dimmed at rest (← PaletteMid);
234 // the default theme pins fixed grays
235 // via theme.filter.history.
236
237 // Code editor (Lua Debugger script editor) — surface, gutter and
238 // paused-line colours, then the syntax highlighting foreground
239 // colours. Every key is optional; an omitted one uses the
240 // editor's built-in light / dark default.
241 CodeBackground,
242 CodeText,
243 CodeSelection,
244 CodeSelectionText,
245 CodeGutter,
246 CodeGutterText,
247 CodePausedLine,
248 CodeString,
249 CodeKeyword,
250 CodeNumber,
251 CodeBuiltIn,
252 CodeFunctionCall,
253 CodeComment,
254
255 // Syntax highlighting — foreground text colours for code- and
256 // data-viewers (JSON, hex, etc.). Derived so
257 // the variant is darker in light mode and lighter in dark
258 // mode, keeping enough contrast against PaletteBase.
259 SyntaxKey,
260 SyntaxString,
261 SyntaxNumber,
262
263 // Palette Overrides
264 PaletteWindow,
265 PaletteBase,
266 PaletteText,
267 PaletteWindowText,
268 PaletteAlternateBase,
269 PaletteMid,
270 PaletteMidlight,
271 // Selection background — when omitted, the builder uses
272 // packets.selection, else brand.primary.
273 PaletteHighlight,
274 // Selection foreground — when omitted, the builder picks
275 // contrastingText() of the resolved highlight. Themes whose
276 // selection colour sits on the WCAG luminance threshold (e.g.
277 // saturated mid-tone teals/blues where contrastingText flips to
278 // black even though white reads better) can pin this explicitly
279 // to white.
280 PaletteHighlightedText,
281#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
282 PaletteAccent,
283#endif
284
285 // Welcome header bar (derived from the palette's Highlight)
286 HeaderBackground,
287
288 // Section headers (derived from brand + QPalette)
289 SectionHeader,
290 SectionHeaderHover,
291
292 // Structural form chrome — borders, dividers, muted text.
293 // ControlBorder is the WCAG-enforced derivation of palette.mid
294 // against palette.base (the non-text ratio of the active
295 // contrast set), used to frame interactive controls without
296 // overshooting like palette.shadow does in light mode.
297 // MutedText is a derived de-emphasized text color
298 // (palette text mixed toward base, contrast-adapted to base)
299 // for secondary labels and plot reference lines.
300 ControlBorder,
301 MutedText,
302
303 // Theme border section, every key optional.
304 // BorderField frames text fields; when the theme does not set
305 // it, it is ControlBorder.
306 // BorderSeparator is a softer mix of base + mid, kept the divider
307 // ratio of the active contrast set apart from base, for
308 // hairlines between sections; themes set it when the derived
309 // value is too pronounced (Stratoshark uses an explicit value
310 // matching its midlight).
311 BorderField,
312 BorderSeparator,
313
314 // Raised surfaces ("cards") on Base and the colors drawn on
315 // them. In dark mode CardBackground is kept the dark card
316 // surface ratio of the active contrast set apart from Base,
317 // starting from the theme's palette.alternateBase pin or Base;
318 // in light mode it is that color unchanged.
319 // CardBorder frames it and is used for lines on it.
320 // CardSectionHeader* and CardFocusRing are the section header
321 // and focus colors re-derived to read on the card. CardLink is
322 // the application Link in light mode (lifted only when it does
323 // not read on the card), lifted further in dark mode so that it
324 // reads on CardLinkHover at body-text contrast. CardLinkHover
325 // is the item hover fill with the card as base, moved toward
326 // the card until CardLink reads on it (large-text contrast in
327 // light mode). OverlayCard* is the card surface for overlays
328 // over a black scrim, such as the startup splash: the lighter of
329 // Base and CardBackground, with its own border.
330 CardBackground,
331 CardBorder,
332 CardSectionHeader,
333 CardSectionHeaderHover,
334 CardLink,
335 CardLinkHover,
336 CardFocusRing,
337 OverlayCardBackground,
338 OverlayCardBorder,
339
340 // Welcome header title and version text (derived with HeaderBackground)
341 HeaderText,
342 HeaderTextMuted,
343
344 // Update bar (derived from accent.success). UpdateDot: the
345 // status dot, accent.success, kept non-text contrast apart
346 // from the bar in high contrast.
347 UpdateBackground,
348 UpdateBorder,
349 UpdateText,
350 UpdateTextHighlight,
351 UpdateLink,
352 UpdateLinkHover,
353 UpdateLinkPressed,
354 UpdateButtonBg,
355 UpdateButtonHover,
356 UpdateButtonPressed,
357 UpdateButtonDisabledBg,
358 UpdateButtonDisabledText,
359 UpdateDismissHoverBg,
360 UpdateDismissPressedBg,
361 UpdateDot,
362
363 // Accent aliases (derived)
364 HighlightColorOrange,
365 HighlightColorGreen,
366
367 // Foreground colors for solid accent backgrounds — derived via
368 // contrastingText() so they pick black or white depending on
369 // the accent's WCAG luminance. Use these when a QSS rule
370 // paints text on top of an accent-coloured surface (the
371 // "Development Build" badge, the "Download Update" button,
372 // etc.) so the text stays readable across themes whose
373 // accents fall on different sides of the contrast threshold.
374 //
375 // NOTE: these are intentionally named `TextOn*` rather than
376 // `Accent*Text`. ThemeManager's section auto-grouping treats
377 // any enum name starting with "accent" as a required input in
378 // the "accent" JSONC section (theme_manager.cpp section loop),
379 // so `Accent*Text` derived tokens would make the parser demand
380 // them from every theme file. The `TextOn*` prefix keeps
381 // them out of the accent section.
382 TextOnSuccess,
383 TextOnWarning,
384 TextOnError,
385 TextOnInfo,
386
387 // Foreground on the filter validity tints, derived as the
388 // contrastingText() of the matching Filter* token. Named
389 // `TextOn*` for the same section-grouping reason as above.
390 TextOnFilterValid,
391 TextOnFilterInvalid,
392 TextOnFilterDeprecated,
393
394 // Line tints of a text diff (added, changed, removed lines) and notice
395 // banners (background and frame): palette.base tinted toward an
396 // accent, stronger in dark mode. Derived only, not settable.
397 DiffAdded,
398 DiffChanged,
399 DiffRemoved,
400 NoticeInfo,
401 NoticeInfoBorder,
402 NoticeError,
403 NoticeErrorBorder,
404
405 // None
406 NoRole
407 };
408 Q_ENUM(ThemeToken)
409
410
417 static ThemeToken tokenFromName(const QString &name);
418
419 static ThemeManager* instance();
420
439 static QString defaultThemeName();
440
452 static QString resolveThemeName(const QString &name);
453
459 static void init(const QString &theme = QString());
460
461 void cleanup();
462
466 ThemeInfo info() const;
467
479 static QList<ThemeInfo> availableThemes();
480
485 static bool isBuiltInTheme(const QString &internalName);
486
490 static QString personalThemesDir();
491
497 static QString personalThemePath(const QString &stem);
498
503 static QString selectedThemeName();
504
510 static void setSelectedThemeName(const QString &name);
511
525 bool loadDraft(const QString &internalName, ThemeDraft &out,
526 QString *errorString = nullptr,
527 ThemeParseReport *report = nullptr) const;
528
540 bool parseDraft(const QByteArray &jsonc, const QString &internalName,
541 ThemeDraft &out, QString *errorString = nullptr,
542 ThemeParseReport *report = nullptr) const;
543
564 QHash<ThemeToken, ThemeColorPair> resolveDraftTokens(const ThemeDraft &draft,
565 bool dark,
566 QPalette *palette = nullptr,
567 QHash<ThemeToken, const char *> *sources = nullptr,
568 int gui_contrast = UseRecentContrast) const;
569
575 ThemeToken paletteRoleToken(QPalette::ColorRole role) const;
576
583 ThemeToken tokenForName(const QString &name) const;
584
588 static QHash<ThemeToken, QColor> flattenTokens(const QHash<ThemeToken, ThemeColorPair> &tokens,
589 bool dark);
590
595 const QList<ThemeToken> &requiredTokens() const { return requiredTokens_; }
596
598 struct SettableKey {
599 ThemeToken token;
600 QString section;
601 QString key;
602 };
603
610 const QList<SettableKey> &settableKeys() const { return settableKeys_; }
611
620 QColor color(ThemeToken role) const;
621
622 bool colorIsAvailable(ThemeToken role) const;
623
658 QHash<ThemeToken, QColor> previewTheme(const QString &internalName,
659 PreviewScheme scheme,
660 int gui_contrast = UseRecentContrast,
661 bool *declaresWindow = nullptr) const;
662
677 bool validateThemeFile(const QString &filePath) const;
678
703 QString loadStyleSheet(const QString &name) const;
704
718 static QString styleSheet(const QString &name);
719
731 static void setValidationState(QWidget *w, const QString &state);
732
739 static void setNoticeState(QWidget *w, const QString &state);
740
746 static bool isDark();
747
768 bool isDarkMode() const;
769
776 ThemeMode mode() const;
777
784 void setMode(ThemeMode mode);
785
791 ThemeContrast contrast() const;
792
798 static bool canDetectSystemContrast();
799
804 static bool systemPrefersHighContrast();
805
815 static ThemeContrast effectiveContrast(int gui_contrast, ThemeContrast declared);
816
821 static constexpr int UseRecentContrast = -1;
822
833 QColor graphColor(int idx) const;
834
840 qsizetype graphColorCount() const;
841
842signals:
843 void themeChanged();
844
845protected:
846 explicit ThemeManager(QObject *parent = nullptr);
848
849private:
850 static ThemeManager* instance_;
851 static QMutex mutex_;
852
853 ThemeInfo info_;
854
855 // Flattened token -> color value, one map per mode
856 QHash<ThemeManager::ThemeToken, ThemeColorPair> themeColors_;
858 QHash<ThemeManager::ThemeToken, ThemeColorPair> parsedColors_;
859 QList<ThemeColorPair> graphColors_;
860
861 QHash<QString, ThemeSectionInfo> sections_;
862 QList<ThemeToken> requiredTokens_;
863 QList<SettableKey> settableKeys_;
864
865 // mapping caches for role and palette resolution
866 QHash<QString, ThemeToken> colorRoleCache_;
867 QHash<QString, QPalette::ColorRole> paletteRoleCache_;
868
869 // Light/dark selection: user's explicit choice plus the detector
870 // that tracks the OS preference when mode == System. The detector
871 // is a private member — external code never sees it. Lifetime is
872 // managed via Qt's parent-child ownership: constructed with `this`
873 // as parent, destroyed automatically with the ThemeManager.
874 ThemeMode mode_ = ThemeMode::System;
875 SystemThemeDetector *detector_ = nullptr;
876
877 // Contrast level of the last apply; compared against a fresh
878 // effectiveContrast() when the OS contrast preference changes.
879 ThemeContrast contrast_ = ThemeContrast::Standard;
880
881 // Pristine OS palette snapshot used as the baseline when the palette
882 // builder constructs a new theme palette. Captured once at ctor
883 // entry (before any setPalette() call could pollute QApplication's
884 // live palette), and refreshed whenever the effective light/dark
885 // scheme changes (mode flip or OS notification): on macOS by dropping
886 // the application palette and reading back the platform palette Qt
887 // resolves, on Windows (Qt ≥ 6.8) from
888 // `qApp->style()->standardPalette()`. Using this snapshot instead of
889 // QApplication::palette() prevents the previous theme's palette
890 // overrides from leaking into the next theme's baseline — without it,
891 // switching from a theme with palette overrides (e.g. "inverted") to
892 // one without (e.g. "default") would leave the prior overrides
893 // visible. Only consulted on platforms where the OS palette is
894 // trusted (macOS, Windows Qt ≥ 6.8); ignored elsewhere.
895 QPalette osBaseline_;
896
907 QPalette baselineForBuild() const;
908
915 void reapplyForSchemeChange();
916
925 void applyTheme();
926
932 static ThemeMode modeFromPrefs(int gui_color_scheme);
933
940 void applyToStyleHints();
941
950 void applyApplicationStyleSheet();
951
965 bool loadTheme(const QString &themeName = QString());
966
967};
968
975 QHash<ThemeManager::ThemeToken, ThemeColorPair> colors;
976 QList<ThemeColorPair> graphColors;
979
981 static const QList<ThemeManager::ThemeToken> &requiredTokens();
982
984 ThemeManager::ThemeToken firstMissingRequired() const;
985
987 bool hasRequiredColors() const;
988
994 bool declaresWindow() const;
995
997 bool sameColors(const ThemeDraft &other) const;
998
1006
1012 bool operator==(const ThemeDraft &other) const;
1013 bool operator!=(const ThemeDraft &other) const { return !(*this == other); }
1014};
Definition system_theme_detector.h:38
static bool isDark()
Definition theme_manager.cpp:427
static void init(const QString &theme=QString())
Definition theme_manager.cpp:312
static bool systemPrefersHighContrast()
Definition theme_manager.cpp:486
static ThemeContrast effectiveContrast(int gui_contrast, ThemeContrast declared)
Definition theme_manager.cpp:495
static QString selectedThemeName()
Definition theme_manager.cpp:804
void setMode(ThemeMode mode)
Definition theme_manager.cpp:512
QColor graphColor(int idx) const
Returns the graph color for the given index, cycling through available graph colors if necessary.
Definition theme_manager.cpp:1081
static QString styleSheet(const QString &name)
Definition theme_manager.cpp:699
QHash< ThemeToken, ThemeColorPair > resolveDraftTokens(const ThemeDraft &draft, bool dark, QPalette *palette=nullptr, QHash< ThemeToken, const char * > *sources=nullptr, int gui_contrast=UseRecentContrast) const
Definition theme_manager.cpp:885
static ThemeToken tokenFromName(const QString &name)
Definition theme_manager.cpp:274
ThemeToken tokenForName(const QString &name) const
Definition theme_manager.cpp:877
static QHash< ThemeToken, QColor > flattenTokens(const QHash< ThemeToken, ThemeColorPair > &tokens, bool dark)
Definition theme_manager.cpp:938
PreviewScheme
Definition theme_manager.h:162
QString loadStyleSheet(const QString &name) const
Definition theme_manager.cpp:690
static bool isBuiltInTheme(const QString &internalName)
Definition theme_manager.cpp:786
bool loadDraft(const QString &internalName, ThemeDraft &out, QString *errorString=nullptr, ThemeParseReport *report=nullptr) const
Definition theme_manager.cpp:828
bool parseDraft(const QByteArray &jsonc, const QString &internalName, ThemeDraft &out, QString *errorString=nullptr, ThemeParseReport *report=nullptr) const
Definition theme_manager.cpp:851
ThemeMode
Definition theme_manager.h:140
static void setNoticeState(QWidget *w, const QString &state)
Definition theme_manager.cpp:725
ThemeMode mode() const
Definition theme_manager.cpp:467
static bool canDetectSystemContrast()
Definition theme_manager.cpp:477
const QList< ThemeToken > & requiredTokens() const
Definition theme_manager.h:595
QHash< ThemeToken, QColor > previewTheme(const QString &internalName, PreviewScheme scheme, int gui_contrast=UseRecentContrast, bool *declaresWindow=nullptr) const
Definition theme_manager.cpp:741
static QString personalThemesDir()
Definition theme_manager.cpp:791
static QString defaultThemeName()
Definition theme_manager.cpp:284
const QList< SettableKey > & settableKeys() const
Definition theme_manager.h:610
qsizetype graphColorCount() const
Returns the number of graph colors defined in the theme.
Definition theme_manager.cpp:1095
static void setSelectedThemeName(const QString &name)
Definition theme_manager.cpp:809
bool isDarkMode() const
Definition theme_manager.cpp:432
static QString personalThemePath(const QString &stem)
Definition theme_manager.cpp:796
static void setValidationState(QWidget *w, const QString &state)
Definition theme_manager.cpp:720
static QList< ThemeInfo > availableThemes()
Definition theme_manager.cpp:349
ThemeContrast contrast() const
Definition theme_manager.cpp:472
ThemeToken paletteRoleToken(QPalette::ColorRole role) const
Definition theme_manager.cpp:868
static QString resolveThemeName(const QString &name)
Definition theme_manager.cpp:300
bool validateThemeFile(const QString &filePath) const
Definition theme_manager.cpp:1002
static constexpr int UseRecentContrast
Definition theme_manager.h:821
ThemeManager(QObject *parent=nullptr)
Definition theme_manager.cpp:94
ThemeInfo info() const
Definition theme_manager.cpp:344
QColor color(ThemeToken role) const
Definition theme_manager.cpp:658
Definition theme_manager.h:47
void setForMode(bool isDarkMode, const QColor &color)
Definition theme_manager.h:54
QColor forMode(bool isDarkMode) const
Definition theme_manager.h:52
bool isValid() const
Definition theme_manager.h:61
static bool sameColor(const QColor &a, const QColor &b)
Definition theme_manager.h:57
Definition theme_manager.h:973
bool operator==(const ThemeDraft &other) const
Definition theme_manager.cpp:994
QHash< ThemeManager::ThemeToken, ThemeColorPair > colors
JSONC color inputs.
Definition theme_manager.h:975
bool sameColors(const ThemeDraft &other) const
Definition theme_manager.cpp:989
QList< ThemeColorPair > graphColors
Graph colors in order.
Definition theme_manager.h:976
ThemeManager::ThemeToken firstMissingRequired() const
Definition theme_manager.cpp:952
ThemeInfo info
Meta data and origin.
Definition theme_manager.h:974
int removeInvalidEntries()
Definition theme_manager.cpp:975
bool hasRequiredColors() const
Definition theme_manager.cpp:962
ThemeFontHint monospaceFont
Monospace font the theme asks for.
Definition theme_manager.h:978
ThemeFontHint regularFont
Regular font the theme asks for.
Definition theme_manager.h:977
static const QList< ThemeManager::ThemeToken > & requiredTokens()
Definition theme_manager.cpp:947
bool declaresWindow() const
Definition theme_manager.cpp:967
Definition theme_manager.h:72
QString family
Font family; empty when unset.
Definition theme_manager.h:73
int pointSize
Size in points; 0 when unset.
Definition theme_manager.h:74
bool isSet() const
Definition theme_manager.h:77
Definition theme_manager.h:31
int version
Schema version (currently 1).
Definition theme_manager.h:34
QString filePath
Resource path or file path the theme was read from.
Definition theme_manager.h:39
QString name
Display name, e.g. "Wireshark Default".
Definition theme_manager.h:32
bool builtIn
True for themes bundled as resources, false for personal files.
Definition theme_manager.h:38
ThemeContrast contrast
Declared by the theme's top-level "contrast" field.
Definition theme_manager.h:37
QString author
Theme author or organization.
Definition theme_manager.h:36
QString description
One-line description shown in preferences.
Definition theme_manager.h:35
QString internalName
Internal name, e.g. "default".
Definition theme_manager.h:33
Definition theme_manager.h:598
QString key
key name as written in the file (schema spelling)
Definition theme_manager.h:601
QString section
section name as written in the file
Definition theme_manager.h:600
Definition theme_manager.h:87
int unknownTopLevelKeys
Definition theme_manager.h:95
int invalidValues
Definition theme_manager.h:93
QStringList messages
Definition theme_manager.h:90
Definition theme_manager.h:42
ThemeContrast
Definition theme_manager.h:26