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

0001 /*
0002     SPDX-FileCopyrightText: 2013 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0003     SPDX-FileCopyrightText: 2008 Long Huynh Huu <long.upcase@googlemail.com>
0004     SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
0005     SPDX-FileCopyrightText: 2007 Casper Boemann <cbr@boemann.dk>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include "oxygenstylehelper.h"
0011 
0012 #include <KColorScheme>
0013 #include <KColorUtils>
0014 
0015 #include <QLinearGradient>
0016 #include <QPainter>
0017 #include <QTextStream>
0018 
0019 #include <math.h>
0020 
0021 #if OXYGEN_HAVE_X11
0022 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0023 #include <QX11Info>
0024 #else
0025 #include <private/qtx11extras_p.h>
0026 #endif
0027 #endif
0028 
0029 namespace Oxygen
0030 {
0031 //______________________________________________________________________________
0032 StyleHelper::StyleHelper(KSharedConfigPtr config)
0033     : Helper(config)
0034 {
0035     init();
0036 }
0037 
0038 //______________________________________________________________________________
0039 void StyleHelper::invalidateCaches(void)
0040 {
0041     _slabCache.clear();
0042     _slabSunkenCache.clear();
0043     _dialSlabCache.clear();
0044     _roundSlabCache.clear();
0045     _sliderSlabCache.clear();
0046     _holeCache.clear();
0047 
0048     _midColorCache.clear();
0049 
0050     _dockWidgetButtonCache.clear();
0051     _progressBarCache.clear();
0052     _cornerCache.clear();
0053     _selectionCache.clear();
0054     _holeFlatCache.clear();
0055     _slopeCache.clear();
0056     _slitCache.clear();
0057     _dockFrameCache.clear();
0058     _scrollHoleCache.clear();
0059     _scrollHandleCache.clear();
0060     Helper::invalidateCaches();
0061 }
0062 
0063 //____________________________________________________________________
0064 void StyleHelper::setMaxCacheSize(int value)
0065 {
0066     // base class
0067     Helper::setMaxCacheSize(value);
0068 
0069     // assign max cache size
0070     _slabCache.setMaxCacheSize(value);
0071     _slabSunkenCache.setMaxCost(value);
0072     _dialSlabCache.setMaxCacheSize(value);
0073     _roundSlabCache.setMaxCacheSize(value);
0074     _sliderSlabCache.setMaxCacheSize(value);
0075     _holeCache.setMaxCacheSize(value);
0076     _scrollHandleCache.setMaxCacheSize(value);
0077 
0078     _dockWidgetButtonCache.setMaxCost(value);
0079     _progressBarCache.setMaxCost(value);
0080     _cornerCache.setMaxCost(value);
0081     _selectionCache.setMaxCost(value);
0082     _holeFlatCache.setMaxCost(value);
0083     _slopeCache.setMaxCost(value);
0084     _slitCache.setMaxCost(value);
0085     _dockFrameCache.setMaxCost(value);
0086     _scrollHoleCache.setMaxCost(value);
0087 }
0088 
0089 //____________________________________________________________________
0090 void StyleHelper::renderWindowBackground(QPainter *painter, const QRect &clipRect, const QWidget *widget, const QColor &color, int y_shift)
0091 {
0092     if (_useBackgroundGradient) {
0093         // normal background gradient
0094         Helper::renderWindowBackground(painter, clipRect, widget, widget->window(), color, y_shift);
0095 
0096     } else {
0097         // if background gradient is disabled, simply render flat background
0098         if (clipRect.isValid()) {
0099             painter->setClipRegion(clipRect, Qt::IntersectClip);
0100         }
0101 
0102         painter->fillRect(widget->rect(), color);
0103     }
0104 }
0105 
0106 //____________________________________________________________________
0107 void StyleHelper::setHasBackgroundGradient(WId id, bool value) const
0108 {
0109     Helper::setHasBackgroundGradient(id, value && _useBackgroundGradient);
0110 }
0111 
0112 //____________________________________________________________________
0113 void StyleHelper::renderMenuBackground(QPainter *painter, const QRect &clipRect, const QWidget *widget, const QColor &color)
0114 {
0115     // get coordinates relative to the client area
0116     // this is stupid. One could use mapTo if this was taking const QWidget* and not
0117     // QWidget* as argument.
0118     const QWidget *w(widget);
0119     int x(0);
0120     int y(0);
0121 
0122     while (!w->isWindow() && w != w->parentWidget()) {
0123         x += w->geometry().x();
0124         y += w->geometry().y();
0125         w = w->parentWidget();
0126     }
0127 
0128     if (clipRect.isValid()) {
0129         painter->save();
0130         painter->setClipRegion(clipRect, Qt::IntersectClip);
0131     }
0132 
0133     // calculate upper part height
0134     // special tricks are needed
0135     // to handle both window contents and window decoration
0136     QRect r = w->rect();
0137     const int height(w->frameGeometry().height());
0138     const int splitY(qMin(200, (3 * height) / 4));
0139 
0140     const QRect upperRect(QRect(0, 0, r.width(), splitY));
0141     const QPixmap tile(verticalGradient(color, splitY));
0142     painter->drawTiledPixmap(upperRect, tile);
0143 
0144     const QRect lowerRect(0, splitY, r.width(), r.height() - splitY);
0145     painter->fillRect(lowerRect, backgroundBottomColor(color));
0146 
0147     if (clipRect.isValid()) {
0148         painter->restore();
0149     }
0150 }
0151 
0152 //____________________________________________________________________________________
0153 QColor StyleHelper::arrowColor(const QPalette &palette, StyleOptions options, qreal opacity, AnimationMode mode) const
0154 {
0155     QColor glow(palette.color(QPalette::WindowText));
0156     if (mode == AnimationNone || opacity < 0) {
0157         if (options & Hover)
0158             glow = hoverColor(palette);
0159         else if (options & Focus)
0160             glow = focusColor(palette);
0161 
0162     } else if (mode == AnimationHover) {
0163         // animated color, hover
0164         if (options & Focus)
0165             glow = focusColor(palette);
0166         if (glow.isValid())
0167             glow = KColorUtils::mix(glow, hoverColor(palette), opacity);
0168 
0169     } else if (mode == AnimationFocus) {
0170         if (options & Hover)
0171             glow = hoverColor(palette);
0172         if (glow.isValid())
0173             glow = KColorUtils::mix(glow, focusColor(palette), opacity);
0174     }
0175 
0176     return glow;
0177 }
0178 
0179 //____________________________________________________________________________________
0180 QColor StyleHelper::buttonGlowColor(QPalette::ColorGroup colorGroup, StyleOptions options, qreal opacity, AnimationMode mode) const
0181 {
0182     QColor glow;
0183     if (mode == AnimationNone || opacity < 0) {
0184         if (options & Hover)
0185             glow = hoverColor(colorGroup);
0186         else if (options & Focus)
0187             glow = focusColor(colorGroup);
0188 
0189     } else if (mode == AnimationHover) {
0190         // animated color, hover
0191         if (options & Focus)
0192             glow = focusColor(colorGroup);
0193         if (glow.isValid())
0194             glow = KColorUtils::mix(glow, hoverColor(colorGroup), opacity);
0195         else
0196             glow = alphaColor(hoverColor(colorGroup), opacity);
0197 
0198     } else if (mode == AnimationFocus) {
0199         if (options & Hover)
0200             glow = hoverColor(colorGroup);
0201         if (glow.isValid())
0202             glow = KColorUtils::mix(glow, focusColor(colorGroup), opacity);
0203         else
0204             glow = alphaColor(focusColor(colorGroup), opacity);
0205     }
0206 
0207     return glow;
0208 }
0209 
0210 //____________________________________________________________________________________
0211 QColor StyleHelper::frameGlowColor(QPalette::ColorGroup colorGroup, StyleOptions options, qreal opacity, AnimationMode mode) const
0212 {
0213     QColor glow;
0214     if (mode == AnimationNone || opacity < 0) {
0215         if (options & Focus)
0216             glow = focusColor(colorGroup);
0217         else if (options & Hover)
0218             glow = hoverColor(colorGroup);
0219 
0220     } else if (mode == AnimationFocus) {
0221         if (options & Hover)
0222             glow = hoverColor(colorGroup);
0223         if (glow.isValid())
0224             glow = KColorUtils::mix(glow, focusColor(colorGroup), opacity);
0225         else
0226             glow = alphaColor(focusColor(colorGroup), opacity);
0227 
0228     } else if (mode == AnimationHover) {
0229         // animated color, hover
0230         if (options & Focus)
0231             glow = focusColor(colorGroup);
0232         if (glow.isValid())
0233             glow = KColorUtils::mix(glow, hoverColor(colorGroup), opacity);
0234         else
0235             glow = alphaColor(hoverColor(colorGroup), opacity);
0236     }
0237 
0238     return glow;
0239 }
0240 
0241 //______________________________________________________________________________
0242 QPalette StyleHelper::disabledPalette(const QPalette &source, qreal ratio) const
0243 {
0244     QPalette out(source);
0245     out.setColor(QPalette::Window,
0246                  KColorUtils::mix(source.color(QPalette::Active, QPalette::Window), source.color(QPalette::Disabled, QPalette::Window), 1.0 - ratio));
0247     out.setColor(QPalette::Highlight,
0248                  KColorUtils::mix(source.color(QPalette::Active, QPalette::Highlight), source.color(QPalette::Disabled, QPalette::Highlight), 1.0 - ratio));
0249     out.setColor(QPalette::WindowText,
0250                  KColorUtils::mix(source.color(QPalette::Active, QPalette::WindowText), source.color(QPalette::Disabled, QPalette::WindowText), 1.0 - ratio));
0251     out.setColor(QPalette::ButtonText,
0252                  KColorUtils::mix(source.color(QPalette::Active, QPalette::ButtonText), source.color(QPalette::Disabled, QPalette::ButtonText), 1.0 - ratio));
0253     out.setColor(QPalette::Text,
0254                  KColorUtils::mix(source.color(QPalette::Active, QPalette::Text), source.color(QPalette::Disabled, QPalette::Text), 1.0 - ratio));
0255     out.setColor(QPalette::Button,
0256                  KColorUtils::mix(source.color(QPalette::Active, QPalette::Button), source.color(QPalette::Disabled, QPalette::Button), 1.0 - ratio));
0257     return out;
0258 }
0259 
0260 //______________________________________________________________________________
0261 QPixmap StyleHelper::dockWidgetButton(const QColor &color, bool pressed, int size)
0262 {
0263     const quint64 key((colorKey(color) << 32) | (size << 1) | quint64(pressed));
0264 
0265     if (QPixmap *cachedPixmap = _dockWidgetButtonCache.object(key)) {
0266         return *cachedPixmap;
0267     }
0268 
0269     QPixmap pixmap(highDpiPixmap(size, size));
0270     pixmap.fill(Qt::transparent);
0271 
0272     const QColor light(calcLightColor(color));
0273     const QColor dark(calcDarkColor(color));
0274 
0275     QPainter painter(&pixmap);
0276     painter.setRenderHints(QPainter::Antialiasing);
0277     painter.setPen(Qt::NoPen);
0278     const qreal u(size / 18.0);
0279     painter.translate(0.5 * u, (0.5 - 0.668) * u);
0280 
0281     {
0282         // outline circle
0283         qreal penWidth = 1.2;
0284         QLinearGradient linearGradient(0, u * (1.665 - penWidth), 0, u * (12.33 + 1.665 - penWidth));
0285         linearGradient.setColorAt(0, dark);
0286         linearGradient.setColorAt(1, light);
0287         QRectF r(u * 0.5 * (17 - 12.33 + penWidth), u * (1.665 + penWidth), u * (12.33 - penWidth), u * (12.33 - penWidth));
0288         painter.setPen(QPen(linearGradient, penWidth * u));
0289         painter.drawEllipse(r);
0290         painter.end();
0291     }
0292 
0293     _dockWidgetButtonCache.insert(key, new QPixmap(pixmap));
0294     return pixmap;
0295 }
0296 
0297 //________________________________________________________________________________________________________
0298 TileSet StyleHelper::roundCorner(const QColor &color, int size)
0299 {
0300     const quint64 key((colorKey(color) << 32) | size);
0301     if (TileSet *cachedTileSet = _cornerCache.object(key)) {
0302         return *cachedTileSet;
0303     }
0304 
0305     QPixmap pixmap = QPixmap(size * 2, size * 2);
0306     pixmap.fill(Qt::transparent);
0307 
0308     QPainter painter(&pixmap);
0309     painter.setRenderHint(QPainter::Antialiasing);
0310     painter.setPen(Qt::NoPen);
0311 
0312     QLinearGradient linearGradient = QLinearGradient(0.0, size - 4.5, 0.0, size + 4.5);
0313     linearGradient.setColorAt(0.50, calcLightColor(backgroundTopColor(color)));
0314     linearGradient.setColorAt(0.51, backgroundBottomColor(color));
0315 
0316     // draw ellipse.
0317     painter.setBrush(linearGradient);
0318     painter.drawEllipse(QRectF(size - 4, size - 4, 8, 8));
0319 
0320     // mask
0321     painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
0322     painter.setBrush(Qt::black);
0323     painter.drawEllipse(QRectF(size - 3, size - 3, 6, 6));
0324 
0325     TileSet tileSet(pixmap, size, size, 1, 1);
0326     _cornerCache.insert(key, new TileSet(tileSet));
0327 
0328     return tileSet;
0329 }
0330 
0331 //________________________________________________________________________________________________________
0332 TileSet StyleHelper::slope(const QColor &color, qreal shade, int size)
0333 {
0334     const quint64 key((colorKey(color) << 32) | (quint64(256.0 * shade) << 24) | size);
0335     if (TileSet *cachedTileSet = _slopeCache.object(key)) {
0336         return *cachedTileSet;
0337     }
0338 
0339     QPixmap pixmap(highDpiPixmap(size * 4));
0340     pixmap.fill(Qt::transparent);
0341 
0342     QPainter painter(&pixmap);
0343     painter.setPen(Qt::NoPen);
0344 
0345     // edges
0346     TileSet slabTileSet = slab(color, shade, size);
0347     slabTileSet.render(QRect(0, 0, size * 4, size * 5), &painter, TileSet::Left | TileSet::Right | TileSet::Top);
0348 
0349     int fixedSize(28 * devicePixelRatio(pixmap));
0350     painter.setWindow(0, 0, fixedSize, fixedSize);
0351 
0352     // bottom
0353     QColor light = KColorUtils::shade(calcLightColor(color), shade);
0354     QLinearGradient fillGradient(0, -28, 0, 28);
0355     light.setAlphaF(0.4);
0356     fillGradient.setColorAt(0.0, light);
0357     light.setAlphaF(0.0);
0358     fillGradient.setColorAt(1.0, light);
0359     painter.setBrush(fillGradient);
0360     painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
0361     painter.drawRect(3, 9, 22, 17);
0362 
0363     // fade bottom
0364     QLinearGradient maskGradient(0, 7, 0, 28);
0365     maskGradient.setColorAt(0.0, Qt::black);
0366     maskGradient.setColorAt(1.0, Qt::transparent);
0367 
0368     painter.setBrush(maskGradient);
0369     painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
0370     painter.drawRect(0, 9, 28, 19);
0371 
0372     painter.end();
0373 
0374     TileSet tileSet(pixmap, size, size, size * 2, 2);
0375     _slopeCache.insert(key, new TileSet(tileSet));
0376     return tileSet;
0377 }
0378 
0379 //________________________________________________________________________________________________________
0380 void StyleHelper::drawInverseShadow(QPainter &painter, const QColor &color, int pad, int size, qreal fuzz) const
0381 {
0382     const qreal m(qreal(size) * 0.5);
0383     const qreal offset(0.8);
0384     const qreal k0((m - 2) / qreal(m + 2.0));
0385     QRadialGradient shadowGradient(pad + m, pad + m + offset, m + 2);
0386     for (int i = 0; i < 8; i++) {
0387         // sinusoidal gradient
0388         const qreal k1((qreal(8 - i) + k0 * qreal(i)) * 0.125);
0389         const qreal a((cos(3.14159 * i * 0.125) + 1.0) * 0.25);
0390         shadowGradient.setColorAt(k1, alphaColor(color, a * _shadowGain));
0391     }
0392     shadowGradient.setColorAt(k0, alphaColor(color, 0.0));
0393     painter.setBrush(shadowGradient);
0394     painter.drawEllipse(QRectF(pad - fuzz, pad - fuzz, size + fuzz * 2.0, size + fuzz * 2.0));
0395 }
0396 
0397 //________________________________________________________________________________________________________
0398 void StyleHelper::fillSlab(QPainter &painter, const QRect &rect, int size) const
0399 {
0400     const qreal s(qreal(size) * (3.6 + (0.5 * _slabThickness)) / 7.0);
0401     const QRectF r(QRectF(rect).adjusted(s, s, -s, -s));
0402     if (!r.isValid())
0403         return;
0404 
0405     painter.drawRoundedRect(r, s / 2, s / 2);
0406 }
0407 
0408 //________________________________________________________________________________________________________
0409 void StyleHelper::fillButtonSlab(QPainter &painter, const QRect &r, const QColor &color, bool sunken)
0410 {
0411     painter.save();
0412     painter.setRenderHint(QPainter::Antialiasing);
0413     painter.setPen(Qt::NoPen);
0414 
0415     if (sunken && calcShadowColor(color).value() > color.value()) {
0416         QLinearGradient innerGradient(0, r.top(), 0, r.bottom() + r.height());
0417         innerGradient.setColorAt(0.0, color);
0418         innerGradient.setColorAt(1.0, calcLightColor(color));
0419         painter.setBrush(innerGradient);
0420 
0421     } else if (sunken) {
0422         QLinearGradient innerGradient(0, r.top() - r.height(), 0, r.bottom());
0423         innerGradient.setColorAt(0.0, calcLightColor(color));
0424         innerGradient.setColorAt(1.0, color);
0425         painter.setBrush(innerGradient);
0426 
0427     } else {
0428         QLinearGradient innerGradient(0, r.top() - 0.2 * r.height(), 0, r.bottom() + 0.4 * r.height());
0429         innerGradient.setColorAt(0.0, calcLightColor(color));
0430         innerGradient.setColorAt(0.6, color);
0431         painter.setBrush(innerGradient);
0432     }
0433 
0434     fillSlab(painter, r);
0435     painter.restore();
0436 }
0437 
0438 //________________________________________________________________________________________________________
0439 TileSet StyleHelper::slab(const QColor &color, const QColor &glow, qreal shade, int size)
0440 {
0441     Oxygen::Cache<TileSet>::Value cache(_slabCache.get(color));
0442 
0443     const quint64 key((colorKey(glow) << 32) | (quint64(256.0 * shade) << 24) | size);
0444     if (TileSet *cachedTileSet = cache->object(key)) {
0445         return *cachedTileSet;
0446     }
0447 
0448     QPixmap pixmap(highDpiPixmap(size * 2));
0449     pixmap.fill(Qt::transparent);
0450 
0451     QPainter painter(&pixmap);
0452     painter.setRenderHints(QPainter::Antialiasing);
0453     painter.setPen(Qt::NoPen);
0454 
0455     const int fixedSize(14 * devicePixelRatio(pixmap));
0456     painter.setWindow(0, 0, fixedSize, fixedSize);
0457 
0458     // draw all components
0459     if (color.isValid())
0460         drawShadow(painter, calcShadowColor(color), 14);
0461     if (glow.isValid())
0462         drawOuterGlow(painter, glow, 14);
0463     if (color.isValid())
0464         drawSlab(painter, color, shade);
0465 
0466     painter.end();
0467 
0468     TileSet tileSet(pixmap, size, size, size, size, size - 1, size, 2, 1);
0469 
0470     cache->insert(key, new TileSet(tileSet));
0471     return tileSet;
0472 }
0473 
0474 //________________________________________________________________________________________________________
0475 TileSet StyleHelper::slabSunken(const QColor &color, int size)
0476 {
0477     const quint64 key(colorKey(color) << 32 | size);
0478     if (TileSet *cachedTileSet = _slabSunkenCache.object(key)) {
0479         return *cachedTileSet;
0480     }
0481 
0482     QPixmap pixmap(highDpiPixmap(size * 2));
0483     pixmap.fill(Qt::transparent);
0484 
0485     QPainter painter(&pixmap);
0486     painter.setRenderHints(QPainter::Antialiasing);
0487     painter.setPen(Qt::NoPen);
0488 
0489     const int fixedSize(14 * devicePixelRatio(pixmap));
0490     painter.setWindow(0, 0, fixedSize, fixedSize);
0491 
0492     // shadow
0493     painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
0494     drawInverseShadow(painter, calcShadowColor(color), 3, 8, 0.0);
0495 
0496     // contrast pixel
0497     {
0498         QColor light(calcLightColor(color));
0499         QLinearGradient blend(0, 2, 0, 16);
0500         blend.setColorAt(0.5, Qt::transparent);
0501         blend.setColorAt(1.0, light);
0502 
0503         painter.setBrush(Qt::NoBrush);
0504         painter.setPen(QPen(blend, 1));
0505         painter.drawRoundedRect(QRectF(2.5, 2.5, 9, 9), 4.0, 4.0);
0506         painter.setPen(Qt::NoPen);
0507     }
0508 
0509     painter.end();
0510 
0511     TileSet tileSet(pixmap, size, size, size, size, size - 1, size, 2, 1);
0512     _slabSunkenCache.insert(key, new TileSet(tileSet));
0513 
0514     return tileSet;
0515 }
0516 
0517 //__________________________________________________________________________________________________________
0518 TileSet StyleHelper::progressBarIndicator(const QPalette &pal, int dimension)
0519 {
0520     const QColor highlight(pal.color(QPalette::Highlight));
0521     const quint64 key((colorKey(highlight) << 32) | dimension);
0522 
0523     if (TileSet *cachedTileSet = _progressBarCache.object(key)) {
0524         return *cachedTileSet;
0525     }
0526 
0527     QRect local(0, 0, dimension, dimension);
0528 
0529     QPixmap pixmap(highDpiPixmap(local.size()));
0530     pixmap.fill(Qt::transparent);
0531 
0532     QPainter painter(&pixmap);
0533     painter.setRenderHints(QPainter::Antialiasing);
0534     painter.setBrush(Qt::NoBrush);
0535 
0536     const QColor lhighlight(calcLightColor(highlight));
0537     const QColor color(pal.color(QPalette::Active, QPalette::Window));
0538     const QColor light(calcLightColor(color));
0539     const QColor dark(calcDarkColor(color));
0540     const QColor shadow(calcShadowColor(color));
0541 
0542     // shadow
0543     {
0544         painter.setPen(QPen(alphaColor(shadow, 0.4), 0.6));
0545         painter.drawRoundedRect(QRectF(local).adjusted(0.5, 0.5, -0.5, 0.5), 3.0, 3.0);
0546     }
0547 
0548     // fill
0549     local.adjust(1, 1, -1, 0);
0550     {
0551         painter.setPen(Qt::NoPen);
0552         painter.setBrush(KColorUtils::mix(highlight, dark, 0.2));
0553         painter.drawRoundedRect(local, 2.5, 2.5);
0554     }
0555 
0556     // fake radial gradient
0557     {
0558         QPixmap pixmap(highDpiPixmap(local.size()));
0559         pixmap.fill(Qt::transparent);
0560         {
0561             QRect pixmapRect(QPoint(0, 0), local.size());
0562             QLinearGradient mask(pixmapRect.topLeft(), pixmapRect.topRight());
0563             mask.setColorAt(0.0, Qt::transparent);
0564             mask.setColorAt(0.4, Qt::black);
0565             mask.setColorAt(0.6, Qt::black);
0566             mask.setColorAt(1.0, Qt::transparent);
0567 
0568             QLinearGradient radial(pixmapRect.topLeft(), pixmapRect.bottomLeft());
0569             radial.setColorAt(0.0, KColorUtils::mix(lhighlight, light, 0.3));
0570             radial.setColorAt(0.5, Qt::transparent);
0571             radial.setColorAt(0.6, Qt::transparent);
0572             radial.setColorAt(1.0, KColorUtils::mix(lhighlight, light, 0.3));
0573 
0574             QPainter painter(&pixmap);
0575             painter.fillRect(pixmap.rect(), mask);
0576             painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
0577             painter.fillRect(pixmapRect, radial);
0578             painter.end();
0579         }
0580 
0581         painter.drawPixmap(QPoint(1, 1), pixmap);
0582     }
0583 
0584     // bevel
0585     {
0586         QLinearGradient bevel(QPointF(0, 0.5) + local.topLeft(), QPointF(0, -0.5) + local.bottomLeft());
0587         bevel.setColorAt(0, lhighlight);
0588         bevel.setColorAt(0.5, highlight);
0589         bevel.setColorAt(1, calcDarkColor(highlight));
0590         painter.setBrush(Qt::NoBrush);
0591         painter.setPen(QPen(bevel, 1));
0592         painter.drawRoundedRect(QRectF(local).adjusted(0.5, 0.5, -0.5, -0.5), 2.5, 2.5);
0593     }
0594 
0595     // bright top edge
0596     {
0597         QLinearGradient lightHl(local.topLeft(), local.topRight());
0598         lightHl.setColorAt(0, Qt::transparent);
0599         lightHl.setColorAt(0.5, KColorUtils::mix(highlight, light, 0.8));
0600         lightHl.setColorAt(1, Qt::transparent);
0601 
0602         painter.setPen(QPen(lightHl, 1));
0603         painter.drawLine(QPointF(0.5, 0.5) + local.topLeft(), QPointF(0.5, 0.5) + local.topRight());
0604     }
0605 
0606     painter.end();
0607 
0608     // generate tileSet and save in cache
0609     const int radius = qMin(3, dimension / 2);
0610     TileSet tileSet(pixmap, radius, radius, dimension - 2 * radius, dimension - 2 * radius);
0611     _progressBarCache.insert(key, new TileSet(tileSet));
0612 
0613     return tileSet;
0614 }
0615 
0616 //______________________________________________________________________________
0617 QPixmap StyleHelper::dialSlab(const QColor &color, const QColor &glow, qreal shade, int size)
0618 {
0619     Oxygen::Cache<QPixmap>::Value cache = _dialSlabCache.get(color);
0620 
0621     const quint64 key((colorKey(glow) << 32) | (quint64(256.0 * shade) << 24) | size);
0622     if (QPixmap *cachedPixmap = cache->object(key)) {
0623         return *cachedPixmap;
0624     }
0625 
0626     QPixmap pixmap(highDpiPixmap(size));
0627     pixmap.fill(Qt::transparent);
0628 
0629     QRectF rect(0, 0, size, size);
0630 
0631     QPainter painter(&pixmap);
0632     painter.setPen(Qt::NoPen);
0633     painter.setRenderHints(QPainter::Antialiasing);
0634 
0635     // colors
0636     const QColor base(KColorUtils::shade(color, shade));
0637     const QColor light(KColorUtils::shade(calcLightColor(color), shade));
0638     const QColor dark(KColorUtils::shade(calcDarkColor(color), shade));
0639     const QColor mid(KColorUtils::shade(calcMidColor(color), shade));
0640     const QColor shadow(calcShadowColor(color));
0641 
0642     // shadow
0643     drawShadow(painter, shadow, rect.width());
0644 
0645     if (glow.isValid()) {
0646         drawOuterGlow(painter, glow, rect.width());
0647     }
0648 
0649     const qreal baseOffset(3.5);
0650     {
0651         // plain background
0652         QLinearGradient linearGradient(0, baseOffset - 0.5 * rect.height(), 0, baseOffset + rect.height());
0653         linearGradient.setColorAt(0, light);
0654         linearGradient.setColorAt(0.8, base);
0655 
0656         painter.setBrush(linearGradient);
0657         const qreal offset(baseOffset);
0658         painter.drawEllipse(rect.adjusted(offset, offset, -offset, -offset));
0659     }
0660 
0661     {
0662         // outline circle
0663         const qreal penWidth(0.7);
0664         QLinearGradient linearGradient(0, baseOffset, 0, baseOffset + 2 * rect.height());
0665         linearGradient.setColorAt(0, light);
0666         linearGradient.setColorAt(1, mid);
0667         painter.setBrush(Qt::NoBrush);
0668         painter.setPen(QPen(linearGradient, penWidth));
0669         const qreal offset(baseOffset + 0.5 * penWidth);
0670         painter.drawEllipse(rect.adjusted(offset, offset, -offset, -offset));
0671     }
0672 
0673     cache->insert(key, new QPixmap(pixmap));
0674 
0675     return pixmap;
0676 }
0677 
0678 //__________________________________________________________________________________________________________
0679 QPixmap StyleHelper::roundSlab(const QColor &color, const QColor &glow, qreal shade, int size)
0680 {
0681     Oxygen::Cache<QPixmap>::Value cache(_roundSlabCache.get(color));
0682 
0683     const quint64 key((colorKey(glow) << 32) | (quint64(256.0 * shade) << 24) | size);
0684     if (QPixmap *cachedPixmap = cache->object(key)) {
0685         return *cachedPixmap;
0686     }
0687 
0688     QPixmap pixmap(highDpiPixmap(size * 3));
0689     pixmap.fill(Qt::transparent);
0690 
0691     QPainter painter(&pixmap);
0692     painter.setRenderHints(QPainter::Antialiasing);
0693     painter.setPen(Qt::NoPen);
0694 
0695     const int fixedSize(21 * devicePixelRatio(pixmap));
0696     painter.setWindow(0, 0, fixedSize, fixedSize);
0697 
0698     // draw normal shadow
0699     drawShadow(painter, calcShadowColor(color), 21);
0700 
0701     // draw glow.
0702     if (glow.isValid()) {
0703         drawOuterGlow(painter, glow, 21);
0704     }
0705 
0706     drawRoundSlab(painter, color, shade);
0707 
0708     painter.end();
0709     cache->insert(key, new QPixmap(pixmap));
0710     return pixmap;
0711 }
0712 
0713 //__________________________________________________________________________________________________________
0714 QPixmap StyleHelper::sliderSlab(const QColor &color, const QColor &glow, bool sunken, qreal shade, int size)
0715 {
0716     Oxygen::Cache<QPixmap>::Value cache(_sliderSlabCache.get(color));
0717 
0718     const quint64 key((colorKey(glow) << 32) | (quint64(256.0 * shade) << 24) | (sunken << 23) | size);
0719     if (QPixmap *cachedPixmap = cache->object(key)) {
0720         return *cachedPixmap;
0721     }
0722 
0723     QPixmap pixmap(highDpiPixmap(size * 3));
0724     pixmap.fill(Qt::transparent);
0725 
0726     QPainter painter(&pixmap);
0727     painter.setRenderHints(QPainter::Antialiasing);
0728     painter.setPen(Qt::NoPen);
0729 
0730     if (color.isValid())
0731         drawShadow(painter, alphaColor(calcShadowColor(color), 0.8), 21);
0732     if (glow.isValid())
0733         drawOuterGlow(painter, glow, 21);
0734 
0735     // draw slab
0736     drawSliderSlab(painter, color, sunken, shade);
0737 
0738     painter.end();
0739     cache->insert(key, new QPixmap(pixmap));
0740     return pixmap;
0741 }
0742 
0743 //______________________________________________________________________________
0744 void StyleHelper::renderDebugFrame(QPainter *painter, const QRect &rect) const
0745 {
0746     painter->save();
0747     painter->setRenderHints(QPainter::Antialiasing);
0748     painter->setBrush(Qt::NoBrush);
0749     painter->setPen(Qt::red);
0750     painter->drawRect(QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5));
0751     painter->restore();
0752 }
0753 
0754 //________________________________________________________________________________________________________
0755 void StyleHelper::fillHole(QPainter &painter, const QRect &rect, int offset) const
0756 {
0757     painter.drawRoundedRect(rect.adjusted(offset, offset, -offset, -offset), 4 - offset, 4 - offset);
0758 }
0759 
0760 //____________________________________________________________________________________
0761 void StyleHelper::renderHole(QPainter *painter,
0762                              const QColor &base,
0763                              const QRect &rect,
0764                              StyleOptions options,
0765                              qreal opacity,
0766                              Oxygen::AnimationMode mode,
0767                              TileSet::Tiles tiles)
0768 {
0769     if (!rect.isValid())
0770         return;
0771     const QColor glow(frameGlowColor(QPalette::Active, options, opacity, mode));
0772     hole(base, glow, TileSet::DefaultSize, options).render(rect, painter, tiles);
0773 }
0774 
0775 //________________________________________________________________________________________________________
0776 TileSet StyleHelper::holeFlat(const QColor &color, qreal shade, bool fill, int size)
0777 {
0778     const quint64 key((colorKey(color) << 32) | (quint64(256.0 * shade) << 24) | size << 1 | fill);
0779     if (TileSet *cachedTileSet = _holeFlatCache.object(key)) {
0780         return *cachedTileSet;
0781     }
0782 
0783     QPixmap pixmap(highDpiPixmap(size * 2));
0784     pixmap.fill(Qt::transparent);
0785 
0786     QPainter painter(&pixmap);
0787     painter.setRenderHints(QPainter::Antialiasing);
0788     painter.setPen(Qt::NoPen);
0789 
0790     const int fixedSize(14 * devicePixelRatio(pixmap));
0791     painter.setWindow(0, 0, fixedSize, fixedSize);
0792 
0793     if (fill) {
0794         // hole inside
0795         painter.setBrush(color);
0796         painter.drawRoundedRect(QRectF(1, 0, 12, 13), 3.0, 3.0);
0797         painter.setBrush(Qt::NoBrush);
0798 
0799         {
0800             // shadow (top)
0801             const QColor dark(KColorUtils::shade(calcDarkColor(color), shade));
0802             QLinearGradient gradient(0, -2, 0, 14);
0803             gradient.setColorAt(0.0, dark);
0804             gradient.setColorAt(0.5, Qt::transparent);
0805 
0806             painter.setPen(QPen(gradient, 1));
0807             painter.drawRoundedRect(QRectF(1.5, 0.5, 11, 12), 2.5, 2.5);
0808         }
0809 
0810         {
0811             // contrast (bottom)
0812             const QColor light(KColorUtils::shade(calcLightColor(color), shade));
0813             QLinearGradient gradient(0, 0, 0, 18);
0814             gradient.setColorAt(0.5, Qt::transparent);
0815             gradient.setColorAt(1.0, light);
0816 
0817             painter.setPen(QPen(gradient, 1));
0818             painter.drawRoundedRect(QRectF(0.5, 0.5, 13, 13), 3.5, 3.5);
0819         }
0820 
0821     } else {
0822         // hole inside
0823         painter.setBrush(color);
0824         painter.drawRoundedRect(QRectF(2, 2, 10, 10), 3.0, 3.0);
0825         painter.setBrush(Qt::NoBrush);
0826 
0827         {
0828             // shadow (top)
0829             const QColor dark(KColorUtils::shade(calcDarkColor(color), shade));
0830             QLinearGradient gradient(0, 1, 0, 12);
0831             gradient.setColorAt(0.0, dark);
0832             gradient.setColorAt(0.5, Qt::transparent);
0833 
0834             painter.setPen(QPen(gradient, 1));
0835             painter.drawRoundedRect(QRectF(2.5, 2.5, 10, 10), 2.5, 2.5);
0836         }
0837 
0838         {
0839             // contrast (bottom)
0840             const QColor light(KColorUtils::shade(calcLightColor(color), shade));
0841             QLinearGradient gradient(0, 1, 0, 12);
0842             gradient.setColorAt(0.5, Qt::transparent);
0843             gradient.setColorAt(1.0, light);
0844 
0845             painter.setPen(QPen(gradient, 1));
0846             painter.drawRoundedRect(QRectF(2, 1.5, 10, 11), 3.0, 2.5);
0847         }
0848     }
0849 
0850     painter.end();
0851 
0852     TileSet tileSet(pixmap, size, size, size, size, size - 1, size, 2, 1);
0853 
0854     _holeFlatCache.insert(key, new TileSet(tileSet));
0855 
0856     return tileSet;
0857 }
0858 
0859 //______________________________________________________________________________
0860 TileSet StyleHelper::scrollHole(const QColor &color, Qt::Orientation orientation, bool smallShadow)
0861 {
0862     const quint64 key(colorKey(color) << 32 | (orientation == Qt::Horizontal ? 2 : 0) | (smallShadow ? 1 : 0));
0863     if (TileSet *cachedTileSet = _scrollHoleCache.object(key)) {
0864         return *cachedTileSet;
0865     }
0866 
0867     QPixmap pixmap(highDpiPixmap(15));
0868     pixmap.fill(Qt::transparent);
0869 
0870     QPainter painter(&pixmap);
0871 
0872     const QColor dark(calcDarkColor(color));
0873     const QColor light(calcLightColor(color));
0874     const QColor shadow(calcShadowColor(color));
0875 
0876     // use space for white border
0877     const QRect pixmapRect(0, 0, 15, 15);
0878     const QRect rect(pixmapRect.adjusted(1, 1, -1, -1));
0879 
0880     painter.setRenderHints(QPainter::Antialiasing);
0881     painter.setBrush(dark);
0882     painter.setPen(Qt::NoPen);
0883 
0884     // base
0885     const qreal radius(smallShadow ? 2.5 : 3.0);
0886     painter.drawRoundedRect(rect, radius, radius);
0887 
0888     {
0889         // slight shadow across the whole hole
0890         QLinearGradient shadowGradient(rect.topLeft(), orientation == Qt::Horizontal ? rect.bottomLeft() : rect.topRight());
0891 
0892         shadowGradient.setColorAt(0.0, alphaColor(shadow, 0.1));
0893         shadowGradient.setColorAt(0.6, Qt::transparent);
0894         painter.setBrush(shadowGradient);
0895         painter.drawRoundedRect(rect, radius, radius);
0896     }
0897 
0898     // first create shadow
0899     int shadowSize(5);
0900     QPixmap shadowPixmap(highDpiPixmap(shadowSize * 2));
0901 
0902     {
0903         shadowPixmap.fill(Qt::transparent);
0904 
0905         QPainter painter(&shadowPixmap);
0906         painter.setRenderHints(QPainter::Antialiasing);
0907         painter.setPen(Qt::NoPen);
0908 
0909         // fade-in shadow
0910         QColor shadowColor(calcShadowColor(color));
0911         if (smallShadow)
0912             shadowColor = alphaColor(shadowColor, 0.6);
0913         drawInverseShadow(painter, shadowColor, 1, 8, 0.0);
0914 
0915         painter.end();
0916     }
0917 
0918     // render shadow
0919     TileSet(shadowPixmap, shadowSize, shadowSize, shadowSize, shadowSize, shadowSize - 1, shadowSize, 2, 1)
0920         .render(rect.adjusted(-1, -1, 1, 1), &painter, TileSet::Full);
0921 
0922     // light border
0923     QLinearGradient borderGradient(0, pixmapRect.top(), 0, pixmapRect.bottom());
0924     if (smallShadow && orientation == Qt::Vertical) {
0925         borderGradient.setColorAt(0.8, Qt::transparent);
0926         borderGradient.setColorAt(1.0, alphaColor(light, 0.5));
0927 
0928     } else {
0929         borderGradient.setColorAt(0.5, Qt::transparent);
0930         borderGradient.setColorAt(1.0, alphaColor(light, 0.6));
0931     }
0932 
0933     painter.setPen(QPen(borderGradient, 1.0));
0934     painter.setBrush(Qt::NoBrush);
0935     painter.drawRoundedRect(QRectF(pixmapRect).adjusted(0.5, 0.5, -0.5, -0.5), radius + 0.5, radius + 0.5);
0936 
0937     painter.end();
0938     TileSet tileSet(pixmap, 7, 7, 1, 1);
0939 
0940     _scrollHoleCache.insert(key, new TileSet(tileSet));
0941     return tileSet;
0942 }
0943 
0944 //________________________________________________________________________________________________________
0945 TileSet StyleHelper::scrollHandle(const QColor &color, const QColor &glow, int size)
0946 {
0947     // get key
0948     Oxygen::Cache<TileSet>::Value cache(_scrollHandleCache.get(glow));
0949 
0950     const quint64 key((colorKey(color) << 32) | size);
0951     if (TileSet *cachedTileSet = cache->object(key)) {
0952         return *cachedTileSet;
0953     }
0954 
0955     QPixmap pixmap(highDpiPixmap(2 * size));
0956     pixmap.fill(Qt::transparent);
0957 
0958     QPainter painter(&pixmap);
0959     painter.setRenderHints(QPainter::Antialiasing);
0960     painter.setPen(Qt::NoPen);
0961 
0962     const int fixedSize(14 * devicePixelRatio(pixmap));
0963     painter.setWindow(0, 0, fixedSize, fixedSize);
0964 
0965     QPixmap shadowPixmap(highDpiPixmap(10));
0966     {
0967         shadowPixmap.fill(Qt::transparent);
0968 
0969         QPainter painter(&shadowPixmap);
0970         painter.setRenderHints(QPainter::Antialiasing);
0971         painter.setPen(Qt::NoPen);
0972 
0973         // shadow/glow
0974         drawOuterGlow(painter, glow, 10);
0975 
0976         painter.end();
0977     }
0978 
0979     TileSet(shadowPixmap, 4, 4, 1, 1).render(QRect(0, 0, 14, 14), &painter, TileSet::Full);
0980 
0981     // outline
0982     {
0983         const QColor mid(calcMidColor(color));
0984         QLinearGradient linearGradient(0, 3, 0, 11);
0985         linearGradient.setColorAt(0, color);
0986         linearGradient.setColorAt(1, mid);
0987         painter.setPen(Qt::NoPen);
0988         painter.setBrush(linearGradient);
0989         painter.drawRoundedRect(QRectF(3, 3, 8, 8), 2.5, 2.5);
0990     }
0991 
0992     // contrast
0993     {
0994         const QColor light(calcLightColor(color));
0995         QLinearGradient linearGradient(0, 3, 0, 11);
0996         linearGradient.setColorAt(0., alphaColor(light, 0.9));
0997         linearGradient.setColorAt(0.5, alphaColor(light, 0.44));
0998         painter.setBrush(linearGradient);
0999         painter.drawRoundedRect(QRectF(3, 3, 8, 8), 2.5, 2.5);
1000     }
1001 
1002     painter.end();
1003 
1004     // create tileset and return
1005     TileSet tileSet(pixmap, size - 1, size, 1, 1);
1006     cache->insert(key, new TileSet(tileSet));
1007 
1008     return tileSet;
1009 }
1010 
1011 //________________________________________________________________________________________________________
1012 TileSet StyleHelper::slitFocused(const QColor &glow)
1013 {
1014     const quint64 key((colorKey(glow) << 32));
1015     if (TileSet *cachedTileSet = _slitCache.object(key)) {
1016         return *cachedTileSet;
1017     }
1018 
1019     QPixmap pixmap(highDpiPixmap(9));
1020     pixmap.fill(Qt::transparent);
1021 
1022     QPainter painter(&pixmap);
1023     painter.setRenderHints(QPainter::Antialiasing);
1024     painter.setPen(glow);
1025     painter.drawRoundedRect(QRectF(1.5, 1.5, 6, 6), 2.5, 2.5);
1026     painter.end();
1027 
1028     TileSet tileSet(pixmap, 4, 4, 1, 1);
1029     _slitCache.insert(key, new TileSet(tileSet));
1030     return tileSet;
1031 }
1032 
1033 //____________________________________________________________________
1034 TileSet StyleHelper::dockFrame(const QColor &top, const QColor &bottom)
1035 {
1036     const quint64 key(colorKey(top) << 32 | colorKey(bottom));
1037     if (TileSet *cachedTileSet = _dockFrameCache.object(key)) {
1038         return *cachedTileSet;
1039     }
1040 
1041     int size(13);
1042     QPixmap pm(size, size);
1043     pm.fill(Qt::transparent);
1044 
1045     QPainter painter(&pm);
1046     painter.setRenderHints(QPainter::Antialiasing);
1047     painter.setBrush(Qt::NoBrush);
1048 
1049     const QColor lightTop = alphaColor(calcLightColor(top), 0.5);
1050     const QColor lightBottom = alphaColor(calcLightColor(bottom), 0.5);
1051     const QColor darkTop = alphaColor(calcDarkColor(top), 0.6);
1052     const QColor darkBottom = alphaColor(calcDarkColor(bottom), 0.6);
1053 
1054     // dark frame
1055     {
1056         QLinearGradient linearGradient(0, 0.5, 0, size - 1.5);
1057         linearGradient.setColorAt(0.0, darkTop);
1058         linearGradient.setColorAt(1.0, darkBottom);
1059 
1060         painter.setPen(QPen(linearGradient, 1));
1061         painter.drawRoundedRect(QRectF(1.5, 0.5, size - 3, size - 2), 4, 4);
1062     }
1063 
1064     // bottom contrast
1065     {
1066         QLinearGradient linearGradient(0, 0.5, 0, size - 0.5);
1067         linearGradient.setColorAt(0.0, Qt::transparent);
1068         linearGradient.setColorAt(1.0, lightBottom);
1069         painter.setPen(QPen(linearGradient, 1.0));
1070         painter.drawRoundedRect(QRectF(0.5, 0.5, size - 1, size - 1), 4.5, 4.5);
1071     }
1072 
1073     // top contrast
1074     {
1075         QLinearGradient linearGradient(0, 1.5, 0, size - 2.5);
1076         linearGradient.setColorAt(0.0, lightTop);
1077         linearGradient.setColorAt(1.0, Qt::transparent);
1078         painter.setPen(QPen(linearGradient, 1.0));
1079         painter.drawRoundedRect(QRectF(2.5, 1.5, size - 5, size - 4), 3.5, 3.5);
1080     }
1081 
1082     painter.end();
1083     TileSet tileSet(pm, (size - 1) / 2, (size - 1) / 2, 1, 1);
1084 
1085     _dockFrameCache.insert(key, new TileSet(tileSet));
1086     return tileSet;
1087 }
1088 
1089 //____________________________________________________________________
1090 TileSet StyleHelper::selection(const QColor &color, int height, bool custom)
1091 {
1092     const quint64 key((colorKey(color) << 32) | (height << 1) | custom);
1093     if (TileSet *cachedTileSet = _selectionCache.object(key)) {
1094         return *cachedTileSet;
1095     }
1096 
1097     const qreal rounding(2.5);
1098 
1099     QPixmap pixmap(highDpiPixmap(32 + 16, height));
1100     pixmap.fill(Qt::transparent);
1101 
1102     QRectF r(0, 0, 32 + 16, height);
1103 
1104     QPainter painter(&pixmap);
1105     painter.setRenderHint(QPainter::Antialiasing);
1106 
1107     // items with custom background brushes always have their background drawn
1108     // regardless of whether they are hovered or selected or neither so
1109     // the gradient effect needs to be more subtle
1110 
1111     {
1112         // fill
1113         const int lightenAmount(custom ? 110 : 130);
1114         QLinearGradient gradient(0, 0, 0, r.bottom());
1115         gradient.setColorAt(0, color.lighter(lightenAmount));
1116         gradient.setColorAt(1, color);
1117 
1118         painter.setPen(Qt::NoPen);
1119         painter.setBrush(gradient);
1120         painter.drawRoundedRect(r, rounding + 0.5, rounding + 0.5);
1121     }
1122 
1123     {
1124         // contrast
1125         QLinearGradient gradient(0, 0, 0, r.bottom());
1126         gradient.setColorAt(0, color);
1127         gradient.setColorAt(1, Qt::transparent);
1128 
1129         r.adjust(0.5, 0.5, -0.5, -0.5);
1130         painter.setPen(QPen(color, 1));
1131         painter.setBrush(Qt::NoBrush);
1132         painter.drawRoundedRect(r, rounding, rounding);
1133     }
1134 
1135     TileSet tileSet(pixmap, 8, 0, 32, height);
1136     _selectionCache.insert(key, new TileSet(tileSet));
1137     return tileSet;
1138 }
1139 
1140 //________________________________________________________________________________________________________
1141 void StyleHelper::drawInverseGlow(QPainter &painter, const QColor &color, int pad, int size, int rsize) const
1142 {
1143     const QRectF r(pad, pad, size, size);
1144     const qreal m(qreal(size) * 0.5);
1145 
1146     const qreal width(3.5);
1147     const qreal bias(_glowBias * 7.0 / rsize);
1148     const qreal k0((m - width) / (m - bias));
1149     QRadialGradient glowGradient(pad + m, pad + m, m - bias);
1150     for (int i = 0; i < 8; i++) {
1151         // inverse parabolic gradient
1152         qreal k1 = (k0 * qreal(i) + qreal(8 - i)) * 0.125;
1153         qreal a = 1.0 - sqrt(i * 0.125);
1154         glowGradient.setColorAt(k1, alphaColor(color, a));
1155     }
1156 
1157     glowGradient.setColorAt(k0, alphaColor(color, 0.0));
1158     painter.setBrush(glowGradient);
1159     painter.drawEllipse(r);
1160 }
1161 
1162 //________________________________________________________________________________________________________
1163 bool StyleHelper::compositingActive(void) const
1164 {
1165 #if OXYGEN_HAVE_X11
1166     if (isX11()) {
1167         return QX11Info::isCompositingManagerRunning(QX11Info::appScreen());
1168     }
1169 #endif
1170     return true;
1171 }
1172 
1173 //________________________________________________________________________________________________________
1174 bool StyleHelper::hasDecoration(const QWidget *widget) const
1175 {
1176     if (!widget->isTopLevel())
1177         return false;
1178     if (widget->windowFlags() & (Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint)) {
1179         return false;
1180     }
1181     return true;
1182 }
1183 
1184 //________________________________________________________________________________________________________
1185 TileSet StyleHelper::hole(const QColor &color, const QColor &glow, int size, StyleOptions options)
1186 {
1187     // get key
1188     Oxygen::Cache<TileSet>::Value cache(_holeCache.get(glow));
1189 
1190     const quint64 key((colorKey(color) << 32) | (size << 4) | options);
1191     if (TileSet *cachedTileSet = cache->object(key)) {
1192         return *cachedTileSet;
1193     }
1194 
1195     // first create shadow
1196     const int shadowSize((size * 5) / 7);
1197     QPixmap shadowPixmap(highDpiPixmap(shadowSize * 2));
1198 
1199     // calc alpha channel and fade
1200     const int alpha(glow.isValid() ? glow.alpha() : 0);
1201 
1202     {
1203         shadowPixmap.fill(Qt::transparent);
1204 
1205         QPainter painter(&shadowPixmap);
1206         painter.setRenderHints(QPainter::Antialiasing);
1207         painter.setPen(Qt::NoPen);
1208         const int fixedSize(10 * devicePixelRatio(shadowPixmap));
1209         painter.setWindow(0, 0, fixedSize, fixedSize);
1210 
1211         // fade-in shadow
1212         if (alpha < 255) {
1213             QColor shadowColor(calcShadowColor(color));
1214             shadowColor.setAlpha(255 - alpha);
1215             drawInverseShadow(painter, shadowColor, 1, 8, 0.0);
1216         }
1217 
1218         // fade-out glow
1219         if (alpha > 0) {
1220             drawInverseGlow(painter, glow, 1, 8, shadowSize);
1221         }
1222 
1223         painter.end();
1224     }
1225 
1226     // create pixmap
1227     QPixmap pixmap(highDpiPixmap(size * 2));
1228     pixmap.fill(Qt::transparent);
1229 
1230     QPainter painter(&pixmap);
1231     painter.setRenderHints(QPainter::Antialiasing);
1232     painter.setPen(Qt::NoPen);
1233     const int fixedSize(14 * devicePixelRatio(pixmap));
1234     painter.setWindow(0, 0, fixedSize, fixedSize);
1235 
1236     // hole mask
1237     painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
1238     painter.setBrush(Qt::black);
1239 
1240     painter.drawRoundedRect(QRectF(1, 1, 12, 12), 2.5, 2.5);
1241     painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
1242 
1243     // render shadow
1244     TileSet(shadowPixmap, shadowSize, shadowSize, shadowSize, shadowSize, shadowSize - 1, shadowSize, 2, 1)
1245         .render(QRect(QPoint(0, 0), pixmap.size() / devicePixelRatio(pixmap)), &painter);
1246 
1247     if ((options & HoleOutline) && alpha < 255) {
1248         QColor dark(calcDarkColor(color));
1249         dark.setAlpha(255 - alpha);
1250         QLinearGradient blend(0, 0, 0, 14);
1251         blend.setColorAt(0, Qt::transparent);
1252         blend.setColorAt(0.8, dark);
1253 
1254         painter.setBrush(Qt::NoBrush);
1255         painter.setPen(QPen(blend, 1));
1256         painter.drawRoundedRect(QRectF(1.5, 1.5, 11, 11), 3.0, 3.0);
1257         painter.setPen(Qt::NoPen);
1258     }
1259 
1260     if (options & HoleContrast) {
1261         QColor light(calcLightColor(color));
1262         QLinearGradient blend(0, 0, 0, 18);
1263         blend.setColorAt(0.5, Qt::transparent);
1264         blend.setColorAt(1.0, light);
1265 
1266         painter.setBrush(Qt::NoBrush);
1267         painter.setPen(QPen(blend, 1));
1268         painter.drawRoundedRect(QRectF(0.5, 0.5, 13, 13), 4.0, 4.0);
1269         painter.setPen(Qt::NoPen);
1270     }
1271 
1272     painter.end();
1273 
1274     // create tileset and return
1275     TileSet tileSet(pixmap, size, size, size, size, size - 1, size, 2, 1);
1276     cache->insert(key, new TileSet(tileSet));
1277     return tileSet;
1278 }
1279 
1280 //______________________________________________________________________________________
1281 void StyleHelper::drawSlab(QPainter &painter, const QColor &color, qreal shade)
1282 {
1283     const QColor light(KColorUtils::shade(calcLightColor(color), shade));
1284     const QColor base(alphaColor(light, 0.85));
1285     const QColor dark(KColorUtils::shade(calcDarkColor(color), shade));
1286 
1287     // bevel, part 1
1288     painter.save();
1289     const qreal y(KColorUtils::luma(base));
1290     const qreal yl(KColorUtils::luma(light));
1291     const qreal yd(KColorUtils::luma(dark));
1292     QLinearGradient bevelGradient1(0, 7, 0, 11);
1293     bevelGradient1.setColorAt(0.0, light);
1294     if (y < yl && y > yd) {
1295         // no middle when color is very light/dark
1296         bevelGradient1.setColorAt(0.5, base);
1297     }
1298 
1299     bevelGradient1.setColorAt(0.9, base);
1300     painter.setBrush(bevelGradient1);
1301     painter.drawRoundedRect(QRectF(3.0, 3.0, 8.0, 8.0), 3.5, 3.5);
1302 
1303     // bevel, part 2
1304     if (_slabThickness > 0.0) {
1305         QLinearGradient bevelGradient2(0, 6, 0, 19);
1306         bevelGradient2.setColorAt(0.0, light);
1307         bevelGradient2.setColorAt(0.9, base);
1308         painter.setBrush(bevelGradient2);
1309         painter.drawEllipse(QRectF(3.6, 3.6, 6.8, 6.8));
1310     }
1311 
1312     // inside mask
1313     painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
1314     painter.setBrush(Qt::black);
1315 
1316     const qreal ic(3.6 + 0.5 * _slabThickness);
1317     const qreal is(14.0 - 2.0 * ic);
1318     painter.drawEllipse(QRectF(ic, ic, is, is));
1319     painter.restore();
1320 }
1321 
1322 //__________________________________________________________________________________________________________
1323 void StyleHelper::drawRoundSlab(QPainter &painter, const QColor &color, qreal shade)
1324 {
1325     painter.save();
1326 
1327     // colors
1328     const QColor base(KColorUtils::shade(color, shade));
1329     const QColor light(KColorUtils::shade(calcLightColor(color), shade));
1330 
1331     // bevel, part 1
1332     QLinearGradient bevelGradient1(0, 10, 0, 18);
1333     bevelGradient1.setColorAt(0.0, light);
1334     bevelGradient1.setColorAt(0.9, alphaColor(light, 0.85));
1335     painter.setBrush(bevelGradient1);
1336     painter.drawEllipse(QRectF(3.0, 3.0, 15.0, 15.0));
1337 
1338     // bevel, part 2
1339     if (_slabThickness > 0.0) {
1340         QLinearGradient bevelGradient2(0, 7, 0, 28);
1341         bevelGradient2.setColorAt(0.0, light);
1342         bevelGradient2.setColorAt(0.9, base);
1343         painter.setBrush(bevelGradient2);
1344         painter.drawEllipse(QRectF(3.6, 3.6, 13.8, 13.8));
1345     }
1346 
1347     // inside
1348     QLinearGradient innerGradient(0, -17, 0, 20);
1349     innerGradient.setColorAt(0, light);
1350     innerGradient.setColorAt(1, base);
1351     painter.setBrush(innerGradient);
1352     const qreal ic(3.6 + _slabThickness);
1353     const qreal is(21.0 - 2.0 * ic);
1354     painter.drawEllipse(QRectF(ic, ic, is, is));
1355 
1356     painter.restore();
1357 }
1358 
1359 //__________________________________________________________________________________________________________
1360 void StyleHelper::drawSliderSlab(QPainter &painter, const QColor &color, bool sunken, qreal shade)
1361 {
1362     painter.save();
1363 
1364     const QColor light(KColorUtils::shade(calcLightColor(color), shade));
1365     const QColor dark(KColorUtils::shade(calcDarkColor(color), shade));
1366 
1367     painter.setPen(Qt::NoPen);
1368 
1369     {
1370         // plain background
1371         QLinearGradient linearGradient(0, 3, 0, 21);
1372         linearGradient.setColorAt(0, light);
1373         linearGradient.setColorAt(1, dark);
1374 
1375         const QRectF r(3, 3, 15, 15);
1376         painter.setBrush(linearGradient);
1377         painter.drawEllipse(r);
1378     }
1379 
1380     if (sunken) {
1381         // plain background
1382         QLinearGradient linearGradient(0, 3, 0, 21);
1383         linearGradient.setColorAt(0, dark);
1384         linearGradient.setColorAt(1, light);
1385 
1386         const QRectF r(5, 5, 11, 11);
1387         painter.setBrush(linearGradient);
1388         painter.drawEllipse(r);
1389     }
1390 
1391     {
1392         // outline circle
1393         const qreal penWidth(1);
1394         QLinearGradient linearGradient(0, 3, 0, 30);
1395         linearGradient.setColorAt(0, light);
1396         linearGradient.setColorAt(1, dark);
1397 
1398         const QRectF r(3.5, 3.5, 14, 14);
1399         painter.setPen(QPen(linearGradient, penWidth));
1400         painter.setBrush(Qt::NoBrush);
1401         painter.drawEllipse(r);
1402     }
1403 
1404     painter.restore();
1405 }
1406 
1407 //______________________________________________________________________________
1408 void StyleHelper::init(void)
1409 {
1410     _useBackgroundGradient = true;
1411 
1412 #if OXYGEN_HAVE_X11
1413     if (isX11()) {
1414         // create compositing screen
1415         const QString atomName(QStringLiteral("_NET_WM_CM_S%1").arg(QX11Info::appScreen()));
1416         _compositingManagerAtom = createAtom(atomName);
1417     }
1418 #endif
1419 }
1420 }