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 }