File indexing completed on 2024-05-12 09:37:34

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