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