File indexing completed on 2025-04-27 03:58:33
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2004-08-02 0007 * Description : colors theme manager - private classes 0008 * 0009 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2007 by Matthew Woehlke <mw_triad at users dot sourceforge dot net> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #ifndef DIGIKAM_THEME_MANAGER_P_H 0017 #define DIGIKAM_THEME_MANAGER_P_H 0018 0019 #include "thememanager.h" 0020 0021 // C++ includes 0022 0023 #include <cmath> 0024 0025 // Qt includes 0026 0027 #include <QWidget> 0028 #include <QPainter> 0029 #include <QStringList> 0030 #include <QFileInfo> 0031 #include <QPalette> 0032 #include <QColor> 0033 #include <QPixmap> 0034 #include <QApplication> 0035 #include <QAction> 0036 #include <QStandardPaths> 0037 #include <QDirIterator> 0038 #include <QMenu> 0039 #include <QStyle> 0040 #include <QExplicitlySharedDataPointer> 0041 #include <QBrush> 0042 #include <QBitmap> 0043 #include <QActionGroup> 0044 0045 // KDE includes 0046 0047 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU) 0048 # pragma GCC diagnostic push 0049 # pragma GCC diagnostic ignored "-Wdeprecated-declarations" 0050 #endif 0051 0052 #if defined(Q_CC_CLANG) 0053 # pragma clang diagnostic push 0054 # pragma clang diagnostic ignored "-Wdeprecated-declarations" 0055 #endif 0056 0057 #include <ksharedconfig.h> 0058 #include <kactioncollection.h> 0059 #include <kconfiggroup.h> 0060 #include <klocalizedstring.h> 0061 0062 // Restore warnings 0063 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU) 0064 # pragma GCC diagnostic pop 0065 #endif 0066 0067 #if defined(Q_CC_CLANG) 0068 # pragma clang diagnostic pop 0069 #endif 0070 0071 // Local includes 0072 0073 #include "digikam_debug.h" 0074 #include "dxmlguiwindow.h" 0075 0076 namespace Digikam 0077 { 0078 0079 class SchemeManagerPrivate; 0080 0081 /** 0082 * A set of methods used to work with colors. 0083 * 0084 * SchemeManager currently provides access to the system color palette that the 0085 * user has selected (in the future, it is expected to do more). It greatly 0086 * expands on QPalette by providing five distinct "sets" with several color 0087 * choices each, covering background, foreground, and decoration colors. 0088 * 0089 * A SchemeManager instance represents colors corresponding to a "set", where a 0090 * set consists of those colors used to draw a particular type of element, such 0091 * as a menu, button, view, selected text, or tooltip. Each set has a distinct 0092 * set of colors, so you should always use the correct set for drawing and 0093 * never assume that a particular foreground for one set is the same as the 0094 * foreground for any other set. Individual colors may be quickly referenced by 0095 * creating an anonymous instance and invoking a lookup member. 0096 * 0097 * @note 0098 * The color palettes for the various states of a widget (active, inactive, 0099 * disabled) may be wildly different. Therefore, it is important to take the 0100 * state into account. This is why the SchemeManager constructor requires a 0101 * QPalette::ColorGroup as an argument. 0102 * 0103 * To facilitate working with potentially-varying states, two convenience API's 0104 * are provided. These are SchemeManager::adjustBackground and its sister 0105 * SchemeManager::adjustForeground, and the helper class KStatefulBrush. 0106 * 0107 * @see SchemeManager::ColorSet, SchemeManager::ForegroundRole, 0108 * SchemeManager::BackgroundRole, SchemeManager::DecorationRole, 0109 * SchemeManager::ShadeRole 0110 */ 0111 class Q_DECL_HIDDEN SchemeManager 0112 { 0113 public: 0114 0115 /** 0116 * This enumeration describes the color set for which a color is being 0117 * selected. 0118 * 0119 * Color sets define a color "environment", suitable for drawing all parts 0120 * of a given region. Colors from different sets should not be combined. 0121 */ 0122 enum ColorSet 0123 { 0124 /** 0125 * Views; for example, frames, input fields, etc. 0126 * 0127 * If it contains things that can be selected, it is probably a View. 0128 */ 0129 View, 0130 /** 0131 * Non-editable window elements; for example, menus. 0132 * 0133 * If it isn't a Button, View, or Tooltip, it is probably a Window. 0134 */ 0135 Window, 0136 /** 0137 * Buttons and button-like controls. 0138 * 0139 * In addition to buttons, "button-like" controls such as non-editable 0140 * dropdowns, scrollbar sliders, slider handles, etc. should also use 0141 * this role. 0142 */ 0143 Button, 0144 /** 0145 * Selected items in views. 0146 * 0147 * Note that unfocused or disabled selections should use the Window 0148 * role. This makes it more obvious to the user that the view 0149 * containing the selection does not have input focus. 0150 */ 0151 Selection, 0152 /** 0153 * Tooltips. 0154 * 0155 * The tooltip set can often be substituted for the view 0156 * set when editing is not possible, but the Window set is deemed 0157 * inappropriate. "What's This" help is an excellent example, another 0158 * might be pop-up notifications (depending on taste). 0159 */ 0160 Tooltip, 0161 /** 0162 * Complementary areas. 0163 * 0164 * Some applications want some areas to have a different color scheme. 0165 * Usually dark areas over a light theme. For instance the fullscreen UI 0166 * of a picture viewer, or the logout/lock screen of the plasma workspace 0167 * ask for a dark color scheme even on light themes. 0168 * @since 5.19 0169 */ 0170 Complementary 0171 }; 0172 0173 /** 0174 * This enumeration describes the background color being selected from the 0175 * given set. 0176 * 0177 * Background colors are suitable for drawing under text, and should never 0178 * be used to draw text. In combination with one of the overloads of 0179 * SchemeManager::shade, they may be used to generate colors for drawing 0180 * frames, bevels, and similar decorations. 0181 */ 0182 enum BackgroundRole 0183 { 0184 /** 0185 * Normal background. 0186 */ 0187 NormalBackground = 0, 0188 /** 0189 * Alternate background; for example, for use in lists. 0190 * 0191 * This color may be the same as BackgroundNormal, especially in sets 0192 * other than View and Window. 0193 */ 0194 AlternateBackground = 1, 0195 /** 0196 * Third color; for example, items which are new, active, requesting 0197 * attention, etc. 0198 * 0199 * Alerting the user that a certain field must be filled out would be a 0200 * good usage (although NegativeBackground could be used to the same 0201 * effect, depending on what you are trying to achieve). Unlike 0202 * ActiveText, this should not be used for mouseover effects. 0203 */ 0204 ActiveBackground = 2, 0205 /** 0206 * Fourth color; corresponds to (unvisited) links. 0207 * 0208 * Exactly what this might be used for is somewhat harder to qualify; 0209 * it might be used for bookmarks, as a 'you can click here' indicator, 0210 * or to highlight recent content (i.e. in a most-recently-accessed 0211 * list). 0212 */ 0213 LinkBackground = 3, 0214 /** 0215 * Fifth color; corresponds to visited links. 0216 * 0217 * This can also be used to indicate "not recent" content, especially 0218 * when a color is needed to denote content which is "old" or 0219 * "archival". 0220 */ 0221 VisitedBackground = 4, 0222 /** 0223 * Sixth color; for example, errors, untrusted content, etc. 0224 */ 0225 NegativeBackground = 5, 0226 /** 0227 * Seventh color; for example, warnings, secure/encrypted content. 0228 */ 0229 NeutralBackground = 6, 0230 /** 0231 * Eighth color; for example, success messages, trusted content. 0232 */ 0233 PositiveBackground = 7 0234 }; 0235 0236 /** 0237 * This enumeration describes the foreground color being selected from the 0238 * given set. 0239 * 0240 * Foreground colors are suitable for drawing text or glyphs (such as the 0241 * symbols on window decoration buttons, assuming a suitable background 0242 * brush is used), and should never be used to draw backgrounds. 0243 * 0244 * For window decorations, the following is suggested, but not set in 0245 * stone: 0246 * @li Maximize - PositiveText 0247 * @li Minimize - NeutralText 0248 * @li Close - NegativeText 0249 * @li WhatsThis - LinkText 0250 * @li Sticky - ActiveText 0251 */ 0252 enum ForegroundRole 0253 { 0254 /** 0255 * Normal foreground. 0256 */ 0257 NormalText = 0, 0258 /** 0259 * Second color; for example, comments, items which are old, inactive 0260 * or disabled. Generally used for things that are meant to be "less 0261 * important". InactiveText is not the same role as NormalText in the 0262 * inactive state. 0263 */ 0264 InactiveText = 1, 0265 /** 0266 * Third color; for example items which are new, active, requesting 0267 * attention, etc. May be used as a hover color for clickable items. 0268 */ 0269 ActiveText = 2, 0270 /** 0271 * Fourth color; use for (unvisited) links. May also be used for other 0272 * clickable items or content that indicates relationships, items that 0273 * indicate somewhere the user can visit, etc. 0274 */ 0275 LinkText = 3, 0276 /** 0277 * Fifth color; used for (visited) links. As with LinkText, may be used 0278 * for items that have already been "visited" or accessed. May also be 0279 * used to indicate "historical" (i.e. "old") items or information, 0280 * especially if InactiveText is being used in the same context to 0281 * express something different. 0282 */ 0283 VisitedText = 4, 0284 /** 0285 * Sixth color; for example, errors, untrusted content, deletions, 0286 * etc. 0287 */ 0288 NegativeText = 5, 0289 /** 0290 * Seventh color; for example, warnings, secure/encrypted content. 0291 */ 0292 NeutralText = 6, 0293 /** 0294 * Eighth color; for example, additions, success messages, trusted 0295 * content. 0296 */ 0297 PositiveText = 7 0298 }; 0299 0300 /** 0301 * This enumeration describes the decoration color being selected from the 0302 * given set. 0303 * 0304 * Decoration colors are used to draw decorations (such as frames) for 0305 * special purposes. Like color shades, they are neither foreground nor 0306 * background colors. Text should not be painted over a decoration color, 0307 * and decoration colors should not be used to draw text. 0308 */ 0309 enum DecorationRole 0310 { 0311 /** 0312 * Color used to draw decorations for items which have input focus. 0313 */ 0314 FocusColor, 0315 /** 0316 * Color used to draw decorations for items which will be activated by 0317 * clicking. 0318 */ 0319 HoverColor 0320 }; 0321 0322 /** 0323 * This enumeration describes the color shade being selected from the given 0324 * set. 0325 * 0326 * Color shades are used to draw "3d" elements, such as frames and bevels. 0327 * They are neither foreground nor background colors. Text should not be 0328 * painted over a shade, and shades should not be used to draw text. 0329 */ 0330 enum ShadeRole 0331 { 0332 /** 0333 * The light color is lighter than dark() or shadow() and contrasts 0334 * with the base color. 0335 */ 0336 LightShade, 0337 /** 0338 * The midlight color is in between base() and light(). 0339 */ 0340 MidlightShade, 0341 /** 0342 * The mid color is in between base() and dark(). 0343 */ 0344 MidShade, 0345 /** 0346 * The dark color is in between mid() and shadow(). 0347 */ 0348 DarkShade, 0349 /** 0350 * The shadow color is darker than light() or midlight() and contrasts 0351 * the base color. 0352 */ 0353 ShadowShade 0354 }; 0355 0356 public: 0357 0358 /** 0359 * Construct a copy of another SchemeManager. 0360 */ 0361 SchemeManager(const SchemeManager&); 0362 0363 /** 0364 * Destructor 0365 */ 0366 ~SchemeManager() = default; 0367 0368 /** 0369 * Standard assignment operator 0370 */ 0371 SchemeManager& operator=(const SchemeManager&); 0372 0373 /** 0374 * Construct a palette from given color set and state, using the colors 0375 * from the given KConfig (if null, the system colors are used). 0376 */ 0377 explicit SchemeManager(QPalette::ColorGroup state, 0378 ColorSet set = View, 0379 KSharedConfigPtr config = KSharedConfigPtr()); 0380 0381 /** 0382 * Retrieve the requested background brush. 0383 */ 0384 QBrush background(BackgroundRole = NormalBackground) const; 0385 0386 /** 0387 * Retrieve the requested foreground brush. 0388 */ 0389 QBrush foreground(ForegroundRole = NormalText) const; 0390 0391 /** 0392 * Retrieve the requested decoration brush. 0393 */ 0394 QBrush decoration(DecorationRole) const; 0395 0396 /** 0397 * Retrieve the requested shade color, using 0398 * SchemeManager::background(SchemeManager::NormalBackground) 0399 * as the base color and the contrast setting from the KConfig used to 0400 * create this SchemeManager instance (the system contrast setting, if no 0401 * KConfig was specified). 0402 * 0403 * @note Shades are chosen such that all shades would contrast with the 0404 * base color. This means that if base is very dark, the 'dark' shades will 0405 * be lighter than the base color, with midlight() == shadow(). 0406 * Conversely, if the base color is very light, the 'light' shades will be 0407 * darker than the base color, with light() == mid(). 0408 */ 0409 QColor shade(ShadeRole) const; 0410 0411 /** 0412 * Returns the contrast for borders. 0413 * @return the contrast (between 0 for minimum and 10 for maximum 0414 * contrast) 0415 */ 0416 static int contrast(); 0417 0418 /** 0419 * Returns the contrast for borders as a floating point value. 0420 * @param config pointer to the config from which to read the contrast 0421 * setting (the default is to use KSharedConfig::openConfig()) 0422 * @return the contrast (between 0.0 for minimum and 1.0 for maximum 0423 * contrast) 0424 */ 0425 static qreal contrastF(const KSharedConfigPtr& config = KSharedConfigPtr()); 0426 0427 /** 0428 * Retrieve the requested shade color, using the specified color as the 0429 * base color and the system contrast setting. 0430 * 0431 * @note Shades are chosen such that all shades would contrast with the 0432 * base color. This means that if base is very dark, the 'dark' shades will 0433 * be lighter than the base color, with midlight() == shadow(). 0434 * Conversely, if the base color is very light, the 'light' shades will be 0435 * darker than the base color, with light() == mid(). 0436 */ 0437 static QColor shade(const QColor&, ShadeRole); 0438 0439 /** 0440 * Retrieve the requested shade color, using the specified color as the 0441 * base color and the specified contrast. 0442 * 0443 * @param contrast Amount roughly specifying the contrast by which to 0444 * adjust the base color, between -1.0 and 1.0 (values between 0.0 and 1.0 0445 * correspond to the value from SchemeManager::contrastF) 0446 * @param chromaAdjust (optional) Amount by which to adjust the chroma of 0447 * the shade (1.0 means no adjustment) 0448 * 0449 * @note Shades are chosen such that all shades would contrast with the 0450 * base color. This means that if base is very dark, the 'dark' shades will 0451 * be lighter than the base color, with midlight() == shadow(). 0452 * Conversely, if the base color is very light, the 'light' shades will be 0453 * darker than the base color, with light() == mid(). 0454 * 0455 */ 0456 static QColor shade(const QColor&, 0457 ShadeRole, 0458 qreal contrast, 0459 qreal chromaAdjust = 0.0); 0460 0461 /** 0462 * Adjust a QPalette by replacing the specified QPalette::ColorRole with 0463 * the requested background color for all states. Using this method is 0464 * safer than replacing individual states, as it insulates you against 0465 * changes in QPalette::ColorGroup. 0466 * 0467 * @note Although it is possible to replace a foreground color using this 0468 * method, it's bad usability to do so. Just say "no". 0469 */ 0470 static void adjustBackground(QPalette&, 0471 BackgroundRole newRole = NormalBackground, 0472 QPalette::ColorRole color = QPalette::Base, 0473 ColorSet set = View, 0474 const KSharedConfigPtr& config = KSharedConfigPtr()); 0475 0476 /** 0477 * Adjust a QPalette by replacing the specified QPalette::ColorRole with 0478 * the requested foreground color for all states. Using this method is 0479 * safer than replacing individual states, as it insulates you against 0480 * changes in QPalette::ColorGroup. 0481 * 0482 * @note Although it is possible to replace a background color using this 0483 * method, it's bad usability to do so. Just say "no". 0484 */ 0485 static void adjustForeground(QPalette&, 0486 ForegroundRole newRole = NormalText, 0487 QPalette::ColorRole color = QPalette::Text, 0488 ColorSet set = View, 0489 const KSharedConfigPtr& config = KSharedConfigPtr()); 0490 0491 /** 0492 * Used to obtain the QPalette that will be used to set the application 0493 * palette from KDE Platform theme. 0494 * 0495 * @param config KConfig from which to load the colors 0496 * 0497 * @returns the QPalette 0498 */ 0499 static QPalette createApplicationPalette(const KSharedConfigPtr& config); 0500 0501 private: 0502 0503 QExplicitlySharedDataPointer<SchemeManagerPrivate> d; 0504 }; 0505 0506 // -------------------------------------------------------------------------- 0507 0508 class Q_DECL_HIDDEN ThemeManager::Private 0509 { 0510 public: 0511 0512 explicit Private(); 0513 0514 QPixmap createSchemePreviewIcon(const KSharedConfigPtr& config) const; 0515 0516 public: 0517 0518 const QString defaultThemeName; 0519 QMap<QString, QString> themeMap; ///< map<theme name, theme config path> 0520 0521 QActionGroup* themeMenuActionGroup; 0522 QMenu* themeMenuAction; 0523 }; 0524 0525 } // namespace Digikam 0526 0527 #endif // DIGIKAM_THEME_MANAGER_P_H