File indexing completed on 2024-05-19 13:22:13

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