File indexing completed on 2024-05-12 16:58:28

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "breezehelper.h"
0008 
0009 #include "breeze.h"
0010 #include "breezestyleconfigdata.h"
0011 
0012 #include <KColorScheme>
0013 #include <KColorUtils>
0014 #include <KIconLoader>
0015 #include <KWindowSystem>
0016 
0017 #include <QApplication>
0018 #include <QDBusConnection>
0019 #include <QDockWidget>
0020 #include <QFileInfo>
0021 #include <QMainWindow>
0022 #include <QMdiArea>
0023 #include <QMenuBar>
0024 #include <QPainter>
0025 #include <QWindow>
0026 
0027 #if BREEZE_HAVE_QTX11EXTRAS
0028 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0029 #include <private/qtx11extras_p.h>
0030 #else
0031 #include <QX11Info>
0032 #endif
0033 #endif
0034 
0035 #include <QDialog>
0036 #include <algorithm>
0037 
0038 namespace Breeze
0039 {
0040 //* contrast for arrow and treeline rendering
0041 static const qreal arrowShade = 0.15;
0042 
0043 static const qreal highlightBackgroundAlpha = 0.33;
0044 
0045 static const auto radioCheckSunkenDarkeningFactor = 110;
0046 
0047 //____________________________________________________________________
0048 Helper::Helper(KSharedConfig::Ptr config, QObject *parent)
0049     : QObject(parent)
0050     , _config(std::move(config))
0051     , _kwinConfig(KSharedConfig::openConfig("kwinrc"))
0052     , _decorationConfig(new InternalSettings())
0053 {
0054     if (qApp) {
0055         connect(qApp, &QApplication::paletteChanged, this, [=]() {
0056             if (!qApp->property("KDE_COLOR_SCHEME_PATH").isValid()) {
0057                 return;
0058             }
0059             const auto path = qApp->property("KDE_COLOR_SCHEME_PATH").toString();
0060             if (!path.isEmpty()) {
0061                 KConfig config(path, KConfig::SimpleConfig);
0062                 KConfigGroup group(config.group("WM"));
0063                 const QPalette palette(QApplication::palette());
0064                 _activeTitleBarColor = group.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight));
0065                 _activeTitleBarTextColor = group.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText));
0066                 _inactiveTitleBarColor = group.readEntry("inactiveBackground", palette.color(QPalette::Disabled, QPalette::Highlight));
0067                 _inactiveTitleBarTextColor = group.readEntry("inactiveForeground", palette.color(QPalette::Disabled, QPalette::HighlightedText));
0068             }
0069         });
0070     }
0071 }
0072 
0073 //____________________________________________________________________
0074 KSharedConfig::Ptr Helper::config() const
0075 {
0076     return _config;
0077 }
0078 
0079 //____________________________________________________________________
0080 QSharedPointer<InternalSettings> Helper::decorationConfig() const
0081 {
0082     return _decorationConfig;
0083 }
0084 
0085 //____________________________________________________________________
0086 void Helper::loadConfig()
0087 {
0088     _viewFocusBrush = KStatefulBrush(KColorScheme::View, KColorScheme::FocusColor);
0089     _viewHoverBrush = KStatefulBrush(KColorScheme::View, KColorScheme::HoverColor);
0090     _buttonFocusBrush = KStatefulBrush(KColorScheme::Button, KColorScheme::FocusColor);
0091     _buttonHoverBrush = KStatefulBrush(KColorScheme::Button, KColorScheme::HoverColor);
0092     _viewNegativeTextBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NegativeText);
0093     _viewNeutralTextBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NeutralText);
0094 
0095     const QPalette palette(QApplication::palette());
0096     _config->reparseConfiguration();
0097     _kwinConfig->reparseConfiguration();
0098     _cachedAutoValid = false;
0099     _decorationConfig->load();
0100 
0101     KConfigGroup globalGroup(_config->group("WM"));
0102     _activeTitleBarColor = globalGroup.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight));
0103     _activeTitleBarTextColor = globalGroup.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText));
0104     _inactiveTitleBarColor = globalGroup.readEntry("inactiveBackground", palette.color(QPalette::Disabled, QPalette::Highlight));
0105     _inactiveTitleBarTextColor = globalGroup.readEntry("inactiveForeground", palette.color(QPalette::Disabled, QPalette::HighlightedText));
0106 
0107     if (const QString colorSchemePath = qApp->property("KDE_COLOR_SCHEME_PATH").toString(); !colorSchemePath.isEmpty()) {
0108         KConfig config(colorSchemePath, KConfig::SimpleConfig);
0109         KConfigGroup appGroup(config.group("WM"));
0110         _activeTitleBarColor = appGroup.readEntry("activeBackground", _activeTitleBarColor);
0111         _activeTitleBarTextColor = appGroup.readEntry("activeForeground", _activeTitleBarTextColor);
0112         _inactiveTitleBarColor = appGroup.readEntry("inactiveBackground", _inactiveTitleBarColor);
0113         _inactiveTitleBarTextColor = appGroup.readEntry("inactiveForeground", _inactiveTitleBarTextColor);
0114     }
0115 }
0116 
0117 QColor transparentize(const QColor &color, qreal amount)
0118 {
0119     auto clone = color;
0120     clone.setAlphaF(amount);
0121     return clone;
0122 }
0123 
0124 //____________________________________________________________________
0125 QColor Helper::frameOutlineColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const
0126 {
0127     QColor outline(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.25));
0128 
0129     // focus takes precedence over hover
0130     if (mode == AnimationFocus) {
0131         const QColor focus(focusColor(palette));
0132         const QColor hover(hoverColor(palette));
0133 
0134         if (mouseOver) {
0135             outline = KColorUtils::mix(hover, focus, opacity);
0136         } else {
0137             outline = KColorUtils::mix(outline, focus, opacity);
0138         }
0139 
0140     } else if (hasFocus) {
0141         outline = focusColor(palette);
0142 
0143     } else if (mode == AnimationHover) {
0144         const QColor hover(hoverColor(palette));
0145         outline = KColorUtils::mix(outline, hover, opacity);
0146 
0147     } else if (mouseOver) {
0148         outline = hoverColor(palette);
0149     }
0150 
0151     return outline;
0152 }
0153 
0154 //____________________________________________________________________
0155 QColor Helper::focusOutlineColor(const QPalette &palette) const
0156 {
0157     return KColorUtils::mix(focusColor(palette), palette.color(QPalette::WindowText), 0.15);
0158 }
0159 
0160 //____________________________________________________________________
0161 QColor Helper::hoverOutlineColor(const QPalette &palette) const
0162 {
0163     return KColorUtils::mix(hoverColor(palette), palette.color(QPalette::WindowText), 0.15);
0164 }
0165 
0166 //____________________________________________________________________
0167 QColor Helper::buttonFocusOutlineColor(const QPalette &palette) const
0168 {
0169     return KColorUtils::mix(buttonFocusColor(palette), palette.color(QPalette::ButtonText), 0.15);
0170 }
0171 
0172 //____________________________________________________________________
0173 QColor Helper::buttonHoverOutlineColor(const QPalette &palette) const
0174 {
0175     return KColorUtils::mix(buttonHoverColor(palette), palette.color(QPalette::ButtonText), 0.15);
0176 }
0177 
0178 //____________________________________________________________________
0179 QColor Helper::sidePanelOutlineColor(const QPalette &palette, bool hasFocus, qreal opacity, AnimationMode mode) const
0180 {
0181     QColor outline(palette.color(QPalette::Inactive, QPalette::Highlight));
0182     const QColor &focus = palette.color(QPalette::Active, QPalette::Highlight);
0183 
0184     if (mode == AnimationFocus) {
0185         outline = KColorUtils::mix(outline, focus, opacity);
0186 
0187     } else if (hasFocus) {
0188         outline = focus;
0189     }
0190 
0191     return outline;
0192 }
0193 
0194 //____________________________________________________________________
0195 QColor Helper::frameBackgroundColor(const QPalette &palette, QPalette::ColorGroup group) const
0196 {
0197     return KColorUtils::mix(palette.color(group, QPalette::Window), palette.color(group, QPalette::Base), 0.3);
0198 }
0199 
0200 //____________________________________________________________________
0201 QColor Helper::arrowColor(const QPalette &palette, QPalette::ColorGroup group, QPalette::ColorRole role) const
0202 {
0203     switch (role) {
0204     case QPalette::Text:
0205         return KColorUtils::mix(palette.color(group, QPalette::Text), palette.color(group, QPalette::Base), arrowShade);
0206     case QPalette::WindowText:
0207         return KColorUtils::mix(palette.color(group, QPalette::WindowText), palette.color(group, QPalette::Window), arrowShade);
0208     case QPalette::ButtonText:
0209         return KColorUtils::mix(palette.color(group, QPalette::ButtonText), palette.color(group, QPalette::Button), arrowShade);
0210     default:
0211         return palette.color(group, role);
0212     }
0213 }
0214 
0215 //____________________________________________________________________
0216 QColor Helper::arrowColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const
0217 {
0218     QColor outline(arrowColor(palette, QPalette::WindowText));
0219     if (mode == AnimationHover) {
0220         const QColor focus(focusColor(palette));
0221         const QColor hover(hoverColor(palette));
0222         if (hasFocus) {
0223             outline = KColorUtils::mix(focus, hover, opacity);
0224         } else {
0225             outline = KColorUtils::mix(outline, hover, opacity);
0226         }
0227 
0228     } else if (mouseOver) {
0229         outline = hoverColor(palette);
0230 
0231     } else if (mode == AnimationFocus) {
0232         const QColor focus(focusColor(palette));
0233         outline = KColorUtils::mix(outline, focus, opacity);
0234 
0235     } else if (hasFocus) {
0236         outline = focusColor(palette);
0237     }
0238 
0239     return outline;
0240 }
0241 
0242 //____________________________________________________________________
0243 QColor Helper::sliderOutlineColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const
0244 {
0245     QColor outline(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.4));
0246 
0247     // hover takes precedence over focus
0248     if (mode == AnimationHover) {
0249         const QColor hover(hoverColor(palette));
0250         const QColor focus(focusColor(palette));
0251         if (hasFocus) {
0252             outline = KColorUtils::mix(focus, hover, opacity);
0253         } else {
0254             outline = KColorUtils::mix(outline, hover, opacity);
0255         }
0256 
0257     } else if (mouseOver) {
0258         outline = hoverColor(palette);
0259 
0260     } else if (mode == AnimationFocus) {
0261         const QColor focus(focusColor(palette));
0262         outline = KColorUtils::mix(outline, focus, opacity);
0263 
0264     } else if (hasFocus) {
0265         outline = focusColor(palette);
0266     }
0267 
0268     return outline;
0269 }
0270 
0271 //____________________________________________________________________
0272 QColor Helper::scrollBarHandleColor(const QPalette &palette, bool mouseOver, bool hasFocus, qreal opacity, AnimationMode mode) const
0273 {
0274     QColor color(alphaColor(palette.color(QPalette::WindowText), 0.5));
0275 
0276     // hover takes precedence over focus
0277     if (mode == AnimationHover) {
0278         const QColor hover(hoverColor(palette));
0279         const QColor focus(focusColor(palette));
0280         if (hasFocus) {
0281             color = KColorUtils::mix(focus, hover, opacity);
0282         } else {
0283             color = KColorUtils::mix(color, hover, opacity);
0284         }
0285 
0286     } else if (mouseOver) {
0287         color = hoverColor(palette);
0288 
0289     } else if (mode == AnimationFocus) {
0290         const QColor focus(focusColor(palette));
0291         color = KColorUtils::mix(color, focus, opacity);
0292 
0293     } else if (hasFocus) {
0294         color = focusColor(palette);
0295     }
0296 
0297     return color;
0298 }
0299 
0300 //______________________________________________________________________________
0301 QColor Helper::checkBoxIndicatorColor(const QPalette &palette, bool mouseOver, bool active, qreal opacity, AnimationMode mode) const
0302 {
0303     QColor color(KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.6));
0304     if (mode == AnimationHover) {
0305         const QColor focus(focusColor(palette));
0306         const QColor hover(hoverColor(palette));
0307         if (active) {
0308             color = KColorUtils::mix(focus, hover, opacity);
0309         } else {
0310             color = KColorUtils::mix(color, hover, opacity);
0311         }
0312 
0313     } else if (mouseOver) {
0314         color = hoverColor(palette);
0315 
0316     } else if (active) {
0317         color = focusColor(palette);
0318     }
0319 
0320     return color;
0321 }
0322 
0323 //______________________________________________________________________________
0324 QColor Helper::separatorColor(const QPalette &palette) const
0325 {
0326     return KColorUtils::mix(palette.color(QPalette::Window), palette.color(QPalette::WindowText), 0.25);
0327 }
0328 
0329 //______________________________________________________________________________
0330 QPalette Helper::disabledPalette(const QPalette &source, qreal ratio) const
0331 {
0332     QPalette copy(source);
0333 
0334     const QList<QPalette::ColorRole> roles =
0335         {QPalette::Window, QPalette::Highlight, QPalette::WindowText, QPalette::ButtonText, QPalette::Text, QPalette::Button};
0336     foreach (const QPalette::ColorRole &role, roles) {
0337         copy.setColor(role, KColorUtils::mix(source.color(QPalette::Active, role), source.color(QPalette::Disabled, role), 1.0 - ratio));
0338     }
0339 
0340     return copy;
0341 }
0342 
0343 //____________________________________________________________________
0344 QColor Helper::alphaColor(QColor color, qreal alpha) const
0345 {
0346     if (alpha >= 0 && alpha < 1.0) {
0347         color.setAlphaF(alpha * color.alphaF());
0348     }
0349     return color;
0350 }
0351 
0352 //______________________________________________________________________________
0353 void Helper::renderDebugFrame(QPainter *painter, const QRect &rect) const
0354 {
0355     painter->save();
0356     painter->setRenderHints(QPainter::Antialiasing);
0357     painter->setBrush(Qt::NoBrush);
0358     painter->setPen(Qt::red);
0359     painter->drawRect(strokedRect(rect));
0360     painter->restore();
0361 }
0362 
0363 //______________________________________________________________________________
0364 void Helper::renderFocusRect(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, Sides sides) const
0365 {
0366     if (!color.isValid()) {
0367         return;
0368     }
0369 
0370     painter->save();
0371     painter->setRenderHints(QPainter::Antialiasing);
0372     painter->setBrush(color);
0373 
0374     if (!(outline.isValid() && sides)) {
0375         painter->setPen(Qt::NoPen);
0376         painter->drawRect(rect);
0377 
0378     } else {
0379         painter->setClipRect(rect);
0380 
0381         QRectF copy(strokedRect(rect));
0382 
0383         const qreal radius(frameRadius(PenWidth::Frame));
0384         if (!(sides & SideTop)) {
0385             copy.adjust(0, -radius, 0, 0);
0386         }
0387         if (!(sides & SideBottom)) {
0388             copy.adjust(0, 0, 0, radius);
0389         }
0390         if (!(sides & SideLeft)) {
0391             copy.adjust(-radius, 0, 0, 0);
0392         }
0393         if (!(sides & SideRight)) {
0394             copy.adjust(0, 0, radius, 0);
0395         }
0396 
0397         painter->setPen(outline);
0398         // painter->setBrush( Qt::NoBrush );
0399         painter->drawRoundedRect(copy, radius, radius);
0400     }
0401 
0402     painter->restore();
0403 }
0404 
0405 //______________________________________________________________________________
0406 void Helper::renderFocusLine(QPainter *painter, const QRect &rect, const QColor &color) const
0407 {
0408     if (!color.isValid()) {
0409         return;
0410     }
0411 
0412     painter->save();
0413     painter->setRenderHint(QPainter::Antialiasing, false);
0414     painter->setBrush(Qt::NoBrush);
0415     painter->setPen(color);
0416 
0417     painter->translate(0, 2);
0418     painter->drawLine(rect.bottomLeft(), rect.bottomRight());
0419     painter->restore();
0420 }
0421 
0422 //______________________________________________________________________________
0423 void Helper::renderFrameWithSides(QPainter *painter, const QRect &rect, const QColor &color, Qt::Edges edges, const QColor &outline) const
0424 {
0425     painter->save();
0426 
0427     painter->setRenderHint(QPainter::Antialiasing);
0428 
0429     QRectF frameRect(rect);
0430 
0431     // set brush
0432     painter->setBrush(color);
0433     painter->setPen(Qt::NoPen);
0434 
0435     // render
0436     painter->drawRect(frameRect);
0437 
0438     // set brush again
0439     painter->setBrush(Qt::NoBrush);
0440     painter->setPen(outline);
0441 
0442     // manually apply the effects of StrokedRect here but only to the edges with a frame
0443     if (edges & Qt::LeftEdge) {
0444         frameRect.adjust(0.5, 0.0, 0.0, 0.0);
0445     }
0446     if (edges & Qt::RightEdge) {
0447         frameRect.adjust(0.0, 0, -0.5, 0.0);
0448     }
0449     if (edges & Qt::TopEdge) {
0450         frameRect.adjust(0.0, 0.5, 0.0, 0.0);
0451     }
0452     if (edges & Qt::BottomEdge) {
0453         frameRect.adjust(0.0, 0.0, 0.0, -0.5);
0454     }
0455 
0456     // draw lines
0457     if (edges & Qt::LeftEdge) {
0458         painter->drawLine(QLineF(frameRect.topLeft(), frameRect.bottomLeft()));
0459     }
0460     if (edges & Qt::RightEdge) {
0461         painter->drawLine(QLineF(frameRect.topRight(), frameRect.bottomRight()));
0462     }
0463     if (edges & Qt::TopEdge) {
0464         painter->drawLine(QLineF(frameRect.topLeft(), frameRect.topRight()));
0465     }
0466     if (edges & Qt::BottomEdge) {
0467         painter->drawLine(QLineF(frameRect.bottomLeft(), frameRect.bottomRight()));
0468     }
0469 
0470     painter->restore();
0471 }
0472 
0473 //______________________________________________________________________________
0474 void Helper::renderFrame(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline) const
0475 {
0476     painter->setRenderHint(QPainter::Antialiasing);
0477 
0478     QRectF frameRect(rect.adjusted(1, 1, -1, -1));
0479     qreal radius(frameRadius(PenWidth::NoPen));
0480 
0481     // set pen
0482     if (outline.isValid()) {
0483         painter->setPen(outline);
0484         frameRect = strokedRect(frameRect);
0485         radius = frameRadiusForNewPenWidth(radius, PenWidth::Frame);
0486 
0487     } else {
0488         painter->setPen(Qt::NoPen);
0489     }
0490 
0491     // set brush
0492     if (color.isValid()) {
0493         painter->setBrush(color);
0494     } else {
0495         painter->setBrush(Qt::NoBrush);
0496     }
0497 
0498     // render
0499     painter->drawRoundedRect(frameRect, radius, radius);
0500 }
0501 
0502 //______________________________________________________________________________
0503 void Helper::renderSidePanelFrame(QPainter *painter, const QRect &rect, const QColor &outline, Side side) const
0504 {
0505     // check color
0506     if (!outline.isValid()) {
0507         return;
0508     }
0509 
0510     // adjust rect
0511     QRectF frameRect(strokedRect(rect));
0512 
0513     // setup painter
0514     painter->setRenderHint(QPainter::Antialiasing);
0515     painter->setPen(outline);
0516 
0517     // render
0518     switch (side) {
0519     default:
0520     case SideLeft:
0521         painter->drawLine(frameRect.topRight(), frameRect.bottomRight());
0522         break;
0523 
0524     case SideTop:
0525         painter->drawLine(frameRect.topLeft(), frameRect.topRight());
0526         break;
0527 
0528     case SideRight:
0529         painter->drawLine(frameRect.topLeft(), frameRect.bottomLeft());
0530         break;
0531 
0532     case SideBottom:
0533         painter->drawLine(frameRect.bottomLeft(), frameRect.bottomRight());
0534         break;
0535 
0536     case AllSides: {
0537         const qreal radius(frameRadius(PenWidth::Frame));
0538         painter->drawRoundedRect(frameRect, radius, radius);
0539         break;
0540     }
0541     }
0542 }
0543 
0544 //______________________________________________________________________________
0545 void Helper::renderMenuFrame(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, bool roundCorners, bool isTopMenu) const
0546 {
0547     painter->save();
0548 
0549     // set brush
0550     if (color.isValid()) {
0551         painter->setBrush(color);
0552     } else {
0553         painter->setBrush(Qt::NoBrush);
0554     }
0555 
0556     // We simulate being able to independently adjust corner radii by
0557     // setting a clip region and then extending the rectangle beyond it.
0558     if (isTopMenu) {
0559         painter->setClipRect(rect);
0560     }
0561 
0562     if (roundCorners) {
0563         painter->setRenderHint(QPainter::Antialiasing);
0564         QRectF frameRect(rect);
0565         qreal radius(frameRadius(PenWidth::NoPen));
0566 
0567         if (isTopMenu) {
0568             frameRect.adjust(0, -radius, 0, 0);
0569         }
0570 
0571         // set pen
0572         if (outline.isValid()) {
0573             painter->setPen(outline);
0574             frameRect = strokedRect(frameRect);
0575             radius = frameRadiusForNewPenWidth(radius, PenWidth::Frame);
0576 
0577         } else {
0578             painter->setPen(Qt::NoPen);
0579         }
0580 
0581         // render
0582         painter->drawRoundedRect(frameRect, radius, radius);
0583 
0584     } else {
0585         painter->setRenderHint(QPainter::Antialiasing, false);
0586         QRect frameRect(rect);
0587         if (isTopMenu) {
0588             frameRect.adjust(0, 1, 0, 0);
0589         }
0590 
0591         if (outline.isValid()) {
0592             painter->setPen(outline);
0593             frameRect.adjust(0, 0, -1, -1);
0594 
0595         } else {
0596             painter->setPen(Qt::NoPen);
0597         }
0598 
0599         painter->drawRect(frameRect);
0600     }
0601 
0602     painter->restore();
0603 }
0604 
0605 //______________________________________________________________________________
0606 void Helper::renderButtonFrame(QPainter *painter,
0607                                const QRect &rect,
0608                                const QPalette &palette,
0609                                const QHash<QByteArray, bool> &stateProperties,
0610                                qreal bgAnimation,
0611                                qreal penAnimation) const
0612 {
0613     bool enabled = stateProperties.value("enabled", true);
0614     bool visualFocus = stateProperties.value("visualFocus");
0615     bool hovered = stateProperties.value("hovered");
0616     bool down = stateProperties.value("down");
0617     bool checked = stateProperties.value("checked");
0618     bool flat = stateProperties.value("flat");
0619     bool defaultButton = stateProperties.value("defaultButton");
0620     bool hasNeutralHighlight = stateProperties.value("hasNeutralHighlight");
0621     bool isActiveWindow = stateProperties.value("isActiveWindow");
0622 
0623     // don't render background if flat and not hovered, down, checked, or given visual focus
0624     if (flat && !(hovered || down || checked || visualFocus) && bgAnimation == AnimationData::OpacityInvalid && penAnimation == AnimationData::OpacityInvalid) {
0625         return;
0626     }
0627 
0628     QRectF shadowedRect = this->shadowedRect(rect);
0629     QRectF frameRect = strokedRect(shadowedRect);
0630     qreal radius = frameRadius(PenWidth::Frame);
0631     // setting color group to work around KColorScheme feature
0632     const QColor &highlightColor = palette.color(!enabled ? QPalette::Disabled : QPalette::Active, QPalette::Highlight);
0633     QBrush bgBrush;
0634     QBrush penBrush;
0635 
0636     // Colors
0637     if (flat) {
0638         if (down && enabled) {
0639             bgBrush = alphaColor(highlightColor, highlightBackgroundAlpha);
0640         } else if (checked) {
0641             bgBrush = hasNeutralHighlight ? alphaColor(neutralText(palette), highlightBackgroundAlpha) : alphaColor(palette.buttonText().color(), 0.125);
0642             penBrush = hasNeutralHighlight ? neutralText(palette) : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.3);
0643         } else if (isActiveWindow && defaultButton) {
0644             bgBrush = alphaColor(highlightColor, 0.125);
0645             penBrush = KColorUtils::mix(highlightColor, KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.333), 0.5);
0646         } else {
0647             bgBrush = alphaColor(highlightColor, 0);
0648             penBrush = hasNeutralHighlight ? neutralText(palette) : bgBrush;
0649         }
0650     } else {
0651         if (down && enabled) {
0652             bgBrush = KColorUtils::mix(palette.button().color(), highlightColor, 0.333);
0653         } else if (checked) {
0654             bgBrush = hasNeutralHighlight ? KColorUtils::mix(palette.button().color(), neutralText(palette), 0.333)
0655                                           : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.125);
0656             penBrush = hasNeutralHighlight ? neutralText(palette) : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.3);
0657         } else if (isActiveWindow && defaultButton) {
0658             bgBrush = KColorUtils::mix(palette.button().color(), highlightColor, 0.2);
0659             penBrush = KColorUtils::mix(highlightColor, KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.333), 0.5);
0660         } else {
0661             bgBrush = palette.button().color();
0662             penBrush = hasNeutralHighlight ? neutralText(palette) : KColorUtils::mix(palette.button().color(), palette.buttonText().color(), 0.3);
0663         }
0664     }
0665 
0666     if ((hovered || visualFocus || down) && enabled) {
0667         penBrush = highlightColor;
0668     }
0669 
0670     // Animations
0671     if (bgAnimation != AnimationData::OpacityInvalid && enabled) {
0672         QColor color1 = bgBrush.color();
0673         QColor color2 = flat ? alphaColor(highlightColor, highlightBackgroundAlpha) : KColorUtils::mix(palette.button().color(), highlightColor, 0.333);
0674         bgBrush = KColorUtils::mix(color1, color2, bgAnimation);
0675     }
0676     if (penAnimation != AnimationData::OpacityInvalid && enabled) {
0677         QColor color1 = penBrush.color();
0678         QColor color2 = highlightColor;
0679         penBrush = KColorUtils::mix(color1, color2, penAnimation);
0680     }
0681 
0682     // Shadow
0683     if (isActiveWindow && !(flat || down || checked) && enabled) {
0684         renderRoundedRectShadow(painter, shadowedRect, shadowColor(palette));
0685     }
0686 
0687     // Render button
0688     painter->setRenderHint(QPainter::Antialiasing, true);
0689     painter->setBrush(bgBrush);
0690     painter->setPen(QPen(penBrush, PenWidth::Frame));
0691     painter->drawRoundedRect(frameRect, radius, radius);
0692 }
0693 
0694 //______________________________________________________________________________
0695 void Helper::renderToolBoxFrame(QPainter *painter, const QRect &rect, int tabWidth, const QColor &outline) const
0696 {
0697     if (!outline.isValid()) {
0698         return;
0699     }
0700 
0701     // round radius
0702     const qreal radius(frameRadius(PenWidth::Frame));
0703     const QSizeF cornerSize(2 * radius, 2 * radius);
0704 
0705     // if rect - tabwidth is even, need to increase tabWidth by 1 unit
0706     // for anti aliasing
0707     if (!((rect.width() - tabWidth) % 2)) {
0708         ++tabWidth;
0709     }
0710 
0711     // adjust rect for antialiasing
0712     QRectF baseRect(strokedRect(rect));
0713 
0714     // create path
0715     QPainterPath path;
0716     path.moveTo(0, baseRect.height() - 1);
0717     path.lineTo((baseRect.width() - tabWidth) / 2 - radius, baseRect.height() - 1);
0718     path.arcTo(QRectF(QPointF((baseRect.width() - tabWidth) / 2 - 2 * radius, baseRect.height() - 1 - 2 * radius), cornerSize), 270, 90);
0719     path.lineTo((baseRect.width() - tabWidth) / 2, radius);
0720     path.arcTo(QRectF(QPointF((baseRect.width() - tabWidth) / 2, 0), cornerSize), 180, -90);
0721     path.lineTo((baseRect.width() + tabWidth) / 2 - 1 - radius, 0);
0722     path.arcTo(QRectF(QPointF((baseRect.width() + tabWidth) / 2 - 1 - 2 * radius, 0), cornerSize), 90, -90);
0723     path.lineTo((baseRect.width() + tabWidth) / 2 - 1, baseRect.height() - 1 - radius);
0724     path.arcTo(QRectF(QPointF((baseRect.width() + tabWidth) / 2 - 1, baseRect.height() - 1 - 2 * radius), cornerSize), 180, 90);
0725     path.lineTo(baseRect.width() - 1, baseRect.height() - 1);
0726 
0727     // render
0728     painter->setRenderHints(QPainter::Antialiasing);
0729     painter->setBrush(Qt::NoBrush);
0730     painter->setPen(outline);
0731     painter->translate(baseRect.topLeft());
0732     painter->drawPath(path);
0733 }
0734 
0735 //______________________________________________________________________________
0736 void Helper::renderTabWidgetFrame(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, Corners corners) const
0737 {
0738     painter->setRenderHint(QPainter::Antialiasing);
0739 
0740     QRectF frameRect(rect.adjusted(1, 1, -1, -1));
0741     qreal radius(frameRadius(PenWidth::NoPen));
0742 
0743     // set pen
0744     if (outline.isValid()) {
0745         painter->setPen(outline);
0746         frameRect = strokedRect(frameRect);
0747         radius = frameRadiusForNewPenWidth(radius, PenWidth::Frame);
0748 
0749     } else {
0750         painter->setPen(Qt::NoPen);
0751     }
0752 
0753     // set brush
0754     if (color.isValid()) {
0755         painter->setBrush(color);
0756     } else {
0757         painter->setBrush(Qt::NoBrush);
0758     }
0759 
0760     // render
0761     QPainterPath path(roundedPath(frameRect, corners, radius));
0762     painter->drawPath(path);
0763 }
0764 
0765 //______________________________________________________________________________
0766 void Helper::renderSelection(QPainter *painter, const QRect &rect, const QColor &color) const
0767 {
0768     painter->setRenderHint(QPainter::Antialiasing);
0769     painter->setPen(Qt::NoPen);
0770     painter->setBrush(color);
0771     painter->drawRect(rect);
0772 }
0773 
0774 //______________________________________________________________________________
0775 void Helper::renderSeparator(QPainter *painter, const QRect &rect, const QColor &color, bool vertical) const
0776 {
0777     painter->setRenderHint(QPainter::Antialiasing, false);
0778     painter->setBrush(Qt::NoBrush);
0779     painter->setPen(color);
0780 
0781     if (vertical) {
0782         painter->translate(rect.width() / 2, 0);
0783         painter->drawLine(rect.topLeft(), rect.bottomLeft());
0784 
0785     } else {
0786         painter->translate(0, rect.height() / 2);
0787         painter->drawLine(rect.topLeft(), rect.topRight());
0788     }
0789 }
0790 
0791 //______________________________________________________________________________
0792 void Helper::renderCheckBoxBackground(QPainter *painter,
0793                                       const QRect &rect,
0794                                       const QPalette &palette,
0795                                       CheckBoxState state,
0796                                       bool neutalHighlight,
0797                                       bool sunken,
0798                                       qreal animation) const
0799 {
0800     // setup painter
0801     painter->setRenderHint(QPainter::Antialiasing, true);
0802 
0803     // copy rect
0804     QRectF frameRect(rect);
0805     frameRect.adjust(2, 2, -2, -2);
0806     frameRect = strokedRect(frameRect);
0807 
0808     auto transparent = neutalHighlight ? neutralText(palette) : palette.highlight().color();
0809     transparent.setAlphaF(highlightBackgroundAlpha);
0810 
0811     QBrush penBrush;
0812     if (neutalHighlight) {
0813         penBrush = neutralText(palette);
0814     } else if (state == CheckOn || state == CheckPartial) {
0815         penBrush = palette.highlight().color();
0816     } else {
0817         penBrush = transparentize(palette.text().color(), highlightBackgroundAlpha);
0818     }
0819     painter->setPen(QPen(penBrush, PenWidth::Frame));
0820 
0821     const auto radius = Metrics::CheckBox_Radius;
0822 
0823     switch (state) {
0824     case CheckOff:
0825         painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100));
0826         painter->drawRoundedRect(frameRect, radius, radius);
0827         break;
0828 
0829     case CheckPartial:
0830     case CheckOn:
0831         painter->setBrush(transparent.darker(sunken ? radioCheckSunkenDarkeningFactor : 100));
0832         painter->drawRoundedRect(frameRect, radius, radius);
0833         break;
0834 
0835     case CheckAnimated:
0836         painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100));
0837         painter->drawRoundedRect(frameRect, radius, radius);
0838         painter->setBrush(transparent);
0839         painter->setOpacity(animation);
0840         painter->drawRoundedRect(frameRect, radius, radius);
0841         break;
0842     }
0843 }
0844 
0845 //______________________________________________________________________________
0846 void Helper::renderCheckBox(QPainter *painter,
0847                             const QRect &rect,
0848                             const QPalette &palette,
0849                             bool mouseOver,
0850                             CheckBoxState state,
0851                             CheckBoxState target,
0852                             bool neutalHighlight,
0853                             bool sunken,
0854                             qreal animation,
0855                             qreal hoverAnimation) const
0856 {
0857     Q_UNUSED(sunken)
0858 
0859     // setup painter
0860     painter->setRenderHint(QPainter::Antialiasing, true);
0861 
0862     // copy rect and radius
0863     QRectF frameRect(rect);
0864     frameRect.adjust(2, 2, -2, -2);
0865 
0866     if (mouseOver) {
0867         painter->save();
0868 
0869         if (hoverAnimation != AnimationData::OpacityInvalid) {
0870             painter->setOpacity(hoverAnimation);
0871         }
0872 
0873         painter->setPen(QPen(neutalHighlight ? neutralText(palette).lighter() : focusColor(palette), PenWidth::Frame));
0874         painter->setBrush(Qt::NoBrush);
0875 
0876         painter->drawRoundedRect(frameRect.adjusted(0.5, 0.5, -0.5, -0.5), Metrics::CheckBox_Radius, Metrics::CheckBox_Radius);
0877 
0878         painter->restore();
0879     }
0880 
0881     // check
0882     auto leftPoint = frameRect.center();
0883     leftPoint.setX(frameRect.left() + 4);
0884 
0885     auto bottomPoint = frameRect.center();
0886     bottomPoint.setX(bottomPoint.x() - 1);
0887     bottomPoint.setY(frameRect.bottom() - 5);
0888 
0889     auto rightPoint = frameRect.center();
0890     rightPoint.setX(rightPoint.x() + 4.5);
0891     rightPoint.setY(frameRect.top() + 5.5);
0892 
0893     QPainterPath path;
0894     path.moveTo(leftPoint);
0895     path.lineTo(bottomPoint);
0896     path.lineTo(rightPoint);
0897 
0898     // dots
0899     auto centerDot = QRectF(frameRect.center(), QSize(2, 2));
0900     centerDot.adjust(-1, -1, -1, -1);
0901     auto leftDot = centerDot.adjusted(-4, 0, -4, 0);
0902     auto rightDot = centerDot.adjusted(4, 0, 4, 0);
0903 
0904     painter->setPen(Qt::transparent);
0905     painter->setBrush(Qt::transparent);
0906 
0907     auto checkPen = QPen(palette.text(), PenWidth::Frame * 2);
0908     checkPen.setJoinStyle(Qt::MiterJoin);
0909 
0910     switch (state) {
0911     case CheckOff:
0912         break;
0913     case CheckOn:
0914         painter->setPen(checkPen);
0915         painter->drawPath(path);
0916         break;
0917     case CheckPartial:
0918         painter->setBrush(palette.text());
0919         painter->drawRect(leftDot);
0920         painter->drawRect(centerDot);
0921         painter->drawRect(rightDot);
0922         break;
0923     case CheckAnimated:
0924         checkPen.setDashPattern({path.length() * animation, path.length()});
0925 
0926         switch (target) {
0927         case CheckOff:
0928             break;
0929         case CheckOn:
0930             painter->setPen(checkPen);
0931             painter->drawPath(path);
0932             break;
0933         case CheckPartial:
0934             if (animation >= 3 / 3) {
0935                 painter->drawRect(rightDot);
0936             }
0937             if (animation >= 2 / 3) {
0938                 painter->drawRect(centerDot);
0939             }
0940             if (animation >= 1 / 3) {
0941                 painter->drawRect(leftDot);
0942             }
0943             break;
0944         case CheckAnimated:
0945             break;
0946         }
0947         break;
0948     }
0949 }
0950 
0951 //______________________________________________________________________________
0952 void Helper::renderRadioButtonBackground(QPainter *painter,
0953                                          const QRect &rect,
0954                                          const QPalette &palette,
0955                                          RadioButtonState state,
0956                                          bool neutalHighlight,
0957                                          bool sunken,
0958                                          qreal animation) const
0959 {
0960     // setup painter
0961     painter->setRenderHint(QPainter::Antialiasing, true);
0962 
0963     // copy rect
0964     QRectF frameRect(rect);
0965     frameRect.adjust(2, 2, -2, -2);
0966     frameRect.adjust(0.5, 0.5, -0.5, -0.5);
0967 
0968     auto transparent = neutalHighlight ? neutralText(palette) : palette.highlight().color();
0969     transparent.setAlphaF(highlightBackgroundAlpha);
0970 
0971     QBrush penBrush;
0972     if (neutalHighlight) {
0973         penBrush = neutralText(palette);
0974     } else if (state == RadioOn) {
0975         penBrush = palette.highlight().color();
0976     } else {
0977         penBrush = transparentize(palette.text().color(), highlightBackgroundAlpha);
0978     }
0979     painter->setPen(QPen(penBrush, PenWidth::Frame));
0980 
0981     switch (state) {
0982     case RadioOff:
0983         painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100));
0984         painter->drawEllipse(frameRect);
0985         break;
0986     case RadioOn:
0987         painter->setBrush(transparent.darker(sunken ? radioCheckSunkenDarkeningFactor : 100));
0988         painter->drawEllipse(frameRect);
0989         break;
0990     case RadioAnimated:
0991         painter->setBrush(palette.base().color().darker(sunken ? radioCheckSunkenDarkeningFactor : 100));
0992         painter->drawEllipse(frameRect);
0993         painter->setBrush(transparent);
0994         painter->setOpacity(animation);
0995         painter->drawEllipse(frameRect);
0996         break;
0997     }
0998 }
0999 
1000 //______________________________________________________________________________
1001 void Helper::renderRadioButton(QPainter *painter,
1002                                const QRect &rect,
1003                                const QPalette &palette,
1004                                bool mouseOver,
1005                                RadioButtonState state,
1006                                bool neutralHighlight,
1007                                bool sunken,
1008                                qreal animation,
1009                                qreal animationHover) const
1010 {
1011     Q_UNUSED(sunken)
1012 
1013     // copy rect
1014     QRectF frameRect(rect);
1015     frameRect.adjust(1, 1, -1, -1);
1016 
1017     if (mouseOver) {
1018         painter->save();
1019 
1020         if (animationHover != AnimationData::OpacityInvalid) {
1021             painter->setOpacity(animationHover);
1022         }
1023 
1024         painter->setPen(QPen(neutralHighlight ? neutralText(palette).lighter() : focusColor(palette), PenWidth::Frame));
1025         painter->setBrush(Qt::NoBrush);
1026 
1027         const QRectF contentRect(frameRect.adjusted(1, 1, -1, -1).adjusted(0.5, 0.5, -0.5, -0.5));
1028         painter->drawEllipse(contentRect);
1029 
1030         painter->restore();
1031     }
1032 
1033     painter->setBrush(palette.text());
1034     painter->setPen(Qt::NoPen);
1035 
1036     QRectF markerRect;
1037     markerRect = frameRect.adjusted(6, 6, -6, -6);
1038 
1039     qreal adjustFactor;
1040 
1041     // mark
1042     switch (state) {
1043     case RadioOn:
1044         painter->drawEllipse(markerRect);
1045 
1046         break;
1047     case RadioAnimated:
1048         adjustFactor = markerRect.height() * (1 - animation);
1049         markerRect.adjust(adjustFactor, adjustFactor, -adjustFactor, -adjustFactor);
1050         painter->drawEllipse(markerRect);
1051 
1052         break;
1053     default:
1054         break;
1055     }
1056 }
1057 
1058 //______________________________________________________________________________
1059 void Helper::renderSliderGroove(QPainter *painter, const QRect &rect, const QColor &color) const
1060 {
1061     // setup painter
1062     painter->setRenderHint(QPainter::Antialiasing, true);
1063 
1064     QRectF baseRect(rect);
1065     baseRect.adjust(0.5, 0.5, -0.5, -0.5);
1066     const qreal radius(0.5 * Metrics::Slider_GrooveThickness);
1067 
1068     // content
1069     if (color.isValid()) {
1070         painter->setPen(QPen(color, PenWidth::Frame));
1071         auto bg = color;
1072         bg.setAlphaF(bg.alphaF() / 2);
1073         painter->setBrush(bg);
1074         painter->drawRoundedRect(baseRect, radius, radius);
1075     }
1076 }
1077 
1078 //______________________________________________________________________________
1079 void Helper::renderDialGroove(QPainter *painter, const QRect &rect, const QColor &fg, const QColor &bg, qreal first, qreal last) const
1080 {
1081     // setup painter
1082     painter->setRenderHint(QPainter::Antialiasing, true);
1083 
1084     const QRectF baseRect(rect);
1085 
1086     // content
1087     if (fg.isValid()) {
1088         const qreal penWidth(Metrics::Slider_GrooveThickness);
1089         const QRectF grooveRect(rect.adjusted(penWidth / 2, penWidth / 2, -penWidth / 2, -penWidth / 2));
1090 
1091         // setup angles
1092         const int angleStart(first * 180 * 16 / M_PI);
1093         const int angleSpan((last - first) * 180 * 16 / M_PI);
1094 
1095         const QPen bgPen(fg, penWidth, Qt::SolidLine, Qt::RoundCap);
1096         const QPen fgPen(KColorUtils::overlayColors(bg, alphaColor(fg, 0.5)), penWidth - 2, Qt::SolidLine, Qt::RoundCap);
1097 
1098         // setup pen
1099         if (angleSpan != 0) {
1100             painter->setPen(bgPen);
1101             painter->setBrush(Qt::NoBrush);
1102             painter->drawArc(grooveRect, angleStart, angleSpan);
1103             painter->setPen(fgPen);
1104             painter->drawArc(grooveRect, angleStart, angleSpan);
1105         }
1106     }
1107 }
1108 
1109 //______________________________________________________________________________
1110 void Helper::renderSliderHandle(QPainter *painter, const QRect &rect, const QColor &color, const QColor &outline, const QColor &shadow, bool sunken) const
1111 {
1112     // setup painter
1113     painter->setRenderHint(QPainter::Antialiasing, true);
1114 
1115     // copy rect
1116     QRectF frameRect(rect);
1117     frameRect.adjust(1, 1, -1, -1);
1118 
1119     // shadow
1120     if (!sunken) {
1121         renderEllipseShadow(painter, frameRect, shadow);
1122     }
1123 
1124     // set pen
1125     if (outline.isValid()) {
1126         painter->setPen(QPen(outline, PenWidth::Frame));
1127         frameRect = strokedRect(frameRect);
1128 
1129     } else {
1130         painter->setPen(Qt::NoPen);
1131     }
1132 
1133     // set brush
1134     if (color.isValid()) {
1135         painter->setBrush(color);
1136     } else {
1137         painter->setBrush(Qt::NoBrush);
1138     }
1139 
1140     // render
1141     painter->drawEllipse(frameRect);
1142 }
1143 
1144 //______________________________________________________________________________
1145 void Helper::renderProgressBarGroove(QPainter *painter, const QRect &rect, const QColor &fg, const QColor &bg) const
1146 {
1147     // setup painter
1148     painter->setRenderHint(QPainter::Antialiasing, true);
1149 
1150     QRectF baseRect(rect);
1151     baseRect.adjust(0.5, 0.5, -0.5, -0.5);
1152     const qreal radius(0.5 * Metrics::ProgressBar_Thickness);
1153 
1154     // content
1155     if (fg.isValid()) {
1156         painter->setPen(QPen(fg, PenWidth::Frame));
1157         painter->setBrush(KColorUtils::overlayColors(bg, alphaColor(fg, 0.5)));
1158         painter->drawRoundedRect(baseRect, radius, radius);
1159     }
1160 }
1161 
1162 //______________________________________________________________________________
1163 void Helper::renderProgressBarBusyContents(QPainter *painter,
1164                                            const QRect &rect,
1165                                            const QColor &first,
1166                                            const QColor &second,
1167                                            bool horizontal,
1168                                            bool reverse,
1169                                            int progress) const
1170 {
1171     // setup painter
1172     painter->setRenderHint(QPainter::Antialiasing, true);
1173 
1174     const QRectF baseRect(rect);
1175     const qreal radius(0.5 * Metrics::ProgressBar_Thickness);
1176 
1177     // setup brush
1178     QPixmap pixmap(horizontal ? 2 * Metrics::ProgressBar_BusyIndicatorSize : 1, horizontal ? 1 : 2 * Metrics::ProgressBar_BusyIndicatorSize);
1179     pixmap.fill(second);
1180     if (horizontal) {
1181         QPainter painter(&pixmap);
1182         painter.setBrush(first);
1183         painter.setPen(Qt::NoPen);
1184 
1185         progress %= 2 * Metrics::ProgressBar_BusyIndicatorSize;
1186         if (reverse) {
1187             progress = 2 * Metrics::ProgressBar_BusyIndicatorSize - progress - 1;
1188         }
1189         painter.drawRect(QRect(0, 0, Metrics::ProgressBar_BusyIndicatorSize, 1).translated(progress, 0));
1190 
1191         if (progress > Metrics::ProgressBar_BusyIndicatorSize) {
1192             painter.drawRect(QRect(0, 0, Metrics::ProgressBar_BusyIndicatorSize, 1).translated(progress - 2 * Metrics::ProgressBar_BusyIndicatorSize, 0));
1193         }
1194 
1195     } else {
1196         QPainter painter(&pixmap);
1197         painter.setBrush(first);
1198         painter.setPen(Qt::NoPen);
1199 
1200         progress %= 2 * Metrics::ProgressBar_BusyIndicatorSize;
1201         progress = 2 * Metrics::ProgressBar_BusyIndicatorSize - progress - 1;
1202         painter.drawRect(QRect(0, 0, 1, Metrics::ProgressBar_BusyIndicatorSize).translated(0, progress));
1203 
1204         if (progress > Metrics::ProgressBar_BusyIndicatorSize) {
1205             painter.drawRect(QRect(0, 0, 1, Metrics::ProgressBar_BusyIndicatorSize).translated(0, progress - 2 * Metrics::ProgressBar_BusyIndicatorSize));
1206         }
1207     }
1208 
1209     painter->setPen(Qt::NoPen);
1210     painter->setBrush(pixmap);
1211     painter->drawRoundedRect(baseRect, radius, radius);
1212 }
1213 
1214 //______________________________________________________________________________
1215 void Helper::renderScrollBarHandle(QPainter *painter, const QRect &rect, const QColor &fg, const QColor &bg) const
1216 {
1217     // setup painter
1218     painter->setRenderHint(QPainter::Antialiasing, true);
1219 
1220     const QRectF baseRect(rect);
1221     const qreal radius(0.5 * std::min({baseRect.width(), baseRect.height(), (qreal)Metrics::ScrollBar_SliderWidth}));
1222 
1223     painter->setPen(Qt::NoPen);
1224     painter->setPen(QPen(fg, 1.001));
1225     painter->setBrush(KColorUtils::overlayColors(bg, alphaColor(fg, 0.5)));
1226     painter->drawRoundedRect(strokedRect(baseRect), radius, radius);
1227 }
1228 
1229 void Helper::renderScrollBarGroove(QPainter *painter, const QRect &rect, const QColor &color) const
1230 {
1231     // setup painter
1232     painter->setRenderHint(QPainter::Antialiasing, true);
1233 
1234     const QRectF baseRect(rect);
1235     const qreal radius(0.5 * std::min({baseRect.width(), baseRect.height(), (qreal)Metrics::ScrollBar_SliderWidth}));
1236 
1237     // content
1238     if (color.isValid()) {
1239         painter->setPen(Qt::NoPen);
1240         auto bg = color;
1241         bg.setAlphaF(bg.alphaF() / 2.0);
1242         painter->setBrush(bg);
1243         painter->setPen(QPen(color, 1.001));
1244         painter->drawRoundedRect(strokedRect(baseRect), radius, radius);
1245     }
1246 }
1247 
1248 //______________________________________________________________________________
1249 void Helper::renderScrollBarBorder(QPainter *painter, const QRect &rect, const QColor &color) const
1250 {
1251     // content
1252     if (color.isValid()) {
1253         painter->setPen(Qt::NoPen);
1254         painter->setBrush(color);
1255         painter->drawRect(rect);
1256     }
1257 }
1258 
1259 //______________________________________________________________________________
1260 void Helper::renderTabBarTab(QPainter *painter,
1261                              const QRect &rect,
1262                              const QPalette &palette,
1263                              const QHash<QByteArray, bool> &stateProperties,
1264                              Corners corners,
1265                              qreal animation) const
1266 {
1267     bool enabled = stateProperties.value("enabled", true);
1268     bool visualFocus = stateProperties.value("visualFocus");
1269     bool hovered = stateProperties.value("hovered");
1270     bool down = stateProperties.value("down");
1271     bool selected = stateProperties.value("selected");
1272     bool documentMode = stateProperties.value("documentMode");
1273     bool north = stateProperties.value("north");
1274     bool south = stateProperties.value("south");
1275     bool west = stateProperties.value("west");
1276     bool east = stateProperties.value("east");
1277     bool animated = animation != AnimationData::OpacityInvalid;
1278     bool isQtQuickControl = stateProperties.value("isQtQuickControl");
1279     bool hasAlteredBackground = stateProperties.value("hasAlteredBackground");
1280 
1281     // setup painter
1282     painter->setRenderHint(QPainter::Antialiasing, true);
1283     QRectF frameRect = rect;
1284     QColor bgBrush;
1285 
1286     if (selected) {
1287         if (north) {
1288             // overlap bottom border
1289             frameRect.adjust(0, 0, 0, 1);
1290         } else if (south) {
1291             // overlap top border
1292             frameRect.adjust(0, -1, 0, 0);
1293         } else if (west) {
1294             // overlap right border
1295             frameRect.adjust(0, 0, 1, 0);
1296         } else if (east) {
1297             // overlap left border
1298             frameRect.adjust(-1, 0, 0, 0);
1299         }
1300         if (documentMode && !isQtQuickControl && !hasAlteredBackground) {
1301             bgBrush = palette.color(QPalette::Window);
1302         } else {
1303             bgBrush = frameBackgroundColor(palette);
1304         }
1305         QColor penBrush = KColorUtils::mix(bgBrush, palette.color(QPalette::WindowText), 0.25);
1306         painter->setBrush(bgBrush);
1307         painter->setPen(QPen(penBrush, PenWidth::Frame));
1308         QRectF highlightRect = frameRect;
1309         if (north || south) {
1310             highlightRect.setHeight(Metrics::Frame_FrameRadius);
1311         } else if (west || east) {
1312             highlightRect.setWidth(Metrics::Frame_FrameRadius);
1313         }
1314         if (south) {
1315             highlightRect.moveBottom(frameRect.bottom());
1316         } else if (east) {
1317             highlightRect.moveRight(frameRect.right());
1318         }
1319         QPainterPath path = roundedPath(strokedRect(frameRect), corners, frameRadius(PenWidth::Frame));
1320         painter->drawPath(path);
1321         QPainterPath highlightPath = roundedPath(highlightRect, corners, Metrics::Frame_FrameRadius);
1322         painter->setBrush(palette.color(QPalette::Highlight));
1323         painter->setPen(Qt::NoPen);
1324         painter->drawPath(highlightPath);
1325     } else {
1326         if (north) {
1327             // don't overlap bottom border
1328             frameRect.adjust(0, 0, 0, -1);
1329         } else if (south) {
1330             // don't overlap top border
1331             frameRect.adjust(0, 1, 0, 0);
1332         } else if (west) {
1333             // don't overlap right border
1334             frameRect.adjust(0, 0, -1, 0);
1335         } else if (east) {
1336             // don't overlap left border
1337             frameRect.adjust(1, 0, 0, 0);
1338         }
1339         const auto windowColor = palette.color(QPalette::Window);
1340         bgBrush = windowColor.darker(120);
1341         const auto hover = alphaColor(hoverColor(palette), 0.2);
1342         if (animated) {
1343             bgBrush = KColorUtils::mix(bgBrush, hover, animation);
1344         } else if (enabled && hovered && !selected) {
1345             bgBrush = hover;
1346         }
1347         painter->setBrush(bgBrush);
1348         painter->setPen(Qt::NoPen);
1349         QPainterPath path = roundedPath(frameRect, corners, Metrics::Frame_FrameRadius);
1350         painter->drawPath(path);
1351     }
1352 }
1353 
1354 //______________________________________________________________________________
1355 void Helper::renderArrow(QPainter *painter, const QRect &rect, const QColor &color, ArrowOrientation orientation) const
1356 {
1357     int size = std::min({rect.width(), rect.height(), Metrics::ArrowSize});
1358     // No point in trying to draw if it's too small
1359     if (size <= 0) {
1360         return;
1361     }
1362 
1363     qreal penOffset = PenWidth::Symbol / 2.0;
1364     qreal center = size / 2.0;
1365     qreal maxExtent = size * 0.75;
1366     qreal minExtent = size / 4.0;
1367     qreal sizeOffset = 0;
1368     int remainder = size % 4;
1369     if (remainder == 2) {
1370         sizeOffset = 0.5;
1371     } else if (remainder == 1) {
1372         sizeOffset = -0.25;
1373     } else if (remainder == 3) {
1374         sizeOffset = 0.25;
1375     }
1376 
1377     QPolygonF arrow;
1378     switch (orientation) {
1379     case ArrowUp:
1380         arrow = QVector<QPointF>{
1381             {penOffset, maxExtent - penOffset - sizeOffset}, // left
1382             {center, minExtent - sizeOffset}, // mid
1383             {size - penOffset, maxExtent - penOffset - sizeOffset} // right
1384         };
1385         break;
1386     case ArrowDown:
1387         arrow = QVector<QPointF>{
1388             {penOffset, minExtent + penOffset + sizeOffset}, // left
1389             {center, maxExtent + sizeOffset}, // mid
1390             {size - penOffset, minExtent + penOffset + sizeOffset} // right
1391         };
1392         break;
1393     case ArrowLeft:
1394         arrow = QVector<QPointF>{
1395             {maxExtent - penOffset - sizeOffset, penOffset}, // top
1396             {minExtent - sizeOffset, center}, // mid
1397             {maxExtent - penOffset - sizeOffset, size - penOffset}, // bottom
1398         };
1399         break;
1400     case ArrowRight:
1401         arrow = QVector<QPointF>{
1402             {minExtent + penOffset + sizeOffset, penOffset}, // top
1403             {maxExtent + sizeOffset, center}, // mid
1404             {minExtent + penOffset + sizeOffset, size - penOffset}, // bottom
1405         };
1406         break;
1407     default:
1408         break;
1409     }
1410 
1411     arrow.translate(rect.x() + (rect.width() - size) / 2.0,
1412                     rect.y() + (rect.height() - size) / 2.0);
1413 
1414     painter->save();
1415     painter->setRenderHints(QPainter::Antialiasing);
1416     painter->setBrush(Qt::NoBrush);
1417     QPen pen(color, PenWidth::Symbol);
1418     pen.setCapStyle(Qt::SquareCap);
1419     pen.setJoinStyle(Qt::MiterJoin);
1420     painter->setPen(pen);
1421     painter->drawPolyline(arrow);
1422     painter->restore();
1423 }
1424 
1425 //______________________________________________________________________________
1426 void Helper::renderDecorationButton(QPainter *painter, const QRect &rect, const QColor &color, ButtonType buttonType, bool inverted) const
1427 {
1428     painter->save();
1429     painter->setViewport(rect);
1430     painter->setWindow(0, 0, 18, 18);
1431     painter->setRenderHints(QPainter::Antialiasing);
1432 
1433     // initialize pen
1434     QPen pen;
1435     pen.setCapStyle(Qt::RoundCap);
1436     pen.setJoinStyle(Qt::MiterJoin);
1437 
1438     if (inverted) {
1439         // render circle
1440         painter->setPen(Qt::NoPen);
1441         painter->setBrush(color);
1442         painter->drawEllipse(QRectF(0, 0, 18, 18));
1443 
1444         // take out the inner part
1445         painter->setCompositionMode(QPainter::CompositionMode_DestinationOut);
1446         painter->setBrush(Qt::NoBrush);
1447         pen.setColor(Qt::black);
1448 
1449     } else {
1450         painter->setBrush(Qt::NoBrush);
1451         pen.setColor(color);
1452     }
1453 
1454     pen.setCapStyle(Qt::RoundCap);
1455     pen.setJoinStyle(Qt::MiterJoin);
1456     pen.setWidthF(PenWidth::Symbol * qMax(1.0, 18.0 / rect.width()));
1457     painter->setPen(pen);
1458 
1459     switch (buttonType) {
1460     case ButtonClose: {
1461         painter->drawLine(QPointF(5, 5), QPointF(13, 13));
1462         painter->drawLine(13, 5, 5, 13);
1463         break;
1464     }
1465 
1466     case ButtonMaximize: {
1467         painter->drawPolyline(QVector<QPointF>{QPointF(4, 11), QPointF(9, 6), QPointF(14, 11)});
1468         break;
1469     }
1470 
1471     case ButtonMinimize: {
1472         painter->drawPolyline(QVector<QPointF>{QPointF(4, 7), QPointF(9, 12), QPointF(14, 7)});
1473         break;
1474     }
1475 
1476     case ButtonRestore: {
1477         pen.setJoinStyle(Qt::RoundJoin);
1478         painter->setPen(pen);
1479         painter->drawPolygon(QVector<QPointF>{QPointF(4.5, 9), QPointF(9, 4.5), QPointF(13.5, 9), QPointF(9, 13.5)});
1480         break;
1481     }
1482 
1483     default:
1484         break;
1485     }
1486 
1487     painter->restore();
1488 }
1489 
1490 //______________________________________________________________________________
1491 void Helper::renderRoundedRectShadow(QPainter *painter, const QRectF &rect, const QColor &color, qreal radius) const
1492 {
1493     if (!color.isValid()) {
1494         return;
1495     }
1496 
1497     painter->setRenderHint(QPainter::Antialiasing, true);
1498 
1499     qreal adjustment = 0.5 * PenWidth::Shadow; // Translate for the pen
1500     QRectF shadowRect = rect.adjusted(adjustment, adjustment, -adjustment, adjustment);
1501 
1502     painter->setPen(QPen(color, PenWidth::Shadow));
1503     painter->setBrush(Qt::NoBrush);
1504     painter->drawRoundedRect(shadowRect, radius, radius);
1505 }
1506 
1507 //______________________________________________________________________________
1508 void Helper::renderEllipseShadow(QPainter *painter, const QRectF &rect, const QColor &color) const
1509 {
1510     if (!color.isValid()) {
1511         return;
1512     }
1513 
1514     painter->save();
1515 
1516     // Clipping does not improve performance here
1517 
1518     qreal adjustment = 0.5 * PenWidth::Shadow; // Adjust for the pen
1519 
1520     qreal radius = rect.width() / 2 - adjustment;
1521 
1522     /* The right side is offset by +0.5 for the visible part of the shadow.
1523      * The other sides are offset by +0.5 or -0.5 because of the pen.
1524      */
1525     QRectF shadowRect = rect.adjusted(adjustment, adjustment, adjustment, -adjustment);
1526 
1527     painter->translate(rect.center());
1528     painter->rotate(45);
1529     painter->translate(-rect.center());
1530     painter->setPen(color);
1531     painter->setBrush(Qt::NoBrush);
1532     painter->drawRoundedRect(shadowRect, radius, radius);
1533 
1534     painter->restore();
1535 }
1536 
1537 //______________________________________________________________________________
1538 bool Helper::isX11()
1539 {
1540     static const bool s_isX11 = KWindowSystem::isPlatformX11();
1541     return s_isX11;
1542 }
1543 
1544 //______________________________________________________________________________
1545 bool Helper::isWayland()
1546 {
1547     static const bool s_isWayland = KWindowSystem::isPlatformWayland();
1548     return s_isWayland;
1549 }
1550 
1551 //______________________________________________________________________________
1552 QRectF Helper::strokedRect(const QRectF &rect, const qreal penWidth) const
1553 {
1554     /* With a pen stroke width of 1, the rectangle should have each of its
1555      * sides moved inwards by half a pixel. This allows the stroke to be
1556      * pixel perfect instead of blurry from sitting between pixels and
1557      * prevents the rectangle with a stroke from becoming larger than the
1558      * original size of the rectangle.
1559      */
1560     qreal adjustment = 0.5 * penWidth;
1561     return rect.adjusted(adjustment, adjustment, -adjustment, -adjustment);
1562 }
1563 
1564 //______________________________________________________________________________
1565 QPainterPath Helper::roundedPath(const QRectF &rect, Corners corners, qreal radius) const
1566 {
1567     QPainterPath path;
1568 
1569     // simple cases
1570     if (corners == 0) {
1571         path.addRect(rect);
1572         return path;
1573     }
1574 
1575     if (corners == AllCorners) {
1576         path.addRoundedRect(rect, radius, radius);
1577         return path;
1578     }
1579 
1580     const QSizeF cornerSize(2 * radius, 2 * radius);
1581 
1582     // rotate counterclockwise
1583     // top left corner
1584     if (corners & CornerTopLeft) {
1585         path.moveTo(rect.topLeft() + QPointF(radius, 0));
1586         path.arcTo(QRectF(rect.topLeft(), cornerSize), 90, 90);
1587 
1588     } else {
1589         path.moveTo(rect.topLeft());
1590     }
1591 
1592     // bottom left corner
1593     if (corners & CornerBottomLeft) {
1594         path.lineTo(rect.bottomLeft() - QPointF(0, radius));
1595         path.arcTo(QRectF(rect.bottomLeft() - QPointF(0, 2 * radius), cornerSize), 180, 90);
1596 
1597     } else {
1598         path.lineTo(rect.bottomLeft());
1599     }
1600 
1601     // bottom right corner
1602     if (corners & CornerBottomRight) {
1603         path.lineTo(rect.bottomRight() - QPointF(radius, 0));
1604         path.arcTo(QRectF(rect.bottomRight() - QPointF(2 * radius, 2 * radius), cornerSize), 270, 90);
1605 
1606     } else {
1607         path.lineTo(rect.bottomRight());
1608     }
1609 
1610     // top right corner
1611     if (corners & CornerTopRight) {
1612         path.lineTo(rect.topRight() + QPointF(0, radius));
1613         path.arcTo(QRectF(rect.topRight() - QPointF(2 * radius, 0), cornerSize), 0, 90);
1614 
1615     } else {
1616         path.lineTo(rect.topRight());
1617     }
1618 
1619     path.closeSubpath();
1620     return path;
1621 }
1622 
1623 //________________________________________________________________________________________________________
1624 bool Helper::compositingActive() const
1625 {
1626 #if BREEZE_HAVE_QTX11EXTRAS
1627     if (isX11()) {
1628         return QX11Info::isCompositingManagerRunning(QX11Info::appScreen());
1629     }
1630 #endif
1631 
1632     return true;
1633 }
1634 
1635 //____________________________________________________________________
1636 bool Helper::hasAlphaChannel(const QWidget *widget) const
1637 {
1638     return compositingActive() && widget && widget->testAttribute(Qt::WA_TranslucentBackground);
1639 }
1640 
1641 //______________________________________________________________________________________
1642 
1643 QPixmap Helper::coloredIcon(const QIcon &icon, const QPalette &palette, const QSize &size, QIcon::Mode mode, QIcon::State state)
1644 {
1645     const QPalette activePalette = KIconLoader::global()->customPalette();
1646     const bool changePalette = activePalette != palette;
1647     if (changePalette) {
1648         KIconLoader::global()->setCustomPalette(palette);
1649     }
1650     const QPixmap pixmap = icon.pixmap(size, mode, state);
1651     if (changePalette) {
1652         if (activePalette == QPalette()) {
1653             KIconLoader::global()->resetPalette();
1654         } else {
1655             KIconLoader::global()->setCustomPalette(activePalette);
1656         }
1657     }
1658     return pixmap;
1659 }
1660 
1661 bool Helper::shouldDrawToolsArea(const QWidget *widget) const
1662 {
1663     if (!widget) {
1664         return false;
1665     }
1666     static bool isAuto = false;
1667     static QString borderSize;
1668     if (!_cachedAutoValid) {
1669         KConfigGroup kdecorationGroup(_kwinConfig->group("org.kde.kdecoration2"));
1670         isAuto = kdecorationGroup.readEntry("BorderSizeAuto", true);
1671         borderSize = kdecorationGroup.readEntry("BorderSize", "Normal");
1672         _cachedAutoValid = true;
1673     }
1674     if (isAuto) {
1675         auto window = widget->window();
1676         if (qobject_cast<const QDialog *>(widget)) {
1677             return true;
1678         }
1679         if (window) {
1680             auto handle = window->windowHandle();
1681             if (handle) {
1682                 auto toolbar = qobject_cast<const QToolBar *>(widget);
1683                 if (toolbar) {
1684                     if (toolbar->isFloating()) {
1685                         return false;
1686                     }
1687                 }
1688                 return true;
1689             }
1690         } else {
1691             return false;
1692         }
1693     }
1694     if (borderSize != "None" && borderSize != "NoSides") {
1695         return false;
1696     }
1697     return true;
1698 }
1699 }