File indexing completed on 2024-05-05 05:35:17

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