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