472 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
 | 
						|
 * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
 | 
						|
 */
 | 
						|
#include "breezebutton.h"
 | 
						|
 | 
						|
#include <KDecoration2/DecoratedClient>
 | 
						|
#include <KColorUtils>
 | 
						|
#include <KIconLoader>
 | 
						|
 | 
						|
#include <QPainter>
 | 
						|
#include <QVariantAnimation>
 | 
						|
#include <QPainterPath>
 | 
						|
 | 
						|
namespace Breeze
 | 
						|
{
 | 
						|
 | 
						|
    using KDecoration2::ColorRole;
 | 
						|
    using KDecoration2::ColorGroup;
 | 
						|
    using KDecoration2::DecorationButtonType;
 | 
						|
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    Button::Button(DecorationButtonType type, Decoration* decoration, QObject* parent)
 | 
						|
        : DecorationButton(type, decoration, parent)
 | 
						|
        , m_animation( new QVariantAnimation( this ) )
 | 
						|
    {
 | 
						|
 | 
						|
        // setup animation
 | 
						|
        // It is important start and end value are of the same type, hence 0.0 and not just 0
 | 
						|
        m_animation->setStartValue( 0.0 );
 | 
						|
        m_animation->setEndValue( 1.0 );
 | 
						|
        m_animation->setEasingCurve( QEasingCurve::InOutQuad );
 | 
						|
        connect(m_animation, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) {
 | 
						|
            setOpacity(value.toReal());
 | 
						|
        });
 | 
						|
 | 
						|
        // setup default geometry
 | 
						|
        const int height = decoration->buttonHeight();
 | 
						|
        setGeometry(QRect(0, 0, height, height));
 | 
						|
        setIconSize(QSize( height, height ));
 | 
						|
 | 
						|
        // connections
 | 
						|
        connect(decoration->client().data(), SIGNAL(iconChanged(QIcon)), this, SLOT(update()));
 | 
						|
        connect(decoration->settings().data(), &KDecoration2::DecorationSettings::reconfigured, this, &Button::reconfigure);
 | 
						|
        connect( this, &KDecoration2::DecorationButton::hoveredChanged, this, &Button::updateAnimationState );
 | 
						|
 | 
						|
        reconfigure();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    Button::Button(QObject *parent, const QVariantList &args)
 | 
						|
        : Button(args.at(0).value<DecorationButtonType>(), args.at(1).value<Decoration*>(), parent)
 | 
						|
    {
 | 
						|
        m_flag = FlagStandalone;
 | 
						|
        //! icon size must return to !valid because it was altered from the default constructor,
 | 
						|
        //! in Standalone mode the button is not using the decoration metrics but its geometry
 | 
						|
        m_iconSize = QSize(-1, -1);
 | 
						|
    }
 | 
						|
            
 | 
						|
    //__________________________________________________________________
 | 
						|
    Button *Button::create(DecorationButtonType type, KDecoration2::Decoration *decoration, QObject *parent)
 | 
						|
    {
 | 
						|
        if (auto d = qobject_cast<Decoration*>(decoration))
 | 
						|
        {
 | 
						|
            Button *b = new Button(type, d, parent);
 | 
						|
            switch( type )
 | 
						|
            {
 | 
						|
 | 
						|
                case DecorationButtonType::Close:
 | 
						|
                b->setVisible( d->client().data()->isCloseable() );
 | 
						|
                QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::closeableChanged, b, &Breeze::Button::setVisible );
 | 
						|
                break;
 | 
						|
 | 
						|
                case DecorationButtonType::Maximize:
 | 
						|
                b->setVisible( d->client().data()->isMaximizeable() );
 | 
						|
                QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::maximizeableChanged, b, &Breeze::Button::setVisible );
 | 
						|
                break;
 | 
						|
 | 
						|
                case DecorationButtonType::Minimize:
 | 
						|
                b->setVisible( d->client().data()->isMinimizeable() );
 | 
						|
                QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::minimizeableChanged, b, &Breeze::Button::setVisible );
 | 
						|
                break;
 | 
						|
 | 
						|
                case DecorationButtonType::ContextHelp:
 | 
						|
                b->setVisible( d->client().data()->providesContextHelp() );
 | 
						|
                QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::providesContextHelpChanged, b, &Breeze::Button::setVisible );
 | 
						|
                break;
 | 
						|
 | 
						|
                case DecorationButtonType::Shade:
 | 
						|
                b->setVisible( d->client().data()->isShadeable() );
 | 
						|
                QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::shadeableChanged, b, &Breeze::Button::setVisible );
 | 
						|
                break;
 | 
						|
 | 
						|
                case DecorationButtonType::Menu:
 | 
						|
                QObject::connect(d->client().data(), &KDecoration2::DecoratedClient::iconChanged, b, [b]() { b->update(); });
 | 
						|
                break;
 | 
						|
 | 
						|
                default: break;
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
            return b;
 | 
						|
        }
 | 
						|
 | 
						|
        return nullptr;
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    void Button::paint(QPainter *painter, const QRect &repaintRegion)
 | 
						|
    {
 | 
						|
        Q_UNUSED(repaintRegion)
 | 
						|
 | 
						|
        if (!decoration()) return;
 | 
						|
 | 
						|
        painter->save();
 | 
						|
 | 
						|
        // translate from offset
 | 
						|
        if( m_flag == FlagFirstInList ) painter->translate( m_offset );
 | 
						|
        else painter->translate( 0, m_offset.y() );
 | 
						|
 | 
						|
        if( !m_iconSize.isValid() ) m_iconSize = geometry().size().toSize();
 | 
						|
 | 
						|
        // menu button
 | 
						|
        if (type() == DecorationButtonType::Menu)
 | 
						|
        {
 | 
						|
 | 
						|
            const QRectF iconRect( geometry().topLeft(), m_iconSize );
 | 
						|
            if (auto deco =  qobject_cast<Decoration*>(decoration())) {
 | 
						|
                const QPalette activePalette = KIconLoader::global()->customPalette();
 | 
						|
                QPalette palette = decoration()->client().data()->palette();
 | 
						|
                palette.setColor(QPalette::Foreground, deco->fontColor());
 | 
						|
                KIconLoader::global()->setCustomPalette(palette);
 | 
						|
                decoration()->client().data()->icon().paint(painter, iconRect.toRect());
 | 
						|
                if (activePalette == QPalette()) {
 | 
						|
                    KIconLoader::global()->resetPalette();
 | 
						|
                }    else {
 | 
						|
                    KIconLoader::global()->setCustomPalette(palette);
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                decoration()->client().data()->icon().paint(painter, iconRect.toRect());
 | 
						|
            }
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            drawIcon( painter );
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        painter->restore();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    void Button::drawIcon( QPainter *painter ) const
 | 
						|
    {
 | 
						|
 | 
						|
        painter->setRenderHints( QPainter::Antialiasing );
 | 
						|
 | 
						|
        /*
 | 
						|
        scale painter so that its window matches QRect( -1, -1, 20, 20 )
 | 
						|
        this makes all further rendering and scaling simpler
 | 
						|
        all further rendering is preformed inside QRect( 0, 0, 18, 18 )
 | 
						|
        */
 | 
						|
        painter->translate( geometry().topLeft() );
 | 
						|
 | 
						|
        const qreal width( m_iconSize.width() );
 | 
						|
        painter->scale( width/20, width/20 );
 | 
						|
        painter->translate( 1, 1 );
 | 
						|
 | 
						|
        // render background
 | 
						|
        const QColor backgroundColor( this->backgroundColor() );
 | 
						|
        if( backgroundColor.isValid() )
 | 
						|
        {
 | 
						|
            painter->setPen( Qt::NoPen );
 | 
						|
            painter->setBrush( backgroundColor );
 | 
						|
            painter->drawEllipse( QRectF( 0, 0, 18, 18 ) );
 | 
						|
        }
 | 
						|
 | 
						|
        // render mark
 | 
						|
        const QColor foregroundColor( this->foregroundColor() );
 | 
						|
        if( foregroundColor.isValid() )
 | 
						|
        {
 | 
						|
 | 
						|
            // setup painter
 | 
						|
            QPen pen( foregroundColor );
 | 
						|
            pen.setCapStyle( Qt::RoundCap );
 | 
						|
            pen.setJoinStyle( Qt::MiterJoin );
 | 
						|
            pen.setWidthF( PenWidth::Symbol*qMax((qreal)2.0, 20/width ) );
 | 
						|
 | 
						|
            painter->setPen( pen );
 | 
						|
            painter->setBrush( Qt::NoBrush );
 | 
						|
 | 
						|
            switch( type() )
 | 
						|
            {
 | 
						|
 | 
						|
                case DecorationButtonType::Close:
 | 
						|
                {
 | 
						|
                    painter->drawLine( QPointF( 6, 6 ), QPointF( 12, 12 ) );
 | 
						|
                    painter->drawLine( QPointF( 6, 12 ), QPointF( 12, 6 ) );
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::Maximize:
 | 
						|
                {
 | 
						|
                    pen.setJoinStyle( Qt::RoundJoin );
 | 
						|
                    painter->setPen( pen );
 | 
						|
 | 
						|
                    painter->drawPolygon( QVector<QPointF>{
 | 
						|
                        QPointF( 6, 6 ),
 | 
						|
                        QPointF( 6, 12 ),
 | 
						|
                        QPointF( 12, 12 ),
 | 
						|
                        QPointF( 12, 6 )} );
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::Minimize:
 | 
						|
                {
 | 
						|
                    painter->drawLine( QPointF( 6, 12 ), QPointF( 12, 12 ) );
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::OnAllDesktops:
 | 
						|
                {
 | 
						|
                    painter->setPen( Qt::NoPen );
 | 
						|
                    painter->setBrush( foregroundColor );
 | 
						|
 | 
						|
                    if( isChecked())
 | 
						|
                    {
 | 
						|
 | 
						|
                        // outer ring
 | 
						|
                        painter->drawEllipse( QRectF( 3, 3, 12, 12 ) );
 | 
						|
 | 
						|
                        // center dot
 | 
						|
                        QColor backgroundColor( this->backgroundColor() );
 | 
						|
                        auto d = qobject_cast<Decoration*>( decoration() );
 | 
						|
                        if( !backgroundColor.isValid() && d ) backgroundColor = d->titleBarColor();
 | 
						|
 | 
						|
                        if( backgroundColor.isValid() )
 | 
						|
                        {
 | 
						|
                            painter->setBrush( backgroundColor );
 | 
						|
                            painter->drawEllipse( QRectF( 8, 8, 2, 2 ) );
 | 
						|
                        }
 | 
						|
 | 
						|
                    } else {
 | 
						|
 | 
						|
                        painter->drawPolygon( QVector<QPointF> {
 | 
						|
                            QPointF( 6.5, 8.5 ),
 | 
						|
                            QPointF( 12, 3 ),
 | 
						|
                            QPointF( 15, 6 ),
 | 
						|
                            QPointF( 9.5, 11.5 )} );
 | 
						|
 | 
						|
                        painter->setPen( pen );
 | 
						|
                        painter->drawLine( QPointF( 5.5, 7.5 ), QPointF( 10.5, 12.5 ) );
 | 
						|
                        painter->drawLine( QPointF( 12, 6 ), QPointF( 4.5, 13.5 ) );
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::Shade:
 | 
						|
                {
 | 
						|
 | 
						|
                    if (isChecked())
 | 
						|
                    {
 | 
						|
 | 
						|
                        painter->drawLine( QPointF( 4, 5.5 ), QPointF( 14, 5.5 ) );
 | 
						|
                        painter->drawPolyline( QVector<QPointF> {
 | 
						|
                            QPointF( 4, 8 ),
 | 
						|
                            QPointF( 9, 13 ),
 | 
						|
                            QPointF( 14, 8 )} );
 | 
						|
 | 
						|
                    } else {
 | 
						|
 | 
						|
                        painter->drawLine( QPointF( 4, 5.5 ), QPointF( 14, 5.5 ) );
 | 
						|
                        painter->drawPolyline(  QVector<QPointF> {
 | 
						|
                            QPointF( 4, 13 ),
 | 
						|
                            QPointF( 9, 8 ),
 | 
						|
                            QPointF( 14, 13 ) });
 | 
						|
                    }
 | 
						|
 | 
						|
                    break;
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::KeepBelow:
 | 
						|
                {
 | 
						|
 | 
						|
                    painter->drawPolyline(  QVector<QPointF> {
 | 
						|
                        QPointF( 4, 5 ),
 | 
						|
                        QPointF( 9, 10 ),
 | 
						|
                        QPointF( 14, 5 ) });
 | 
						|
 | 
						|
                    painter->drawPolyline(  QVector<QPointF> {
 | 
						|
                        QPointF( 4, 9 ),
 | 
						|
                        QPointF( 9, 14 ),
 | 
						|
                        QPointF( 14, 9 ) });
 | 
						|
                    break;
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::KeepAbove:
 | 
						|
                {
 | 
						|
                    painter->drawPolyline(  QVector<QPointF> {
 | 
						|
                        QPointF( 4, 9 ),
 | 
						|
                        QPointF( 9, 4 ),
 | 
						|
                        QPointF( 14, 9 ) });
 | 
						|
 | 
						|
                    painter->drawPolyline(  QVector<QPointF> {
 | 
						|
                        QPointF( 4, 13 ),
 | 
						|
                        QPointF( 9, 8 ),
 | 
						|
                        QPointF( 14, 13 ) });
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
 | 
						|
                case DecorationButtonType::ApplicationMenu:
 | 
						|
                {
 | 
						|
                    painter->drawRect( QRectF( 3.5, 4.5, 11, 1 ) );
 | 
						|
                    painter->drawRect( QRectF( 3.5, 8.5, 11, 1 ) );
 | 
						|
                    painter->drawRect( QRectF( 3.5, 12.5, 11, 1 ) );
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case DecorationButtonType::ContextHelp:
 | 
						|
                {
 | 
						|
                    QPainterPath path;
 | 
						|
                    path.moveTo( 5, 6 );
 | 
						|
                    path.arcTo( QRectF( 5, 3.5, 8, 5 ), 180, -180 );
 | 
						|
                    path.cubicTo( QPointF(12.5, 9.5), QPointF( 9, 7.5 ), QPointF( 9, 11.5 ) );
 | 
						|
                    painter->drawPath( path );
 | 
						|
 | 
						|
                    painter->drawRect( QRectF( 9, 15, 0.5, 0.5 ) );
 | 
						|
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                default: break;
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    QColor Button::foregroundColor() const
 | 
						|
    {
 | 
						|
        auto d = qobject_cast<Decoration*>( decoration() );
 | 
						|
        if( !d ) {
 | 
						|
 | 
						|
            return QColor();
 | 
						|
 | 
						|
        } else if( isPressed() ) {
 | 
						|
 | 
						|
            return d->titleBarColor();
 | 
						|
 | 
						|
        } else if( type() == DecorationButtonType::Close && d->internalSettings()->outlineCloseButton() ) {
 | 
						|
 | 
						|
            return d->titleBarColor();
 | 
						|
 | 
						|
        } else if( ( type() == DecorationButtonType::KeepBelow || type() == DecorationButtonType::KeepAbove || type() == DecorationButtonType::Shade ) && isChecked() ) {
 | 
						|
 | 
						|
            return d->titleBarColor();
 | 
						|
 | 
						|
        } else if( m_animation->state() == QAbstractAnimation::Running ) {
 | 
						|
 | 
						|
            return KColorUtils::mix( d->fontColor(), d->titleBarColor(), m_opacity );
 | 
						|
 | 
						|
        } else if( isHovered() ) {
 | 
						|
 | 
						|
            return d->titleBarColor();
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            return d->fontColor();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    QColor Button::backgroundColor() const
 | 
						|
    {
 | 
						|
        auto d = qobject_cast<Decoration*>( decoration() );
 | 
						|
        if( !d ) {
 | 
						|
 | 
						|
            return QColor();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        auto c = d->client().data();
 | 
						|
        QColor blueColor( c->palette().color( QPalette::Highlight ) );
 | 
						|
 | 
						|
        if( isPressed() ) {
 | 
						|
 | 
						|
            if( type() == DecorationButtonType::Close ) return blueColor.darker();
 | 
						|
            else return KColorUtils::mix( d->titleBarColor(), d->fontColor(), 0.3 );
 | 
						|
 | 
						|
        } else if( ( type() == DecorationButtonType::KeepBelow || type() == DecorationButtonType::KeepAbove || type() == DecorationButtonType::Shade ) && isChecked() ) {
 | 
						|
 | 
						|
            return d->fontColor();
 | 
						|
 | 
						|
        } else if( m_animation->state() == QAbstractAnimation::Running ) {
 | 
						|
 | 
						|
            if( type() == DecorationButtonType::Close )
 | 
						|
            {
 | 
						|
                if( d->internalSettings()->outlineCloseButton() )
 | 
						|
                {
 | 
						|
 | 
						|
                    return c->isActive() ? KColorUtils::mix( blueColor, blueColor.lighter(), m_opacity ) : KColorUtils::mix( blueColor.lighter(), blueColor, m_opacity );
 | 
						|
 | 
						|
                } else {
 | 
						|
 | 
						|
                    QColor color( blueColor.lighter() );
 | 
						|
                    color.setAlpha( color.alpha()*m_opacity );
 | 
						|
                    return color;
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
            } else {
 | 
						|
 | 
						|
                QColor color( d->fontColor() );
 | 
						|
                color.setAlpha( color.alpha()*m_opacity );
 | 
						|
                return color;
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
        } else if( isHovered() ) {
 | 
						|
 | 
						|
            if( type() == DecorationButtonType::Close ) return c->isActive() ? blueColor.lighter() : blueColor;
 | 
						|
            else return d->fontColor();
 | 
						|
 | 
						|
        } else if( type() == DecorationButtonType::Close && d->internalSettings()->outlineCloseButton() ) {
 | 
						|
 | 
						|
            return c->isActive() ? blueColor : d->fontColor();
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            return QColor();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Button::reconfigure()
 | 
						|
    {
 | 
						|
 | 
						|
        // animation
 | 
						|
        auto d = qobject_cast<Decoration*>(decoration());
 | 
						|
        if( d )  m_animation->setDuration( d->animationsDuration() );
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //__________________________________________________________________
 | 
						|
    void Button::updateAnimationState( bool hovered )
 | 
						|
    {
 | 
						|
 | 
						|
        auto d = qobject_cast<Decoration*>(decoration());
 | 
						|
        if( !(d && d->animationsDuration() > 0 ) ) return;
 | 
						|
 | 
						|
        m_animation->setDirection( hovered ? QAbstractAnimation::Forward : QAbstractAnimation::Backward );
 | 
						|
        if( m_animation->state() != QAbstractAnimation::Running ) m_animation->start();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
} // namespace
 |