| 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 | |