| 1 | // Copyright (C) 2022 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | // |
| 5 | // W A R N I N G |
| 6 | // ------------- |
| 7 | // |
| 8 | // This file is not part of the Qt API. It exists purely as an |
| 9 | // implementation detail. This header file may change from version to |
| 10 | // version without notice, or even be removed. |
| 11 | // |
| 12 | // We mean it. |
| 13 | // |
| 14 | |
| 15 | #include "qgtk3json_p.h" |
| 16 | #include "qgtk3storage_p.h" |
| 17 | #include <qpa/qwindowsysteminterface.h> |
| 18 | #if QT_CONFIG(dbus) |
| 19 | # include <QtGui/private/qgnomeportalinterface_p.h> |
| 20 | #endif |
| 21 | |
| 22 | QT_BEGIN_NAMESPACE |
| 23 | |
| 24 | QGtk3Storage::QGtk3Storage() |
| 25 | { |
| 26 | m_interface.reset(p: new QGtk3Interface(this)); |
| 27 | #if QT_CONFIG(dbus) |
| 28 | m_portalInterface.reset(p: new QGnomePortalInterface); |
| 29 | #endif |
| 30 | populateMap(); |
| 31 | } |
| 32 | |
| 33 | QGtk3Storage::~QGtk3Storage() { } |
| 34 | |
| 35 | /*! |
| 36 | \internal |
| 37 | \enum QGtk3Storage::SourceType |
| 38 | \brief This enum represents the type of a color source. |
| 39 | |
| 40 | \value Gtk Color is read from a GTK widget |
| 41 | \value Fixed A fixed brush is specified |
| 42 | \value Modified The color is a modification of another color (fixed or read from GTK) |
| 43 | \omitvalue Invalid |
| 44 | */ |
| 45 | |
| 46 | /*! |
| 47 | \internal |
| 48 | \brief Find a brush from a source. |
| 49 | |
| 50 | Returns a QBrush from a given \param source and a \param map of available brushes |
| 51 | to search from. |
| 52 | |
| 53 | A null QBrush is returned, if no brush corresponding to the source has been found. |
| 54 | */ |
| 55 | QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const |
| 56 | { |
| 57 | switch (source.sourceType) { |
| 58 | case SourceType::Gtk: |
| 59 | return m_interface ? QBrush(m_interface->brush(wtype: source.gtk3.gtkWidgetType, |
| 60 | source: source.gtk3.source, state: source.gtk3.state)) |
| 61 | : QBrush(); |
| 62 | |
| 63 | case SourceType::Modified: { |
| 64 | // don't loop through modified sources, break if modified source not found |
| 65 | Source recSource = brush(brush: TargetBrush(source.rec.colorGroup, source.rec.colorRole, |
| 66 | source.rec.colorScheme), map); |
| 67 | |
| 68 | if (!recSource.isValid() || (recSource.sourceType == SourceType::Modified)) |
| 69 | return QBrush(); |
| 70 | |
| 71 | // Set brush and alter color |
| 72 | QBrush b = brush(source: recSource, map); |
| 73 | if (source.rec.width > 0 && source.rec.height > 0) |
| 74 | b.setTexture(QPixmap(source.rec.width, source.rec.height)); |
| 75 | QColor c = b.color().lighter(f: source.rec.lighter); |
| 76 | c = QColor((c.red() + source.rec.deltaRed), |
| 77 | (c.green() + source.rec.deltaGreen), |
| 78 | (c.blue() + source.rec.deltaBlue)); |
| 79 | b.setColor(c); |
| 80 | return b; |
| 81 | } |
| 82 | |
| 83 | case SourceType::Mixed: { |
| 84 | // check the mixing source to be valid and be a Gtk source |
| 85 | constexpr auto check_source = [](const Source &source) -> bool |
| 86 | { |
| 87 | return source.isValid() && (source.sourceType == SourceType::Gtk); |
| 88 | }; |
| 89 | |
| 90 | const Source source1 = brush(brush: TargetBrush(source.mix.sourceGroup, |
| 91 | source.mix.colorRole1), map); |
| 92 | if (!check_source(source1)) |
| 93 | return QBrush(); |
| 94 | |
| 95 | const Source source2 = brush(brush: TargetBrush(source.mix.sourceGroup, |
| 96 | source.mix.colorRole2), map); |
| 97 | if (!check_source(source2)) |
| 98 | return QBrush(); |
| 99 | |
| 100 | const QBrush brush2 = brush(source: source2, map); |
| 101 | // the output brush is a copy of the brush from the first source |
| 102 | QBrush brush1 = brush(source: source1, map); |
| 103 | // only color is mixed |
| 104 | brush1.setColor(MixSources::mixColors(color1: brush1.color(), color2: brush2.color())); |
| 105 | return brush1; |
| 106 | } |
| 107 | |
| 108 | case SourceType::Fixed: |
| 109 | return source.fix.fixedBrush; |
| 110 | |
| 111 | case SourceType::Invalid: |
| 112 | return QBrush(); |
| 113 | } |
| 114 | |
| 115 | // needed because of the scope after recursive |
| 116 | Q_UNREACHABLE(); |
| 117 | } |
| 118 | |
| 119 | /*! |
| 120 | \internal |
| 121 | \brief Recurse to find a source brush for modification. |
| 122 | |
| 123 | Returns the source specified by the target brush \param b in the \param map of brushes. |
| 124 | Takes dark/light/unknown into consideration. |
| 125 | Returns an empty brush if no suitable one can be found. |
| 126 | */ |
| 127 | QGtk3Storage::Source QGtk3Storage::brush(const TargetBrush &b, const BrushMap &map) const |
| 128 | { |
| 129 | #define FIND(brush) if (map.contains(brush))\ |
| 130 | return map.value(brush) |
| 131 | |
| 132 | // Return exact match |
| 133 | FIND(b); |
| 134 | |
| 135 | // unknown color scheme can find anything |
| 136 | if (b.colorScheme == Qt::ColorScheme::Unknown) { |
| 137 | FIND(TargetBrush(b, Qt::ColorScheme::Dark)); |
| 138 | FIND(TargetBrush(b, Qt::ColorScheme::Light)); |
| 139 | } |
| 140 | |
| 141 | // Color group All can always be found |
| 142 | if (b.colorGroup != QPalette::All) |
| 143 | return brush(b: TargetBrush(QPalette::All, b.colorRole, b.colorScheme), map); |
| 144 | |
| 145 | // Brush not found |
| 146 | return Source(); |
| 147 | #undef FIND |
| 148 | } |
| 149 | |
| 150 | /*! |
| 151 | \internal |
| 152 | \brief Returns a simple, hard coded base palette. |
| 153 | |
| 154 | Create a hard coded palette with default colors as a fallback for any color that can't be |
| 155 | obtained from GTK. |
| 156 | |
| 157 | \note This palette will be used as a default baseline for the system palette, which then |
| 158 | will be used as a default baseline for any other palette type. |
| 159 | */ |
| 160 | QPalette QGtk3Storage::standardPalette() |
| 161 | { |
| 162 | QColor backgroundColor(0xd4, 0xd0, 0xc8); |
| 163 | QColor lightColor(backgroundColor.lighter()); |
| 164 | QColor darkColor(backgroundColor.darker()); |
| 165 | const QBrush darkBrush(darkColor); |
| 166 | QColor midColor(Qt::gray); |
| 167 | QPalette palette(Qt::black, backgroundColor, lightColor, darkColor, |
| 168 | midColor, Qt::black, Qt::white); |
| 169 | palette.setBrush(cg: QPalette::Disabled, cr: QPalette::WindowText, brush: darkBrush); |
| 170 | palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Text, brush: darkBrush); |
| 171 | palette.setBrush(cg: QPalette::Disabled, cr: QPalette::ButtonText, brush: darkBrush); |
| 172 | palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Base, brush: QBrush(backgroundColor)); |
| 173 | return palette; |
| 174 | } |
| 175 | |
| 176 | /*! |
| 177 | \internal |
| 178 | \brief Return a GTK styled QPalette. |
| 179 | |
| 180 | Returns the pointer to a (cached) QPalette for \param type, with its brushes |
| 181 | populated according to the current GTK theme. |
| 182 | */ |
| 183 | const QPalette *QGtk3Storage::palette(QPlatformTheme::Palette type) const |
| 184 | { |
| 185 | if (type >= QPlatformTheme::NPalettes) |
| 186 | return nullptr; |
| 187 | |
| 188 | if (m_paletteCache[type].has_value()) { |
| 189 | qCDebug(lcQGtk3Interface) << "Returning palette from cache:" |
| 190 | << QGtk3Json::fromPalette(palette: type); |
| 191 | |
| 192 | return &m_paletteCache[type].value(); |
| 193 | } |
| 194 | |
| 195 | // Read system palette as a baseline first |
| 196 | if (!m_paletteCache[QPlatformTheme::SystemPalette].has_value() && type != QPlatformTheme::SystemPalette) |
| 197 | palette(); |
| 198 | |
| 199 | // Fall back to system palette for unknown types |
| 200 | if (!m_palettes.contains(key: type) && type != QPlatformTheme::SystemPalette) { |
| 201 | qCDebug(lcQGtk3Interface) << "Returning system palette for unknown type" |
| 202 | << QGtk3Json::fromPalette(palette: type); |
| 203 | return palette(); |
| 204 | } |
| 205 | |
| 206 | BrushMap brushes = m_palettes.value(key: type); |
| 207 | |
| 208 | // Standard palette is base for system palette. System palette is base for all others. |
| 209 | QPalette p = QPalette( type == QPlatformTheme::SystemPalette ? standardPalette() |
| 210 | : m_paletteCache[QPlatformTheme::SystemPalette].value()); |
| 211 | |
| 212 | qCDebug(lcQGtk3Interface) << "Creating palette:" << QGtk3Json::fromPalette(palette: type); |
| 213 | for (auto i = brushes.begin(); i != brushes.end(); ++i) { |
| 214 | Source source = i.value(); |
| 215 | |
| 216 | // Brush is set if |
| 217 | // - theme and source color scheme match |
| 218 | // - or either of them is unknown |
| 219 | const auto appSource = i.key().colorScheme; |
| 220 | const auto appTheme = colorScheme(); |
| 221 | const bool setBrush = (appSource == appTheme) || |
| 222 | (appSource == Qt::ColorScheme::Unknown) || |
| 223 | (appTheme == Qt::ColorScheme::Unknown); |
| 224 | |
| 225 | if (setBrush) { |
| 226 | p.setBrush(cg: i.key().colorGroup, cr: i.key().colorRole, brush: brush(source, map: brushes)); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | m_paletteCache[type].emplace(args&: p); |
| 231 | if (type == QPlatformTheme::SystemPalette) |
| 232 | qCDebug(lcQGtk3Interface) << "System Palette defined" << themeName() << colorScheme() << p; |
| 233 | |
| 234 | return &m_paletteCache[type].value(); |
| 235 | } |
| 236 | |
| 237 | /*! |
| 238 | \internal |
| 239 | \brief Return a GTK styled font. |
| 240 | |
| 241 | Returns a QFont of \param type, styled according to the current GTK theme. |
| 242 | */ |
| 243 | const QFont *QGtk3Storage::font(QPlatformTheme::Font type) const |
| 244 | { |
| 245 | if (m_fontCache[type].has_value()) |
| 246 | return &m_fontCache[type].value(); |
| 247 | |
| 248 | m_fontCache[type].emplace(args: m_interface->font(type)); |
| 249 | return &m_fontCache[type].value(); |
| 250 | } |
| 251 | |
| 252 | /*! |
| 253 | \internal |
| 254 | \brief Return a GTK styled standard pixmap if available. |
| 255 | |
| 256 | Returns a pixmap specified by \param standardPixmap and \param size. |
| 257 | Returns an empty pixmap if GTK doesn't support the requested one. |
| 258 | */ |
| 259 | QPixmap QGtk3Storage::standardPixmap(QPlatformTheme::StandardPixmap standardPixmap, |
| 260 | const QSizeF &size) const |
| 261 | { |
| 262 | if (m_pixmapCache.contains(key: standardPixmap)) |
| 263 | return QPixmap::fromImage(image: m_pixmapCache.object(key: standardPixmap)->scaled(s: size.toSize())); |
| 264 | |
| 265 | if (!m_interface) |
| 266 | return QPixmap(); |
| 267 | |
| 268 | QImage image = m_interface->standardPixmap(standardPixmap); |
| 269 | if (image.isNull()) |
| 270 | return QPixmap(); |
| 271 | |
| 272 | m_pixmapCache.insert(key: standardPixmap, object: new QImage(image)); |
| 273 | return QPixmap::fromImage(image: image.scaled(s: size.toSize())); |
| 274 | } |
| 275 | |
| 276 | /*! |
| 277 | \internal |
| 278 | \brief Returns a GTK styled file icon corresponding to \param fileInfo. |
| 279 | */ |
| 280 | QIcon QGtk3Storage::fileIcon(const QFileInfo &fileInfo) const |
| 281 | { |
| 282 | return m_interface ? m_interface->fileIcon(fileInfo) : QIcon(); |
| 283 | } |
| 284 | |
| 285 | /*! |
| 286 | \internal |
| 287 | \brief Clears all caches. |
| 288 | */ |
| 289 | void QGtk3Storage::clear() |
| 290 | { |
| 291 | m_colorScheme = Qt::ColorScheme::Unknown; |
| 292 | m_palettes.clear(); |
| 293 | for (auto &cache : m_paletteCache) |
| 294 | cache.reset(); |
| 295 | |
| 296 | for (auto &cache : m_fontCache) |
| 297 | cache.reset(); |
| 298 | } |
| 299 | |
| 300 | /*! |
| 301 | \internal |
| 302 | \brief Handles a theme change at runtime. |
| 303 | |
| 304 | Clear all caches, re-populate with current GTK theme and notify the window system interface. |
| 305 | This method is a callback for the theme change signal sent from GTK. |
| 306 | */ |
| 307 | void QGtk3Storage::handleThemeChange() |
| 308 | { |
| 309 | populateMap(); |
| 310 | QWindowSystemInterface::handleThemeChange(); |
| 311 | } |
| 312 | |
| 313 | /*! |
| 314 | \internal |
| 315 | \brief Populates a map with information about how to locate colors in GTK. |
| 316 | |
| 317 | This method creates a data structure to locate color information for each brush of a QPalette |
| 318 | within GTK. The structure can hold mapping information for each QPlatformTheme::Palette |
| 319 | enum value. If no specific mapping is stored for an enum value, the system palette is returned |
| 320 | instead of a specific one. If no mapping is stored for the system palette, it will fall back to |
| 321 | QGtk3Storage::standardPalette. |
| 322 | |
| 323 | The method will populate the data structure with a standard mapping, covering the following |
| 324 | palette types: |
| 325 | \list |
| 326 | \li QPlatformTheme::SystemPalette |
| 327 | \li QPlatformTheme::CheckBoxPalette |
| 328 | \li QPlatformTheme::RadioButtonPalette |
| 329 | \li QPlatformTheme::ComboBoxPalette |
| 330 | \li QPlatformTheme::GroupBoxPalette |
| 331 | \li QPlatformTheme::MenuPalette |
| 332 | \li QPlatformTheme::TextLineEditPalette |
| 333 | \endlist |
| 334 | |
| 335 | The method will check the environment variable {{QT_GUI_GTK_JSON_SAVE}}. If it points to a |
| 336 | valid path with write access, it will write the standard mapping into a Json file. |
| 337 | That Json file can be modified and/or extended. |
| 338 | The Json syntax is |
| 339 | - "QGtk3Palettes" (top level value) |
| 340 | - QPlatformTheme::Palette |
| 341 | - QPalette::ColorRole |
| 342 | - Qt::ColorScheme |
| 343 | - Qt::ColorGroup |
| 344 | - Source data |
| 345 | - Source Type |
| 346 | - [source data] |
| 347 | |
| 348 | If the environment variable {{QT_GUI_GTK_JSON_HARDCODED}} contains the keyword \c true, |
| 349 | all sources are converted to fixed sources. In that case, they contain the hard coded HexRGBA |
| 350 | values read from GTK. |
| 351 | |
| 352 | The method will also check the environment variable {{QT_GUI_GTK_JSON}}. If it points to a valid |
| 353 | Json file with read access, it will be parsed instead of creating a standard mapping. |
| 354 | Parsing errors will be printed out with qCInfo if the logging category {{qt.qpa.gtk}} is activated. |
| 355 | In case of a parsing error, the method will fall back to creating a standard mapping. |
| 356 | |
| 357 | \note |
| 358 | If a Json file contains only fixed brushes (e.g. exported with {{QT_GUI_GTK_JSON_HARDCODED=true}}), |
| 359 | no colors will be imported from GTK. |
| 360 | */ |
| 361 | void QGtk3Storage::populateMap() |
| 362 | { |
| 363 | static QString m_themeName; |
| 364 | |
| 365 | // Distiguish initialization, theme change or call without theme change |
| 366 | Qt::ColorScheme newColorScheme = Qt::ColorScheme::Unknown; |
| 367 | const QString newThemeName = themeName(); |
| 368 | |
| 369 | #if QT_CONFIG(dbus) |
| 370 | // Prefer color scheme we get from xdg-desktop-portal as this is what GNOME |
| 371 | // relies on these days |
| 372 | newColorScheme = m_portalInterface->colorScheme(); |
| 373 | #endif |
| 374 | |
| 375 | if (newColorScheme == Qt::ColorScheme::Unknown) { |
| 376 | // Derive color scheme from theme name |
| 377 | newColorScheme = newThemeName.contains(s: "dark"_L1 , cs: Qt::CaseInsensitive) |
| 378 | ? Qt::ColorScheme::Dark : m_interface->colorSchemeByColors(); |
| 379 | } |
| 380 | |
| 381 | if (m_themeName == newThemeName && m_colorScheme == newColorScheme) |
| 382 | return; |
| 383 | |
| 384 | clear(); |
| 385 | |
| 386 | if (m_themeName.isEmpty()) { |
| 387 | qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << newColorScheme; |
| 388 | } else { |
| 389 | qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << newColorScheme; |
| 390 | } |
| 391 | m_colorScheme = newColorScheme; |
| 392 | m_themeName = newThemeName; |
| 393 | |
| 394 | // create standard mapping or load from Json file? |
| 395 | const QString jsonInput = qEnvironmentVariable(varName: "QT_GUI_GTK_JSON" ); |
| 396 | if (!jsonInput.isEmpty()) { |
| 397 | if (load(filename: jsonInput)) { |
| 398 | return; |
| 399 | } else { |
| 400 | qWarning() << "Falling back to standard GTK mapping." ; |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | createMapping(); |
| 405 | |
| 406 | const QString jsonOutput = qEnvironmentVariable(varName: "QT_GUI_GTK_JSON_SAVE" ); |
| 407 | if (!jsonOutput.isEmpty() && !save(filename: jsonOutput)) |
| 408 | qWarning() << "File" << jsonOutput << "could not be saved.\n" ; |
| 409 | } |
| 410 | |
| 411 | /*! |
| 412 | \internal |
| 413 | \brief Return a palette map for saving. |
| 414 | |
| 415 | This method returns the existing palette map, if the environment variable |
| 416 | {{QT_GUI_GTK_JSON_HARDCODED}} is not set or does not contain the keyword \c true. |
| 417 | If it contains the keyword \c true, it returns a palette map with all brush |
| 418 | sources converted to fixed sources. |
| 419 | */ |
| 420 | const QGtk3Storage::PaletteMap QGtk3Storage::savePalettes() const |
| 421 | { |
| 422 | const QString hard = qEnvironmentVariable(varName: "QT_GUI_GTK_JSON_HARDCODED" ); |
| 423 | if (!hard.contains(s: "true"_L1 , cs: Qt::CaseInsensitive)) |
| 424 | return m_palettes; |
| 425 | |
| 426 | // Json output is supposed to be readable without GTK connection |
| 427 | // convert palette map into hard coded brushes |
| 428 | PaletteMap map = m_palettes; |
| 429 | for (auto paletteIterator = map.begin(); paletteIterator != map.end(); |
| 430 | ++paletteIterator) { |
| 431 | QGtk3Storage::BrushMap &bm = paletteIterator.value(); |
| 432 | for (auto brushIterator = bm.begin(); brushIterator != bm.end(); |
| 433 | ++brushIterator) { |
| 434 | QGtk3Storage::Source &s = brushIterator.value(); |
| 435 | switch (s.sourceType) { |
| 436 | |
| 437 | // Read the brush and convert it into a fixed brush |
| 438 | case SourceType::Gtk: { |
| 439 | const QBrush fixedBrush = brush(source: s, map: bm); |
| 440 | s.fix.fixedBrush = fixedBrush; |
| 441 | s.sourceType = SourceType::Fixed; |
| 442 | } |
| 443 | break; |
| 444 | case SourceType::Fixed: |
| 445 | case SourceType::Modified: |
| 446 | case SourceType::Mixed: |
| 447 | case SourceType::Invalid: |
| 448 | break; |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | return map; |
| 453 | } |
| 454 | |
| 455 | /*! |
| 456 | \internal |
| 457 | \brief Saves current palette mapping to a \param filename with Json format \param f. |
| 458 | |
| 459 | Saves the current palette mapping into a QJson file, |
| 460 | taking {{QT_GUI_GTK_JSON_HARDCODED}} into consideration. |
| 461 | Returns \c true if saving was successful and \c false otherwise. |
| 462 | */ |
| 463 | bool QGtk3Storage::save(const QString &filename, QJsonDocument::JsonFormat f) const |
| 464 | { |
| 465 | return QGtk3Json::save(map: savePalettes(), fileName: filename, format: f); |
| 466 | } |
| 467 | |
| 468 | /*! |
| 469 | \internal |
| 470 | \brief Returns a QJsonDocument with current palette mapping. |
| 471 | |
| 472 | Saves the current palette mapping into a QJsonDocument, |
| 473 | taking {{QT_GUI_GTK_JSON_HARDCODED}} into consideration. |
| 474 | Returns \c true if saving was successful and \c false otherwise. |
| 475 | */ |
| 476 | QJsonDocument QGtk3Storage::save() const |
| 477 | { |
| 478 | return QGtk3Json::save(map: savePalettes()); |
| 479 | } |
| 480 | |
| 481 | /*! |
| 482 | \internal |
| 483 | \brief Loads palette mapping from Json file \param filename. |
| 484 | |
| 485 | Returns \c true if the file was successfully parsed and \c false otherwise. |
| 486 | */ |
| 487 | bool QGtk3Storage::load(const QString &filename) |
| 488 | { |
| 489 | return QGtk3Json::load(map&: m_palettes, fileName: filename); |
| 490 | } |
| 491 | |
| 492 | /*! |
| 493 | \internal |
| 494 | \brief Creates a standard palette mapping. |
| 495 | |
| 496 | The method creates a hard coded standard mapping, used if no external Json file |
| 497 | containing a valid mapping has been specified in the environment variable {{QT_GUI_GTK_JSON}}. |
| 498 | */ |
| 499 | void QGtk3Storage::createMapping() |
| 500 | { |
| 501 | // Hard code standard mapping |
| 502 | BrushMap map; |
| 503 | Source source; |
| 504 | |
| 505 | // Define a GTK source |
| 506 | #define GTK(wtype, colorSource, state)\ |
| 507 | source = Source(QGtk3Interface::QGtkWidget::gtk_ ##wtype,\ |
| 508 | QGtk3Interface::QGtkColorSource::colorSource, GTK_STATE_FLAG_ ##state) |
| 509 | |
| 510 | // Define a modified source |
| 511 | #define LIGHTER(group, role, lighter)\ |
| 512 | source = Source(QPalette::group, QPalette::role,\ |
| 513 | Qt::ColorScheme::Unknown, lighter) |
| 514 | #define MODIFY(group, role, red, green, blue)\ |
| 515 | source = Source(QPalette::group, QPalette::role,\ |
| 516 | Qt::ColorScheme::Unknown, red, green, blue) |
| 517 | |
| 518 | // Define fixed source |
| 519 | #define FIX(color) source = FixedSource(color); |
| 520 | |
| 521 | // Add the source to a target brush |
| 522 | // Use default Qt::ColorScheme::Unknown, if no color scheme was specified |
| 523 | #define ADD_2(group, role) map.insert(TargetBrush(QPalette::group, QPalette::role), source); |
| 524 | #define ADD_3(group, role, app) map.insert(TargetBrush(QPalette::group, QPalette::role,\ |
| 525 | Qt::ColorScheme::app), source); |
| 526 | #define ADD_X(x, group, role, app, FUNC, ...) FUNC |
| 527 | #define ADD(...) ADD_X(,##__VA_ARGS__, ADD_3(__VA_ARGS__), ADD_2(__VA_ARGS__)) |
| 528 | // Save target brushes to a palette type |
| 529 | #define SAVE(palette) m_palettes.insert(QPlatformTheme::palette, map) |
| 530 | // Clear brushes to start next palette |
| 531 | #define CLEAR map.clear() |
| 532 | |
| 533 | /* |
| 534 | Macro usage: |
| 535 | |
| 536 | 1. Define a source |
| 537 | GTK(QGtkWidget, QGtkColorSource, GTK_STATE_FLAG) |
| 538 | Fetch the color from a GtkWidget, related to a source and a state. |
| 539 | |
| 540 | LIGHTER(ColorGroup, ColorROle, lighter) |
| 541 | Use a color of the same QPalette related to ColorGroup and ColorRole. |
| 542 | Make the color lighter (if lighter >100) or darker (if lighter < 100) |
| 543 | |
| 544 | MODIFY(ColorGroup, ColorRole, red, green, blue) |
| 545 | Use a color of the same QPalette related to ColorGroup and ColorRole. |
| 546 | Modify it by adding red, green, blue. |
| 547 | |
| 548 | FIX(const QBrush &) |
| 549 | Use a fixed brush without querying GTK |
| 550 | |
| 551 | 2. Define the target |
| 552 | Use ADD(ColorGroup, ColorRole) to use the defined source for the |
| 553 | color group / role in the current palette. |
| 554 | |
| 555 | Use ADD(ColorGroup, ColorRole, ColorScheme) to use the defined source |
| 556 | only for a specific color scheme |
| 557 | |
| 558 | 3. Save mapping |
| 559 | Save the defined mappings for a specific palette. |
| 560 | If a mapping entry does not cover all color groups and roles of a palette, |
| 561 | the system palette will be used for the remaining values. |
| 562 | If the system palette does not have all combination of color groups and roles, |
| 563 | the remaining ones will be populated by a hard coded fusion-style like palette. |
| 564 | |
| 565 | 4. Clear mapping |
| 566 | Use CLEAR to clear the mapping and begin a new one. |
| 567 | */ |
| 568 | |
| 569 | |
| 570 | // System palette |
| 571 | { |
| 572 | // background color and calculate derivates |
| 573 | GTK(Default, Background, INSENSITIVE); |
| 574 | ADD(All, Window); |
| 575 | ADD(All, Button); |
| 576 | ADD(All, Base); |
| 577 | LIGHTER(Normal, Window, 125); |
| 578 | ADD(Normal, Light); |
| 579 | ADD(Inactive, Light); |
| 580 | LIGHTER(Normal, Window, 70); |
| 581 | ADD(Normal, Shadow); |
| 582 | LIGHTER(Normal, Window, 80); |
| 583 | ADD(Normal, Dark); |
| 584 | ADD(Inactive, Dark) |
| 585 | |
| 586 | GTK(button, Foreground, ACTIVE); |
| 587 | ADD(Inactive, WindowText); |
| 588 | |
| 589 | auto ADD_MIX = [&map](QPalette::ColorGroup targetGroup, |
| 590 | QPalette::ColorRole targetRole, |
| 591 | QPalette::ColorGroup sourceGroup, |
| 592 | QPalette::ColorRole role1, |
| 593 | QPalette::ColorRole role2) |
| 594 | { |
| 595 | const Source source{sourceGroup, role1, role2}; |
| 596 | map.insert(key: TargetBrush(targetGroup, targetRole), value: source); |
| 597 | }; |
| 598 | ADD_MIX(QPalette::Disabled, QPalette::Text, |
| 599 | QPalette::Normal, QPalette::Base, QPalette::Text); |
| 600 | ADD_MIX(QPalette::Disabled, QPalette::WindowText, |
| 601 | QPalette::Normal, QPalette::Window, QPalette::WindowText); |
| 602 | ADD_MIX(QPalette::Disabled, QPalette::ButtonText, |
| 603 | QPalette::Normal, QPalette::Button, QPalette::ButtonText); |
| 604 | |
| 605 | GTK(button, Text, NORMAL); |
| 606 | ADD(Inactive, ButtonText); |
| 607 | |
| 608 | // special background colors |
| 609 | GTK(Default, Background, SELECTED); |
| 610 | ADD(Disabled, Highlight); |
| 611 | ADD(Normal, Highlight); |
| 612 | ADD(Inactive, Highlight); |
| 613 | |
| 614 | GTK(entry, Foreground, SELECTED); |
| 615 | ADD(Normal, HighlightedText); |
| 616 | ADD(Inactive, HighlightedText); |
| 617 | |
| 618 | // text color and friends |
| 619 | GTK(entry, Text, NORMAL); |
| 620 | ADD(Normal, ButtonText); |
| 621 | ADD(Normal, WindowText); |
| 622 | ADD(Disabled, HighlightedText); |
| 623 | |
| 624 | GTK(Default, Text, NORMAL); |
| 625 | ADD(Normal, Text); |
| 626 | ADD(Inactive, Text); |
| 627 | ADD(Normal, HighlightedText); |
| 628 | LIGHTER(Normal, Base, 93); |
| 629 | ADD(All, AlternateBase); |
| 630 | |
| 631 | GTK(Default, Foreground, NORMAL); |
| 632 | MODIFY(Normal, Text, 100, 100, 100); |
| 633 | ADD(All, PlaceholderText, Light); |
| 634 | MODIFY(Normal, Text, -100, -100, -100); |
| 635 | ADD(All, PlaceholderText, Dark); |
| 636 | |
| 637 | // Light, midlight, dark, mid, shadow colors |
| 638 | LIGHTER(Normal, Button, 125); |
| 639 | ADD(All, Light) |
| 640 | LIGHTER(Normal, Button, 113); |
| 641 | ADD(All, Midlight) |
| 642 | LIGHTER(Normal, Button, 113); |
| 643 | ADD(All, Mid) |
| 644 | LIGHTER(Normal, Button, 87); |
| 645 | ADD(All, Dark) |
| 646 | LIGHTER(Normal, Button, 5); |
| 647 | ADD(All, Shadow) |
| 648 | |
| 649 | SAVE(SystemPalette); |
| 650 | CLEAR; |
| 651 | } |
| 652 | |
| 653 | // Label and TabBar Palette |
| 654 | { |
| 655 | GTK(entry, Text, NORMAL); |
| 656 | ADD(Normal, WindowText); |
| 657 | ADD(Inactive, WindowText); |
| 658 | |
| 659 | SAVE(LabelPalette); |
| 660 | SAVE(TabBarPalette); |
| 661 | CLEAR; |
| 662 | } |
| 663 | |
| 664 | // Checkbox and RadioButton Palette |
| 665 | { |
| 666 | GTK(button, Text, ACTIVE); |
| 667 | ADD(Normal, Base, Dark); |
| 668 | ADD(Inactive, WindowText, Dark); |
| 669 | |
| 670 | GTK(Default, Foreground, NORMAL); |
| 671 | ADD(All, Text); |
| 672 | |
| 673 | GTK(Default, Background, NORMAL); |
| 674 | ADD(All, Base); |
| 675 | |
| 676 | GTK(button, Text, NORMAL); |
| 677 | ADD(Normal, Base, Light); |
| 678 | ADD(Inactive, WindowText, Light); |
| 679 | |
| 680 | SAVE(CheckBoxPalette); |
| 681 | SAVE(RadioButtonPalette); |
| 682 | CLEAR; |
| 683 | } |
| 684 | |
| 685 | // ComboBox, GroupBox & Frame Palette |
| 686 | { |
| 687 | GTK(combo_box, Text, NORMAL); |
| 688 | ADD(Normal, ButtonText, Dark); |
| 689 | ADD(Normal, Text, Dark); |
| 690 | ADD(Inactive, WindowText, Dark); |
| 691 | |
| 692 | GTK(combo_box, Text, ACTIVE); |
| 693 | ADD(Normal, ButtonText, Light); |
| 694 | ADD(Normal, Text, Light); |
| 695 | ADD(Inactive, WindowText, Light); |
| 696 | |
| 697 | SAVE(ComboBoxPalette); |
| 698 | SAVE(GroupBoxPalette); |
| 699 | CLEAR; |
| 700 | } |
| 701 | |
| 702 | // MenuBar Palette |
| 703 | { |
| 704 | GTK(Default, Text, ACTIVE); |
| 705 | ADD(Normal, ButtonText); |
| 706 | SAVE(MenuPalette); |
| 707 | CLEAR; |
| 708 | } |
| 709 | |
| 710 | // LineEdit Palette |
| 711 | { |
| 712 | GTK(Default, Background, NORMAL); |
| 713 | ADD(All, Base); |
| 714 | SAVE(TextLineEditPalette); |
| 715 | CLEAR; |
| 716 | } |
| 717 | |
| 718 | #undef GTK |
| 719 | #undef REC |
| 720 | #undef FIX |
| 721 | #undef ADD |
| 722 | #undef ADD_2 |
| 723 | #undef ADD_3 |
| 724 | #undef ADD_X |
| 725 | #undef SAVE |
| 726 | #undef LOAD |
| 727 | } |
| 728 | |
| 729 | QT_END_NAMESPACE |
| 730 | |