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