File indexing completed on 2024-05-05 05:35:33

0001 #ifndef oxygen_helper_h
0002 #define oxygen_helper_h
0003 
0004 /*
0005  * SPDX-FileCopyrightText: 2016 Michael Pyne <mpyne@kde.org>
0006  * SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007  * SPDX-FileCopyrightText: 2008 Long Huynh Huu <long.upcase@googlemail.com>
0008  * SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
0009  * SPDX-FileCopyrightText: 2007 Casper Boemann <cbr@boemann.dk>
0010  * SPDX-FileCopyrightText: 2007 Fredrik H ?glund <fredrik@kde.org>
0011  *
0012  * SPDX-License-Identifier: LGPL-2.0-only
0013  */
0014 
0015 #include "config-liboxygen.h"
0016 #include "oxygentileset.h"
0017 
0018 #include "liboxygen.h"
0019 
0020 #include <KSharedConfig>
0021 #include <KStatefulBrush>
0022 
0023 #include <QBitmap>
0024 #include <QCache>
0025 #include <QColor>
0026 #include <QPainterPath>
0027 #include <QPixmap>
0028 #include <QQueue>
0029 #include <QScopedPointer>
0030 #include <QWidget>
0031 
0032 #if OXYGEN_HAVE_X11
0033 #include <xcb/xcb.h>
0034 #endif
0035 
0036 namespace Oxygen
0037 {
0038 template<typename T>
0039 class BaseCache : public QCache<quint64, T>
0040 {
0041 public:
0042     //* constructor
0043     BaseCache(int maxCost)
0044         : QCache<quint64, T>(maxCost)
0045         , _enabled(true)
0046     {
0047     }
0048 
0049     //* constructor
0050     explicit BaseCache(void)
0051         : _enabled(true)
0052     {
0053     }
0054 
0055     //* destructor
0056     ~BaseCache(void)
0057     {
0058     }
0059 
0060     //* enable
0061     void setEnabled(bool value)
0062     {
0063         _enabled = value;
0064     }
0065 
0066     //* enable state
0067     bool enabled(void) const
0068     {
0069         return _enabled;
0070     }
0071 
0072     //* access
0073     T *object(const quint64 &key)
0074     {
0075         return _enabled ? QCache<quint64, T>::object(key) : nullptr;
0076     }
0077 
0078     //* max cost
0079     void setMaxCost(int cost)
0080     {
0081         if (cost <= 0) {
0082             QCache<quint64, T>::clear();
0083             QCache<quint64, T>::setMaxCost(1);
0084             setEnabled(false);
0085 
0086         } else {
0087             setEnabled(true);
0088             QCache<quint64, T>::setMaxCost(cost);
0089         }
0090     }
0091 
0092 private:
0093     //* enable flag
0094     bool _enabled;
0095 };
0096 
0097 /**
0098  * Holds up to a limited number of items keyed by quint64. If items must be
0099  * removed to fall within limit, removes those added the earliest.
0100  */
0101 template<typename T>
0102 class FIFOCache
0103 {
0104     using CachePair = QPair<quint64, T>;
0105 
0106 public:
0107     //* constructor
0108     FIFOCache(size_t _limit = 256)
0109         : m_limit(_limit)
0110     {
0111     }
0112 
0113     //* insert
0114     void insert(quint64 key, T value)
0115     {
0116         if (static_cast<size_t>(m_queue.size()) > m_limit) {
0117             (void)m_queue.dequeue();
0118         }
0119         m_queue.enqueue(CachePair(key, value));
0120     }
0121 
0122     //* find
0123     T find(quint64 key) const
0124     {
0125         for (const auto &item : m_queue) {
0126             if (item.first == key)
0127                 return item.second;
0128         }
0129         return T();
0130     }
0131 
0132     //* for_each
0133     template<typename F>
0134     void for_each(F f)
0135     {
0136         for (auto &item : m_queue) {
0137             f(item.second);
0138         }
0139     }
0140 
0141     //* maxCost
0142     void setMaxCost(size_t max)
0143     {
0144         m_limit = max;
0145         while (static_cast<size_t>(m_queue.size()) > m_limit) {
0146             (void)m_queue.dequeue();
0147         }
0148     }
0149 
0150     //* maxCost
0151     size_t maxCost() const
0152     {
0153         return m_limit;
0154     }
0155 
0156     //* clear
0157     void clear()
0158     {
0159         m_queue.clear();
0160     }
0161 
0162 private:
0163     //* queue
0164     QQueue<CachePair> m_queue;
0165 
0166     //* max size
0167     size_t m_limit;
0168 };
0169 
0170 template<typename T>
0171 class Cache
0172 {
0173 public:
0174     //* constructor
0175     Cache()
0176     {
0177     }
0178 
0179     //* destructor
0180     ~Cache()
0181     {
0182     }
0183 
0184     using Value = QSharedPointer<BaseCache<T>>;
0185 
0186     //* return cache matching a given key
0187     Value get(const QColor &color)
0188     {
0189         const quint64 key = (color.isValid() ? color.rgba() : 0);
0190 
0191         Value retValue = data_.find(key);
0192         if (!retValue) {
0193             retValue = Value(new BaseCache<T>(data_.maxCost()));
0194             data_.insert(key, retValue);
0195         }
0196 
0197         return retValue;
0198     }
0199 
0200     //* clear
0201     void clear(void)
0202     {
0203         data_.clear();
0204     }
0205 
0206     //* max cache size
0207     void setMaxCacheSize(int value)
0208     {
0209         data_.setMaxCost(value);
0210         data_.for_each([value](Value item) {
0211             item->setMaxCost(value);
0212         });
0213     }
0214 
0215 private:
0216     //* data
0217     FIFOCache<Value> data_;
0218 };
0219 
0220 //* oxygen style helper class.
0221 /** contains utility functions used at multiple places in both oxygen style and oxygen window decoration */
0222 class OXYGEN_EXPORT Helper
0223 {
0224 public:
0225     //* constructor
0226     explicit Helper(KSharedConfig::Ptr config);
0227 
0228     //* destructor
0229     virtual ~Helper()
0230     {
0231     }
0232 
0233     //* load configuration
0234     virtual void loadConfig();
0235 
0236     //* pointer to shared config
0237     KSharedConfig::Ptr config() const;
0238 
0239     //* reset all caches
0240     virtual void invalidateCaches();
0241 
0242     //* update maximum cache size
0243     virtual void setMaxCacheSize(int);
0244 
0245     //*@name window background gradients
0246     //@{
0247     /**
0248     \par y_shift: shift the background gradient upwards, to fit with the windec
0249     \par gradientHeight: the height of the generated gradient.
0250     for different heights, the gradient is translated so that it is always at the same position from the bottom
0251     */
0252     virtual void renderWindowBackground(QPainter *p, const QRect &clipRect, const QWidget *widget, const QPalette &pal, int y_shift = -23)
0253     {
0254         renderWindowBackground(p, clipRect, widget, pal.color(widget->window()->backgroundRole()), y_shift);
0255     }
0256 
0257     /**
0258     y_shift: shift the background gradient upwards, to fit with the windec
0259     gradientHeight: the height of the generated gradient.
0260     for different heights, the gradient is translated so that it is always at the same position from the bottom
0261     */
0262     virtual void
0263     renderWindowBackground(QPainter *p, const QRect &clipRect, const QWidget *widget, const QWidget *window, const QPalette &pal, int y_shift = -23)
0264     {
0265         renderWindowBackground(p, clipRect, widget, window, pal.color(window->backgroundRole()), y_shift);
0266     }
0267 
0268     //* render window background using a given color as a reference
0269     virtual void renderWindowBackground(QPainter *p, const QRect &clipRect, const QWidget *widget, const QColor &color, int y_shift = -23)
0270     {
0271         renderWindowBackground(p, clipRect, widget, widget->window(), color, y_shift);
0272     }
0273 
0274     //* render window background using a given color as a reference
0275     virtual void
0276     renderWindowBackground(QPainter *p, const QRect &clipRect, const QWidget *widget, const QWidget *window, const QColor &color, int y_shift = -23);
0277 
0278     virtual void renderWindowBackground(QPainter *p, const QRect &clipRect, const QRect &windowRect, const QColor &color, int y_shift);
0279 
0280     //@}
0281 
0282     //* dots
0283     void renderDot(QPainter *, const QPoint &, const QColor &);
0284 
0285     //* returns true for too 'dark' colors
0286     bool lowThreshold(const QColor &color);
0287 
0288     //* returns true for too 'light' colors
0289     bool highThreshold(const QColor &color);
0290 
0291     //* add alpha channel multiplier to color
0292     static QColor alphaColor(QColor color, qreal alpha);
0293 
0294     //* calculated light color from argument
0295     virtual QColor calcLightColor(const QColor &color);
0296 
0297     //* calculated dark color from argument
0298     virtual QColor calcDarkColor(const QColor &color);
0299 
0300     //* calculated shadow color from argument
0301     virtual QColor calcShadowColor(const QColor &color);
0302 
0303     //* returns menu background color matching position in a given top level widget
0304     virtual QColor backgroundColor(const QColor &color, const QWidget *w, const QPoint &point)
0305     {
0306         if (!(w && w->window()) || checkAutoFillBackground(w))
0307             return color;
0308         else
0309             return backgroundColor(color, w->window()->height(), w->mapTo(w->window(), point).y());
0310     }
0311 
0312     //* returns menu background color matching position in a top level widget of given height
0313     virtual QColor backgroundColor(const QColor &color, int height, int y)
0314     {
0315         return backgroundColor(color, qMin(qreal(1.0), qreal(y) / qMin(300, 3 * height / 4)));
0316     }
0317 
0318     //* color used for background radial gradient
0319     virtual QColor backgroundRadialColor(const QColor &color);
0320 
0321     //* color used at the top of window background
0322     virtual QColor backgroundTopColor(const QColor &color);
0323 
0324     //* color used at the bottom of window background
0325     virtual QColor backgroundBottomColor(const QColor &color);
0326 
0327     //* vertical gradient for window background
0328     virtual QPixmap verticalGradient(const QColor &color, int height, int offset = 0);
0329 
0330     //* radial gradient for window background
0331     virtual QPixmap radialGradient(const QColor &color, int width, int height = 20);
0332 
0333     //* merge background and front color for check marks, arrows, etc. using _contrast
0334     virtual QColor decoColor(const QColor &background, const QColor &color);
0335 
0336     //* returns a region matching given rect, with rounded corners, based on the multipliers
0337     /** setting any of the multipliers to zero will result in no corners shown on the corresponding side */
0338     virtual QRegion roundedMask(const QRect &, int left = 1, int right = 1, int top = 1, int bottom = 1) const;
0339 
0340     //* returns a region matching given rect, with rounded corners
0341     virtual QBitmap roundedMask(const QSize &, Corners corners = AllCorners, qreal radius = 4) const;
0342 
0343     //* return rounded path in a given rect, with only selected corners rounded, and for a given radius
0344     QPainterPath roundedPath(const QRect &, Corners = AllCorners, qreal = 4) const;
0345 
0346     //* draw frame that mimics some sort of shadows around a panel
0347     /** it is used for menus, detached dock panels and toolbar, as well as window decoration when compositing is disabled */
0348     virtual void drawFloatFrame(QPainter *p,
0349                                 const QRect r,
0350                                 const QColor &color,
0351                                 bool drawUglyShadow = true,
0352                                 bool isActive = false,
0353                                 const QColor &frameColor = QColor(),
0354                                 TileSet::Tiles tiles = TileSet::Ring);
0355 
0356     //* draw dividing line
0357     virtual void drawSeparator(QPainter *, const QRect &, const QColor &, Qt::Orientation);
0358 
0359     //* focus color
0360     QColor focusColor(const QPalette &palette) const
0361     {
0362         return _viewFocusBrush.brush(palette).color();
0363     }
0364 
0365     //* hover color
0366     QColor hoverColor(const QPalette &palette) const
0367     {
0368         return _viewHoverBrush.brush(palette).color();
0369     }
0370 
0371     //* negative text color
0372     QColor negativeTextColor(const QPalette &palette) const
0373     {
0374         return _viewNegativeTextBrush.brush(palette).color();
0375     }
0376 
0377     //* focus color
0378     QColor focusColor(QPalette::ColorGroup group) const
0379     {
0380         return _viewFocusBrush.brush(group).color();
0381     }
0382 
0383     //* hover color
0384     QColor hoverColor(QPalette::ColorGroup group) const
0385     {
0386         return _viewHoverBrush.brush(group).color();
0387     }
0388 
0389     //* negative text color
0390     QColor negativeTextColor(QPalette::ColorGroup group) const
0391     {
0392         return _viewNegativeTextBrush.brush(group).color();
0393     }
0394 
0395     /**
0396     returns first widget in parent chain that sets autoFillBackground to true,
0397     or nullptr if none
0398     */
0399     const QWidget *checkAutoFillBackground(const QWidget *) const;
0400 
0401     //*@name background gradient XProperty
0402     //@{
0403 
0404     //* set background gradient hint to widget
0405     virtual void setHasBackgroundGradient(WId, bool) const;
0406 
0407     //* true if background gradient hint is set
0408     virtual bool hasBackgroundGradient(WId) const;
0409 
0410     //@}
0411 
0412     //@name high dpi utility functions
0413     //@{
0414 
0415     //* return dpi-aware pixmap of given size
0416     virtual QPixmap highDpiPixmap(const QSize &size) const
0417     {
0418         return highDpiPixmap(size.width(), size.height());
0419     }
0420 
0421     //* return dpi-aware pixmap of given size
0422     virtual QPixmap highDpiPixmap(int width) const
0423     {
0424         return highDpiPixmap(width, width);
0425     }
0426 
0427     //* return dpi-aware pixmap of given size
0428     virtual QPixmap highDpiPixmap(int width, int height) const;
0429 
0430     //* return device pixel ratio for a given pixmap
0431     virtual qreal devicePixelRatio(const QPixmap &) const;
0432 
0433     //@}
0434 
0435     //*@name compositing utilities
0436     //@{
0437 
0438     //* true if style was compiled for and is running on X11
0439     static bool isX11(void);
0440 
0441     //@}
0442 
0443 #if OXYGEN_HAVE_X11
0444 
0445     //* xcb connection
0446     static xcb_connection_t *connection(void);
0447 
0448     //* create xcb atom
0449     xcb_atom_t createAtom(const QString &) const;
0450 
0451 #endif
0452 
0453     //* scoped pointer convenience typedef
0454     template<typename T>
0455     using ScopedPointer = QScopedPointer<T, QScopedPointerPodDeleter>;
0456 
0457 protected:
0458     //* return color key for a given color, properly accounting for invalid colors
0459     quint64 colorKey(const QColor &color) const
0460     {
0461         return color.isValid() ? color.rgba() : 0;
0462     }
0463 
0464     //* generic outer shadow (to be stored in tilesets)
0465     virtual void drawShadow(QPainter &, const QColor &, int size);
0466 
0467     //* generic outer glow (to be stored in tilesets)
0468     virtual void drawOuterGlow(QPainter &, const QColor &, int size);
0469 
0470     //* return background adjusted color matching relative vertical position in window
0471     QColor backgroundColor(const QColor &, qreal ratio);
0472 
0473     //*@name global configuration parameters
0474     //@{
0475 
0476     static const qreal _glowBias;
0477     static const qreal _slabThickness;
0478     static const qreal _shadowGain;
0479     qreal _contrast;
0480 
0481     //@}
0482 
0483     //* shortcut to color caches
0484     /** it is made protected because it is also used in the style helper */
0485     using ColorCache = BaseCache<QColor>;
0486 
0487     //* shortcut to pixmap cache
0488     using PixmapCache = BaseCache<QPixmap>;
0489 
0490 private:
0491     //* initialize
0492     void init(void);
0493 
0494     //* configuration
0495     KSharedConfig::Ptr _config;
0496     qreal _bgcontrast;
0497 
0498     //*@name brushes
0499     //@{
0500     KStatefulBrush _viewFocusBrush;
0501     KStatefulBrush _viewHoverBrush;
0502     KStatefulBrush _viewNegativeTextBrush;
0503     //@}
0504 
0505     //*@name color caches
0506     //@{
0507     ColorCache _decoColorCache;
0508     ColorCache _lightColorCache;
0509     ColorCache _darkColorCache;
0510     ColorCache _shadowColorCache;
0511     ColorCache _backgroundTopColorCache;
0512     ColorCache _backgroundBottomColorCache;
0513     ColorCache _backgroundRadialColorCache;
0514     ColorCache _backgroundColorCache;
0515     //@}
0516 
0517     PixmapCache _backgroundCache;
0518     PixmapCache _dotCache;
0519 
0520     //* high threshold colors
0521     using ColorMap = QMap<quint32, bool>;
0522     ColorMap _highThreshold;
0523     ColorMap _lowThreshold;
0524 
0525 #if OXYGEN_HAVE_X11
0526 
0527     //* set value for given hint
0528     void setHasHint(xcb_window_t, xcb_atom_t, bool) const;
0529 
0530     //* value for given hint
0531     bool hasHint(xcb_window_t, xcb_atom_t) const;
0532 
0533     //* background gradient hint atom
0534     xcb_atom_t _backgroundGradientAtom;
0535 
0536 #endif
0537 
0538     bool _isX11;
0539 };
0540 }
0541 
0542 #endif