File indexing completed on 2024-05-12 09:37:34

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