File indexing completed on 2024-04-14 14:09:17

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