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

0001 /*
0002     SPDX-FileCopyrightText: 2009-2010 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     SPDX-FileCopyrightText: 2007 Fredrik H ?glund <fredrik@kde.org>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-only
0009 */
0010 
0011 #include "oxygenhelper.h"
0012 
0013 #include <KColorScheme>
0014 #include <KColorUtils>
0015 #include <KWindowSystem>
0016 
0017 #include <QApplication>
0018 #include <QPainter>
0019 #include <QTextStream>
0020 #include <math.h>
0021 
0022 #if OXYGEN_HAVE_X11
0023 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0024 #include <QX11Info>
0025 #else
0026 #include <private/qtx11extras_p.h>
0027 #endif
0028 #endif
0029 
0030 namespace Oxygen
0031 {
0032 const qreal Helper::_slabThickness = 0.45;
0033 const qreal Helper::_shadowGain = 1.5;
0034 const qreal Helper::_glowBias = 0.6;
0035 
0036 //____________________________________________________________________
0037 Helper::Helper(KSharedConfig::Ptr config)
0038     : _config(config)
0039 {
0040     init();
0041 }
0042 
0043 //____________________________________________________________________
0044 KSharedConfig::Ptr Helper::config() const
0045 {
0046     return _config;
0047 }
0048 
0049 //____________________________________________________________________
0050 void Helper::loadConfig()
0051 {
0052     _contrast = KColorScheme::contrastF(_config);
0053 
0054     _bgcontrast = qMin(1.0, 0.9 * _contrast / 0.7);
0055 
0056     _viewFocusBrush = KStatefulBrush(KColorScheme::View, KColorScheme::FocusColor, _config);
0057     _viewHoverBrush = KStatefulBrush(KColorScheme::View, KColorScheme::HoverColor, _config);
0058     _viewNegativeTextBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NegativeText, _config);
0059 }
0060 
0061 //____________________________________________________________________
0062 void Helper::invalidateCaches()
0063 {
0064     _decoColorCache.clear();
0065     _lightColorCache.clear();
0066     _darkColorCache.clear();
0067     _shadowColorCache.clear();
0068     _backgroundTopColorCache.clear();
0069     _backgroundBottomColorCache.clear();
0070     _backgroundRadialColorCache.clear();
0071     _backgroundColorCache.clear();
0072     _backgroundCache.clear();
0073     _dotCache.clear();
0074 }
0075 
0076 //____________________________________________________________________
0077 void Helper::setMaxCacheSize(int value)
0078 {
0079     // assign value
0080     _backgroundCache.setMaxCost(value);
0081     _dotCache.setMaxCost(value);
0082 
0083     /* note: we do not limit the size of the color caches on purpose, since they should be small anyway */
0084 }
0085 
0086 //____________________________________________________________________
0087 void Helper::renderWindowBackground(QPainter *p, const QRect &clipRect, const QRect &windowRect, const QColor &color, int yShift)
0088 {
0089     if (clipRect.isValid()) {
0090         p->save();
0091         p->setClipRegion(clipRect, Qt::IntersectClip);
0092     }
0093 
0094     // draw upper linear gradient
0095     const int splitY(qMin(300, (3 * windowRect.height()) / 4));
0096 
0097     QRect upperRect = windowRect;
0098     if (splitY + yShift > 0) {
0099         upperRect.setHeight(splitY + yShift);
0100         QPixmap tile(verticalGradient(color, splitY + yShift, yShift));
0101         p->drawTiledPixmap(upperRect, tile);
0102     }
0103 
0104     // draw lower flat part
0105     const QRect lowerRect = windowRect.adjusted(0, splitY + yShift, 0, 0);
0106     if (lowerRect.isValid()) {
0107         p->fillRect(lowerRect, backgroundBottomColor(color));
0108     }
0109 
0110     // draw upper radial gradient
0111     const int radialW(qMin(600, windowRect.width()));
0112     const QRect radialRect((windowRect.width() - radialW) / 2 + windowRect.x(), windowRect.y(), radialW, 64 + yShift);
0113     if (clipRect.intersects(radialRect)) {
0114         QPixmap tile = radialGradient(color, radialW, 64 + yShift);
0115         p->drawPixmap(radialRect, tile);
0116     }
0117 
0118     if (clipRect.isValid()) {
0119         p->restore();
0120     }
0121 }
0122 
0123 //____________________________________________________________________
0124 void Helper::renderWindowBackground(QPainter *p, const QRect &clipRect, const QWidget *widget, const QWidget *window, const QColor &color, int yShift)
0125 {
0126     // get coordinates relative to the client area
0127     // this is stupid. One could use mapTo if this was taking const QWidget* and not
0128     // QWidget* as argument.
0129     const QWidget *w(widget);
0130     int x(0);
0131     int y(0);
0132 
0133     while (w != window && !w->isWindow() && w != w->parentWidget()) {
0134         x += w->geometry().x();
0135         y += w->geometry().y();
0136         w = w->parentWidget();
0137     }
0138 
0139     // translate and call the base method
0140     const QRect r = window->rect().translated(-x, -y);
0141     renderWindowBackground(p, clipRect, r, color, yShift);
0142 }
0143 
0144 //_____________________________________________________________
0145 void Helper::renderDot(QPainter *p, const QPoint &point, const QColor &baseColor)
0146 {
0147     const quint64 key(colorKey(baseColor));
0148     QPixmap pixmap;
0149 
0150     if (QPixmap *cachedPixmap = _dotCache.object(key)) {
0151         pixmap = *cachedPixmap;
0152 
0153     } else {
0154         pixmap = highDpiPixmap(4);
0155         pixmap.fill(Qt::transparent);
0156         const qreal diameter(1.8);
0157 
0158         QPainter painter(&pixmap);
0159         painter.setRenderHint(QPainter::Antialiasing);
0160         painter.setPen(Qt::NoPen);
0161 
0162         const QPoint center(QRect(0, 0, 4, 4).center());
0163 
0164         // light ellipse
0165         painter.setBrush(calcLightColor(baseColor));
0166         painter.drawEllipse(QRectF(center.x() - diameter / 2 + 1.0, center.y() - diameter / 2 + 1.0, diameter, diameter));
0167 
0168         // dark ellipse
0169         painter.setBrush(calcDarkColor(baseColor).darker(130));
0170         painter.drawEllipse(QRectF(center.x() - diameter / 2 + 0.5, center.y() - diameter / 2 + 0.5, diameter, diameter));
0171         painter.end();
0172 
0173         // store in cache
0174         _dotCache.insert(key, new QPixmap(pixmap));
0175     }
0176 
0177     p->save();
0178     p->translate(point - QPoint(1, 1));
0179     p->setRenderHint(QPainter::Antialiasing);
0180     p->drawPixmap(QPoint(0, 0), pixmap);
0181     p->restore();
0182 }
0183 
0184 //____________________________________________________________________
0185 bool Helper::lowThreshold(const QColor &color)
0186 {
0187     const quint32 key(colorKey(color));
0188     ColorMap::iterator iter(_lowThreshold.find(key));
0189     if (iter != _lowThreshold.end())
0190         return iter.value();
0191     else {
0192         const QColor darker(KColorScheme::shade(color, KColorScheme::MidShade, 0.5));
0193         const bool result(KColorUtils::luma(darker) > KColorUtils::luma(color));
0194         _lowThreshold.insert(key, result);
0195         return result;
0196     }
0197 }
0198 
0199 //____________________________________________________________________
0200 bool Helper::highThreshold(const QColor &color)
0201 {
0202     const quint32 key(colorKey(color));
0203     ColorMap::iterator iter(_highThreshold.find(key));
0204     if (iter != _highThreshold.end())
0205         return iter.value();
0206     else {
0207         const QColor lighter(KColorScheme::shade(color, KColorScheme::LightShade, 0.5));
0208         const bool result(KColorUtils::luma(lighter) < KColorUtils::luma(color));
0209         _highThreshold.insert(key, result);
0210         return result;
0211     }
0212 }
0213 
0214 //____________________________________________________________________
0215 QColor Helper::alphaColor(QColor color, qreal alpha)
0216 {
0217     if (alpha >= 0 && alpha < 1.0) {
0218         color.setAlphaF(alpha * color.alphaF());
0219     }
0220     return color;
0221 }
0222 
0223 //____________________________________________________________________
0224 QColor Helper::backgroundRadialColor(const QColor &color)
0225 {
0226     const quint64 key(colorKey(color));
0227     if (QColor *cachedColor = _backgroundRadialColorCache.object(key)) {
0228         return *cachedColor;
0229     }
0230 
0231     QColor out;
0232     if (lowThreshold(color))
0233         out = KColorScheme::shade(color, KColorScheme::LightShade, 0.0);
0234     else if (highThreshold(color))
0235         out = color;
0236     else
0237         out = KColorScheme::shade(color, KColorScheme::LightShade, _bgcontrast);
0238 
0239     _backgroundRadialColorCache.insert(key, new QColor(out));
0240 
0241     return out;
0242 }
0243 
0244 //_________________________________________________________________________
0245 QColor Helper::backgroundTopColor(const QColor &color)
0246 {
0247     const quint64 key(colorKey(color));
0248     if (QColor *cachedColor = _backgroundTopColorCache.object(key)) {
0249         return *cachedColor;
0250     }
0251 
0252     QColor out;
0253     if (lowThreshold(color))
0254         out = KColorScheme::shade(color, KColorScheme::MidlightShade, 0.0);
0255     else {
0256         const qreal my(KColorUtils::luma(KColorScheme::shade(color, KColorScheme::LightShade, 0.0)));
0257         const qreal by(KColorUtils::luma(color));
0258         out = KColorUtils::shade(color, (my - by) * _bgcontrast);
0259     }
0260 
0261     _backgroundTopColorCache.insert(key, new QColor(out));
0262 
0263     return out;
0264 }
0265 
0266 //_________________________________________________________________________
0267 QColor Helper::backgroundBottomColor(const QColor &color)
0268 {
0269     const quint64 key(colorKey(color));
0270     if (QColor *cachedColor = _backgroundBottomColorCache.object(key)) {
0271         return *cachedColor;
0272     }
0273 
0274     QColor out;
0275     const QColor midColor(KColorScheme::shade(color, KColorScheme::MidShade, 0.0));
0276     if (lowThreshold(color))
0277         out = midColor;
0278     else {
0279         const qreal by(KColorUtils::luma(color));
0280         const qreal my(KColorUtils::luma(midColor));
0281         out = KColorUtils::shade(color, (my - by) * _bgcontrast);
0282     }
0283 
0284     _backgroundBottomColorCache.insert(key, new QColor(out));
0285 
0286     return out;
0287 }
0288 
0289 //____________________________________________________________________
0290 QColor Helper::calcLightColor(const QColor &color)
0291 {
0292     const quint64 key(colorKey(color));
0293     if (QColor *cachedColor = _lightColorCache.object(key)) {
0294         return *cachedColor;
0295     }
0296 
0297     QColor out = highThreshold(color) ? color : KColorScheme::shade(color, KColorScheme::LightShade, _contrast);
0298     _lightColorCache.insert(key, new QColor(out));
0299 
0300     return out;
0301 }
0302 
0303 //____________________________________________________________________
0304 QColor Helper::calcDarkColor(const QColor &color)
0305 {
0306     const quint64 key(colorKey(color));
0307     if (QColor *cachedColor = _darkColorCache.object(key)) {
0308         return *cachedColor;
0309     }
0310 
0311     QColor out = (lowThreshold(color)) ? KColorUtils::mix(calcLightColor(color), color, 0.3 + 0.7 * _contrast)
0312                                        : KColorScheme::shade(color, KColorScheme::MidShade, _contrast);
0313     _darkColorCache.insert(key, new QColor(out));
0314 
0315     return out;
0316 }
0317 
0318 //____________________________________________________________________
0319 QColor Helper::calcShadowColor(const QColor &color)
0320 {
0321     const quint64 key(colorKey(color));
0322     if (QColor *cachedColor = _shadowColorCache.object(key)) {
0323         return *cachedColor;
0324     }
0325 
0326     QColor out = (lowThreshold(color)) ? KColorUtils::mix(Qt::black, color, color.alphaF())
0327                                        : KColorScheme::shade(KColorUtils::mix(Qt::black, color, color.alphaF()), KColorScheme::ShadowShade, _contrast);
0328 
0329     // make sure shadow color has the same alpha channel as the input
0330     out.setAlpha(color.alpha());
0331 
0332     // insert in cache
0333     _shadowColorCache.insert(key, new QColor(out));
0334 
0335     return out;
0336 }
0337 
0338 //____________________________________________________________________
0339 QColor Helper::backgroundColor(const QColor &color, qreal ratio)
0340 {
0341     const quint64 key((colorKey(color) << 32) | int(ratio * 512));
0342     if (QColor *cachedColor = _backgroundColorCache.object(key)) {
0343         return *cachedColor;
0344     }
0345 
0346     QColor out;
0347     if (ratio < 0.5) {
0348         const qreal a(2.0 * ratio);
0349         out = KColorUtils::mix(backgroundTopColor(color), color, a);
0350 
0351     } else {
0352         const qreal a(2.0 * ratio - 1);
0353         out = KColorUtils::mix(color, backgroundBottomColor(color), a);
0354     }
0355 
0356     _backgroundColorCache.insert(key, new QColor(out));
0357 
0358     return out;
0359 }
0360 
0361 //____________________________________________________________________
0362 QPixmap Helper::verticalGradient(const QColor &color, int height, int offset)
0363 {
0364     const quint64 key((colorKey(color) << 32) | height | 0x8000);
0365 
0366     if (QPixmap *cachedPixmap = _backgroundCache.object(key)) {
0367         return *cachedPixmap;
0368     }
0369 
0370     QPixmap pixmap(1, height);
0371     pixmap.fill(Qt::transparent);
0372 
0373     QLinearGradient gradient(0, offset, 0, height);
0374     gradient.setColorAt(0.0, backgroundTopColor(color));
0375     gradient.setColorAt(0.5, color);
0376     gradient.setColorAt(1.0, backgroundBottomColor(color));
0377 
0378     QPainter painter(&pixmap);
0379     painter.fillRect(pixmap.rect(), gradient);
0380 
0381     painter.end();
0382 
0383     _backgroundCache.insert(key, new QPixmap(pixmap));
0384     return pixmap;
0385 }
0386 
0387 //____________________________________________________________________
0388 QPixmap Helper::radialGradient(const QColor &color, int width, int height)
0389 {
0390     const quint64 key((colorKey(color) << 32) | width | 0xb000);
0391 
0392     if (QPixmap *cachedPixmap = _backgroundCache.object(key)) {
0393         return *cachedPixmap;
0394     }
0395 
0396     QPixmap pixmap(width, height);
0397     pixmap.fill(Qt::transparent);
0398 
0399     QRadialGradient gradient(64, height - 64, 64);
0400     QColor radialColor = backgroundRadialColor(color);
0401     radialColor.setAlpha(255);
0402     gradient.setColorAt(0, radialColor);
0403     radialColor.setAlpha(101);
0404     gradient.setColorAt(0.5, radialColor);
0405     radialColor.setAlpha(37);
0406     gradient.setColorAt(0.75, radialColor);
0407     radialColor.setAlpha(0);
0408     gradient.setColorAt(1, radialColor);
0409 
0410     QPainter painter(&pixmap);
0411     painter.setWindow(0, 0, 128, height);
0412     painter.fillRect(QRect(0, 0, 128, height), gradient);
0413 
0414     painter.end();
0415 
0416     _backgroundCache.insert(key, new QPixmap(pixmap));
0417 
0418     return pixmap;
0419 }
0420 
0421 //____________________________________________________________________________________
0422 QColor Helper::decoColor(const QColor &background, const QColor &color)
0423 {
0424     const quint64 key((colorKey(background) << 32) | colorKey(color));
0425     if (QColor *cachedColor = _decoColorCache.object(key)) {
0426         return *cachedColor;
0427     }
0428 
0429     QColor out = KColorUtils::mix(background, color, 0.8 * (1.0 + _contrast));
0430     _decoColorCache.insert(key, new QColor(out));
0431 
0432     return out;
0433 }
0434 
0435 //_______________________________________________________________________
0436 QRegion Helper::roundedMask(const QRect &rect, int left, int right, int top, int bottom) const
0437 {
0438     // get rect geometry
0439     int x, y, w, h;
0440     rect.getRect(&x, &y, &w, &h);
0441 
0442     QRegion mask(x + 4 * left, y + 0 * top, w - 4 * (left + right), h - 0 * (top + bottom));
0443     mask += QRegion(x + 0 * left, y + 4 * top, w - 0 * (left + right), h - 4 * (top + bottom));
0444     mask += QRegion(x + 2 * left, y + 1 * top, w - 2 * (left + right), h - 1 * (top + bottom));
0445     mask += QRegion(x + 1 * left, y + 2 * top, w - 1 * (left + right), h - 2 * (top + bottom));
0446 
0447     return mask;
0448 }
0449 
0450 //______________________________________________________________________________
0451 QBitmap Helper::roundedMask(const QSize &size, Corners corners, qreal radius) const
0452 {
0453     QBitmap bitmap(highDpiPixmap(size));
0454     if (corners == 0) {
0455         bitmap.fill(Qt::color1);
0456 
0457     } else {
0458         // initialize bitmap
0459         bitmap.fill(Qt::color0);
0460 
0461         // setup painter
0462         QPainter painter(&bitmap);
0463         painter.setPen(Qt::NoPen);
0464         painter.setBrush(Qt::color1);
0465 
0466         // get path
0467         const QPainterPath path(roundedPath(bitmap.rect(), corners, radius));
0468         painter.drawPath(path);
0469     }
0470 
0471     return bitmap;
0472 }
0473 
0474 //______________________________________________________________________________
0475 QPainterPath Helper::roundedPath(const QRect &rect, Corners corners, qreal radius) const
0476 {
0477     QPainterPath path;
0478 
0479     // simple cases
0480     if (corners == 0) {
0481         path.addRect(rect);
0482         return path;
0483     }
0484 
0485     if (corners == AllCorners) {
0486         path.addRoundedRect(rect, radius, radius);
0487         return path;
0488     }
0489 
0490     const QSizeF cornerSize(2 * radius, 2 * radius);
0491 
0492     // rotate counterclockwise
0493     // top left corner
0494     if (corners & CornerTopLeft) {
0495         path.moveTo(rect.topLeft() + QPointF(radius, 0));
0496         path.arcTo(QRectF(rect.topLeft(), cornerSize), 90, 90);
0497 
0498     } else
0499         path.moveTo(rect.topLeft());
0500 
0501     // bottom left corner
0502     if (corners & CornerBottomLeft) {
0503         path.lineTo(rect.bottomLeft() - QPointF(0, radius));
0504         path.arcTo(QRectF(rect.bottomLeft() - QPointF(0, 2 * radius), cornerSize), 180, 90);
0505 
0506     } else
0507         path.lineTo(rect.bottomLeft());
0508 
0509     // bottom right corner
0510     if (corners & CornerBottomRight) {
0511         path.lineTo(rect.bottomRight() - QPointF(radius, 0));
0512         path.arcTo(QRectF(rect.bottomRight() - QPointF(2 * radius, 2 * radius), cornerSize), 270, 90);
0513 
0514     } else
0515         path.lineTo(rect.bottomRight());
0516 
0517     // top right corner
0518     if (corners & CornerTopRight) {
0519         path.lineTo(rect.topRight() + QPointF(0, radius));
0520         path.arcTo(QRectF(rect.topRight() - QPointF(2 * radius, 0), cornerSize), 0, 90);
0521 
0522     } else
0523         path.lineTo(rect.topRight());
0524 
0525     path.closeSubpath();
0526     return path;
0527 }
0528 
0529 //______________________________________________________________________
0530 void Helper::drawFloatFrame(QPainter *p, const QRect r, const QColor &color, bool drawUglyShadow, bool isActive, const QColor &frameColor, TileSet::Tiles tiles)
0531 {
0532     p->save();
0533     p->setRenderHint(QPainter::Antialiasing);
0534     const QRect frame(r.adjusted(1, 1, -1, -1));
0535     int x, y, w, h;
0536     frame.getRect(&x, &y, &w, &h);
0537 
0538     QColor light(calcLightColor(backgroundTopColor(color)));
0539     QColor dark(calcLightColor(backgroundBottomColor(color)));
0540     p->setBrush(Qt::NoBrush);
0541 
0542     if (drawUglyShadow) {
0543         if (isActive) {
0544             // window active - it's a glow - not a shadow
0545             const QColor glow(KColorUtils::mix(QColor(128, 128, 128), frameColor, 0.7));
0546             p->setPen(glow);
0547 
0548             if (tiles & TileSet::Top) {
0549                 p->drawLine(QPointF(x + 4, y - 0.5), QPointF(x + w - 4, y - 0.5));
0550                 p->drawArc(QRectF(x - 0.5, y - 0.5, 11, 11), 90 * 16, 90 * 16);
0551                 p->drawArc(QRectF(x + w - 11 + 0.5, y - 0.5, 11, 11), 0, 90 * 16);
0552             }
0553 
0554             if (tiles & TileSet::Left)
0555                 p->drawLine(QPointF(x - 0.5, y + 4), QPointF(x - 0.5, y + h - 4));
0556             if (tiles & TileSet::Right)
0557                 p->drawLine(QPointF(x + w + 0.5, y + 4), QPointF(x + w + 0.5, y + h - 4));
0558 
0559             if (tiles & TileSet::Bottom) {
0560                 if (tiles & TileSet::Left)
0561                     p->drawArc(QRectF(x - 0.5, y + h - 11 + 0.5, 11, 11), 180 * 16, 90 * 16);
0562                 if (tiles & TileSet::Right)
0563                     p->drawArc(QRectF(x + w - 11 + 0.5, y + h - 11 + 0.5, 11, 11), 270 * 16, 90 * 16);
0564                 p->drawLine(QPointF(x + 4, y + h + 0.5), QPointF(x + w - 4, y + h + 0.5));
0565             }
0566 
0567             light = KColorUtils::mix(light, frameColor);
0568             dark = KColorUtils::mix(dark, frameColor);
0569 
0570         } else {
0571             // window inactive - draw something resembling shadow
0572             // fully desaturate
0573             const QColor shadow(KColorUtils::darken(color, 0.0, 0.0));
0574 
0575             if (tiles & TileSet::Top) {
0576                 p->setPen(KColorUtils::darken(shadow, 0.2));
0577                 p->drawLine(QPointF(x + 4, y - 0.5), QPointF(x + w - 4, y - 0.5));
0578                 if (tiles & TileSet::Left)
0579                     p->drawArc(QRectF(x - 0.5, y - 0.5, 11, 11), 90 * 16, 90 * 16);
0580                 if (tiles & TileSet::Right)
0581                     p->drawArc(QRectF(x + w - 11 + 0.5, y - 0.5, 11, 11), 0, 90 * 16);
0582             }
0583 
0584             p->setPen(KColorUtils::darken(shadow, 0.35));
0585             if (tiles & TileSet::Left)
0586                 p->drawLine(QPointF(x - 0.5, y + 4), QPointF(x - 0.5, y + h - 4));
0587             if (tiles & TileSet::Right)
0588                 p->drawLine(QPointF(x + w + 0.5, y + 4), QPointF(x + w + 0.5, y + h - 4));
0589 
0590             if (tiles & TileSet::Bottom) {
0591                 p->setPen(KColorUtils::darken(shadow, 0.45));
0592                 if (tiles & TileSet::Left)
0593                     p->drawArc(QRectF(x - 0.5, y + h - 11 + 0.5, 11, 11), 180 * 16, 90 * 16);
0594                 if (tiles & TileSet::Right)
0595                     p->drawArc(QRectF(x + w - 11 + 0.5, y + h - 11 + 0.5, 11, 11), 270 * 16, 90 * 16);
0596                 p->setPen(KColorUtils::darken(shadow, 0.6));
0597                 p->drawLine(QPointF(x + 4, y + h + 0.5), QPointF(x + w - 4, y + h + 0.5));
0598             }
0599         }
0600     }
0601 
0602     // top frame
0603     if (tiles & TileSet::Top) {
0604         p->setPen(QPen(light, 0.8));
0605         p->drawLine(QPointF(x + 4, y + 0.6), QPointF(x + w - 4, y + 0.6));
0606     }
0607 
0608     // corner and side frames
0609     // sides are drawn even if Top only is selected, but with a different gradient
0610     if (h >= 4 + 1.5) {
0611         QLinearGradient lg(0.0, y + 1.5, 0.0, y + h - 4);
0612         lg.setColorAt(0, light);
0613         lg.setColorAt(1, alphaColor(light, 0));
0614 
0615         if (h > 20.5)
0616             lg.setColorAt(qMax(0.0, 1.0 - 12.0 / (h - 5.5)), alphaColor(light, 0.5));
0617         else if (h > 8.5)
0618             lg.setColorAt(qMax(0.0, 3.0 / (h - 5.5)), alphaColor(light, 0.5));
0619 
0620         p->setPen(QPen(lg, 0.8));
0621         if (tiles & TileSet::Left)
0622             p->drawLine(QPointF(x + 0.6, y + 4), QPointF(x + 0.6, y + h - 4));
0623         if (tiles & TileSet::Right)
0624             p->drawLine(QPointF(x + w - 0.6, y + 4), QPointF(x + w - 0.6, y + h - 4));
0625     }
0626 
0627     if (tiles & TileSet::Top) {
0628         const qreal offset = 0.5;
0629         const qreal arc(7.0);
0630         p->drawArc(QRectF(x + offset, y + offset, arc, arc), 90 * 16, 90 * 16);
0631         p->drawArc(QRectF(x + w - arc - offset, y + offset, arc, arc), 0, 90 * 16);
0632     }
0633 
0634     p->restore();
0635 }
0636 
0637 //______________________________________________________________________________________
0638 void Helper::drawSeparator(QPainter *p, const QRect &rect, const QColor &color, Qt::Orientation orientation)
0639 {
0640     QColor light(calcLightColor(color));
0641     QColor dark(calcDarkColor(color));
0642 
0643     p->save();
0644     p->setRenderHint(QPainter::Antialiasing, false);
0645 
0646     QPoint start, end, offset;
0647     if (orientation == Qt::Horizontal) {
0648         start = QPoint(rect.x(), rect.y() + rect.height() / 2 - 1);
0649         end = QPoint(rect.right(), rect.y() + rect.height() / 2 - 1);
0650         offset = QPoint(0, 1);
0651 
0652     } else {
0653         start = QPoint(rect.x() + rect.width() / 2 - 1, rect.y());
0654         end = QPoint(rect.x() + rect.width() / 2 - 1, rect.bottom());
0655         offset = QPoint(1, 0);
0656         light.setAlpha(150);
0657     }
0658 
0659     QLinearGradient lg(start, end);
0660     lg.setColorAt(0.3, dark);
0661     lg.setColorAt(0.7, dark);
0662     dark.setAlpha(0);
0663     lg.setColorAt(0.0, dark);
0664     lg.setColorAt(1.0, dark);
0665     p->setPen(QPen(lg, 1));
0666 
0667     if (orientation == Qt::Horizontal)
0668         p->drawLine(start, end);
0669     else
0670         p->drawLine(start + offset, end + offset);
0671 
0672     lg = QLinearGradient(start, end);
0673     lg.setColorAt(0.3, light);
0674     lg.setColorAt(0.7, light);
0675     light.setAlpha(0);
0676     lg.setColorAt(0.0, light);
0677     lg.setColorAt(1.0, light);
0678     p->setPen(QPen(lg, 1));
0679 
0680     if (orientation == Qt::Horizontal)
0681         p->drawLine(start + offset, end + offset);
0682     else {
0683         p->drawLine(start, end);
0684         p->drawLine(start + offset * 2, end + offset * 2);
0685     }
0686 
0687     p->restore();
0688 }
0689 
0690 //____________________________________________________________________
0691 const QWidget *Helper::checkAutoFillBackground(const QWidget *w) const
0692 {
0693     if (!w)
0694         return nullptr;
0695     if (w->autoFillBackground())
0696         return w;
0697     if (w->isWindow())
0698         return nullptr;
0699 
0700     for (const QWidget *parent = w->parentWidget(); parent != nullptr; parent = parent->parentWidget()) {
0701         if (parent->autoFillBackground())
0702             return parent;
0703         if (parent == w->window())
0704             break;
0705     }
0706 
0707     return nullptr;
0708 }
0709 
0710 //____________________________________________________________________
0711 void Helper::setHasBackgroundGradient(WId id, bool value) const
0712 {
0713 #if OXYGEN_HAVE_X11
0714     setHasHint(id, _backgroundGradientAtom, value);
0715 #else
0716     Q_UNUSED(id);
0717     Q_UNUSED(value);
0718 #endif
0719     return;
0720 }
0721 
0722 //____________________________________________________________________
0723 bool Helper::hasBackgroundGradient(WId id) const
0724 {
0725 #if OXYGEN_HAVE_X11
0726     return hasHint(id, _backgroundGradientAtom);
0727 #else
0728     Q_UNUSED(id);
0729     return false;
0730 #endif
0731 }
0732 
0733 //______________________________________________________________________________________
0734 QPixmap Helper::highDpiPixmap(int width, int height) const
0735 {
0736     const qreal dpiRatio(qApp->devicePixelRatio());
0737     QPixmap pixmap(width * dpiRatio, height * dpiRatio);
0738     pixmap.setDevicePixelRatio(dpiRatio);
0739     return pixmap;
0740 }
0741 
0742 //______________________________________________________________________________________
0743 qreal Helper::devicePixelRatio(const QPixmap &pixmap) const
0744 {
0745     return pixmap.devicePixelRatio();
0746 }
0747 
0748 //______________________________________________________________________________
0749 bool Helper::isX11(void)
0750 {
0751 #if OXYGEN_HAVE_X11
0752     static const bool s_isX11 = KWindowSystem::isPlatformX11();
0753     return s_isX11;
0754 #endif
0755 
0756     return false;
0757 }
0758 
0759 #if OXYGEN_HAVE_X11
0760 
0761 //____________________________________________________________________
0762 xcb_connection_t *Helper::connection(void)
0763 {
0764     return QX11Info::connection();
0765 }
0766 
0767 //____________________________________________________________________
0768 xcb_atom_t Helper::createAtom(const QString &name) const
0769 {
0770     if (!isX11())
0771         return 0;
0772     xcb_intern_atom_cookie_t cookie(xcb_intern_atom(connection(), false, name.size(), qPrintable(name)));
0773     ScopedPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(connection(), cookie, nullptr));
0774     return reply ? reply->atom : 0;
0775 }
0776 
0777 #endif
0778 
0779 //___________________________________________________________________________________________
0780 void Helper::drawShadow(QPainter &painter, const QColor &color, int size)
0781 {
0782     const qreal m(qreal(size - 2) * 0.5);
0783     const qreal offset(0.8);
0784     const qreal k0((m - 4.0) / m);
0785 
0786     QRadialGradient shadowGradient(m + 1.0, m + offset + 1.0, m);
0787     for (int i = 0; i < 8; i++) {
0788         // sinusoidal gradient
0789         const qreal k1((k0 * qreal(8 - i) + qreal(i)) * 0.125);
0790         const qreal a((cos(M_PI * i * 0.125) + 1.0) * 0.30);
0791         shadowGradient.setColorAt(k1, alphaColor(color, a * _shadowGain));
0792     }
0793 
0794     shadowGradient.setColorAt(1.0, alphaColor(color, 0.0));
0795     painter.save();
0796     painter.setBrush(shadowGradient);
0797     painter.drawEllipse(QRectF(0, 0, size, size));
0798     painter.restore();
0799 }
0800 
0801 //_______________________________________________________________________
0802 void Helper::drawOuterGlow(QPainter &painter, const QColor &color, int size)
0803 {
0804     const QRectF r(0, 0, size, size);
0805     const qreal m(qreal(size) * 0.5);
0806     const qreal width(3);
0807 
0808     const qreal bias(_glowBias * qreal(14) / size);
0809 
0810     // k0 is located at width - bias from the outer edge
0811     const qreal gm(m + bias - 0.9);
0812     const qreal k0((m - width + bias) / gm);
0813     QRadialGradient glowGradient(m, m, gm);
0814     for (int i = 0; i < 8; i++) {
0815         // k1 grows linearly from k0 to 1.0
0816         const qreal k1(k0 + qreal(i) * (1.0 - k0) / 8.0);
0817 
0818         // a folows sqrt curve
0819         const qreal a(1.0 - sqrt(qreal(i) / 8));
0820         glowGradient.setColorAt(k1, alphaColor(color, a));
0821     }
0822 
0823     // glow
0824     painter.save();
0825     painter.setBrush(glowGradient);
0826     painter.drawEllipse(r);
0827 
0828     // inside mask
0829     painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
0830     painter.setBrush(Qt::black);
0831     painter.drawEllipse(r.adjusted(width + 0.5, width + 0.5, -width - 1, -width - 1));
0832     painter.restore();
0833 }
0834 
0835 #if OXYGEN_HAVE_X11
0836 
0837 //____________________________________________________________________
0838 void Helper::setHasHint(xcb_window_t id, xcb_atom_t atom, bool value) const
0839 {
0840     if (!isX11())
0841         return;
0842 
0843     // check window id
0844     if (!id)
0845         return;
0846 
0847     quint32 uLongValue(value);
0848     xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, id, atom, XCB_ATOM_CARDINAL, 32, 1, &uLongValue);
0849     xcb_flush(connection());
0850     return;
0851 }
0852 
0853 //____________________________________________________________________
0854 bool Helper::hasHint(xcb_window_t id, xcb_atom_t atom) const
0855 {
0856     if (!isX11())
0857         return false;
0858 
0859     // check window id
0860     if (!id)
0861         return false;
0862 
0863     xcb_get_property_cookie_t cookie(xcb_get_property(connection(), 0, id, atom, XCB_ATOM_CARDINAL, 0, 1));
0864     ScopedPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(connection(), cookie, nullptr));
0865 
0866     return reply && xcb_get_property_value_length(reply.data()) && reinterpret_cast<int32_t *>(xcb_get_property_value(reply.data()))[0];
0867 }
0868 
0869 #endif
0870 
0871 //____________________________________________________________________
0872 void Helper::init(void)
0873 {
0874     _contrast = KColorScheme::contrastF(_config);
0875 
0876     // background contrast is calculated so that it is 0.9
0877     // when KGlobalSettings contrast value of 0.7
0878     _bgcontrast = qMin(1.0, 0.9 * _contrast / 0.7);
0879 
0880     _backgroundCache.setMaxCost(64);
0881 
0882 #if OXYGEN_HAVE_X11
0883     if (isX11()) {
0884         _backgroundGradientAtom = createAtom(QStringLiteral("_KDE_OXYGEN_BACKGROUND_GRADIENT"));
0885 
0886     } else {
0887         _backgroundGradientAtom = 0;
0888     }
0889 
0890 #endif
0891 }
0892 }