File indexing completed on 2024-05-12 17:06:31
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