Warning, file /plasma/oxygen/kdecoration/oxygenbutton.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: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0003     SPDX-FileCopyrightText: 2006, 2007 Riccardo Iaconelli <riccardo@kde.org>
0004     SPDX-FileCopyrightText: 2006, 2007 Casper Boemann <cbr@boemann.dk>
0005     SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #include "oxygenbutton.h"
0011 #include "oxygensettingsprovider.h"
0012 
0013 #include <KColorScheme>
0014 #include <KColorUtils>
0015 #include <KDecoration2/DecoratedClient>
0016 
0017 #include <QPainter>
0018 
0019 namespace Oxygen
0020 {
0021 
0022 //____________________________________________________________________________________
0023 Button *Button::create(KDecoration2::DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent)
0024 {
0025     if (auto d = qobject_cast<Decoration *>(decoration)) {
0026         const auto clientPtr = d->client().toStrongRef();
0027         Button *b = new Button(type, d, parent);
0028         switch (type) {
0029         case KDecoration2::DecorationButtonType::Close:
0030             b->setVisible(clientPtr->isCloseable());
0031             QObject::connect(clientPtr.data(), &KDecoration2::DecoratedClient::closeableChanged, b, &Oxygen::Button::setVisible);
0032             break;
0033 
0034         case KDecoration2::DecorationButtonType::Maximize:
0035             b->setVisible(clientPtr->isMaximizeable());
0036             QObject::connect(clientPtr.data(), &KDecoration2::DecoratedClient::maximizeableChanged, b, &Oxygen::Button::setVisible);
0037             break;
0038 
0039         case KDecoration2::DecorationButtonType::Minimize:
0040             b->setVisible(clientPtr->isMinimizeable());
0041             QObject::connect(clientPtr.data(), &KDecoration2::DecoratedClient::minimizeableChanged, b, &Oxygen::Button::setVisible);
0042             break;
0043 
0044         case KDecoration2::DecorationButtonType::ContextHelp:
0045             b->setVisible(clientPtr->providesContextHelp());
0046             QObject::connect(clientPtr.data(), &KDecoration2::DecoratedClient::providesContextHelpChanged, b, &Oxygen::Button::setVisible);
0047             break;
0048 
0049         case KDecoration2::DecorationButtonType::Shade:
0050             b->setVisible(clientPtr->isShadeable());
0051             QObject::connect(clientPtr.data(), &KDecoration2::DecoratedClient::shadeableChanged, b, &Oxygen::Button::setVisible);
0052             break;
0053 
0054         case KDecoration2::DecorationButtonType::Menu:
0055             QObject::connect(clientPtr.data(), &KDecoration2::DecoratedClient::iconChanged, b, [b]() {
0056                 b->update();
0057             });
0058             break;
0059 
0060         default:
0061             break;
0062         }
0063 
0064         return b;
0065 
0066     } else
0067         return nullptr;
0068 }
0069 
0070 //____________________________________________________________________________________
0071 Button::Button(KDecoration2::DecorationButtonType type, Decoration *decoration, QObject *parent)
0072     : KDecoration2::DecorationButton(type, decoration, parent)
0073     , m_animation(new QPropertyAnimation(this))
0074     , m_opacity(0)
0075 {
0076     // setup animation
0077     m_animation->setStartValue(0);
0078     m_animation->setEndValue(1.0);
0079     m_animation->setTargetObject(this);
0080     m_animation->setPropertyName("opacity");
0081     m_animation->setEasingCurve(QEasingCurve::InOutQuad);
0082 
0083     // setup default geometry
0084     const int height = decoration->buttonHeight();
0085     setGeometry(QRect(0, 0, height, height));
0086     setIconSize(QSize(height, height));
0087 
0088     reconfigure();
0089 
0090     // setup connections
0091     if (isMenuButton()) {
0092         connect(decoration->client().toStrongRef().data(), SIGNAL(iconChanged(QIcon)), this, SLOT(update()));
0093     }
0094 
0095     connect(decoration->settings().data(), &KDecoration2::DecorationSettings::reconfigured, this, &Button::reconfigure);
0096     connect(this, &KDecoration2::DecorationButton::hoveredChanged, this, &Button::updateAnimationState);
0097 }
0098 
0099 //_______________________________________________
0100 Button::Button(QObject *parent, const QVariantList &args)
0101     : Button(args.at(0).value<KDecoration2::DecorationButtonType>(), args.at(1).value<Decoration *>(), parent)
0102 {
0103     m_flag = FlagStandalone;
0104     //! icon size must return to !valid because it was altered from the default constructor,
0105     //! in Standalone mode the button is not using the decoration metrics but its geometry
0106     m_iconSize = QSize(-1, -1);
0107 }
0108 
0109 //_______________________________________________
0110 QColor Button::foregroundColor(const QPalette &palette) const
0111 {
0112     auto d(qobject_cast<Decoration *>(decoration().data()));
0113     if (d->isAnimated()) {
0114         return KColorUtils::mix(foregroundColor(palette, false), foregroundColor(palette, true), d->opacity());
0115 
0116     } else {
0117         return foregroundColor(palette, isActive());
0118     }
0119 }
0120 
0121 //___________________________________________________
0122 QColor Button::foregroundColor(const QPalette &palette, bool active) const
0123 {
0124     auto d(qobject_cast<Decoration *>(decoration().data()));
0125     if (d->internalSettings()->useWindowColors()) {
0126         return palette.color(active ? QPalette::Active : QPalette::Disabled, QPalette::ButtonText);
0127 
0128     } else {
0129         return d->fontColor(palette, active);
0130     }
0131 }
0132 
0133 //_______________________________________________
0134 QColor Button::backgroundColor(const QPalette &palette) const
0135 {
0136     auto d(qobject_cast<Decoration *>(decoration().data()));
0137     if (d->isAnimated()) {
0138         return KColorUtils::mix(backgroundColor(palette, false), backgroundColor(palette, true), d->opacity());
0139 
0140     } else {
0141         return backgroundColor(palette, isActive());
0142     }
0143 }
0144 
0145 //___________________________________________________
0146 QColor Button::backgroundColor(const QPalette &palette, bool active) const
0147 {
0148     auto d(qobject_cast<Decoration *>(decoration().data()));
0149     if (d->internalSettings()->useWindowColors()) {
0150         return palette.color(active ? QPalette::Active : QPalette::Inactive, QPalette::Button);
0151 
0152     } else {
0153         return d->titleBarColor(palette, active);
0154     }
0155 }
0156 
0157 //___________________________________________________
0158 bool Button::isActive(void) const
0159 {
0160     return decoration().data()->client().toStrongRef()->isActive();
0161 }
0162 
0163 //___________________________________________________
0164 void Button::reconfigure(void)
0165 {
0166     // animation
0167     auto d = qobject_cast<Decoration *>(decoration());
0168     if (d)
0169         m_animation->setDuration(d->internalSettings()->buttonAnimationsDuration());
0170 }
0171 
0172 //___________________________________________________
0173 void Button::paint(QPainter *painter, const QRect &repaintRegion)
0174 {
0175     Q_UNUSED(repaintRegion)
0176 
0177     if (!decoration())
0178         return;
0179 
0180     painter->save();
0181 
0182     // translate from offset
0183     if (m_flag == FlagFirstInList)
0184         painter->translate(m_offset);
0185     else
0186         painter->translate(0, m_offset.y());
0187 
0188     if (!m_iconSize.isValid() || isStandAlone())
0189         m_iconSize = geometry().size().toSize();
0190 
0191     const auto clientPtr = decoration()->client().toStrongRef();
0192     // menu buttons
0193     if (isMenuButton()) {
0194         const QRectF iconRect(geometry().topLeft(), m_iconSize);
0195         clientPtr->icon().paint(painter, iconRect.toRect());
0196         painter->restore();
0197         return;
0198     }
0199 
0200     // palette
0201     QPalette palette(clientPtr->palette());
0202     palette.setCurrentColorGroup(isActive() ? QPalette::Active : QPalette::Inactive);
0203 
0204     // base button color
0205     QColor base = backgroundColor(palette);
0206 
0207     // text color
0208     QColor color = foregroundColor(palette);
0209 
0210     // decide decoration color
0211     QColor glow;
0212     if (isAnimated() || isHovered() || (isToggleButton() && isChecked())) {
0213         QColor toggleColor = SettingsProvider::self()->helper()->focusColor(palette);
0214         QColor toggledHoverGlow = foregroundColor(palette, false);
0215         QColor toggledHoverColor = KColorUtils::mix(toggledHoverColor, color, 0.6);
0216 
0217         if (isCloseButton())
0218             glow = SettingsProvider::self()->helper()->negativeTextColor(palette); // Button is close button
0219         else if (isHovered() && (isToggleButton() && isChecked()))
0220             glow = toggledHoverGlow; // Button is checked and hovered
0221         else if (isToggleButton() && isChecked())
0222             glow = toggleColor; // Button is checked but not hovered
0223         else
0224             glow = SettingsProvider::self()->helper()->hoverColor(palette); // Button is hovered but not checked
0225 
0226         if (isAnimated()) {
0227             if (isToggleButton() && isChecked()) {
0228                 color = KColorUtils::mix(toggleColor, toggledHoverColor, m_opacity);
0229                 glow = KColorUtils::mix(toggleColor, toggledHoverGlow, m_opacity);
0230             } else {
0231                 color = KColorUtils::mix(color, glow, m_opacity);
0232                 glow = SettingsProvider::self()->helper()->alphaColor(glow, m_opacity);
0233             }
0234 
0235         } else if (!isHovered() != !(isToggleButton() && isChecked()))
0236             color = glow; // If button is eigther hovered or checked, use glow color as text color
0237         else if ((isToggleButton() && isChecked()))
0238             color = toggledHoverColor; // If button is checked and hovered, use different color
0239     }
0240 
0241     // draw button shape
0242     const bool sunken = isPressed() || (isToggleButton() && isChecked());
0243     const QRectF iconRect(geometry().topLeft(), m_iconSize);
0244     painter->drawPixmap(iconRect.topLeft(), SettingsProvider::self()->helper()->windecoButton(base, glow, sunken, m_iconSize.width()));
0245 
0246     // Icon
0247     painter->setRenderHints(QPainter::Antialiasing);
0248     painter->translate(geometry().topLeft());
0249 
0250     qreal width(1.2);
0251 
0252     // contrast
0253     painter->setBrush(Qt::NoBrush);
0254     painter->translate(0, 1.5);
0255     painter->setPen(QPen(SettingsProvider::self()->helper()->calcLightColor(base), width, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
0256     drawIcon(painter);
0257 
0258     // main
0259     painter->translate(0, -1.5);
0260     painter->setPen(QPen(color, width, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
0261     drawIcon(painter);
0262 
0263     painter->restore();
0264 }
0265 
0266 //___________________________________________________
0267 void Button::drawIcon(QPainter *painter)
0268 {
0269     painter->save();
0270 
0271     // keep all co-ordinates between 0 and 21
0272     const qreal width(m_iconSize.width());
0273     painter->scale(width / 21, width / 21);
0274 
0275     // make sure pen width is always larger than 1.1 in "real" coordinates
0276     QPen pen(painter->pen());
0277     pen.setWidthF(qMax(1.1 * 21 / width, pen.widthF()));
0278     painter->setPen(pen);
0279 
0280     switch (type()) {
0281     case KDecoration2::DecorationButtonType::OnAllDesktops:
0282         painter->drawPoint(QPointF(10.5, 10.5));
0283         break;
0284 
0285     case KDecoration2::DecorationButtonType::ContextHelp:
0286         painter->translate(1.5, 1.5);
0287         painter->drawArc(7, 5, 4, 4, 135 * 16, -180 * 16);
0288         painter->drawArc(9, 8, 4, 4, 135 * 16, 45 * 16);
0289         painter->drawPoint(9, 12);
0290         painter->translate(-1.5, -1.5);
0291         break;
0292 
0293     case KDecoration2::DecorationButtonType::ApplicationMenu:
0294         painter->drawLine(QPointF(7.5, 7.5), QPointF(13.5, 7.5));
0295         painter->drawLine(QPointF(7.5, 10.5), QPointF(13.5, 10.5));
0296         painter->drawLine(QPointF(7.5, 13.5), QPointF(13.5, 13.5));
0297         break;
0298 
0299     case KDecoration2::DecorationButtonType::Minimize: {
0300         painter->drawPolyline(QPolygonF() << QPointF(7.5, 9.5) << QPointF(10.5, 12.5) << QPointF(13.5, 9.5));
0301         break;
0302     }
0303 
0304     case KDecoration2::DecorationButtonType::Maximize:
0305         if (decoration()->client().toStrongRef()->isMaximized()) {
0306             painter->drawPolygon(QPolygonF() << QPointF(7.5, 10.5) << QPointF(10.5, 7.5) << QPointF(13.5, 10.5) << QPointF(10.5, 13.5));
0307 
0308         } else {
0309             painter->drawPolyline(QPolygonF() << QPointF(7.5, 11.5) << QPointF(10.5, 8.5) << QPointF(13.5, 11.5));
0310         }
0311         break;
0312 
0313     case KDecoration2::DecorationButtonType::Close:
0314         painter->drawLine(QPointF(7.5, 7.5), QPointF(13.5, 13.5));
0315         painter->drawLine(QPointF(13.5, 7.5), QPointF(7.5, 13.5));
0316         break;
0317 
0318     case KDecoration2::DecorationButtonType::KeepAbove: {
0319         painter->drawPolyline(QPolygonF() << QPointF(7.5, 14) << QPointF(10.5, 11) << QPointF(13.5, 14));
0320 
0321         painter->drawPolyline(QPolygonF() << QPointF(7.5, 10) << QPointF(10.5, 7) << QPointF(13.5, 10));
0322         break;
0323     }
0324 
0325     case KDecoration2::DecorationButtonType::KeepBelow: {
0326         painter->drawPolyline(QPolygonF() << QPointF(7.5, 11) << QPointF(10.5, 14) << QPointF(13.5, 11));
0327 
0328         painter->drawPolyline(QPolygonF() << QPointF(7.5, 7) << QPointF(10.5, 10) << QPointF(13.5, 7));
0329 
0330         break;
0331     }
0332 
0333     case KDecoration2::DecorationButtonType::Shade:
0334         if (!isChecked()) {
0335             // shade button
0336             painter->drawPolyline(QPolygonF() << QPointF(7.5, 7.5) << QPointF(10.5, 10.5) << QPointF(13.5, 7.5));
0337 
0338             painter->drawLine(QPointF(7.5, 13.0), QPointF(13.5, 13.0));
0339 
0340         } else {
0341             painter->drawPolyline(QPolygonF() << QPointF(7.5, 10.5) << QPointF(10.5, 7.5) << QPointF(13.5, 10.5));
0342 
0343             painter->drawLine(QPointF(7.5, 13.0), QPointF(13.5, 13.0));
0344         }
0345         break;
0346 
0347     default:
0348         break;
0349     }
0350     painter->restore();
0351     return;
0352 }
0353 
0354 //__________________________________________________________________
0355 void Button::updateAnimationState(bool hovered)
0356 {
0357     auto d = qobject_cast<Decoration *>(decoration());
0358     if (!(d && d->internalSettings()->animationsEnabled()))
0359         return;
0360 
0361     m_animation->setDirection(hovered ? QPropertyAnimation::Forward : QPropertyAnimation::Backward);
0362     if (m_animation->state() != QPropertyAnimation::Running)
0363         m_animation->start();
0364 }
0365 }