818 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			818 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
 | 
						|
* SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
 | 
						|
* SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
 | 
						|
*
 | 
						|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
 | 
						|
*/
 | 
						|
 | 
						|
#include "breezedecoration.h"
 | 
						|
 | 
						|
#include "breezesettingsprovider.h"
 | 
						|
#include "config-breeze.h"
 | 
						|
#include "config/breezeconfigwidget.h"
 | 
						|
 | 
						|
#include "breezebutton.h"
 | 
						|
#include "breezesizegrip.h"
 | 
						|
 | 
						|
#include "breezeboxshadowrenderer.h"
 | 
						|
 | 
						|
#include <KDecoration2/DecorationButtonGroup>
 | 
						|
#include <KDecoration2/DecorationShadow>
 | 
						|
 | 
						|
#include <KConfigGroup>
 | 
						|
#include <KColorUtils>
 | 
						|
#include <KSharedConfig>
 | 
						|
#include <KPluginFactory>
 | 
						|
 | 
						|
#include <QPainter>
 | 
						|
#include <QTextStream>
 | 
						|
#include <QTimer>
 | 
						|
#include <QDBusConnection>
 | 
						|
 | 
						|
#if BREEZE_HAVE_X11
 | 
						|
#include <QX11Info>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <cmath>
 | 
						|
 | 
						|
