Warning, file /plasma/oxygen/kdecoration/oxygendecoration.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004     SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "oxygendecoration.h"
0010 
0011 #include "config-oxygen.h"
0012 #include "config/oxygenconfigwidget.h"
0013 #include "oxygensettingsprovider.h"
0014 #include "oxygenshadowcache.h"
0015 
0016 #include "oxygenbutton.h"
0017 #include "oxygensizegrip.h"
0018 
0019 #include <KDecoration2/DecorationButtonGroup>
0020 #include <KDecoration2/DecorationShadow>
0021 
0022 #include <KColorUtils>
0023 #include <KConfigGroup>
0024 #include <KPluginFactory>
0025 #include <KSharedConfig>
0026 
0027 #include <QPainter>
0028 #include <QTextStream>
0029 #include <QTimer>
0030 
0031 #if OXYGEN_HAVE_X11
0032 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0033 #include <QX11Info>
0034 #else
0035 #include <private/qtx11extras_p.h>
0036 #endif
0037 #endif
0038 
0039 K_PLUGIN_FACTORY_WITH_JSON(OxygenDecoFactory, "oxygen.json", registerPlugin<Oxygen::Decoration>(); registerPlugin<Oxygen::Button>();
0040                            registerPlugin<Oxygen::ConfigWidget>();)
0041 
0042 namespace Oxygen
0043 {
0044 
0045 using KDecoration2::ColorGroup;
0046 using KDecoration2::ColorRole;
0047 
0048 //________________________________________________________________
0049 using DecorationShadowPointer = QSharedPointer<KDecoration2::DecorationShadow>;
0050 using ShadowMap = QHash<int, DecorationShadowPointer>;
0051 
0052 static int g_sDecoCount = 0;
0053 static ShadowMap g_sShadows;
0054 
0055 Decoration::Decoration(QObject *parent, const QVariantList &args)
0056     : KDecoration2::Decoration(parent, args)
0057     , m_animation(new QPropertyAnimation(this))
0058 {
0059     g_sDecoCount++;
0060 }
0061 
0062 //________________________________________________________________
0063 Decoration::~Decoration()
0064 {
0065     g_sDecoCount--;
0066     if (g_sDecoCount == 0)
0067         g_sShadows.clear();
0068 
0069     deleteSizeGrip();
0070 }
0071 
0072 //________________________________________________________________
0073 void Decoration::setOpacity(qreal value)
0074 {
0075     if (m_opacity == value)
0076         return;
0077     m_opacity = value;
0078     updateShadow();
0079     update();
0080 
0081     if (m_sizeGrip)
0082         m_sizeGrip->update();
0083 }
0084 
0085 //_________________________________________________________
0086 QColor Decoration::titleBarColor(const QPalette &palette) const
0087 {
0088     if (m_animation->state() == QPropertyAnimation::Running) {
0089         return KColorUtils::mix(titleBarColor(palette, false), titleBarColor(palette, true), m_opacity);
0090 
0091     } else {
0092         return titleBarColor(palette, client().toStrongRef()->isActive());
0093     }
0094 }
0095 
0096 //_________________________________________________________
0097 QColor Decoration::titleBarColor(const QPalette &palette, bool active) const
0098 {
0099     if (m_internalSettings->useWindowColors()) {
0100         return palette.color(active ? QPalette::Active : QPalette::Inactive, QPalette::Window);
0101 
0102     } else {
0103         return client().toStrongRef()->color(active ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::TitleBar);
0104     }
0105 }
0106 
0107 //_________________________________________________________
0108 QColor Decoration::fontColor(const QPalette &palette) const
0109 {
0110     if (hideTitleBar())
0111         return fontColor(palette, false);
0112     if (m_animation->state() == QPropertyAnimation::Running) {
0113         return KColorUtils::mix(fontColor(palette, false), fontColor(palette, true), m_opacity);
0114 
0115     } else {
0116         return fontColor(palette, client().toStrongRef()->isActive());
0117     }
0118 }
0119 
0120 //_________________________________________________________
0121 QColor Decoration::fontColor(const QPalette &palette, bool active) const
0122 {
0123     if (m_internalSettings->useWindowColors()) {
0124         return palette.color(active ? QPalette::Active : QPalette::Disabled, QPalette::WindowText);
0125 
0126     } else {
0127         return client().toStrongRef()->color(active ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::Foreground);
0128     }
0129 }
0130 
0131 //_________________________________________________________
0132 QColor Decoration::contrastColor(const QPalette &palette) const
0133 {
0134     if (m_internalSettings->useWindowColors())
0135         return contrastColor(palette.color(QPalette::Window));
0136     else {
0137         const auto cl = client().toStrongRef();
0138         return contrastColor(cl->color(cl->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::TitleBar));
0139     }
0140 }
0141 
0142 //_________________________________________________________
0143 QColor Decoration::contrastColor(const QColor &color) const
0144 {
0145     return SettingsProvider::self()->helper()->calcLightColor(color);
0146 }
0147 
0148 //________________________________________________________________
0149 void Decoration::init()
0150 {
0151     // active state change animation
0152     m_animation->setStartValue(0);
0153     m_animation->setEndValue(1.0);
0154     m_animation->setTargetObject(this);
0155     m_animation->setPropertyName("opacity");
0156     m_animation->setEasingCurve(QEasingCurve::InOutQuad);
0157 
0158     reconfigure();
0159     updateTitleBar();
0160     auto s = settings();
0161     connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, this, &Decoration::recalculateBorders);
0162 
0163     // a change in font might cause the borders to change
0164     connect(s.data(), &KDecoration2::DecorationSettings::fontChanged, this, &Decoration::recalculateBorders);
0165     connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::recalculateBorders);
0166 
0167     // buttons
0168     connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::updateButtonsGeometryDelayed);
0169     connect(s.data(), &KDecoration2::DecorationSettings::decorationButtonsLeftChanged, this, &Decoration::updateButtonsGeometryDelayed);
0170     connect(s.data(), &KDecoration2::DecorationSettings::decorationButtonsRightChanged, this, &Decoration::updateButtonsGeometryDelayed);
0171 
0172     // full reconfiguration
0173     connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::reconfigure);
0174     connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, SettingsProvider::self(), &SettingsProvider::reconfigure, Qt::UniqueConnection);
0175 
0176     const auto *cl = client().toStrongRef().data();
0177     connect(cl, &KDecoration2::DecoratedClient::adjacentScreenEdgesChanged, this, &Decoration::recalculateBorders);
0178     connect(cl, &KDecoration2::DecoratedClient::maximizedHorizontallyChanged, this, &Decoration::recalculateBorders);
0179     connect(cl, &KDecoration2::DecoratedClient::maximizedVerticallyChanged, this, &Decoration::recalculateBorders);
0180     connect(cl, &KDecoration2::DecoratedClient::captionChanged, this, [this]() {
0181         // update the caption area
0182         update(titleBar());
0183     });
0184 
0185     connect(cl, &KDecoration2::DecoratedClient::activeChanged, this, &Decoration::updateAnimationState);
0186     connect(cl, &KDecoration2::DecoratedClient::activeChanged, this, &Decoration::updateShadow);
0187 
0188     // decoration has an overloaded update function, force the compiler to choose the right one
0189     connect(cl, &KDecoration2::DecoratedClient::paletteChanged, this, static_cast<void (Decoration::*)()>(&Decoration::update));
0190     connect(cl, &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateTitleBar);
0191     connect(cl, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateTitleBar);
0192     connect(cl, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::setOpaque);
0193 
0194     connect(cl, &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateButtonsGeometry);
0195     connect(cl, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateButtonsGeometry);
0196     connect(cl, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::recalculateBorders);
0197     connect(cl, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateButtonsGeometry);
0198 
0199     createButtons();
0200     updateShadow();
0201 }
0202 
0203 //________________________________________________________________
0204 void Decoration::updateTitleBar()
0205 {
0206     auto s = settings();
0207     const bool maximized = isMaximized();
0208     const auto clientPtr = client().toStrongRef();
0209     const int width = maximized ? clientPtr->width() : clientPtr->width() - 2 * s->largeSpacing() * Metrics::TitleBar_SideMargin;
0210     const int height = maximized ? borderTop() : borderTop() - s->smallSpacing() * Metrics::TitleBar_TopMargin;
0211     const int x = maximized ? 0 : s->largeSpacing() * Metrics::TitleBar_SideMargin;
0212     const int y = maximized ? 0 : s->smallSpacing() * Metrics::TitleBar_TopMargin;
0213     setTitleBar(QRect(x, y, width, height));
0214 }
0215 
0216 //________________________________________________________________
0217 void Decoration::updateAnimationState()
0218 {
0219     if (m_internalSettings->animationsEnabled()) {
0220         m_animation->setDirection(client().toStrongRef()->isActive() ? QPropertyAnimation::Forward : QPropertyAnimation::Backward);
0221         if (m_animation->state() != QPropertyAnimation::Running)
0222             m_animation->start();
0223 
0224     } else {
0225         update();
0226     }
0227 }
0228 
0229 //________________________________________________________________
0230 void Decoration::updateSizeGripVisibility()
0231 {
0232     const auto c = client().toStrongRef();
0233     if (m_sizeGrip) {
0234         m_sizeGrip->setVisible(c->isResizeable() && !isMaximized() && !c->isShaded());
0235     }
0236 }
0237 
0238 //________________________________________________________________
0239 int Decoration::borderSize(bool bottom) const
0240 {
0241     const int baseSize = settings()->smallSpacing();
0242     if (m_internalSettings && (m_internalSettings->mask() & BorderSize)) {
0243         switch (m_internalSettings->borderSize()) {
0244         case InternalSettings::BorderNone:
0245             return 0;
0246         case InternalSettings::BorderNoSides:
0247             return bottom ? qMax(4, baseSize) : 0;
0248         default:
0249         case InternalSettings::BorderTiny:
0250             return bottom ? qMax(4, baseSize) : baseSize;
0251         case InternalSettings::BorderNormal:
0252             return baseSize * 2;
0253         case InternalSettings::BorderLarge:
0254             return baseSize * 3;
0255         case InternalSettings::BorderVeryLarge:
0256             return baseSize * 4;
0257         case InternalSettings::BorderHuge:
0258             return baseSize * 5;
0259         case InternalSettings::BorderVeryHuge:
0260             return baseSize * 6;
0261         case InternalSettings::BorderOversized:
0262             return baseSize * 10;
0263         }
0264 
0265     } else {
0266         switch (settings()->borderSize()) {
0267         case KDecoration2::BorderSize::None:
0268             return 0;
0269         case KDecoration2::BorderSize::NoSides:
0270             return bottom ? qMax(4, baseSize) : 0;
0271         default:
0272         case KDecoration2::BorderSize::Tiny:
0273             return bottom ? qMax(4, baseSize) : baseSize;
0274         case KDecoration2::BorderSize::Normal:
0275             return baseSize * 2;
0276         case KDecoration2::BorderSize::Large:
0277             return baseSize * 3;
0278         case KDecoration2::BorderSize::VeryLarge:
0279             return baseSize * 4;
0280         case KDecoration2::BorderSize::Huge:
0281             return baseSize * 5;
0282         case KDecoration2::BorderSize::VeryHuge:
0283             return baseSize * 6;
0284         case KDecoration2::BorderSize::Oversized:
0285             return baseSize * 10;
0286         }
0287     }
0288 }
0289 
0290 //________________________________________________________________
0291 void Decoration::reconfigure()
0292 {
0293     m_internalSettings = SettingsProvider::self()->internalSettings(this);
0294 
0295     // animation
0296     m_animation->setDuration(m_internalSettings->shadowAnimationsDuration());
0297 
0298     // borders
0299     recalculateBorders();
0300 
0301     // clear shadows
0302     g_sShadows.clear();
0303 
0304     // size grip
0305     if (hasNoBorders() && m_internalSettings->drawSizeGrip())
0306         createSizeGrip();
0307     else
0308         deleteSizeGrip();
0309 }
0310 
0311 //________________________________________________________________
0312 void Decoration::recalculateBorders()
0313 {
0314     auto s = settings();
0315     const auto c = client().toStrongRef();
0316     const auto edges = c->adjacentScreenEdges();
0317 
0318     // left, right and bottom borders
0319     auto testFlag = [&](Qt::Edge edge) {
0320         return edges.testFlag(edge) && !m_internalSettings->drawBorderOnMaximizedWindows();
0321     };
0322     const int left = isMaximizedHorizontally() || testFlag(Qt::LeftEdge) ? 0 : borderSize();
0323     const int right = isMaximizedHorizontally() || testFlag(Qt::RightEdge) ? 0 : borderSize();
0324     const int bottom = isMaximizedVertically() || c->isShaded() || testFlag(Qt::BottomEdge) ? 0 : borderSize(true);
0325 
0326     int top = 0;
0327     if (hideTitleBar())
0328         top = bottom;
0329     else {
0330         QFontMetrics fm(s->font());
0331         top += qMax(fm.height(), buttonHeight());
0332 
0333         // padding below
0334         const int baseSize = s->smallSpacing();
0335         top += baseSize * Metrics::TitleBar_BottomMargin;
0336 
0337         // padding above
0338         top += baseSize * TitleBar_TopMargin;
0339     }
0340 
0341     setBorders(QMargins(left, top, right, bottom));
0342 
0343     // extended sizes
0344     const int extSize = s->largeSpacing();
0345     int extSides = 0;
0346     int extBottom = 0;
0347     if (hasNoBorders()) {
0348         if (!isMaximizedHorizontally())
0349             extSides = extSize;
0350         if (!isMaximizedVertically())
0351             extBottom = extSize;
0352 
0353     } else if (hasNoSideBorders() && !isMaximizedHorizontally()) {
0354         extSides = extSize;
0355     }
0356 
0357     setResizeOnlyBorders(QMargins(extSides, 0, extSides, extBottom));
0358 }
0359 
0360 //________________________________________________________________
0361 void Decoration::createButtons()
0362 {
0363     m_leftButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Left, this, &Button::create);
0364     m_rightButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Right, this, &Button::create);
0365     updateButtonsGeometry();
0366 }
0367 
0368 //________________________________________________________________
0369 void Decoration::updateButtonsGeometryDelayed()
0370 {
0371     QTimer::singleShot(0, this, &Decoration::updateButtonsGeometry);
0372 }
0373 
0374 //________________________________________________________________
0375 void Decoration::updateButtonsGeometry()
0376 {
0377     auto s = settings();
0378 
0379     // adjust button position
0380     const int bHeight = captionHeight() + (isMaximized() ? s->smallSpacing() * Metrics::TitleBar_TopMargin : 0);
0381     const int bWidth = buttonHeight();
0382     const int verticalOffset = (isMaximized() ? s->smallSpacing() * Metrics::TitleBar_TopMargin : 0) + (captionHeight() - buttonHeight()) / 2;
0383 
0384     const QVector<QPointer<KDecoration2::DecorationButton>> leftButtons = m_leftButtons->buttons();
0385     const QVector<QPointer<KDecoration2::DecorationButton>> rightButtons = m_rightButtons->buttons();
0386 
0387     const auto allButtons = leftButtons + rightButtons;
0388     for (const auto &button : allButtons) {
0389         button.data()->setGeometry(QRectF(QPoint(0, 0), QSizeF(bWidth, bHeight)));
0390         static_cast<Button *>(button.data())->setOffset(QPointF(0, verticalOffset));
0391         static_cast<Button *>(button.data())->setIconSize(QSize(bWidth, bWidth));
0392     }
0393 
0394     // left buttons
0395     if (!leftButtons.isEmpty()) {
0396         // spacing
0397         m_leftButtons->setSpacing(s->smallSpacing() * Metrics::TitleBar_ButtonSpacing);
0398 
0399         // padding
0400         const int vPadding = isMaximized() ? 0 : s->smallSpacing() * Metrics::TitleBar_TopMargin;
0401         const int hPadding = s->smallSpacing() * Metrics::TitleBar_SideMargin;
0402         if (isMaximizedHorizontally()) {
0403             // add offsets on the side buttons, to preserve padding, but satisfy Fitts law
0404             auto button = static_cast<Button *>(leftButtons.front().data());
0405             button->setGeometry(QRectF(QPoint(0, 0), QSizeF(bWidth + hPadding, bHeight)));
0406             button->setFlag(Button::FlagFirstInList);
0407             button->setHorizontalOffset(hPadding);
0408 
0409             m_leftButtons->setPos(QPointF(0, vPadding));
0410 
0411         } else
0412             m_leftButtons->setPos(QPointF(hPadding + borderLeft(), vPadding));
0413     }
0414 
0415     // right buttons
0416     if (!rightButtons.isEmpty()) {
0417         // spacing
0418         m_rightButtons->setSpacing(s->smallSpacing() * Metrics::TitleBar_ButtonSpacing);
0419 
0420         // padding
0421         const int vPadding = isMaximized() ? 0 : s->smallSpacing() * Metrics::TitleBar_TopMargin;
0422         const int hPadding = s->smallSpacing() * Metrics::TitleBar_SideMargin;
0423         if (isMaximizedHorizontally()) {
0424             auto button = static_cast<Button *>(rightButtons.back().data());
0425             button->setGeometry(QRectF(QPoint(0, 0), QSizeF(bWidth + hPadding, bHeight)));
0426             button->setFlag(Button::FlagLastInList);
0427 
0428             m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width(), vPadding));
0429 
0430         } else
0431             m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width() - hPadding - borderRight(), vPadding));
0432     }
0433 
0434     update();
0435 }
0436 
0437 //________________________________________________________________
0438 void Decoration::paint(QPainter *painter, const QRect &repaintRegion)
0439 {
0440     const auto c = client().toStrongRef();
0441     const auto palette = c->palette();
0442 
0443     const auto rect = c->isShaded() ? QRect(QPoint(0, 0), QSize(size().width(), borderTop())) : this->rect();
0444     renderWindowBorder(painter, rect, palette);
0445     if (!isMaximized())
0446         renderCorners(painter, rect, palette);
0447 
0448     if (!hideTitleBar()) {
0449         m_leftButtons->paint(painter, repaintRegion);
0450         m_rightButtons->paint(painter, repaintRegion);
0451 
0452         renderTitleText(painter, palette);
0453     }
0454 }
0455 
0456 //________________________________________________________________
0457 int Decoration::buttonHeight() const
0458 {
0459     const int baseSize = settings()->gridUnit() + 2; // oxygen icons were always slightly larger
0460     switch (m_internalSettings->buttonSize()) {
0461     case InternalSettings::ButtonSmall:
0462         return baseSize * 1.5;
0463     default:
0464     case InternalSettings::ButtonDefault:
0465         return baseSize * 2;
0466     case InternalSettings::ButtonLarge:
0467         return baseSize * 2.5;
0468     case InternalSettings::ButtonVeryLarge:
0469         return baseSize * 3.5;
0470     }
0471 }
0472 
0473 //________________________________________________________________
0474 int Decoration::captionHeight() const
0475 {
0476     return hideTitleBar() ? borderTop() : borderTop() - settings()->smallSpacing() * (Metrics::TitleBar_BottomMargin + Metrics::TitleBar_TopMargin);
0477 }
0478 
0479 //________________________________________________________________
0480 QPair<QRect, Qt::Alignment> Decoration::captionRect() const
0481 {
0482     if (hideTitleBar())
0483         return qMakePair(QRect(), Qt::AlignCenter);
0484     else {
0485         const int leftOffset = m_leftButtons->buttons().isEmpty()
0486             ? Metrics::TitleBar_SideMargin * settings()->smallSpacing()
0487             : m_leftButtons->geometry().x() + m_leftButtons->geometry().width() + Metrics::TitleBar_SideMargin * settings()->smallSpacing();
0488 
0489         const int rightOffset = m_rightButtons->buttons().isEmpty()
0490             ? Metrics::TitleBar_SideMargin * settings()->smallSpacing()
0491             : size().width() - m_rightButtons->geometry().x() + Metrics::TitleBar_SideMargin * settings()->smallSpacing();
0492 
0493         const int yOffset = settings()->smallSpacing() * Metrics::TitleBar_TopMargin;
0494         const QRect maxRect(leftOffset, yOffset, size().width() - leftOffset - rightOffset, captionHeight());
0495 
0496         switch (m_internalSettings->titleAlignment()) {
0497         case InternalSettings::AlignLeft:
0498             return qMakePair(maxRect, Qt::AlignVCenter | Qt::AlignLeft);
0499 
0500         case InternalSettings::AlignRight:
0501             return qMakePair(maxRect, Qt::AlignVCenter | Qt::AlignRight);
0502 
0503         case InternalSettings::AlignCenter:
0504             return qMakePair(maxRect, Qt::AlignCenter);
0505 
0506         default:
0507         case InternalSettings::AlignCenterFullWidth: {
0508             // full caption rect
0509             const QRect fullRect = QRect(0, yOffset, size().width(), captionHeight());
0510             QRect boundingRect(settings()->fontMetrics().boundingRect(client().toStrongRef()->caption()).toRect());
0511 
0512             // text bounding rect
0513             boundingRect.setTop(yOffset);
0514             boundingRect.setHeight(captionHeight());
0515             boundingRect.moveLeft((size().width() - boundingRect.width()) / 2);
0516 
0517             if (boundingRect.left() < leftOffset)
0518                 return qMakePair(maxRect, Qt::AlignVCenter | Qt::AlignLeft);
0519             else if (boundingRect.right() > size().width() - rightOffset)
0520                 return qMakePair(maxRect, Qt::AlignVCenter | Qt::AlignRight);
0521             else
0522                 return qMakePair(fullRect, Qt::AlignCenter);
0523         }
0524         }
0525     }
0526 }
0527 
0528 //________________________________________________________________
0529 void Decoration::updateShadow()
0530 {
0531     // do nothing if palettes are disabled
0532     if (!(SettingsProvider::self()->shadowCache()->isEnabled(QPalette::Active) || SettingsProvider::self()->shadowCache()->isEnabled(QPalette::Inactive))) {
0533         return;
0534     }
0535 
0536     // see if shadow should be animated
0537     const bool animated(m_animation->state() == QPropertyAnimation::Running && SettingsProvider::self()->shadowCache()->isEnabled(QPalette::Active)
0538                         && SettingsProvider::self()->shadowCache()->isEnabled(QPalette::Inactive));
0539 
0540     // generate key
0541     ShadowCache::Key key;
0542     const auto clientPtr = client().toStrongRef();
0543     key.active = SettingsProvider::self()->shadowCache()->isEnabled(QPalette::Active) && clientPtr->isActive();
0544     key.isShade = clientPtr->isShaded();
0545     key.hasBorder = !hasNoBorders();
0546 
0547     if (animated) {
0548         static const int maxIndex = 255;
0549         key.index = m_opacity * maxIndex;
0550     }
0551 
0552     const int hash(key.hash());
0553 
0554     // find key in map
0555     auto iter = g_sShadows.find(hash);
0556     if (iter == g_sShadows.end()) {
0557         auto decorationShadow = DecorationShadowPointer::create();
0558         QPixmap shadowPixmap =
0559             animated ? SettingsProvider::self()->shadowCache()->animatedPixmap(key, m_opacity) : SettingsProvider::self()->shadowCache()->pixmap(key);
0560 
0561         const int shadowSize(shadowPixmap.width() / 2);
0562         const int overlap = 4;
0563         decorationShadow->setPadding(QMargins(shadowSize - overlap, shadowSize - overlap, shadowSize - overlap, shadowSize - overlap));
0564         decorationShadow->setInnerShadowRect(QRect(shadowSize, shadowSize, 1, 1));
0565         decorationShadow->setShadow(shadowPixmap.toImage());
0566 
0567         iter = g_sShadows.insert(hash, decorationShadow);
0568     }
0569 
0570     setShadow(iter.value());
0571 }
0572 
0573 //_________________________________________________________
0574 void Decoration::renderCorners(QPainter *painter, const QRect &frame, const QPalette &palette) const
0575 {
0576     const QColor color(titleBarColor(palette));
0577 
0578     QLinearGradient lg = QLinearGradient(0, -0.5, 0, qreal(frame.height()) + 0.5);
0579     lg.setColorAt(0.0, SettingsProvider::self()->helper()->calcLightColor(SettingsProvider::self()->helper()->backgroundTopColor(color)));
0580     lg.setColorAt(0.51, SettingsProvider::self()->helper()->backgroundBottomColor(color));
0581     lg.setColorAt(1.0, SettingsProvider::self()->helper()->backgroundBottomColor(color));
0582 
0583     painter->setPen(QPen(lg, 1));
0584     painter->setBrush(Qt::NoBrush);
0585     painter->drawRoundedRect(QRectF(frame).adjusted(0.5, 0.5, -0.5, -0.5), 3.5, 3.5);
0586 }
0587 
0588 //_________________________________________________________
0589 void Decoration::renderWindowBackground(QPainter *painter, const QRect &clipRect, const QPalette &palette) const
0590 {
0591     const auto c = client().toStrongRef();
0592     auto innerClientRect = c->isShaded() ? QRect(QPoint(0, 0), QSize(size().width(), borderTop())) : rect();
0593 
0594     // size of window minus the outlines for the rounded corners
0595     if (settings()->isAlphaChannelSupported() && !isMaximized()) {
0596         innerClientRect.adjust(1, 1, -1, -1);
0597     }
0598 
0599     if (SettingsProvider::self()->helper()->hasBackgroundGradient(c->windowId())) {
0600         SettingsProvider::self()->helper()->renderWindowBackground(painter, clipRect, innerClientRect, titleBarColor(palette), borderTop() - 24);
0601 
0602     } else {
0603         painter->fillRect(innerClientRect, titleBarColor(palette));
0604     }
0605 }
0606 
0607 //_________________________________________________________
0608 void Decoration::renderWindowBorder(QPainter *painter, const QRect &clipRect, const QPalette &palette) const
0609 {
0610     // save painter
0611     if (clipRect.isValid()) {
0612         painter->save();
0613         painter->setClipRegion(clipRect, Qt::IntersectClip);
0614     }
0615 
0616     // title height
0617     renderWindowBackground(painter, clipRect, palette);
0618 
0619     // restore painter
0620     if (clipRect.isValid()) {
0621         painter->restore();
0622     }
0623 }
0624 
0625 //_________________________________________________________
0626 void Decoration::renderTitleText(QPainter *painter, const QPalette &palette) const
0627 {
0628     // setup font
0629     painter->setFont(settings()->font());
0630 
0631     // caption rect
0632     const auto cR = captionRect();
0633 
0634     // copy caption
0635     const QString caption = painter->fontMetrics().elidedText(client().toStrongRef()->caption(), Qt::ElideMiddle, cR.first.width());
0636 
0637     const auto contrast(contrastColor(palette));
0638     if (contrast.isValid()) {
0639         painter->setPen(contrast);
0640         painter->translate(0, 1);
0641         painter->drawText(cR.first, cR.second | Qt::TextSingleLine, caption);
0642         painter->translate(0, -1);
0643     }
0644 
0645     const auto color(fontColor(palette));
0646     painter->setPen(color);
0647     painter->drawText(cR.first, cR.second | Qt::TextSingleLine, caption);
0648 }
0649 
0650 //_________________________________________________________________
0651 void Decoration::createSizeGrip(void)
0652 {
0653     // do nothing if size grip already exist
0654     if (m_sizeGrip)
0655         return;
0656 
0657 #if OXYGEN_HAVE_X11
0658     if (!QX11Info::isPlatformX11())
0659         return;
0660 
0661     // access client
0662     const auto clientPtr = client().toStrongRef();
0663     if (!clientPtr)
0664         return;
0665 
0666     if (clientPtr->windowId() != 0) {
0667         m_sizeGrip = new SizeGrip(this);
0668         const auto *clientP = clientPtr.data();
0669         connect(clientP, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateSizeGripVisibility);
0670         connect(clientP, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateSizeGripVisibility);
0671         connect(clientP, &KDecoration2::DecoratedClient::resizeableChanged, this, &Decoration::updateSizeGripVisibility);
0672     }
0673 #endif
0674 }
0675 
0676 //_________________________________________________________________
0677 void Decoration::deleteSizeGrip(void)
0678 {
0679     if (m_sizeGrip) {
0680         m_sizeGrip->deleteLater();
0681         m_sizeGrip = nullptr;
0682     }
0683 }
0684 
0685 } // namespace
0686 
0687 #include "oxygendecoration.moc"