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