Line data Source code
1 : #pragma once
2 :
3 : #include <QColor>
4 : #include <QObject>
5 : #include <QString>
6 : #include <QStringList>
7 : #include <QMap>
8 :
9 : class QApplication;
10 :
11 : /**
12 : * @brief The ThemeManager class centralizes UI styling for MailJD.
13 : *
14 : * It supports Light and Dark modes by loading QSS templates and injecting
15 : * theme-specific variables. This ensures a consistent design language
16 : * across all components while maintaining high performance.
17 : *
18 : * 67.B2: A user-facing Mode (Light / Dark / System) sits above the
19 : * concrete Theme. System follows the OS color scheme via QStyleHints
20 : * (Qt >= 6.5) and reacts to runtime scheme changes. The mode is
21 : * persisted as QSettings "appearance/theme" = light|dark|system.
22 : */
23 : class ThemeManager : public QObject {
24 : Q_OBJECT
25 : #ifdef MAILJD_UNIT_TEST
26 : friend class TestThemeManager;
27 : #endif
28 : public:
29 : enum Theme {
30 : Light,
31 : Dark
32 : };
33 :
34 : // User-facing theme mode (67.B2). System resolves to Light/Dark from
35 : // the OS color scheme.
36 : enum Mode {
37 : ModeLight,
38 : ModeDark,
39 : ModeSystem
40 : };
41 1 : Q_ENUM(Theme)
42 0 : Q_ENUM(Mode)
43 :
44 : static ThemeManager& instance();
45 :
46 : // Apply a specific theme to the application
47 : void applyTheme(Theme theme);
48 :
49 : // Current active theme
50 7 : Theme currentTheme() const { return m_currentTheme; }
51 :
52 : // Helper to get a color value for the current theme. Falls back to
53 : // the Light palette when no theme has been applied yet (e.g. unit
54 : // tests), so consumers never need their own literal fallbacks.
55 : QString color(const QString &key) const;
56 :
57 : // ── 67.B3: shared programmatic palettes ───────────────
58 : // All color decisions live here (docs/DESIGN.md) — src/ outside this
59 : // class must not contain hex literals (ctest gate no_inline_colors).
60 :
61 : // Confidence → color mapping for folder-suggestion UI
62 : // (MailListModel, MailThreadModel, SuggestionOverlay, status label).
63 : static QColor confidenceColor(double confidence);
64 :
65 : // Default calendar colors (theme-independent data palette).
66 : static QStringList calendarPalette();
67 :
68 : // Muted confidence palette for the status-bar suggestion label
69 : // (T-198 semantics: > 0.8 blue, > 0.6 warm grey, else light grey).
70 : static QColor mutedConfidenceColor(double confidence);
71 :
72 : // Label/keyword chip palette (LabelDelegate): red, orange, green,
73 : // blue, purple, gold, gray.
74 : static QList<QColor> labelPalette();
75 :
76 : // 67.B4: deterministic avatar color for a sender address — same
77 : // input always yields the same hue (HSL, readable with white text).
78 : static QColor avatarColor(const QString &key);
79 :
80 : // 67.B6: shortcut-help overlay canvas (dark scrim/card in BOTH
81 : // themes — the overlay is intentionally theme-invariant).
82 : static QColor overlayScrimColor();
83 : static QColor overlayCardColor();
84 :
85 : // ── 67.B2: mode handling ──────────────────────────────
86 :
87 : // Set the mode, apply the resolved theme, persist the choice
88 : // (QSettings appearance/theme) and start/stop the OS scheme watch.
89 : void setMode(Mode mode);
90 :
91 4 : Mode mode() const { return m_mode; }
92 :
93 : // Resolve a mode to a concrete theme. System uses
94 : // QStyleHints::colorScheme() (Qt >= 6.5; falls back to Light).
95 : Theme resolveTheme(Mode mode) const;
96 :
97 : // Read QSettings appearance/theme (default: system) and apply it.
98 : // Called once at startup from main().
99 : void initFromSettings();
100 :
101 : static Mode modeFromString(const QString &value);
102 : static QString modeToString(Mode mode);
103 :
104 : signals:
105 : // Emitted after every applyTheme(). Consumers with code-painted
106 : // colors (folder icons, delegates, calendar) re-pull color() here.
107 : void themeChanged(ThemeManager::Theme theme);
108 :
109 : private:
110 : explicit ThemeManager(QObject *parent = nullptr);
111 : QString loadAndProcessQss(const QString &path, const QMap<QString, QString> &vars);
112 : void applyModeInternal(Mode mode, bool persist);
113 : static QMap<QString, QString> lightPalette();
114 : static QMap<QString, QString> darkPalette();
115 :
116 : Theme m_currentTheme = Light;
117 : Mode m_mode = ModeLight;
118 : bool m_systemWatchActive = false;
119 : QMap<QString, QString> m_currentVars;
120 : };
|