K_PLUGIN_FACTORY_WITH_JSON(
 | 
						|
    BreezeDecoFactory,
 | 
						|
    "breeze.json",
 | 
						|
    registerPlugin<Breeze::Decoration>();
 | 
						|
    registerPlugin<Breeze::Button>(QStringLiteral("button"));
 | 
						|
    registerPlugin<Breeze::ConfigWidget>(QStringLiteral("kcmodule"));
 | 
						|
)
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
    struct ShadowParams {
 | 
						|
        ShadowParams()
 | 
						|
            : offset(QPoint(0, 0))
 | 
						|
            , radius(0)
 | 
						|
            , opacity(0) {}
 | 
						|
 | 
						|
        ShadowParams(const QPoint &offset, int radius, qreal opacity)
 | 
						|
            : offset(offset)
 | 
						|
            , radius(radius)
 | 
						|
            , opacity(opacity) {}
 | 
						|
 | 
						|
        QPoint offset;
 | 
						|
        int radius;
 | 
						|
        qreal opacity;
 | 
						|
    };
 | 
						|
 | 
						|
    struct CompositeShadowParams {
 | 
						|
        CompositeShadowParams() = default;
 | 
						|
 | 
						|
        CompositeShadowParams(
 | 
						|
                const QPoint &offset,
 | 
						|
                const ShadowParams &shadow1,
 | 
						|
                const ShadowParams &shadow2)
 | 
						|
            : offset(offset)
 | 
						|
            , shadow1(shadow1)
 | 
						|
            , shadow2(shadow2) {}
 | 
						|
 | 
						|
        bool isNone() const {
 | 
						|
            return qMax(shadow1.radius, shadow2.radius) == 0;
 | 
						|
        }
 | 
						|
 | 
						|
        QPoint offset;
 | 
						|
        ShadowParams shadow1;
 | 
						|
        ShadowParams shadow2;
 | 
						|
    };
 | 
						|
 | 
						|
    const CompositeShadowParams s_shadowParams[] = {
 | 
						|
        // None
 | 
						|
        CompositeShadowParams(),
 | 
						|
        // Small
 | 
						|
        CompositeShadowParams(
 | 
						|
            QPoint(0, 4),
 | 
						|
            ShadowParams(QPoint(0, 0), 16, 1),
 | 
						|
            ShadowParams(QPoint(0, -2), 8, 0.4)),
 | 
						|
        // Medium
 | 
						|
        CompositeShadowParams(
 | 
						|
            QPoint(0, 8),
 | 
						|
            ShadowParams(QPoint(0, 0), 32, 0.9),
 | 
						|
            ShadowParams(QPoint(0, -4), 16, 0.3)),
 | 
						|
        // Large
 | 
						|
        CompositeShadowParams(
 | 
						|
            QPoint(0, 12),
 | 
						|
            ShadowParams(QPoint(0, 0), 48, 0.8),
 | 
						|
            ShadowParams(QPoint(0, -6), 24, 0.2)),
 | 
						|
        // Very large
 | 
						|
        CompositeShadowParams(
 | 
						|
            QPoint(0, 16),
 | 
						|
            ShadowParams(QPoint(0, 0), 64, 0.7),
 | 
						|
            ShadowParams(QPoint(0, -8), 32, 0.1)),
 | 
						|
    };
 | 
						|
 | 
						|
    inline CompositeShadowParams lookupShadowParams(int size)
 | 
						|
    {
 | 
						|
        switch (size) {
 | 
						|
        case Breeze::InternalSettings::ShadowNone:
 | 
						|
            return s_shadowParams[0];
 | 
						|
        case Breeze::InternalSettings::ShadowSmall:
 | 
						|
            return s_shadowParams[1];
 | 
						|
        case Breeze::InternalSettings::ShadowMedium:
 | 
						|
            return s_shadowParams[2];
 | 
						|
        case Breeze::InternalSettings::ShadowLarge:
 | 
						|
            return s_shadowParams[3];
 | 
						|
        case Breeze::InternalSettings::ShadowVeryLarge:
 | 
						|
            return s_shadowParams[4];
 | 
						|
        default:
 | 
						|
            // Fallback to the Large size.
 | 
						|
            return s_shadowParams[3];
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
namespace Breeze
 | 
						|
{
 | 
						|
 | 
						|
    using KDecoration2::ColorRole;
 | 
						|
    using KDecoration2::ColorGroup;
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    static int g_sDecoCount = 0;
 | 
						|
    static int g_shadowSizeEnum = InternalSettings::ShadowLarge;
 | 
						|
    static int g_shadowStrength = 255;
 | 
						|
    static QColor g_shadowColor = Qt::black;
 | 
						|
    static QSharedPointer<KDecoration2::DecorationShadow> g_sShadow;
 | 
						|
    static QSharedPointer<KDecoration2::DecorationShadow> g_sShadowInactive;
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    Decoration::Decoration(QObject *parent, const QVariantList &args)
 | 
						|
        : KDecoration2::Decoration(parent, args)
 | 
						|
        , m_animation( new QVariantAnimation( this ) )
 | 
						|
        , m_shadowAnimation( new QVariantAnimation( this ) )
 | 
						|
    {
 | 
						|
        g_sDecoCount++;
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    Decoration::~Decoration()
 | 
						|
    {
 | 
						|
        g_sDecoCount--;
 | 
						|
        if (g_sDecoCount == 0) {
 | 
						|
            // last deco destroyed, clean up shadow
 | 
						|
            g_sShadow.clear();
 | 
						|
        }
 | 
						|
 | 
						|
        deleteSizeGrip();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::setOpacity( qreal value )
 | 
						|
    {
 | 
						|
        if( m_opacity == value ) return;
 | 
						|
        m_opacity = value;
 | 
						|
        update();
 | 
						|
 | 
						|
        if( m_sizeGrip ) m_sizeGrip->update();
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    QColor Decoration::titleBarColor() const
 | 
						|
    {
 | 
						|
 | 
						|
        auto c = client().data();
 | 
						|
        if( hideTitleBar() ) return c->color( ColorGroup::Inactive, ColorRole::TitleBar );
 | 
						|
        else if( m_animation->state() == QAbstractAnimation::Running )
 | 
						|
        {
 | 
						|
            return KColorUtils::mix(
 | 
						|
                c->color( ColorGroup::Inactive, ColorRole::TitleBar ),
 | 
						|
                c->color( ColorGroup::Active, ColorRole::TitleBar ),
 | 
						|
                m_opacity );
 | 
						|
        } else return c->color( c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::TitleBar );
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    QColor Decoration::outlineColor() const
 | 
						|
    {
 | 
						|
 | 
						|
        auto c( client().data() );
 | 
						|
        if( !m_internalSettings->drawTitleBarSeparator() ) return QColor();
 | 
						|
        if( m_animation->state() == QAbstractAnimation::Running )
 | 
						|
        {
 | 
						|
            QColor color( c->palette().color( QPalette::Highlight ) );
 | 
						|
            color.setAlpha( color.alpha()*m_opacity );
 | 
						|
            return color;
 | 
						|
        } else if( c->isActive() ) return c->palette().color( QPalette::Highlight );
 | 
						|
        else return QColor();
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    QColor Decoration::fontColor() const
 | 
						|
    {
 | 
						|
 | 
						|
        auto c = client().data();
 | 
						|
        if( m_animation->state() == QAbstractAnimation::Running )
 | 
						|
        {
 | 
						|
            return KColorUtils::mix(
 | 
						|
                c->color( ColorGroup::Inactive, ColorRole::Foreground ),
 | 
						|
                c->color( ColorGroup::Active, ColorRole::Foreground ),
 | 
						|
                m_opacity );
 | 
						|
        } else return  c->color( c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::Foreground );
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::init()
 | 
						|
    {
 | 
						|
        auto c = client().data();
 | 
						|
 | 
						|
        // active state change 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 );
 | 
						|
        // Linear to have the same easing as Breeze animations
 | 
						|
        m_animation->setEasingCurve( QEasingCurve::Linear );
 | 
						|
        connect(m_animation, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) {
 | 
						|
            setOpacity(value.toReal());
 | 
						|
        });
 | 
						|
 | 
						|
        m_shadowAnimation->setStartValue( 0.0 );
 | 
						|
        m_shadowAnimation->setEndValue( 1.0 );
 | 
						|
        m_shadowAnimation->setEasingCurve( QEasingCurve::InCubic );
 | 
						|
        connect(m_shadowAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& value) {
 | 
						|
            m_shadowOpacity = value.toReal();
 | 
						|
            updateShadow();
 | 
						|
        });
 | 
						|
 | 
						|
        // use DBus connection to update on breeze configuration change
 | 
						|
        auto dbus = QDBusConnection::sessionBus();
 | 
						|
        dbus.connect( QString(),
 | 
						|
            QStringLiteral( "/KGlobalSettings" ),
 | 
						|
            QStringLiteral( "org.kde.KGlobalSettings" ),
 | 
						|
            QStringLiteral( "notifyChange" ), this, SLOT(reconfigure()) );
 | 
						|
 | 
						|
        reconfigure();
 | 
						|
        updateTitleBar();
 | 
						|
        auto s = settings();
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, this, &Decoration::recalculateBorders);
 | 
						|
 | 
						|
        // a change in font might cause the borders to change
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::fontChanged, this, &Decoration::recalculateBorders);
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::recalculateBorders);
 | 
						|
 | 
						|
        // buttons
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::spacingChanged, this, &Decoration::updateButtonsGeometryDelayed);
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::decorationButtonsLeftChanged, this, &Decoration::updateButtonsGeometryDelayed);
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::decorationButtonsRightChanged, this, &Decoration::updateButtonsGeometryDelayed);
 | 
						|
 | 
						|
        // full reconfiguration
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::reconfigure);
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, SettingsProvider::self(), &SettingsProvider::reconfigure, Qt::UniqueConnection );
 | 
						|
        connect(s.data(), &KDecoration2::DecorationSettings::reconfigured, this, &Decoration::updateButtonsGeometryDelayed);
 | 
						|
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::adjacentScreenEdgesChanged, this, &Decoration::recalculateBorders);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::maximizedHorizontallyChanged, this, &Decoration::recalculateBorders);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::maximizedVerticallyChanged, this, &Decoration::recalculateBorders);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::recalculateBorders);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::captionChanged, this,
 | 
						|
            [this]()
 | 
						|
            {
 | 
						|
                // update the caption area
 | 
						|
                update(titleBar());
 | 
						|
            }
 | 
						|
        );
 | 
						|
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::activeChanged, this, &Decoration::updateAnimationState);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateTitleBar);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateTitleBar);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::setOpaque);
 | 
						|
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateButtonsGeometry);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateButtonsGeometry);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::adjacentScreenEdgesChanged, this, &Decoration::updateButtonsGeometry);
 | 
						|
        connect(c, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateButtonsGeometry);
 | 
						|
 | 
						|
        createButtons();
 | 
						|
        updateShadow();
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::updateTitleBar()
 | 
						|
    {
 | 
						|
        auto s = settings();
 | 
						|
        auto c = client().data();
 | 
						|
        const bool maximized = isMaximized();
 | 
						|
        const int width =  maximized ? c->width() : c->width() - 2*s->largeSpacing()*Metrics::TitleBar_SideMargin;
 | 
						|
        const int height = maximized ? borderTop() : borderTop() - s->smallSpacing()*Metrics::TitleBar_TopMargin;
 | 
						|
        const int x = maximized ? 0 : s->largeSpacing()*Metrics::TitleBar_SideMargin;
 | 
						|
        const int y = maximized ? 0 : s->smallSpacing()*Metrics::TitleBar_TopMargin;
 | 
						|
        setTitleBar(QRect(x, y, width, height));
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::updateAnimationState()
 | 
						|
    {
 | 
						|
        if( m_shadowAnimation->duration() > 0 )
 | 
						|
        {
 | 
						|
 | 
						|
            auto c = client().data();
 | 
						|
            m_shadowAnimation->setDirection( c->isActive() ? QAbstractAnimation::Forward : QAbstractAnimation::Backward );
 | 
						|
            if( m_shadowAnimation->state() != QAbstractAnimation::Running ) m_shadowAnimation->start();
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            updateShadow();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        if( m_animation->duration() > 0 )
 | 
						|
        {
 | 
						|
 | 
						|
            auto c = client().data();
 | 
						|
            m_animation->setDirection( c->isActive() ? QAbstractAnimation::Forward : QAbstractAnimation::Backward );
 | 
						|
            if( m_animation->state() != QAbstractAnimation::Running ) m_animation->start();
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            update();
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::updateSizeGripVisibility()
 | 
						|
    {
 | 
						|
        auto c = client().data();
 | 
						|
        if( m_sizeGrip )
 | 
						|
        { m_sizeGrip->setVisible( c->isResizeable() && !isMaximized() && !c->isShaded() ); }
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    int Decoration::borderSize(bool bottom) const
 | 
						|
    {
 | 
						|
        const int baseSize = settings()->smallSpacing();
 | 
						|
        return baseSize*Metrics::Frame_BorderSize;
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::reconfigure()
 | 
						|
    {
 | 
						|
 | 
						|
        m_internalSettings = SettingsProvider::self()->internalSettings( this );
 | 
						|
 | 
						|
        // animation
 | 
						|
 | 
						|
        KSharedConfig::Ptr config = KSharedConfig::openConfig();
 | 
						|
        const KConfigGroup cg(config, QStringLiteral("KDE"));
 | 
						|
 | 
						|
        m_animation->setDuration(0);
 | 
						|
        // Syncing anis between client and decoration is troublesome, so we're not using
 | 
						|
        // any animations right now.
 | 
						|
        // m_animation->setDuration( cg.readEntry("AnimationDurationFactor", 1.0f) * 100.0f );
 | 
						|
 | 
						|
        // But the shadow is fine to animate like this!
 | 
						|
        m_shadowAnimation->setDuration( cg.readEntry("AnimationDurationFactor", 1.0f) * 100.0f );
 | 
						|
 | 
						|
        // borders
 | 
						|
        recalculateBorders();
 | 
						|
 | 
						|
        // shadow
 | 
						|
        updateShadow();
 | 
						|
 | 
						|
        // size grip
 | 
						|
        deleteSizeGrip();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::recalculateBorders()
 | 
						|
    {
 | 
						|
        auto c = client().data();
 | 
						|
        auto s = settings();
 | 
						|
 | 
						|
        // left, right and bottom borders
 | 
						|
        const int left   = borderSize();
 | 
						|
        const int right  = borderSize();
 | 
						|
        const int bottom = c->isShaded() ? 0 : borderSize();
 | 
						|
 | 
						|
        int top = 0;
 | 
						|
        if( hideTitleBar() ) top = bottom;
 | 
						|
        else {
 | 
						|
 | 
						|
            QFontMetrics fm(s->font());
 | 
						|
            top += qMax(fm.height(), buttonHeight() );
 | 
						|
 | 
						|
            // padding below
 | 
						|
            // extra pixel is used for the active window outline
 | 
						|
            const int baseSize = s->smallSpacing();
 | 
						|
            top += baseSize*Metrics::TitleBar_BottomMargin + 1;
 | 
						|
 | 
						|
            // padding above
 | 
						|
            top += baseSize*TitleBar_TopMargin;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        setBorders(QMargins(left, top, right, bottom));
 | 
						|
 | 
						|
        // extended sizes
 | 
						|
        const int extSize = s->largeSpacing();
 | 
						|
        int extSides = 0;
 | 
						|
        int extBottom = 0;
 | 
						|
 | 
						|
        setResizeOnlyBorders(QMargins(extSides, 0, extSides, extBottom));
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::createButtons()
 | 
						|
    {
 | 
						|
        m_leftButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Left, this, &Button::create);
 | 
						|
        m_rightButtons = new KDecoration2::DecorationButtonGroup(KDecoration2::DecorationButtonGroup::Position::Right, this, &Button::create);
 | 
						|
        updateButtonsGeometry();
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::updateButtonsGeometryDelayed()
 | 
						|
    { QTimer::singleShot( 0, this, &Decoration::updateButtonsGeometry ); }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::updateButtonsGeometry()
 | 
						|
    {
 | 
						|
        const auto s = settings();
 | 
						|
 | 
						|
        // adjust button position
 | 
						|
        const int bHeight = captionHeight() + (isTopEdge() ? s->smallSpacing()*Metrics::TitleBar_TopMargin:0);
 | 
						|
        const int bWidth = buttonHeight();
 | 
						|
        const int verticalOffset = (isTopEdge() ? s->smallSpacing()*Metrics::TitleBar_TopMargin:0) + (captionHeight()-buttonHeight())/2;
 | 
						|
        foreach( const QPointer<KDecoration2::DecorationButton>& button, m_leftButtons->buttons() + m_rightButtons->buttons() )
 | 
						|
        {
 | 
						|
            button.data()->setGeometry( QRectF( QPoint( 0, 0 ), QSizeF( bWidth, bHeight ) ) );
 | 
						|
            static_cast<Button*>( button.data() )->setOffset( QPointF( 0, verticalOffset ) );
 | 
						|
            static_cast<Button*>( button.data() )->setIconSize( QSize( bWidth, bWidth ) );
 | 
						|
        }
 | 
						|
 | 
						|
        // padding
 | 
						|
        const int vPadding = isTopEdge() ? 0 : s->smallSpacing()*Metrics::TitleBar_TopMargin;
 | 
						|
        const int hPadding = s->smallSpacing()*Metrics::TitleBar_SideMargin;
 | 
						|
 | 
						|
        // left buttons
 | 
						|
        if( !m_leftButtons->buttons().isEmpty() )
 | 
						|
        {
 | 
						|
            // spacing
 | 
						|
            m_leftButtons->setSpacing(s->smallSpacing()*Metrics::TitleBar_ButtonSpacing);
 | 
						|
            m_leftButtons->setPos(QPointF(hPadding + borderLeft(), vPadding));
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        // right buttons
 | 
						|
        if( !m_rightButtons->buttons().isEmpty() )
 | 
						|
        {
 | 
						|
            // spacing
 | 
						|
            m_rightButtons->setSpacing(s->smallSpacing()*Metrics::TitleBar_ButtonSpacing);
 | 
						|
            m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width() - hPadding - borderRight(), vPadding));
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        update();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::paint(QPainter *painter, const QRect &repaintRegion)
 | 
						|
    {
 | 
						|
        // TODO: optimize based on repaintRegion
 | 
						|
        auto c = client().data();
 | 
						|
        auto s = settings();
 | 
						|
        const int radius = s->smallSpacing()*Metrics::Frame_FrameRadius;
 | 
						|
 | 
						|
        // paint background
 | 
						|
        if( !c->isShaded() )
 | 
						|
        {
 | 
						|
            painter->fillRect(rect(), Qt::transparent);
 | 
						|
            painter->save();
 | 
						|
            painter->setRenderHint(QPainter::Antialiasing);
 | 
						|
            painter->setPen(Qt::NoPen);
 | 
						|
            painter->setBrush( c->color( c->isActive() ? ColorGroup::Active : ColorGroup::Inactive, ColorRole::Frame ) );
 | 
						|
 | 
						|
            // clip away the top part
 | 
						|
            if( !hideTitleBar() ) painter->setClipRect(0, borderTop(), size().width(), size().height() - borderTop(), Qt::IntersectClip);
 | 
						|
 | 
						|
            if( s->isAlphaChannelSupported() ) painter->drawRoundedRect(rect(),
 | 
						|
                isBottomEdge() ? 0:radius,
 | 
						|
                isBottomEdge() ? 0:radius);
 | 
						|
            else painter->drawRect( rect() );
 | 
						|
 | 
						|
            painter->restore();
 | 
						|
        }
 | 
						|
 | 
						|
        if( !hideTitleBar() ) paintTitleBar(painter, repaintRegion);
 | 
						|
 | 
						|
        if( !s->isAlphaChannelSupported() )
 | 
						|
        {
 | 
						|
            painter->save();
 | 
						|
            painter->setRenderHint(QPainter::Antialiasing, false);
 | 
						|
            painter->setBrush( Qt::NoBrush );
 | 
						|
            painter->setPen( c->isActive() ?
 | 
						|
                c->color( ColorGroup::Active, ColorRole::TitleBar ):
 | 
						|
                c->color( ColorGroup::Inactive, ColorRole::Foreground ) );
 | 
						|
 | 
						|
            painter->drawRect( rect().adjusted( 0, 0, -1, -1 ) );
 | 
						|
            painter->restore();
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::paintTitleBar(QPainter *painter, const QRect &repaintRegion)
 | 
						|
    {
 | 
						|
        const auto c = client().data();
 | 
						|
        const QRect titleRect(QPoint(0, 0), QSize(size().width(), borderTop()));
 | 
						|
 | 
						|
        if ( !titleRect.intersects(repaintRegion) ) return;
 | 
						|
 | 
						|
        painter->save();
 | 
						|
        painter->setPen(Qt::NoPen);
 | 
						|
 | 
						|
        // render a linear gradient on title area
 | 
						|
        if( c->isActive() && m_internalSettings->drawBackgroundGradient() )
 | 
						|
        {
 | 
						|
 | 
						|
            const QColor titleBarColor( this->titleBarColor() );
 | 
						|
            QLinearGradient gradient( 0, 0, 0, titleRect.height() );
 | 
						|
            gradient.setColorAt(0.0, titleBarColor.lighter( 120 ) );
 | 
						|
            gradient.setColorAt(0.8, titleBarColor);
 | 
						|
            painter->setBrush(gradient);
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            painter->setBrush( titleBarColor() );
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        auto s = settings();
 | 
						|
        const int radius = s->smallSpacing()*Metrics::Frame_FrameRadius;
 | 
						|
        if( isMaximized() || !s->isAlphaChannelSupported() )
 | 
						|
        {
 | 
						|
 | 
						|
            painter->drawRect(titleRect);
 | 
						|
 | 
						|
        } else if( c->isShaded() ) {
 | 
						|
 | 
						|
            painter->drawRoundedRect(titleRect, radius, radius);
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            painter->setClipRect(titleRect, Qt::IntersectClip);
 | 
						|
 | 
						|
            // the rect is made a little bit larger to be able to clip away the rounded corners at the bottom and sides
 | 
						|
            painter->drawRoundedRect(titleRect.adjusted(
 | 
						|
                isLeftEdge() ? -radius:0,
 | 
						|
                isTopEdge() ? -radius:0,
 | 
						|
                isRightEdge() ? radius:0,
 | 
						|
                radius),
 | 
						|
                radius, radius);
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        const QColor outlineColor( this->outlineColor() );
 | 
						|
        if( !c->isShaded() && outlineColor.isValid() )
 | 
						|
        {
 | 
						|
            // outline
 | 
						|
            painter->setRenderHint( QPainter::Antialiasing, false );
 | 
						|
            painter->setBrush( Qt::NoBrush );
 | 
						|
            painter->setPen( outlineColor );
 | 
						|
            painter->drawLine( titleRect.bottomLeft(), titleRect.bottomRight() );
 | 
						|
        }
 | 
						|
 | 
						|
        painter->restore();
 | 
						|
 | 
						|
        // draw caption
 | 
						|
        painter->setFont(s->font());
 | 
						|
        painter->setPen( fontColor() );
 | 
						|
        const auto cR = captionRect();
 | 
						|
        const QString caption = painter->fontMetrics().elidedText(c->caption(), Qt::ElideMiddle, cR.first.width());
 | 
						|
        painter->drawText(cR.first, cR.second | Qt::TextSingleLine, caption);
 | 
						|
 | 
						|
        // draw all buttons
 | 
						|
        m_leftButtons->paint(painter, repaintRegion);
 | 
						|
        m_rightButtons->paint(painter, repaintRegion);
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    int Decoration::buttonHeight() const
 | 
						|
    {
 | 
						|
        const int baseSize = settings()->gridUnit();
 | 
						|
        switch( m_internalSettings->buttonSize() )
 | 
						|
        {
 | 
						|
            case InternalSettings::ButtonTiny: return baseSize;
 | 
						|
            case InternalSettings::ButtonSmall: return baseSize*1.5;
 | 
						|
            default:
 | 
						|
            case InternalSettings::ButtonDefault: return baseSize*2;
 | 
						|
            case InternalSettings::ButtonLarge: return baseSize*2.5;
 | 
						|
            case InternalSettings::ButtonVeryLarge: return baseSize*3.5;
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    int Decoration::captionHeight() const
 | 
						|
    { return hideTitleBar() ? borderTop() : borderTop() - settings()->smallSpacing()*(Metrics::TitleBar_BottomMargin + Metrics::TitleBar_TopMargin ) - 1; }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    QPair<QRect,Qt::Alignment> Decoration::captionRect() const
 | 
						|
    {
 | 
						|
        if( hideTitleBar() ) return qMakePair( QRect(), Qt::AlignCenter );
 | 
						|
        else {
 | 
						|
 | 
						|
            auto c = client().data();
 | 
						|
            const int leftOffset = m_leftButtons->buttons().isEmpty() ?
 | 
						|
                Metrics::TitleBar_SideMargin*settings()->smallSpacing():
 | 
						|
                m_leftButtons->geometry().x() + m_leftButtons->geometry().width() + Metrics::TitleBar_SideMargin*settings()->smallSpacing();
 | 
						|
 | 
						|
            const int rightOffset = m_rightButtons->buttons().isEmpty() ?
 | 
						|
                Metrics::TitleBar_SideMargin*settings()->smallSpacing() :
 | 
						|
                size().width() - m_rightButtons->geometry().x() + Metrics::TitleBar_SideMargin*settings()->smallSpacing();
 | 
						|
 | 
						|
            const int yOffset = settings()->smallSpacing()*Metrics::TitleBar_TopMargin;
 | 
						|
            const QRect maxRect( leftOffset, yOffset, size().width() - leftOffset - rightOffset, captionHeight() );
 | 
						|
 | 
						|
            switch( m_internalSettings->titleAlignment() )
 | 
						|
            {
 | 
						|
                case InternalSettings::AlignLeft:
 | 
						|
                return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignLeft );
 | 
						|
 | 
						|
                case InternalSettings::AlignRight:
 | 
						|
                return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignRight );
 | 
						|
 | 
						|
                case InternalSettings::AlignCenter:
 | 
						|
                return qMakePair( maxRect, Qt::AlignCenter );
 | 
						|
 | 
						|
                default:
 | 
						|
                case InternalSettings::AlignCenterFullWidth:
 | 
						|
                {
 | 
						|
 | 
						|
                    // full caption rect
 | 
						|
                    const QRect fullRect = QRect( 0, yOffset, size().width(), captionHeight() );
 | 
						|
                    QRect boundingRect( settings()->fontMetrics().boundingRect( c->caption()).toRect() );
 | 
						|
 | 
						|
                    // text bounding rect
 | 
						|
                    boundingRect.setTop( yOffset );
 | 
						|
                    boundingRect.setHeight( captionHeight() );
 | 
						|
                    boundingRect.moveLeft( ( size().width() - boundingRect.width() )/2 );
 | 
						|
 | 
						|
                    if( boundingRect.left() < leftOffset ) return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignLeft );
 | 
						|
                    else if( boundingRect.right() > size().width() - rightOffset ) return qMakePair( maxRect, Qt::AlignVCenter|Qt::AlignRight );
 | 
						|
                    else return qMakePair(fullRect, Qt::AlignCenter);
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    void Decoration::updateShadow()
 | 
						|
    {
 | 
						|
        const auto s = Decoration::settings();
 | 
						|
 | 
						|
        // Animated case, no cached shadow object
 | 
						|
        if ( (m_shadowAnimation->state() == QAbstractAnimation::Running) && (m_shadowOpacity != 0.0) && (m_shadowOpacity != 1.0) )
 | 
						|
        {
 | 
						|
            setShadow(createShadowObject(m_internalSettings, 0.5 + m_shadowOpacity * 0.5, s->smallSpacing()));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (g_shadowSizeEnum != m_internalSettings->shadowSize()
 | 
						|
                || g_shadowStrength != m_internalSettings->shadowStrength()
 | 
						|
                || g_shadowColor != m_internalSettings->shadowColor())
 | 
						|
        {
 | 
						|
            g_sShadow.clear();
 | 
						|
            g_sShadowInactive.clear();
 | 
						|
            g_shadowSizeEnum = m_internalSettings->shadowSize();
 | 
						|
            g_shadowStrength = m_internalSettings->shadowStrength();
 | 
						|
            g_shadowColor = m_internalSettings->shadowColor();
 | 
						|
        }
 | 
						|
 | 
						|
        auto c = client().toStrongRef();
 | 
						|
        auto& shadow = (c->isActive()) ? g_sShadow : g_sShadowInactive;
 | 
						|
        if ( !shadow )
 | 
						|
        {
 | 
						|
            shadow = createShadowObject(m_internalSettings, c->isActive() ? 1.0 : 0.5, s->smallSpacing());
 | 
						|
        }
 | 
						|
        setShadow(shadow);
 | 
						|
    }
 | 
						|
 | 
						|
    //________________________________________________________________
 | 
						|
    QSharedPointer<KDecoration2::DecorationShadow> Decoration::createShadowObject(const InternalSettingsPtr& internalSettings, const float strengthScale, const int scale)
 | 
						|
    {
 | 
						|
          const CompositeShadowParams params = lookupShadowParams(internalSettings->shadowSize());
 | 
						|
          if (params.isNone())
 | 
						|
          {
 | 
						|
              return nullptr;
 | 
						|
          }
 | 
						|
 | 
						|
          auto withOpacity = [](const QColor& color, qreal opacity) -> QColor {
 | 
						|
              QColor c(color);
 | 
						|
              c.setAlphaF(opacity);
 | 
						|
              return c;
 | 
						|
          };
 | 
						|
 | 
						|
          const int radius = scale*Metrics::Frame_FrameRadius;
 | 
						|
 | 
						|
          const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
 | 
						|
              .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
 | 
						|
 | 
						|
          BoxShadowRenderer shadowRenderer;
 | 
						|
          shadowRenderer.setBorderRadius(radius + 0.5);
 | 
						|
          shadowRenderer.setBoxSize(boxSize);
 | 
						|
          shadowRenderer.setDevicePixelRatio(1.0); // TODO: Create HiDPI shadows?
 | 
						|
 | 
						|
          const qreal strength = internalSettings->shadowStrength() / 255.0 * strengthScale;
 | 
						|
          shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,
 | 
						|
              withOpacity(internalSettings->shadowColor(), params.shadow1.opacity * strength));
 | 
						|
          shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius,
 | 
						|
              withOpacity(internalSettings->shadowColor(), params.shadow2.opacity * strength));
 | 
						|
 | 
						|
          QImage shadowTexture = shadowRenderer.render();
 | 
						|
 | 
						|
          QPainter painter(&shadowTexture);
 | 
						|
          painter.setRenderHint(QPainter::Antialiasing);
 | 
						|
 | 
						|
          const QRect outerRect = shadowTexture.rect();
 | 
						|
 | 
						|
          QRect boxRect(QPoint(0, 0), boxSize);
 | 
						|
          boxRect.moveCenter(outerRect.center());
 | 
						|
 | 
						|
          // Mask out inner rect.
 | 
						|
          const QMargins padding = QMargins(
 | 
						|
              boxRect.left() - outerRect.left() - Metrics::Shadow_Overlap - params.offset.x(),
 | 
						|
              boxRect.top() - outerRect.top() - Metrics::Shadow_Overlap - params.offset.y(),
 | 
						|
              outerRect.right() - boxRect.right() - Metrics::Shadow_Overlap + params.offset.x(),
 | 
						|
              outerRect.bottom() - boxRect.bottom() - Metrics::Shadow_Overlap + params.offset.y());
 | 
						|
          const QRect innerRect = outerRect - padding;
 | 
						|
 | 
						|
          painter.setPen(Qt::NoPen);
 | 
						|
          painter.setBrush(Qt::black);
 | 
						|
          painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
 | 
						|
          painter.drawRoundedRect(
 | 
						|
              innerRect,
 | 
						|
              radius + 0.5,
 | 
						|
              radius + 0.5);
 | 
						|
 | 
						|
          // Draw outline.
 | 
						|
          painter.setPen(withOpacity(internalSettings->shadowColor(), 0.2 * strength));
 | 
						|
          painter.setBrush(Qt::NoBrush);
 | 
						|
          painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
 | 
						|
          painter.drawRoundedRect(
 | 
						|
              innerRect,
 | 
						|
              radius - 0.5,
 | 
						|
              radius - 0.5);
 | 
						|
 | 
						|
          painter.end();
 | 
						|
 | 
						|
          auto ret = QSharedPointer<KDecoration2::DecorationShadow>::create();
 | 
						|
          ret->setPadding(padding);
 | 
						|
          ret->setInnerShadowRect(QRect(outerRect.center(), QSize(1, 1)));
 | 
						|
          ret->setShadow(shadowTexture);
 | 
						|
          return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    //_________________________________________________________________
 | 
						|
    void Decoration::createSizeGrip()
 | 
						|
    {
 | 
						|
 | 
						|
        // do nothing if size grip already exist
 | 
						|
        if( m_sizeGrip ) return;
 | 
						|
 | 
						|
        #if BREEZE_HAVE_X11
 | 
						|
        if( !QX11Info::isPlatformX11() ) return;
 | 
						|
 | 
						|
        // access client
 | 
						|
        auto c = client().data();
 | 
						|
        if( !c ) return;
 | 
						|
 | 
						|
        if( c->windowId() != 0 )
 | 
						|
        {
 | 
						|
            m_sizeGrip = new SizeGrip( this );
 | 
						|
            connect( c, &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateSizeGripVisibility );
 | 
						|
            connect( c, &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateSizeGripVisibility );
 | 
						|
            connect( c, &KDecoration2::DecoratedClient::resizeableChanged, this, &Decoration::updateSizeGripVisibility );
 | 
						|
        }
 | 
						|
        #endif
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //_________________________________________________________________
 | 
						|
    void Decoration::deleteSizeGrip()
 | 
						|
    {
 | 
						|
        if( m_sizeGrip )
 | 
						|
        {
 | 
						|
            m_sizeGrip->deleteLater();
 | 
						|
            m_sizeGrip = nullptr;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
 | 
						|
#include "breezedecoration.moc"
 |