File indexing completed on 2024-04-28 05:26:21

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #pragma once
0008 
0009 #include "breeze.h"
0010 #include "breezeanimationdata.h"
0011 #include "breezemetrics.h"
0012 #include "breezesettings.h"
0013 #include "config-breeze.h"
0014 
0015 #include <KConfigWatcher>
0016 #include <KSharedConfig>
0017 #include <KStatefulBrush>
0018 
0019 #include <QIcon>
0020 #include <QPainterPath>
0021 #include <QToolBar>
0022 #include <QWidget>
0023 #include <qpainter.h>
0024 
0025 class QSlider;
0026 class QStyleOptionSlider;
0027 
0028 namespace Breeze
0029 {
0030 class PaletteChangedEventFilter;
0031 //* breeze style helper class.
0032 /** contains utility functions used at multiple places in both breeze style and breeze window decoration */
0033 class Helper : public QObject
0034 {
0035     Q_OBJECT
0036 
0037 public:
0038     //* constructor
0039     explicit Helper(KSharedConfig::Ptr, QObject *parent = nullptr);
0040 
0041     //* destructor
0042     virtual ~Helper()
0043     {
0044     }
0045 
0046     //* load configuration
0047     virtual void loadConfig();
0048 
0049     //* pointer to shared config
0050     KSharedConfig::Ptr config() const;
0051 
0052     //* pointer to kdecoration config
0053     QSharedPointer<InternalSettings> decorationConfig() const;
0054 
0055     //* install event filter for palette change event
0056     void installEventFilter(QApplication *app) const;
0057 
0058     //* uninstall event filter
0059     void removeEventFilter(QApplication *app) const;
0060 
0061     //*@name color utilities
0062     //@{
0063 
0064     //* add alpha channel multiplier to color
0065     QColor alphaColor(QColor color, qreal alpha) const;
0066 
0067     //* mouse over color
0068     QColor hoverColor(const QPalette &palette) const
0069     {
0070         return _viewHoverBrush.brush(palette).color();
0071     }
0072 
0073     //* focus color
0074     QColor focusColor(const QPalette &palette) const
0075     {
0076         return _viewFocusBrush.brush(palette).color();
0077     }
0078 
0079     //* mouse over color for buttons
0080     QColor buttonHoverColor(const QPalette &palette) const
0081     {
0082         return _buttonHoverBrush.brush(palette).color();
0083     }
0084 
0085     //* focus color for buttons
0086     QColor buttonFocusColor(const QPalette &palette) const
0087     {
0088         return _buttonFocusBrush.brush(palette).color();
0089     }
0090 
0091     //* negative text color (used for close button)
0092     QColor negativeText(const QPalette &palette) const
0093     {
0094         return _viewNegativeTextBrush.brush(palette).color();
0095     }
0096 
0097     //* neutral text color
0098     QColor neutralText(const QPalette &palette) const
0099     {
0100         return _viewNeutralTextBrush.brush(palette).color();
0101     }
0102 
0103     //* shadow
0104     QColor shadowColor([[maybe_unused]] const QPalette &palette, qreal opacity = 0.125) const
0105     {
0106         return QColor::fromRgbF(0, 0, 0, opacity);
0107     }
0108 
0109     //* titlebar color
0110     const QColor &titleBarColor(bool active) const
0111     {
0112         return active ? _activeTitleBarColor : _inactiveTitleBarColor;
0113     }
0114 
0115     //* titlebar text color
0116     const QColor &titleBarTextColor(bool active) const
0117     {
0118         return active ? _activeTitleBarTextColor : _inactiveTitleBarTextColor;
0119     }
0120 
0121     //* frame outline color, using animations
0122     QColor frameOutlineColor(const QPalette &,
0123                              bool mouseOver = false,
0124                              bool hasFocus = false,
0125                              qreal opacity = AnimationData::OpacityInvalid,
0126                              AnimationMode = AnimationNone) const;
0127 
0128     //* focus outline color, using animations
0129     QColor focusOutlineColor(const QPalette &) const;
0130 
0131     //* hover outline color, using animations
0132     QColor hoverOutlineColor(const QPalette &) const;
0133 
0134     //* focus outline color, using animations
0135     QColor buttonFocusOutlineColor(const QPalette &) const;
0136 
0137     //* hover outline color, using animations
0138     QColor buttonHoverOutlineColor(const QPalette &) const;
0139 
0140     //* side panel outline color, using animations
0141     QColor sidePanelOutlineColor(const QPalette &, bool hasFocus = false, qreal opacity = AnimationData::OpacityInvalid, AnimationMode = AnimationNone) const;
0142 
0143     //* frame background color
0144     QColor frameBackgroundColor(const QPalette &palette) const
0145     {
0146         return frameBackgroundColor(palette, palette.currentColorGroup());
0147     }
0148 
0149     //* frame background color
0150     QColor frameBackgroundColor(const QPalette &, QPalette::ColorGroup) const;
0151 
0152     //* arrow outline color
0153     QColor arrowColor(const QPalette &, QPalette::ColorGroup, QPalette::ColorRole) const;
0154 
0155     //* arrow outline color
0156     QColor arrowColor(const QPalette &palette, QPalette::ColorRole role) const
0157     {
0158         return arrowColor(palette, palette.currentColorGroup(), role);
0159     }
0160 
0161     //* arrow outline color, using animations
0162     QColor arrowColor(const QPalette &, bool mouseOver, bool hasFocus, qreal opacity = AnimationData::OpacityInvalid, AnimationMode = AnimationNone) const;
0163 
0164     //* slider outline color, using animations
0165     QColor
0166     sliderOutlineColor(const QPalette &, bool mouseOver, bool hasFocus, qreal opacity = AnimationData::OpacityInvalid, AnimationMode = AnimationNone) const;
0167 
0168     //* scrollbar handle color, using animations
0169     QColor
0170     scrollBarHandleColor(const QPalette &, bool mouseOver, bool hasFocus, qreal opacity = AnimationData::OpacityInvalid, AnimationMode = AnimationNone) const;
0171 
0172     //* checkbox indicator, using animations
0173     QColor
0174     checkBoxIndicatorColor(const QPalette &, bool mouseOver, bool active, qreal opacity = AnimationData::OpacityInvalid, AnimationMode = AnimationNone) const;
0175 
0176     //* separator color
0177     QColor separatorColor(const QPalette &) const;
0178 
0179     //* merge active and inactive palettes based on ratio, for smooth enable state change transition
0180     QPalette disabledPalette(const QPalette &, qreal ratio) const;
0181 
0182     //@}
0183 
0184     //*@name rendering utilities
0185     //@{
0186 
0187     //* debug frame
0188     void renderDebugFrame(QPainter *, const QRectF &) const;
0189 
0190     //* focus rect
0191     void renderFocusRect(QPainter *, const QRectF &, const QColor &, const QColor &outline = QColor(), Sides = {}) const;
0192 
0193     //* focus line
0194     void renderFocusLine(QPainter *, const QRectF &, const QColor &) const;
0195 
0196     //* generic frame
0197     void renderFrame(QPainter *, const QRectF &, const QColor &color, const QColor &outline = QColor()) const;
0198 
0199     //* generic frame, with separators only on the side
0200     void renderFrameWithSides(QPainter *, const QRectF &, const QColor &color, Qt::Edges edges, const QColor &outline = QColor()) const;
0201 
0202     //* side panel frame
0203     void renderSidePanelFrame(QPainter *, const QRectF &, const QColor &outline, Side) const;
0204 
0205     //* menu frame
0206     void renderMenuFrame(QPainter *,
0207                          const QRectF &,
0208                          const QColor &color,
0209                          const QColor &outline,
0210                          bool roundCorners = true,
0211                          Qt::Edges seamlessEdges = Qt::Edges()) const;
0212 
0213     //* button frame
0214     void renderButtonFrame(QPainter *painter,
0215                            const QRectF &rect,
0216                            const QPalette &palette,
0217                            const QHash<QByteArray, bool> &stateProperties,
0218                            qreal bgAnimation = AnimationData::OpacityInvalid,
0219                            qreal penAnimation = AnimationData::OpacityInvalid) const;
0220 
0221     //* toolbutton frame
0222     void renderToolBoxFrame(QPainter *, const QRectF &, int tabWidth, const QColor &color) const;
0223 
0224     //* tab widget frame
0225     void renderTabWidgetFrame(QPainter *, const QRectF &, const QColor &color, const QColor &outline, Corners) const;
0226 
0227     //* selection frame
0228     void renderSelection(QPainter *, const QRectF &, const QColor &) const;
0229 
0230     //* separator
0231     void renderSeparator(QPainter *, const QRectF &, const QColor &, bool vertical = false) const;
0232 
0233     //* checkbox
0234     void renderCheckBoxBackground(QPainter *,
0235                                   const QRectF &,
0236                                   const QPalette &palette,
0237                                   CheckBoxState state,
0238                                   bool neutalHighlight,
0239                                   bool sunken,
0240                                   qreal animation = AnimationData::OpacityInvalid) const;
0241 
0242     //* checkbox
0243     void renderCheckBox(QPainter *,
0244                         const QRectF &,
0245                         const QPalette &palette,
0246                         bool mouseOver,
0247                         CheckBoxState state,
0248                         CheckBoxState target,
0249                         bool neutalHighlight,
0250                         bool sunken,
0251                         qreal animation = AnimationData::OpacityInvalid,
0252                         qreal hoverAnimation = AnimationData::OpacityInvalid) const;
0253 
0254     //* radio button
0255     void renderRadioButtonBackground(QPainter *,
0256                                      const QRectF &,
0257                                      const QPalette &palette,
0258                                      RadioButtonState state,
0259                                      bool neutalHighlight,
0260                                      bool sunken,
0261                                      qreal animation = AnimationData::OpacityInvalid) const;
0262 
0263     //* radio button
0264     void renderRadioButton(QPainter *,
0265                            const QRectF &,
0266                            const QPalette &palette,
0267                            bool mouseOver,
0268                            RadioButtonState state,
0269                            bool neutalHighlight,
0270                            bool sunken,
0271                            qreal animation = AnimationData::OpacityInvalid,
0272                            qreal hoverAnimation = AnimationData::OpacityInvalid) const;
0273 
0274     //* slider groove
0275     void renderSliderGroove(QPainter *, const QRectF &, const QColor &fg, const QColor &bg) const;
0276 
0277     //* reimplementation of protected method
0278     void initSliderStyleOption(const QSlider *, QStyleOptionSlider *) const;
0279 
0280     //* slider focus frame
0281     QRectF pathForSliderHandleFocusFrame(QPainterPath &, const QRectF &, int hmargin, int vmargin) const;
0282 
0283     //* slider handle
0284     void renderSliderHandle(QPainter *, const QRectF &, const QColor &, const QColor &outline, const QColor &shadow, bool sunken) const;
0285 
0286     //* dial groove
0287     void renderDialGroove(QPainter *, const QRectF &, const QColor &fg, const QColor &bg, qreal first, qreal last) const;
0288 
0289     //* progress bar groove
0290     void renderProgressBarGroove(QPainter *, const QRectF &, const QColor &fg, const QColor &bg) const;
0291 
0292     //* progress bar contents (animated)
0293     void renderProgressBarBusyContents(QPainter *painter,
0294                                        const QRectF &rect,
0295                                        const QColor &first,
0296                                        const QColor &second,
0297                                        bool horizontal,
0298                                        bool reverse,
0299                                        int progress) const;
0300 
0301     //* scrollbar handle
0302     void renderScrollBarHandle(QPainter *, const QRectF &, const QColor &fg, const QColor &bg) const;
0303 
0304     //* separator between scrollbar and contents
0305     void renderScrollBarBorder(QPainter *, const QRectF &, const QColor &) const;
0306 
0307     //* tabbar tab
0308     void renderTabBarTab(QPainter *, const QRectF &, const QPalette &palette, const QHash<QByteArray, bool> &stateProperties, Corners corners, qreal animation)
0309         const;
0310     // TODO(janet): document should be set based on whether or not we consider the
0311     // tab user-editable, but Qt apps often misuse or don't use documentMode property
0312     // so we're currently just always setting it to true for now
0313     qreal devicePixelRatio(QPainter *) const;
0314 
0315     //* generic arrow
0316     void renderArrow(QPainter *, const QRectF &, const QColor &, ArrowOrientation) const;
0317 
0318     //* generic button (for mdi decorations, tabs and dock widgets)
0319     void renderDecorationButton(QPainter *, const QRectF &, const QColor &, ButtonType, bool inverted) const;
0320 
0321     //* generic shadow for rounded rectangles
0322     void renderRoundedRectShadow(QPainter *, const QRectF &, const QColor &, qreal radius = Metrics::Frame_FrameRadius - PenWidth::Shadow / 2) const;
0323 
0324     //* generic shadow for ellipses
0325     void renderEllipseShadow(QPainter *, const QRectF &, const QColor &) const;
0326 
0327     //@}
0328 
0329     //*@name compositing utilities
0330     //@{
0331 
0332     //* true if style was compiled for and is running on X11
0333     static bool isX11();
0334 
0335     //* true if running on platform Wayland
0336     static bool isWayland();
0337 
0338     //* returns true if compositing is active
0339     bool compositingActive() const;
0340 
0341     //* returns true if a given widget supports alpha channel
0342     bool hasAlphaChannel(const QWidget *) const;
0343 
0344     //* returns true if the tools area should be drawn
0345     bool shouldDrawToolsArea(const QWidget *) const;
0346 
0347     //@}
0348 
0349     //* frame radius
0350     constexpr qreal frameRadius(const int penWidth = PenWidth::NoPen, const qreal bias = 0) const
0351     {
0352         return qMax(Metrics::Frame_FrameRadius - (0.5 * penWidth) + bias, 0.0);
0353     }
0354 
0355     //* frame radius with new pen width
0356     constexpr qreal frameRadiusForNewPenWidth(const qreal oldRadius, const int penWidth) const
0357     {
0358         return qMax(oldRadius - (0.5 * penWidth), 0.0);
0359     }
0360 
0361     //* return a QRectF with the appropriate size for a rectangle with a pen stroke
0362     QRectF strokedRect(const QRectF &rect, const qreal penWidth = PenWidth::Frame) const;
0363 
0364     //* return a QRectF with the appropriate size for a rectangle with a shadow around it
0365     QRectF shadowedRect(const QRectF &rect, const qreal shadowSize = PenWidth::Shadow) const
0366     {
0367         return rect.adjusted(shadowSize, shadowSize, -shadowSize, -shadowSize);
0368     }
0369 
0370     QPixmap coloredIcon(const QIcon &icon,
0371                         const QPalette &palette,
0372                         const QSize &size,
0373                         qreal devicePixelRatio,
0374                         QIcon::Mode mode = QIcon::Normal,
0375                         QIcon::State state = QIcon::Off);
0376 
0377     static Qt::Edges menuSeamlessEdges(const QWidget *);
0378 
0379 protected:
0380     //* return rounded path in a given rect, with only selected corners rounded, and for a given radius
0381     QPainterPath roundedPath(const QRectF &, Corners, qreal) const;
0382 
0383 private:
0384     //* configuration
0385     KSharedConfig::Ptr _config;
0386 
0387     //* KWin configuration
0388     KSharedConfig::Ptr _kwinConfig;
0389 
0390     //* decoration configuration
0391     QSharedPointer<InternalSettings> _decorationConfig;
0392 
0393     //* event filter
0394     PaletteChangedEventFilter *_eventFilter;
0395 
0396     //*@name brushes
0397     //@{
0398     KStatefulBrush _viewFocusBrush;
0399     KStatefulBrush _viewHoverBrush;
0400     KStatefulBrush _buttonFocusBrush;
0401     KStatefulBrush _buttonHoverBrush;
0402     KStatefulBrush _viewNegativeTextBrush;
0403     KStatefulBrush _viewNeutralTextBrush;
0404     //@}
0405 
0406     //*@name windeco colors
0407     //@{
0408     QColor _activeTitleBarColor;
0409     QColor _activeTitleBarTextColor;
0410     QColor _inactiveTitleBarColor;
0411     QColor _inactiveTitleBarTextColor;
0412     //@}
0413 
0414     mutable bool _cachedAutoValid = false;
0415 
0416     friend class ToolsAreaManager;
0417     friend class PaletteChangedEventFilter;
0418 };
0419 
0420 class PaletteChangedEventFilter : public QObject
0421 {
0422     Q_OBJECT
0423 
0424 public:
0425     explicit PaletteChangedEventFilter(Helper *);
0426 
0427 protected:
0428     bool eventFilter(QObject *watched, QEvent *event) override;
0429 
0430 private:
0431     Helper *_helper;
0432 };
0433 }