1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kcolorscheme.h" |
9 | #include "kcolorschemehelpers_p.h" |
10 | |
11 | #include "kcolorscheme_debug.h" |
12 | |
13 | #include <KColorUtils> |
14 | #include <KConfig> |
15 | #include <KConfigGroup> |
16 | |
17 | #include <QBrush> |
18 | #include <QColor> |
19 | #include <QGuiApplication> |
20 | #include <QPalette> |
21 | |
22 | // BEGIN StateEffects |
23 | StateEffects::StateEffects(QPalette::ColorGroup state, const KSharedConfigPtr &config) |
24 | : _color(0, 0, 0, 0) //, _chain(0) not needed yet |
25 | { |
26 | QString group; |
27 | if (state == QPalette::Disabled) { |
28 | group = QStringLiteral("ColorEffects:Disabled" ); |
29 | } else if (state == QPalette::Inactive) { |
30 | group = QStringLiteral("ColorEffects:Inactive" ); |
31 | } |
32 | |
33 | for (auto &effect : _effects) { |
34 | effect = 0; |
35 | } |
36 | |
37 | // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp |
38 | if (!group.isEmpty()) { |
39 | KConfigGroup cfg(config, group); |
40 | const bool enabledByDefault = (state == QPalette::Disabled); |
41 | if (cfg.readEntry(key: "Enable" , defaultValue: enabledByDefault)) { |
42 | _effects[Intensity] = cfg.readEntry(key: "IntensityEffect" , defaultValue: (int)(state == QPalette::Disabled ? IntensityDarken : IntensityNoEffect)); |
43 | _effects[Color] = cfg.readEntry(key: "ColorEffect" , defaultValue: (int)(state == QPalette::Disabled ? ColorNoEffect : ColorDesaturate)); |
44 | _effects[Contrast] = cfg.readEntry(key: "ContrastEffect" , defaultValue: (int)(state == QPalette::Disabled ? ContrastFade : ContrastTint)); |
45 | _amount[Intensity] = cfg.readEntry(key: "IntensityAmount" , defaultValue: state == QPalette::Disabled ? 0.10 : 0.0); |
46 | _amount[Color] = cfg.readEntry(key: "ColorAmount" , defaultValue: state == QPalette::Disabled ? 0.0 : -0.9); |
47 | _amount[Contrast] = cfg.readEntry(key: "ContrastAmount" , defaultValue: state == QPalette::Disabled ? 0.65 : 0.25); |
48 | if (_effects[Color] > ColorNoEffect) { |
49 | _color = cfg.readEntry(key: "Color" , defaultValue: state == QPalette::Disabled ? QColor(56, 56, 56) : QColor(112, 111, 110)); |
50 | } |
51 | } |
52 | } |
53 | } |
54 | |
55 | QBrush StateEffects::brush(const QBrush &background) const |
56 | { |
57 | QColor color = background.color(); // TODO - actually work on brushes |
58 | switch (_effects[Intensity]) { |
59 | case IntensityShade: |
60 | color = KColorUtils::shade(color, lumaAmount: _amount[Intensity]); |
61 | break; |
62 | case IntensityDarken: |
63 | color = KColorUtils::darken(color, amount: _amount[Intensity]); |
64 | break; |
65 | case IntensityLighten: |
66 | color = KColorUtils::lighten(color, amount: _amount[Intensity]); |
67 | break; |
68 | } |
69 | switch (_effects[Color]) { |
70 | case ColorDesaturate: |
71 | color = KColorUtils::darken(color, amount: 0.0, chromaGain: 1.0 - _amount[Color]); |
72 | break; |
73 | case ColorFade: |
74 | color = KColorUtils::mix(c1: color, c2: _color, bias: _amount[Color]); |
75 | break; |
76 | case ColorTint: |
77 | color = KColorUtils::tint(base: color, color: _color, amount: _amount[Color]); |
78 | break; |
79 | } |
80 | return QBrush(color); |
81 | } |
82 | |
83 | QBrush StateEffects::brush(const QBrush &foreground, const QBrush &background) const |
84 | { |
85 | QColor color = foreground.color(); // TODO - actually work on brushes |
86 | QColor bg = background.color(); |
87 | // Apply the foreground effects |
88 | switch (_effects[Contrast]) { |
89 | case ContrastFade: |
90 | color = KColorUtils::mix(c1: color, c2: bg, bias: _amount[Contrast]); |
91 | break; |
92 | case ContrastTint: |
93 | color = KColorUtils::tint(base: color, color: bg, amount: _amount[Contrast]); |
94 | break; |
95 | } |
96 | // Now apply global effects |
97 | return brush(background: color); |
98 | } |
99 | // END StateEffects |
100 | |
101 | // BEGIN default colors |
102 | struct SerializedColors { |
103 | QColor NormalBackground; |
104 | QColor AlternateBackground; |
105 | QColor NormalText; |
106 | QColor InactiveText; |
107 | QColor ActiveText; |
108 | QColor LinkText; |
109 | QColor VisitedText; |
110 | QColor NegativeText; |
111 | QColor NeutralText; |
112 | QColor PositiveText; |
113 | }; |
114 | |
115 | struct DecorationColors { |
116 | QColor Focus; |
117 | QColor Hover; |
118 | }; |
119 | |
120 | // clang-format off |
121 | // These numbers come from the default color scheme which is currently |
122 | // Breeze Light ([breeze repo]/colors/BreezeLight.colors) |
123 | static const SerializedColors defaultViewColors = { |
124 | .NormalBackground: { 255, 255, 255 }, // Background |
125 | .AlternateBackground: { 247, 247, 247 }, // Alternate |
126 | .NormalText: { 35, 38, 41 }, // Normal |
127 | .InactiveText: { 112, 125, 138 }, // Inactive |
128 | .ActiveText: { 61, 174, 233 }, // Active |
129 | .LinkText: { 41, 128, 185 }, // Link |
130 | .VisitedText: { 155, 89, 182 }, // Visited |
131 | .NegativeText: { 218, 68, 83 }, // Negative |
132 | .NeutralText: { 246, 116, 0 }, // Neutral |
133 | .PositiveText: { 39, 174, 96 } // Positive |
134 | }; |
135 | |
136 | static const SerializedColors defaultWindowColors = { |
137 | .NormalBackground: { 239, 240, 241 }, // Background |
138 | .AlternateBackground: { 227, 229, 231 }, // Alternate |
139 | .NormalText: { 35, 38, 41 }, // Normal |
140 | .InactiveText: { 112, 125, 138 }, // Inactive |
141 | .ActiveText: { 61, 174, 233 }, // Active |
142 | .LinkText: { 41, 128, 185 }, // Link |
143 | .VisitedText: { 155, 89, 182 }, // Visited |
144 | .NegativeText: { 218, 68, 83 }, // Negative |
145 | .NeutralText: { 246, 116, 0 }, // Neutral |
146 | .PositiveText: { 39, 174, 96 } // Positive |
147 | }; |
148 | |
149 | static const SerializedColors defaultButtonColors = { |
150 | .NormalBackground: { 252, 252, 252 }, // Background |
151 | .AlternateBackground: { 163, 212, 250 }, // Alternate |
152 | .NormalText: { 35, 38, 41 }, // Normal |
153 | .InactiveText: { 112, 125, 138 }, // Inactive |
154 | .ActiveText: { 61, 174, 233 }, // Active |
155 | .LinkText: { 41, 128, 185 }, // Link |
156 | .VisitedText: { 155, 89, 182 }, // Visited |
157 | .NegativeText: { 218, 68, 83 }, // Negative |
158 | .NeutralText: { 246, 116, 0 }, // Neutral |
159 | .PositiveText: { 39, 174, 96 } // Positive |
160 | }; |
161 | |
162 | static const SerializedColors defaultSelectionColors = { |
163 | .NormalBackground: { 61, 174, 233 }, // Background |
164 | .AlternateBackground: { 163, 212, 250 }, // Alternate |
165 | .NormalText: { 255, 255, 255 }, // Normal |
166 | .InactiveText: { 112, 125, 138 }, // Inactive |
167 | .ActiveText: { 255, 255, 255 }, // Active |
168 | .LinkText: { 253, 188, 75 }, // Link |
169 | .VisitedText: { 155, 89, 182 }, // Visited |
170 | .NegativeText: { 176, 55, 69 }, // Negative |
171 | .NeutralText: { 198, 92, 0 }, // Neutral |
172 | .PositiveText: { 23, 104, 57 } // Positive |
173 | }; |
174 | |
175 | static const SerializedColors defaultTooltipColors = { |
176 | .NormalBackground: { 247, 247, 247 }, // Background |
177 | .AlternateBackground: { 239, 240, 241 }, // Alternate |
178 | .NormalText: { 35, 38, 41 }, // Normal |
179 | .InactiveText: { 112, 125, 138 }, // Inactive |
180 | .ActiveText: { 61, 174, 233 }, // Active |
181 | .LinkText: { 41, 128, 185 }, // Link |
182 | .VisitedText: { 155, 89, 182 }, // Visited |
183 | .NegativeText: { 218, 68, 83 }, // Negative |
184 | .NeutralText: { 246, 116, 0 }, // Neutral |
185 | .PositiveText: { 39, 174, 96 } // Positive |
186 | }; |
187 | |
188 | static const SerializedColors defaultComplementaryColors = { |
189 | .NormalBackground: { 42, 46, 50 }, // Background |
190 | .AlternateBackground: { 27, 30, 32 }, // Alternate |
191 | .NormalText: { 252, 252, 252 }, // Normal |
192 | .InactiveText: { 161, 169, 177 }, // Inactive |
193 | .ActiveText: { 61, 174, 233 }, // Active |
194 | .LinkText: { 29, 153, 243 }, // Link |
195 | .VisitedText: { 155, 89, 182 }, // Visited |
196 | .NegativeText: { 218, 68, 83 }, // Negative |
197 | .NeutralText: { 246, 116, 0 }, // Neutral |
198 | .PositiveText: { 39, 174, 96 } // Positive |
199 | }; |
200 | |
201 | static const SerializedColors = { |
202 | .NormalBackground: { 222, 224, 226 }, // Background |
203 | .AlternateBackground: { 239, 240, 241 }, // Alternate |
204 | .NormalText: { 35, 38, 41 }, // Normal |
205 | .InactiveText: { 112, 125, 138 }, // Inactive |
206 | .ActiveText: { 61, 174, 233 }, // Active |
207 | .LinkText: { 41, 128, 185 }, // Link |
208 | .VisitedText: { 155, 89, 182 }, // Visited |
209 | .NegativeText: { 218, 68, 83 }, // Negative |
210 | .NeutralText: { 246, 116, 0 }, // Neutral |
211 | .PositiveText: { 39, 174, 96 } // Positive |
212 | }; |
213 | |
214 | static const DecorationColors defaultDecorationColors = { |
215 | .Focus: { 61, 174, 233 }, // Focus |
216 | .Hover: { 147, 206, 233 }, // Hover |
217 | }; |
218 | // END default colors |
219 | // clang-format off |
220 | |
221 | //BEGIN KColorSchemePrivate |
222 | class KColorSchemePrivate : public QSharedData |
223 | { |
224 | public: |
225 | explicit KColorSchemePrivate(const KSharedConfigPtr &, QPalette::ColorGroup state, KColorScheme::ColorSet set); |
226 | ~KColorSchemePrivate() |
227 | { |
228 | } |
229 | |
230 | void initFromConfig(const KSharedConfigPtr &config, QPalette::ColorGroup state, KColorScheme::ColorSet set); |
231 | void initFromSystemPalette(QPalette::ColorGroup state, KColorScheme::ColorSet set); |
232 | |
233 | QBrush background(KColorScheme::BackgroundRole) const; |
234 | QBrush foreground(KColorScheme::ForegroundRole) const; |
235 | QBrush decoration(KColorScheme::DecorationRole) const; |
236 | qreal contrast() const; |
237 | |
238 | struct Brushes { |
239 | std::array<QBrush, KColorScheme::NForegroundRoles> fg; |
240 | std::array<QBrush, KColorScheme::NBackgroundRoles> bg; |
241 | std::array<QBrush, KColorScheme::NDecorationRoles> deco; |
242 | |
243 | bool operator==(const Brushes &b) const |
244 | { |
245 | return this == &b || (fg == b.fg && bg == b.bg && deco == b.deco); |
246 | } |
247 | } _brushes; |
248 | |
249 | qreal _contrast; |
250 | }; |
251 | |
252 | static SerializedColors loadSerializedColors(const KConfigGroup &group, const SerializedColors &defaults) |
253 | { |
254 | constexpr std::array configMap = { |
255 | std::pair{"ForegroundNormal" , &SerializedColors::NormalText}, |
256 | std::pair{"ForegroundInactive" , &SerializedColors::InactiveText}, |
257 | std::pair{"ForegroundActive" , &SerializedColors::ActiveText}, |
258 | std::pair{"ForegroundLink" , &SerializedColors::LinkText}, |
259 | std::pair{"ForegroundVisited" , &SerializedColors::VisitedText}, |
260 | std::pair{"ForegroundNegative" , &SerializedColors::NegativeText}, |
261 | std::pair{"ForegroundNeutral" , &SerializedColors::NeutralText}, |
262 | std::pair{"ForegroundPositive" , &SerializedColors::PositiveText}, |
263 | std::pair{"BackgroundNormal" , &SerializedColors::NormalBackground}, |
264 | std::pair{"BackgroundAlternate" , &SerializedColors::AlternateBackground}, |
265 | }; |
266 | SerializedColors loadedColors; |
267 | for (const auto &entry : configMap) { |
268 | loadedColors.*(entry.second) = group.readEntry(key: entry.first, defaultValue: defaults.*(entry.second)); |
269 | } |
270 | return loadedColors; |
271 | } |
272 | |
273 | static DecorationColors loadDecorationColors(const KConfigGroup &group, const DecorationColors &defaults) |
274 | { |
275 | DecorationColors colors; |
276 | colors.Focus = group.readEntry(key: "DecorationFocus" , defaultValue: defaults.Focus); |
277 | colors.Hover = group.readEntry(key: "DecorationHover" , defaultValue: defaults.Hover); |
278 | return colors; |
279 | } |
280 | |
281 | KColorSchemePrivate::KColorSchemePrivate(const KSharedConfigPtr &config, QPalette::ColorGroup state, KColorScheme::ColorSet set) |
282 | { |
283 | if (config) { |
284 | initFromConfig(config, state, set); |
285 | } else { |
286 | initFromSystemPalette(state, set); |
287 | } |
288 | } |
289 | |
290 | void KColorSchemePrivate::initFromConfig(const KSharedConfigPtr &config, QPalette::ColorGroup state, KColorScheme::ColorSet set) |
291 | { |
292 | QString groupName; |
293 | SerializedColors defaultColors; |
294 | DecorationColors defaultDecoColors = defaultDecorationColors; |
295 | QColor tint; |
296 | switch (set) { |
297 | case KColorScheme::Window: |
298 | groupName = QStringLiteral("Colors:Window" ); |
299 | defaultColors = defaultWindowColors; |
300 | break; |
301 | case KColorScheme::Button: |
302 | groupName = QStringLiteral("Colors:Button" ); |
303 | defaultColors = defaultButtonColors; |
304 | break; |
305 | case KColorScheme::Selection: { |
306 | const KConfigGroup inactiveEffectGroup(config, QStringLiteral("ColorEffects:Inactive" )); |
307 | // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp |
308 | const bool inactiveSelectionEffect = inactiveEffectGroup.readEntry(key: "ChangeSelectionColor" , defaultValue: inactiveEffectGroup.readEntry(key: "Enable" , defaultValue: true)); |
309 | // if enabled, inactive/disabled uses Window colors instead, ala gtk |
310 | // ...except tinted with the Selection:NormalBackground color so it looks more like selection |
311 | if (state == QPalette::Active || (state == QPalette::Inactive && !inactiveSelectionEffect)) { |
312 | groupName = QStringLiteral("Colors:Selection" ); |
313 | defaultColors = defaultSelectionColors; |
314 | } else if (state == QPalette::Inactive) { |
315 | groupName = QStringLiteral("Colors:Window" ); |
316 | defaultColors = defaultWindowColors; |
317 | tint = config->group(QStringLiteral("Colors:Selection" )).readEntry(key: "BackgroundNormal" , defaultValue: defaultSelectionColors.NormalBackground); |
318 | } else { // disabled (...and still want this branch when inactive+disabled exists) |
319 | groupName = QStringLiteral("Colors:Window" ); |
320 | defaultColors = defaultWindowColors; |
321 | } |
322 | } break; |
323 | case KColorScheme::Tooltip: |
324 | groupName = QStringLiteral("Colors:Tooltip" ); |
325 | defaultColors = defaultTooltipColors; |
326 | break; |
327 | case KColorScheme::Complementary: |
328 | groupName = QStringLiteral("Colors:Complementary" ); |
329 | defaultColors = defaultComplementaryColors; |
330 | break; |
331 | case KColorScheme::Header: |
332 | groupName = QStringLiteral("Colors:Header" ); |
333 | defaultColors = loadSerializedColors(group: config->group(QStringLiteral("Colors:Window" )), defaults: defaultHeaderColors); |
334 | defaultDecoColors = loadDecorationColors(group: config->group(QStringLiteral("Colors:Window" )), defaults: defaultDecorationColors); |
335 | break; |
336 | case KColorScheme::NColorSets: |
337 | qCWarning(KCOLORSCHEME) << "ColorSet::NColorSets is not a valid color set value to pass to KColorScheme::KColorScheme" ; |
338 | [[fallthrough]]; |
339 | case KColorScheme::View: |
340 | groupName = QStringLiteral("Colors:View" ); |
341 | defaultColors = defaultViewColors; |
342 | break; |
343 | } |
344 | |
345 | KConfigGroup cfg(config, groupName); |
346 | bool hasInactivePalette = false; |
347 | if (state == QPalette::Inactive) { |
348 | KConfigGroup inactiveGroup = KConfigGroup(&cfg, QStringLiteral("Inactive" )); |
349 | if (inactiveGroup.exists()) { |
350 | cfg = inactiveGroup; |
351 | hasInactivePalette = true; |
352 | } |
353 | } |
354 | |
355 | _contrast = KColorScheme::contrastF(config); |
356 | |
357 | const SerializedColors loadedColors = loadSerializedColors(group: cfg, defaults: defaultColors); |
358 | const DecorationColors loadedDecoColors = loadDecorationColors(group: cfg, defaults: defaultDecoColors); |
359 | |
360 | _brushes.fg[KColorScheme::NormalText] = loadedColors.NormalText; |
361 | _brushes.fg[KColorScheme::InactiveText] = loadedColors.InactiveText; |
362 | _brushes.fg[KColorScheme::ActiveText] = loadedColors.ActiveText; |
363 | _brushes.fg[KColorScheme::LinkText] = loadedColors.LinkText; |
364 | _brushes.fg[KColorScheme::VisitedText] = loadedColors.VisitedText; |
365 | _brushes.fg[KColorScheme::NegativeText] = loadedColors.NegativeText; |
366 | _brushes.fg[KColorScheme::NeutralText] = loadedColors.NeutralText; |
367 | _brushes.fg[KColorScheme::PositiveText] = loadedColors.PositiveText; |
368 | |
369 | _brushes.bg[KColorScheme::NormalBackground] = loadedColors.NormalBackground; |
370 | _brushes.bg[KColorScheme::AlternateBackground] = loadedColors.AlternateBackground; |
371 | |
372 | _brushes.deco[KColorScheme::FocusColor] = loadedDecoColors.Focus; |
373 | _brushes.deco[KColorScheme::HoverColor] = loadedDecoColors.Hover; |
374 | |
375 | if (tint.isValid()) { |
376 | // adjustment |
377 | _brushes.bg[KColorScheme::NormalBackground] = |
378 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), color: tint, amount: 0.4); |
379 | _brushes.bg[KColorScheme::AlternateBackground] = |
380 | KColorUtils::tint(base: _brushes.bg[KColorScheme::AlternateBackground].color(), color: tint, amount: 0.4); |
381 | } |
382 | |
383 | // apply state adjustments |
384 | if (state != QPalette::Active || (state == QPalette::Inactive && !hasInactivePalette)) { |
385 | StateEffects effects(state, config); |
386 | for (auto &fg : _brushes.fg) { |
387 | fg = effects.brush(foreground: fg, background: _brushes.bg[KColorScheme::NormalBackground]); |
388 | } |
389 | for (auto &deco : _brushes.deco) { |
390 | deco = effects.brush(foreground: deco, background: _brushes.bg[KColorScheme::NormalBackground]); |
391 | } |
392 | _brushes.bg[KColorScheme::NormalBackground] = effects.brush(background: _brushes.bg[KColorScheme::NormalBackground]); |
393 | _brushes.bg[KColorScheme::AlternateBackground] = effects.brush(background: _brushes.bg[KColorScheme::AlternateBackground]); |
394 | } |
395 | |
396 | // calculated backgrounds |
397 | _brushes.bg[KColorScheme::ActiveBackground] = |
398 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), |
399 | color: _brushes.fg[KColorScheme::ActiveText].color()); |
400 | _brushes.bg[KColorScheme::LinkBackground] = |
401 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), |
402 | color: _brushes.fg[KColorScheme::LinkText].color()); |
403 | _brushes.bg[KColorScheme::VisitedBackground] = |
404 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), |
405 | color: _brushes.fg[KColorScheme::VisitedText].color()); |
406 | _brushes.bg[KColorScheme::NegativeBackground] = |
407 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), |
408 | color: _brushes.fg[KColorScheme::NegativeText].color()); |
409 | _brushes.bg[KColorScheme::NeutralBackground] = |
410 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), |
411 | color: _brushes.fg[KColorScheme::NeutralText].color()); |
412 | _brushes.bg[KColorScheme::PositiveBackground] = |
413 | KColorUtils::tint(base: _brushes.bg[KColorScheme::NormalBackground].color(), |
414 | color: _brushes.fg[KColorScheme::PositiveText].color()); |
415 | } |
416 | |
417 | void KColorSchemePrivate::initFromSystemPalette(QPalette::ColorGroup state, KColorScheme::ColorSet set) |
418 | { |
419 | // Initialize the color scheme from the system palette. This is supposed |
420 | // to be done if high-contrast mode is active (on Windows). |
421 | const QPalette systemPalette = qApp->palette(); |
422 | |
423 | QColor foreground; |
424 | QColor background; |
425 | switch (set) { |
426 | case KColorScheme::Button: |
427 | foreground = systemPalette.color(cg: state, cr: QPalette::ButtonText); |
428 | background = systemPalette.color(cg: state, cr: QPalette::Button); |
429 | break; |
430 | case KColorScheme::Tooltip: |
431 | foreground = systemPalette.color(cg: state, cr: QPalette::ToolTipText); |
432 | background = systemPalette.color(cg: state, cr: QPalette::ToolTipBase); |
433 | break; |
434 | case KColorScheme::Selection: |
435 | foreground = systemPalette.color(cg: state, cr: QPalette::HighlightedText); |
436 | background = systemPalette.color(cg: state, cr: QPalette::Highlight); |
437 | break; |
438 | case KColorScheme::View: |
439 | foreground = systemPalette.color(cg: state, cr: QPalette::Text); |
440 | background = systemPalette.color(cg: state, cr: QPalette::Base); |
441 | break; |
442 | case KColorScheme::NColorSets: |
443 | qCWarning(KCOLORSCHEME) << "ColorSet::NColorSets is not a valid color set value to pass to KColorScheme::KColorScheme" ; |
444 | [[fallthrough]]; |
445 | case KColorScheme::Window: |
446 | case KColorScheme::Complementary: |
447 | case KColorScheme::Header: |
448 | foreground = systemPalette.color(cg: state, cr: QPalette::WindowText); |
449 | background = systemPalette.color(cg: state, cr: QPalette::Window); |
450 | break; |
451 | } |
452 | |
453 | _contrast = KColorScheme::contrastF(config: {}); |
454 | |
455 | _brushes.fg[KColorScheme::NormalText] = foreground; |
456 | _brushes.fg[KColorScheme::InactiveText] = foreground; |
457 | _brushes.fg[KColorScheme::ActiveText] = foreground; |
458 | _brushes.fg[KColorScheme::LinkText] = systemPalette.color(cg: state, cr: QPalette::Link); |
459 | _brushes.fg[KColorScheme::VisitedText] = systemPalette.color(cg: state, cr: QPalette::LinkVisited); |
460 | _brushes.fg[KColorScheme::NegativeText] = foreground; |
461 | _brushes.fg[KColorScheme::NeutralText] = foreground; |
462 | _brushes.fg[KColorScheme::PositiveText] = foreground; |
463 | |
464 | _brushes.bg[KColorScheme::NormalBackground] = background; |
465 | _brushes.bg[KColorScheme::AlternateBackground] = systemPalette.color(cg: state, cr: QPalette::AlternateBase); |
466 | _brushes.bg[KColorScheme::ActiveBackground] = background; |
467 | _brushes.bg[KColorScheme::LinkBackground] = background; |
468 | _brushes.bg[KColorScheme::VisitedBackground] = background; |
469 | _brushes.bg[KColorScheme::NegativeBackground] = background; |
470 | _brushes.bg[KColorScheme::NeutralBackground] = background; |
471 | _brushes.bg[KColorScheme::PositiveBackground] = background; |
472 | |
473 | _brushes.deco[KColorScheme::FocusColor] = systemPalette.color(cg: state, cr: QPalette::Highlight); |
474 | _brushes.deco[KColorScheme::HoverColor] = systemPalette.color(cg: state, cr: QPalette::Highlight); |
475 | } |
476 | |
477 | QBrush KColorSchemePrivate::background(KColorScheme::BackgroundRole role) const |
478 | { |
479 | if (role >= KColorScheme::NormalBackground && role < KColorScheme::NBackgroundRoles) { |
480 | return _brushes.bg[role]; |
481 | } else { |
482 | return _brushes.bg[KColorScheme::NormalBackground]; |
483 | } |
484 | } |
485 | |
486 | QBrush KColorSchemePrivate::foreground(KColorScheme::ForegroundRole role) const |
487 | { |
488 | if (role >= KColorScheme::NormalText && role < KColorScheme::NForegroundRoles) { |
489 | return _brushes.fg[role]; |
490 | } else { |
491 | return _brushes.fg[KColorScheme::NormalText]; |
492 | } |
493 | } |
494 | |
495 | QBrush KColorSchemePrivate::decoration(KColorScheme::DecorationRole role) const |
496 | { |
497 | if (role >= KColorScheme::FocusColor && role < KColorScheme::NDecorationRoles) { |
498 | return _brushes.deco[role]; |
499 | } else { |
500 | return _brushes.deco[KColorScheme::FocusColor]; |
501 | } |
502 | } |
503 | |
504 | qreal KColorSchemePrivate::contrast() const |
505 | { |
506 | return _contrast; |
507 | } |
508 | //END KColorSchemePrivate |
509 | |
510 | //BEGIN KColorScheme |
511 | KColorScheme::KColorScheme(const KColorScheme &) = default; |
512 | KColorScheme &KColorScheme::operator=(const KColorScheme &) = default; |
513 | KColorScheme::KColorScheme(KColorScheme &&) = default; |
514 | KColorScheme &KColorScheme::operator=(KColorScheme &&) = default; |
515 | KColorScheme::~KColorScheme() = default; |
516 | |
517 | KColorScheme::KColorScheme(QPalette::ColorGroup state, ColorSet set, KSharedConfigPtr config) |
518 | : d(new KColorSchemePrivate(config ? config : defaultConfig(), state, set)) |
519 | { |
520 | } |
521 | |
522 | bool KColorScheme::operator==(const KColorScheme &other) const |
523 | { |
524 | return d == other.d |
525 | || (d->_contrast == other.d->_contrast |
526 | && d->_brushes == other.d->_brushes) |
527 | ; |
528 | } |
529 | |
530 | // static |
531 | qreal KColorScheme::contrastF(const KSharedConfigPtr &config) |
532 | { |
533 | KSharedConfigPtr conf = config ? config : defaultConfig(); |
534 | if (!conf) { |
535 | return 0.7; |
536 | } |
537 | KConfigGroup g(conf, QStringLiteral("KDE" )); |
538 | return 0.1 * g.readEntry(key: "contrast" , defaultValue: 7); |
539 | } |
540 | |
541 | QBrush KColorScheme::background(BackgroundRole role) const |
542 | { |
543 | return d->background(role); |
544 | } |
545 | |
546 | QBrush KColorScheme::foreground(ForegroundRole role) const |
547 | { |
548 | return d->foreground(role); |
549 | } |
550 | |
551 | QBrush KColorScheme::decoration(DecorationRole role) const |
552 | { |
553 | return d->decoration(role); |
554 | } |
555 | |
556 | QColor KColorScheme::shade(ShadeRole role) const |
557 | { |
558 | return shade(background().color(), role, contrast: d->contrast()); |
559 | } |
560 | |
561 | QColor KColorScheme::shade(const QColor &color, ShadeRole role) |
562 | { |
563 | return shade(color, role, contrast: KColorScheme::contrastF()); |
564 | } |
565 | |
566 | QColor KColorScheme::shade(const QColor &color, ShadeRole role, qreal contrast, qreal chromaAdjust) |
567 | { |
568 | // nan -> 1.0 |
569 | contrast = (1.0 > contrast ? (-1.0 < contrast ? contrast : -1.0) : 1.0); |
570 | qreal y = KColorUtils::luma(color); |
571 | qreal yi = 1.0 - y; |
572 | |
573 | // handle very dark colors (base, mid, dark, shadow == midlight, light) |
574 | if (y < 0.006) { |
575 | switch (role) { |
576 | case KColorScheme::LightShade: |
577 | return KColorUtils::shade(color, lumaAmount: 0.05 + 0.95 * contrast, chromaAmount: chromaAdjust); |
578 | case KColorScheme::MidShade: |
579 | return KColorUtils::shade(color, lumaAmount: 0.01 + 0.20 * contrast, chromaAmount: chromaAdjust); |
580 | case KColorScheme::DarkShade: |
581 | return KColorUtils::shade(color, lumaAmount: 0.02 + 0.40 * contrast, chromaAmount: chromaAdjust); |
582 | default: |
583 | return KColorUtils::shade(color, lumaAmount: 0.03 + 0.60 * contrast, chromaAmount: chromaAdjust); |
584 | } |
585 | } |
586 | |
587 | // handle very light colors (base, midlight, light == mid, dark, shadow) |
588 | if (y > 0.93) { |
589 | switch (role) { |
590 | case KColorScheme::MidlightShade: |
591 | return KColorUtils::shade(color, lumaAmount: -0.02 - 0.20 * contrast, chromaAmount: chromaAdjust); |
592 | case KColorScheme::DarkShade: |
593 | return KColorUtils::shade(color, lumaAmount: -0.06 - 0.60 * contrast, chromaAmount: chromaAdjust); |
594 | case KColorScheme::ShadowShade: |
595 | return KColorUtils::shade(color, lumaAmount: -0.10 - 0.90 * contrast, chromaAmount: chromaAdjust); |
596 | default: |
597 | return KColorUtils::shade(color, lumaAmount: -0.04 - 0.40 * contrast, chromaAmount: chromaAdjust); |
598 | } |
599 | } |
600 | |
601 | // handle everything else |
602 | qreal lightAmount = (0.05 + y * 0.55) * (0.25 + contrast * 0.75); |
603 | qreal darkAmount = (- y) * (0.55 + contrast * 0.35); |
604 | switch (role) { |
605 | case KColorScheme::LightShade: |
606 | return KColorUtils::shade(color, lumaAmount: lightAmount, chromaAmount: chromaAdjust); |
607 | case KColorScheme::MidlightShade: |
608 | return KColorUtils::shade(color, lumaAmount: (0.15 + 0.35 * yi) * lightAmount, chromaAmount: chromaAdjust); |
609 | case KColorScheme::MidShade: |
610 | return KColorUtils::shade(color, lumaAmount: (0.35 + 0.15 * y) * darkAmount, chromaAmount: chromaAdjust); |
611 | case KColorScheme::DarkShade: |
612 | return KColorUtils::shade(color, lumaAmount: darkAmount, chromaAmount: chromaAdjust); |
613 | default: |
614 | return KColorUtils::darken(KColorUtils::shade(color, lumaAmount: darkAmount, chromaAmount: chromaAdjust), amount: 0.5 + 0.3 * y); |
615 | } |
616 | } |
617 | |
618 | void KColorScheme::adjustBackground(QPalette &palette, BackgroundRole newRole, QPalette::ColorRole color, |
619 | ColorSet set, KSharedConfigPtr config) |
620 | { |
621 | palette.setBrush(cg: QPalette::Active, cr: color, brush: KColorScheme(QPalette::Active, set, config).background(role: newRole)); |
622 | palette.setBrush(cg: QPalette::Inactive, cr: color, brush: KColorScheme(QPalette::Inactive, set, config).background(role: newRole)); |
623 | palette.setBrush(cg: QPalette::Disabled, cr: color, brush: KColorScheme(QPalette::Disabled, set, config).background(role: newRole)); |
624 | } |
625 | |
626 | void KColorScheme::adjustForeground(QPalette &palette, ForegroundRole newRole, QPalette::ColorRole color, |
627 | ColorSet set, KSharedConfigPtr config) |
628 | { |
629 | palette.setBrush(cg: QPalette::Active, cr: color, brush: KColorScheme(QPalette::Active, set, config).foreground(role: newRole)); |
630 | palette.setBrush(cg: QPalette::Inactive, cr: color, brush: KColorScheme(QPalette::Inactive, set, config).foreground(role: newRole)); |
631 | palette.setBrush(cg: QPalette::Disabled, cr: color, brush: KColorScheme(QPalette::Disabled, set, config).foreground(role: newRole)); |
632 | } |
633 | |
634 | bool KColorScheme::isColorSetSupported(const KSharedConfigPtr &config, KColorScheme::ColorSet set) |
635 | { |
636 | switch (set) { |
637 | case View: |
638 | return config->hasGroup(QStringLiteral("Colors:View" )); |
639 | case Window: |
640 | return config->hasGroup(QStringLiteral("Colors:Window" )); |
641 | case Button: |
642 | return config->hasGroup(QStringLiteral("Colors:Button" )); |
643 | case Selection: |
644 | return config->hasGroup(QStringLiteral("Colors:Selection" )); |
645 | case Tooltip: |
646 | return config->hasGroup(QStringLiteral("Colors:Tooltip" )); |
647 | case Complementary: |
648 | return config->hasGroup(QStringLiteral("Colors:Complementary" )); |
649 | case Header: |
650 | return config->hasGroup(QStringLiteral("Colors:Header" )); |
651 | case NColorSets: |
652 | break; |
653 | } |
654 | |
655 | return false; |
656 | } |
657 | |
658 | QPalette KColorScheme::createApplicationPalette(const KSharedConfigPtr &config) |
659 | { |
660 | static const QPalette::ColorGroup states[QPalette::NColorGroups] = { |
661 | QPalette::Active, QPalette::Inactive, QPalette::Disabled |
662 | }; |
663 | |
664 | // TT thinks tooltips shouldn't use active, so we use our active colors for all states |
665 | KColorScheme schemeTooltip(QPalette::Active, KColorScheme::Tooltip, config); |
666 | |
667 | QPalette palette; |
668 | for (auto state : states) { |
669 | KColorScheme schemeView(state, KColorScheme::View, config); |
670 | KColorScheme schemeWindow(state, KColorScheme::Window, config); |
671 | KColorScheme schemeButton(state, KColorScheme::Button, config); |
672 | KColorScheme schemeSelection(state, KColorScheme::Selection, config); |
673 | |
674 | palette.setBrush(cg: state, cr: QPalette::WindowText, brush: schemeWindow.foreground()); |
675 | palette.setBrush(cg: state, cr: QPalette::Window, brush: schemeWindow.background()); |
676 | palette.setBrush(cg: state, cr: QPalette::Base, brush: schemeView.background()); |
677 | palette.setBrush(cg: state, cr: QPalette::Text, brush: schemeView.foreground()); |
678 | palette.setBrush(cg: state, cr: QPalette::Button, brush: schemeButton.background()); |
679 | palette.setBrush(cg: state, cr: QPalette::ButtonText, brush: schemeButton.foreground()); |
680 | palette.setBrush(cg: state, cr: QPalette::Highlight, brush: schemeSelection.background()); |
681 | palette.setBrush(cg: state, cr: QPalette::HighlightedText, brush: schemeSelection.foreground()); |
682 | palette.setBrush(cg: state, cr: QPalette::ToolTipBase, brush: schemeTooltip.background()); |
683 | palette.setBrush(cg: state, cr: QPalette::ToolTipText, brush: schemeTooltip.foreground()); |
684 | palette.setBrush(cg: state, cr: QPalette::PlaceholderText, brush: schemeView.foreground(role: KColorScheme::InactiveText)); |
685 | palette.setBrush(cg: state, cr: QPalette::Accent, brush: schemeSelection.background()); |
686 | |
687 | palette.setColor(acg: state, acr: QPalette::Light, acolor: schemeWindow.shade(role: KColorScheme::LightShade)); |
688 | palette.setColor(acg: state, acr: QPalette::Midlight, acolor: schemeWindow.shade(role: KColorScheme::MidlightShade)); |
689 | palette.setColor(acg: state, acr: QPalette::Mid, acolor: schemeWindow.shade(role: KColorScheme::MidShade)); |
690 | palette.setColor(acg: state, acr: QPalette::Dark, acolor: schemeWindow.shade(role: KColorScheme::DarkShade)); |
691 | palette.setColor(acg: state, acr: QPalette::Shadow, acolor: schemeWindow.shade(role: KColorScheme::ShadowShade)); |
692 | |
693 | palette.setBrush(cg: state, cr: QPalette::AlternateBase, brush: schemeView.background(role: KColorScheme::AlternateBackground)); |
694 | palette.setBrush(cg: state, cr: QPalette::Link, brush: schemeView.foreground(role: KColorScheme::LinkText)); |
695 | palette.setBrush(cg: state, cr: QPalette::LinkVisited, brush: schemeView.foreground(role: KColorScheme::VisitedText)); |
696 | } |
697 | |
698 | return palette; |
699 | } |
700 | |
701 | //END KColorScheme |
702 | |