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
23StateEffects::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
55QBrush 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
83QBrush 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
102struct 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
115struct 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)
123static 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
136static 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
149static 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
162static 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
175static 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
188static 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
201static const SerializedColors defaultHeaderColors = {
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
214static 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
222class KColorSchemePrivate : public QSharedData
223{
224public:
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
252static 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
273static 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
281KColorSchemePrivate::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
290void 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
417void 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
477QBrush 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
486QBrush 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
495QBrush 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
504qreal KColorSchemePrivate::contrast() const
505{
506 return _contrast;
507}
508//END KColorSchemePrivate
509
510//BEGIN KColorScheme
511KColorScheme::KColorScheme(const KColorScheme &) = default;
512KColorScheme &KColorScheme::operator=(const KColorScheme &) = default;
513KColorScheme::KColorScheme(KColorScheme &&) = default;
514KColorScheme &KColorScheme::operator=(KColorScheme &&) = default;
515KColorScheme::~KColorScheme() = default;
516
517KColorScheme::KColorScheme(QPalette::ColorGroup state, ColorSet set, KSharedConfigPtr config)
518 : d(new KColorSchemePrivate(config ? config : defaultConfig(), state, set))
519{
520}
521
522bool 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
531qreal 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
541QBrush KColorScheme::background(BackgroundRole role) const
542{
543 return d->background(role);
544}
545
546QBrush KColorScheme::foreground(ForegroundRole role) const
547{
548 return d->foreground(role);
549}
550
551QBrush KColorScheme::decoration(DecorationRole role) const
552{
553 return d->decoration(role);
554}
555
556QColor KColorScheme::shade(ShadeRole role) const
557{
558 return shade(background().color(), role, contrast: d->contrast());
559}
560
561QColor KColorScheme::shade(const QColor &color, ShadeRole role)
562{
563 return shade(color, role, contrast: KColorScheme::contrastF());
564}
565
566QColor 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
618void 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
626void 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
634bool 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
658QPalette 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

source code of kcolorscheme/src/kcolorscheme.cpp