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 }