diff --git a/kdecoration/breezedecoration.cpp b/kdecoration/breezedecoration.cpp index 46644c21..800a5174 100644 --- a/kdecoration/breezedecoration.cpp +++ b/kdecoration/breezedecoration.cpp @@ -1,742 +1,764 @@ /* * Copyright 2014 Martin Gräßlin * Copyright 2014 Hugo Pereira Da Costa * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "breezedecoration.h" #include "breeze.h" #include "breezesettingsprovider.h" #include "config-breeze.h" #include "config/breezeconfigwidget.h" #include "breezebutton.h" #include "breezesizegrip.h" #include #include #include #include #include #include #include #include #include #include #include #if BREEZE_HAVE_X11 #include #endif #include K_PLUGIN_FACTORY_WITH_JSON( BreezeDecoFactory, "breeze.json", registerPlugin(); registerPlugin(QStringLiteral("button")); registerPlugin(QStringLiteral("kcmodule")); ) namespace Breeze { using KDecoration2::ColorRole; using KDecoration2::ColorGroup; //________________________________________________________________ static int g_sDecoCount = 0; - static int g_shadowSize = 0; - static int g_shadowStrength = 0; + static int g_shadowSizeEnum = InternalSettings::ShadowLarge; + static int g_shadowStrength = 90; static QColor g_shadowColor = Qt::black; static QSharedPointer g_sShadow; //________________________________________________________________ Decoration::Decoration(QObject *parent, const QVariantList &args) : KDecoration2::Decoration(parent, args) , m_animation( new QPropertyAnimation( 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() == QPropertyAnimation::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() == QPropertyAnimation::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() == QPropertyAnimation::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 m_animation->setStartValue( 0 ); m_animation->setEndValue( 1.0 ); m_animation->setTargetObject( this ); m_animation->setPropertyName( "opacity" ); m_animation->setEasingCurve( QEasingCurve::InOutQuad ); 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(); createShadow(); } //________________________________________________________________ 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_internalSettings->animationsEnabled() ) { auto c = client().data(); m_animation->setDirection( c->isActive() ? QPropertyAnimation::Forward : QPropertyAnimation::Backward ); if( m_animation->state() != QPropertyAnimation::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(); if( m_internalSettings && (m_internalSettings->mask() & BorderSize ) ) { switch (m_internalSettings->borderSize()) { case InternalSettings::BorderNone: return 0; case InternalSettings::BorderNoSides: return bottom ? qMax(4, baseSize) : 0; default: case InternalSettings::BorderTiny: return bottom ? qMax(4, baseSize) : baseSize; case InternalSettings::BorderNormal: return baseSize*2; case InternalSettings::BorderLarge: return baseSize*3; case InternalSettings::BorderVeryLarge: return baseSize*4; case InternalSettings::BorderHuge: return baseSize*5; case InternalSettings::BorderVeryHuge: return baseSize*6; case InternalSettings::BorderOversized: return baseSize*10; } } else { switch (settings()->borderSize()) { case KDecoration2::BorderSize::None: return 0; case KDecoration2::BorderSize::NoSides: return bottom ? qMax(4, baseSize) : 0; default: case KDecoration2::BorderSize::Tiny: return bottom ? qMax(4, baseSize) : baseSize; case KDecoration2::BorderSize::Normal: return baseSize*2; case KDecoration2::BorderSize::Large: return baseSize*3; case KDecoration2::BorderSize::VeryLarge: return baseSize*4; case KDecoration2::BorderSize::Huge: return baseSize*5; case KDecoration2::BorderSize::VeryHuge: return baseSize*6; case KDecoration2::BorderSize::Oversized: return baseSize*10; } } } //________________________________________________________________ void Decoration::reconfigure() { m_internalSettings = SettingsProvider::self()->internalSettings( this ); // animation m_animation->setDuration( m_internalSettings->animationsDuration() ); // borders recalculateBorders(); // shadow createShadow(); // size grip if( hasNoBorders() && m_internalSettings->drawSizeGrip() ) createSizeGrip(); else deleteSizeGrip(); } //________________________________________________________________ void Decoration::recalculateBorders() { auto c = client().data(); auto s = settings(); // left, right and bottom borders const int left = isLeftEdge() ? 0 : borderSize(); const int right = isRightEdge() ? 0 : borderSize(); const int bottom = (c->isShaded() || isBottomEdge()) ? 0 : borderSize(true); 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; if( hasNoBorders() ) { extSides = extSize; extBottom = extSize; } else if( hasNoSideBorders() ) { extSides = extSize; } 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& button, m_leftButtons->buttons() + m_rightButtons->buttons() ) { button.data()->setGeometry( QRectF( QPoint( 0, 0 ), QSizeF( bWidth, bHeight ) ) ); static_cast( button.data() )->setOffset( QPointF( 0, verticalOffset ) ); static_cast( button.data() )->setIconSize( QSize( bWidth, bWidth ) ); } // left buttons if( !m_leftButtons->buttons().isEmpty() ) { // spacing m_leftButtons->setSpacing(s->smallSpacing()*Metrics::TitleBar_ButtonSpacing); // padding const int vPadding = isTopEdge() ? 0 : s->smallSpacing()*Metrics::TitleBar_TopMargin; const int hPadding = s->smallSpacing()*Metrics::TitleBar_SideMargin; if( isLeftEdge() ) { // add offsets on the side buttons, to preserve padding, but satisfy Fitts law auto button = static_cast( m_leftButtons->buttons().front().data() ); button->setGeometry( QRectF( QPoint( 0, 0 ), QSizeF( bWidth + hPadding, bHeight ) ) ); button->setFlag( Button::FlagFirstInList ); button->setHorizontalOffset( hPadding ); m_leftButtons->setPos(QPointF(0, vPadding)); } else m_leftButtons->setPos(QPointF(hPadding + borderLeft(), vPadding)); } // right buttons if( !m_rightButtons->buttons().isEmpty() ) { // spacing m_rightButtons->setSpacing(s->smallSpacing()*Metrics::TitleBar_ButtonSpacing); // padding const int vPadding = isTopEdge() ? 0 : s->smallSpacing()*Metrics::TitleBar_TopMargin; const int hPadding = s->smallSpacing()*Metrics::TitleBar_SideMargin; if( isRightEdge() ) { auto button = static_cast( m_rightButtons->buttons().back().data() ); button->setGeometry( QRectF( QPoint( 0, 0 ), QSizeF( bWidth + hPadding, bHeight ) ) ); button->setFlag( Button::FlagLastInList ); m_rightButtons->setPos(QPointF(size().width() - m_rightButtons->geometry().width(), vPadding)); } else 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(); // 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(), Metrics::Frame_FrameRadius, Metrics::Frame_FrameRadius); else painter->drawRect( rect() ); painter->restore(); } if( !hideTitleBar() ) paintTitleBar(painter, repaintRegion); if( hasBorders() && !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(); if( isMaximized() || !s->isAlphaChannelSupported() ) { painter->drawRect(titleRect); } else if( c->isShaded() ) { painter->drawRoundedRect(titleRect, Metrics::Frame_FrameRadius, Metrics::Frame_FrameRadius); } 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() ? -Metrics::Frame_FrameRadius:0, isTopEdge() ? -Metrics::Frame_FrameRadius:0, isRightEdge() ? Metrics::Frame_FrameRadius:0, Metrics::Frame_FrameRadius), Metrics::Frame_FrameRadius, Metrics::Frame_FrameRadius); } 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 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::createShadow() { // assign global shadow if exists and parameters match if( !g_sShadow || - g_shadowSize != m_internalSettings->shadowSize() || + g_shadowSizeEnum != m_internalSettings->shadowSize() || g_shadowStrength != m_internalSettings->shadowStrength() || g_shadowColor != m_internalSettings->shadowColor() ) { // assign parameters - g_shadowSize = m_internalSettings->shadowSize(); + g_shadowSizeEnum = m_internalSettings->shadowSize(); g_shadowStrength = m_internalSettings->shadowStrength(); g_shadowColor = m_internalSettings->shadowColor(); - const int shadowOffset = qMax( 6*g_shadowSize/16, Metrics::Shadow_Overlap*2 ); + + // shadow size from enum + int shadowSize = 0; + switch( g_shadowSizeEnum ) + { + default: + case InternalSettings::ShadowLarge: shadowSize = 64; break; + + case InternalSettings::ShadowNone: shadowSize = Metrics::Shadow_Overlap + 1; break; + case InternalSettings::ShadowSmall: shadowSize = 16; break; + case InternalSettings::ShadowMedium: shadowSize = 32; break; + case InternalSettings::ShadowVeryLarge: shadowSize = 96; break; + } + + // offset + int shadowOffset = (g_shadowSizeEnum == InternalSettings::ShadowNone) ? 0 : qMax( 6*shadowSize/16, Metrics::Shadow_Overlap*2 ); // create image - QImage image(2*g_shadowSize, 2*g_shadowSize, QImage::Format_ARGB32_Premultiplied); + QImage image(2*shadowSize, 2*shadowSize, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); - // create gradient - // gaussian delta function - auto alpha = [](qreal x) { return std::exp( -x*x/0.15 ); }; + // painter + QPainter painter(&image); + painter.setRenderHint( QPainter::Antialiasing, true ); // color calculation delta function auto gradientStopColor = [](QColor color, int alpha) { color.setAlpha(alpha); return color; }; - QRadialGradient radialGradient( g_shadowSize, g_shadowSize, g_shadowSize ); - for( int i = 0; i < 10; ++i ) + // create gradient + if( g_shadowSizeEnum != InternalSettings::ShadowNone ) { - const qreal x( qreal( i )/9 ); - radialGradient.setColorAt(x, gradientStopColor( g_shadowColor, alpha(x)*g_shadowStrength ) ); - } - radialGradient.setColorAt(1, gradientStopColor( g_shadowColor, 0 ) ); + // gaussian lambda function + auto alpha = [](qreal x) { return std::exp( -x*x/0.15 ); }; - // fill - QPainter painter(&image); - painter.setRenderHint( QPainter::Antialiasing, true ); - painter.fillRect( image.rect(), radialGradient); + QRadialGradient radialGradient( shadowSize, shadowSize, shadowSize ); + for( int i = 0; i < 10; ++i ) + { + const qreal x( qreal( i )/9 ); + radialGradient.setColorAt(x, gradientStopColor( g_shadowColor, alpha(x)*g_shadowStrength ) ); + } + + radialGradient.setColorAt(1, gradientStopColor( g_shadowColor, 0 ) ); + + // fill + painter.fillRect( image.rect(), radialGradient); + + } // contrast pixel QRectF innerRect = QRectF( - g_shadowSize - Metrics::Shadow_Overlap, g_shadowSize - shadowOffset - Metrics::Shadow_Overlap, + shadowSize - Metrics::Shadow_Overlap, shadowSize - shadowOffset - Metrics::Shadow_Overlap, 2*Metrics::Shadow_Overlap, shadowOffset + 2*Metrics::Shadow_Overlap ); - painter.setPen( gradientStopColor( g_shadowColor, g_shadowStrength*0.5 ) ); + painter.setPen( gradientStopColor( g_shadowColor, (g_shadowSizeEnum == InternalSettings::ShadowNone) ? g_shadowStrength:(g_shadowStrength*0.5) ) ); painter.setBrush( Qt::NoBrush ); painter.drawRoundedRect( innerRect, -0.5 + Metrics::Frame_FrameRadius, -0.5 + Metrics::Frame_FrameRadius ); // mask out inner rect painter.setPen( Qt::NoPen ); painter.setBrush( Qt::black ); painter.setCompositionMode(QPainter::CompositionMode_DestinationOut ); painter.drawRoundedRect( innerRect, 0.5 + Metrics::Frame_FrameRadius, 0.5 + Metrics::Frame_FrameRadius ); painter.end(); g_sShadow = QSharedPointer::create(); g_sShadow->setPadding( QMargins( - g_shadowSize - Metrics::Shadow_Overlap, - g_shadowSize - shadowOffset - Metrics::Shadow_Overlap, - g_shadowSize - Metrics::Shadow_Overlap, - g_shadowSize - Metrics::Shadow_Overlap ) ); + shadowSize - Metrics::Shadow_Overlap, + shadowSize - shadowOffset - Metrics::Shadow_Overlap, + shadowSize - Metrics::Shadow_Overlap, + shadowSize - Metrics::Shadow_Overlap ) ); - g_sShadow->setInnerShadowRect(QRect( g_shadowSize, g_shadowSize, 1, 1) ); + g_sShadow->setInnerShadowRect(QRect( shadowSize, shadowSize, 1, 1) ); // assign image g_sShadow->setShadow(image); } setShadow(g_sShadow); } //_________________________________________________________________ void Decoration::createSizeGrip( void ) { // 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( void ) { if( m_sizeGrip ) { m_sizeGrip->deleteLater(); m_sizeGrip = nullptr; } } } // namespace #include "breezedecoration.moc" diff --git a/kdecoration/breezesettingsdata.kcfg b/kdecoration/breezesettingsdata.kcfg index 47d3f2f4..dcfd9d36 100644 --- a/kdecoration/breezesettingsdata.kcfg +++ b/kdecoration/breezesettingsdata.kcfg @@ -1,132 +1,137 @@ - 90 25 255 - - 64 - 6 - 100 + + + + + + + + + + ShadowLarge 35, 38, 41 true BorderNoSides AlignCenterFullWidth ButtonDefault false true true true true 150 false ExceptionWindowClassName true 0 diff --git a/kdecoration/config/breezeconfigwidget.cpp b/kdecoration/config/breezeconfigwidget.cpp index 779f4308..1e5476c8 100644 --- a/kdecoration/config/breezeconfigwidget.cpp +++ b/kdecoration/config/breezeconfigwidget.cpp @@ -1,215 +1,217 @@ ////////////////////////////////////////////////////////////////////////////// // breezeconfigurationui.cpp // ------------------- // // Copyright (c) 2009 Hugo Pereira Da Costa // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////// #include "breezeconfigwidget.h" #include "breezeexceptionlist.h" #include "breezesettings.h" #include #include #include namespace Breeze { //_________________________________________________________ ConfigWidget::ConfigWidget( QWidget* parent, const QVariantList &args ): KCModule(parent, args), m_configuration( KSharedConfig::openConfig( QStringLiteral( "breezerc" ) ) ), m_changed( false ) { // configuration m_ui.setupUi( this ); // track ui changes connect( m_ui.titleAlignment, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) ); connect( m_ui.buttonSize, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) ); connect( m_ui.outlineCloseButton, SIGNAL(clicked()), SLOT(updateChanged()) ); connect( m_ui.drawBorderOnMaximizedWindows, SIGNAL(clicked()), SLOT(updateChanged()) ); connect( m_ui.drawSizeGrip, SIGNAL(clicked()), SLOT(updateChanged()) ); connect( m_ui.drawBackgroundGradient, SIGNAL(clicked()), SLOT(updateChanged()) ); connect( m_ui.drawTitleBarSeparator, SIGNAL(clicked()), SLOT(updateChanged()) ); // track animations changes connect( m_ui.animationsEnabled, SIGNAL(clicked()), SLOT(updateChanged()) ); connect( m_ui.animationsDuration, SIGNAL(valueChanged(int)), SLOT(updateChanged()) ); // track shadows changes - connect( m_ui.shadowSize, SIGNAL(valueChanged(int)), SLOT(updateChanged()) ); + connect( m_ui.shadowSize, SIGNAL(currentIndexChanged(int)), SLOT(updateChanged()) ); connect( m_ui.shadowStrength, SIGNAL(valueChanged(int)), SLOT(updateChanged()) ); connect( m_ui.shadowColor, SIGNAL(changed(QColor)), SLOT(updateChanged()) ); // track exception changes connect( m_ui.exceptions, SIGNAL(changed(bool)), SLOT(updateChanged()) ); } //_________________________________________________________ void ConfigWidget::load( void ) { // create internal settings and load from rc files m_internalSettings = InternalSettingsPtr( new InternalSettings() ); m_internalSettings->load(); // assign to ui m_ui.titleAlignment->setCurrentIndex( m_internalSettings->titleAlignment() ); m_ui.buttonSize->setCurrentIndex( m_internalSettings->buttonSize() ); m_ui.drawBorderOnMaximizedWindows->setChecked( m_internalSettings->drawBorderOnMaximizedWindows() ); m_ui.outlineCloseButton->setChecked( m_internalSettings->outlineCloseButton() ); m_ui.drawSizeGrip->setChecked( m_internalSettings->drawSizeGrip() ); m_ui.drawBackgroundGradient->setChecked( m_internalSettings->drawBackgroundGradient() ); m_ui.animationsEnabled->setChecked( m_internalSettings->animationsEnabled() ); m_ui.animationsDuration->setValue( m_internalSettings->animationsDuration() ); m_ui.drawTitleBarSeparator->setChecked( m_internalSettings->drawTitleBarSeparator() ); // load shadows - m_ui.shadowSize->setValue( m_internalSettings->shadowSize() ); + if( m_internalSettings->shadowSize() <= InternalSettings::ShadowVeryLarge ) m_ui.shadowSize->setCurrentIndex( m_internalSettings->shadowSize() ); + else m_ui.shadowSize->setCurrentIndex( InternalSettings::ShadowLarge ); + m_ui.shadowStrength->setValue( qRound(qreal(m_internalSettings->shadowStrength()*100)/255 ) ); m_ui.shadowColor->setColor( m_internalSettings->shadowColor() ); // load exceptions ExceptionList exceptions; exceptions.readConfig( m_configuration ); m_ui.exceptions->setExceptions( exceptions.get() ); setChanged( false ); } //_________________________________________________________ void ConfigWidget::save( void ) { // create internal settings and load from rc files m_internalSettings = InternalSettingsPtr( new InternalSettings() ); m_internalSettings->load(); // apply modifications from ui m_internalSettings->setTitleAlignment( m_ui.titleAlignment->currentIndex() ); m_internalSettings->setButtonSize( m_ui.buttonSize->currentIndex() ); m_internalSettings->setOutlineCloseButton( m_ui.outlineCloseButton->isChecked() ); m_internalSettings->setDrawBorderOnMaximizedWindows( m_ui.drawBorderOnMaximizedWindows->isChecked() ); m_internalSettings->setDrawSizeGrip( m_ui.drawSizeGrip->isChecked() ); m_internalSettings->setDrawBackgroundGradient( m_ui.drawBackgroundGradient->isChecked() ); m_internalSettings->setAnimationsEnabled( m_ui.animationsEnabled->isChecked() ); m_internalSettings->setAnimationsDuration( m_ui.animationsDuration->value() ); m_internalSettings->setDrawTitleBarSeparator(m_ui.drawTitleBarSeparator->isChecked()); - m_internalSettings->setShadowSize( m_ui.shadowSize->value() ); + m_internalSettings->setShadowSize( m_ui.shadowSize->currentIndex() ); m_internalSettings->setShadowStrength( qRound( qreal(m_ui.shadowStrength->value()*255)/100 ) ); m_internalSettings->setShadowColor( m_ui.shadowColor->color() ); // save configuration m_internalSettings->save(); // get list of exceptions and write InternalSettingsList exceptions( m_ui.exceptions->exceptions() ); ExceptionList( exceptions ).writeConfig( m_configuration ); // sync configuration m_configuration->sync(); setChanged( false ); // needed to tell kwin to reload when running from external kcmshell { QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); QDBusConnection::sessionBus().send(message); } // needed for breeze style to reload shadows { QDBusMessage message( QDBusMessage::createSignal("/BreezeDecoration", "org.kde.Breeze.Style", "reparseConfiguration") ); QDBusConnection::sessionBus().send(message); } } //_________________________________________________________ void ConfigWidget::defaults( void ) { // create internal settings and load from rc files m_internalSettings = InternalSettingsPtr( new InternalSettings() ); m_internalSettings->setDefaults(); // assign to ui m_ui.titleAlignment->setCurrentIndex( m_internalSettings->titleAlignment() ); m_ui.buttonSize->setCurrentIndex( m_internalSettings->buttonSize() ); m_ui.drawBorderOnMaximizedWindows->setChecked( m_internalSettings->drawBorderOnMaximizedWindows() ); m_ui.drawSizeGrip->setChecked( m_internalSettings->drawSizeGrip() ); m_ui.drawBackgroundGradient->setChecked( m_internalSettings->drawBackgroundGradient() ); m_ui.animationsEnabled->setChecked( m_internalSettings->animationsEnabled() ); m_ui.animationsDuration->setValue( m_internalSettings->animationsDuration() ); m_ui.drawTitleBarSeparator->setChecked( m_internalSettings->drawTitleBarSeparator() ); - m_ui.shadowSize->setValue( m_internalSettings->shadowSize() ); + m_ui.shadowSize->setCurrentIndex( m_internalSettings->shadowSize() ); m_ui.shadowStrength->setValue( qRound(qreal(m_internalSettings->shadowStrength()*100)/255 ) ); m_ui.shadowColor->setColor( m_internalSettings->shadowColor() ); } //_______________________________________________ void ConfigWidget::updateChanged( void ) { // check configuration if( !m_internalSettings ) return; // track modifications bool modified( false ); if (m_ui.drawTitleBarSeparator->isChecked() != m_internalSettings->drawTitleBarSeparator()) modified = true; if( m_ui.titleAlignment->currentIndex() != m_internalSettings->titleAlignment() ) modified = true; else if( m_ui.buttonSize->currentIndex() != m_internalSettings->buttonSize() ) modified = true; else if( m_ui.outlineCloseButton->isChecked() != m_internalSettings->outlineCloseButton() ) modified = true; else if( m_ui.drawBorderOnMaximizedWindows->isChecked() != m_internalSettings->drawBorderOnMaximizedWindows() ) modified = true; else if( m_ui.drawSizeGrip->isChecked() != m_internalSettings->drawSizeGrip() ) modified = true; else if( m_ui.drawBackgroundGradient->isChecked() != m_internalSettings->drawBackgroundGradient() ) modified = true; // animations else if( m_ui.animationsEnabled->isChecked() != m_internalSettings->animationsEnabled() ) modified = true; else if( m_ui.animationsDuration->value() != m_internalSettings->animationsDuration() ) modified = true; // shadows - else if( m_ui.shadowSize->value() != m_internalSettings->shadowSize() ) modified = true; + else if( m_ui.shadowSize->currentIndex() != m_internalSettings->shadowSize() ) modified = true; else if( qRound( qreal(m_ui.shadowStrength->value()*255)/100 ) != m_internalSettings->shadowStrength() ) modified = true; else if( m_ui.shadowColor->color() != m_internalSettings->shadowColor() ) modified = true; // exceptions else if( m_ui.exceptions->isChanged() ) modified = true; setChanged( modified ); } //_______________________________________________ void ConfigWidget::setChanged( bool value ) { emit changed( value ); } } diff --git a/kdecoration/config/ui/breezeconfigurationui.ui b/kdecoration/config/ui/breezeconfigurationui.ui index 3d73d543..8899ee98 100644 --- a/kdecoration/config/ui/breezeconfigurationui.ui +++ b/kdecoration/config/ui/breezeconfigurationui.ui @@ -1,429 +1,436 @@ BreezeConfigurationUI 0 0 428 418 - - 0 - - - 0 - - - 0 - - + 0 0 General Tit&le alignment: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter titleAlignment B&utton size: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter buttonSize Tiny Small - Normal + Medium Large Very Large Left Center Center (Full Width) Right Qt::Horizontal 40 20 Qt::Vertical 20 40 Add handle to resize windows with no border Draw separator between Title Bar and Window Allow resizing maximized windows from window edges Draw a circle around close button Draw window background gradient Animations false Anima&tions duration: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter animationsDuration false ms 500 Qt::Horizontal 40 20 Enable animations Qt::Vertical 20 40 Shadows Si&ze: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter shadowSize - - - px - - - 6 - - - 64 - + + + + None + + + + + Small + + + + + Medium + + + + + Large + + + + + Very Large + + S&trength: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter shadowStrength % 10 100 Qt::Horizontal 40 20 Color: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 Window-Specific Overrides 0 0 KColorButton QPushButton
kcolorbutton.h
Breeze::ExceptionListWidget QWidget
config/breezeexceptionlistwidget.h
1
tabWidget titleAlignment buttonSize outlineCloseButton drawBorderOnMaximizedWindows drawBackgroundGradient drawSizeGrip drawTitleBarSeparator animationsEnabled animationsDuration shadowSize shadowStrength shadowColor animationsEnabled toggled(bool) animationsDurationLabel setEnabled(bool) 34 194 84 221 animationsEnabled toggled(bool) animationsDuration setEnabled(bool) 108 194 141 229
diff --git a/kstyle/breeze.kcfg b/kstyle/breeze.kcfg index cf29759a..bd2cc407 100644 --- a/kstyle/breeze.kcfg +++ b/kstyle/breeze.kcfg @@ -1,184 +1,190 @@ 90 25 255 - - 64 - 6 - 100 + + + + + + + + + + ShadowLarge 35, 38, 41 true true 10 100 false true 800 0 0 MN_AUTO true true true true false false false false false true WD_FULL true true 12 false false diff --git a/kstyle/breezeshadowhelper.cpp b/kstyle/breezeshadowhelper.cpp index 4e4d33cc..43dc8926 100644 --- a/kstyle/breezeshadowhelper.cpp +++ b/kstyle/breezeshadowhelper.cpp @@ -1,579 +1,601 @@ /************************************************************************* * Copyright (C) 2014 by Hugo Pereira Da Costa * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * *************************************************************************/ #include "breezeshadowhelper.h" #include "breeze.h" #include "breezehelper.h" #include "breezepropertynames.h" #include "breezestyleconfigdata.h" #include #include #include #include #include #include #include #include #if BREEZE_HAVE_X11 #include #endif #if BREEZE_HAVE_KWAYLAND #include #include #include #include #include #include #endif +namespace +{ + + int shadowSize( int shadowSizeEnum ) + { + + switch( shadowSizeEnum ) + { + default: + case Breeze::StyleConfigData::ShadowLarge: return 16; + case Breeze::StyleConfigData::ShadowNone: return 0; + case Breeze::StyleConfigData::ShadowSmall: return 12; + case Breeze::StyleConfigData::ShadowMedium: return 14; + case Breeze::StyleConfigData::ShadowVeryLarge: return 24; + } + + } + + +} + namespace Breeze { const char ShadowHelper::netWMShadowAtomName[] ="_KDE_NET_WM_SHADOW"; //_____________________________________________________ ShadowHelper::ShadowHelper( QObject* parent, Helper& helper ): QObject( parent ), _helper( helper ) #if BREEZE_HAVE_X11 ,_gc( 0 ), _atom( 0 ) #endif #if BREEZE_HAVE_KWAYLAND , _shadowManager( nullptr ) , _shmPool( nullptr ) #endif { // delay till event dispatcher is running as Wayland is highly async QMetaObject::invokeMethod(this, "initializeWayland", Qt::QueuedConnection); } //_______________________________________________________ ShadowHelper::~ShadowHelper() { #if BREEZE_HAVE_X11 if( Helper::isX11() ) { foreach( const quint32& value, _pixmaps ) xcb_free_pixmap( Helper::connection(), value ); } #endif } //_______________________________________________________ void ShadowHelper::initializeWayland() { #if BREEZE_HAVE_KWAYLAND if( !Helper::isWayland() ) return; using namespace KWayland::Client; auto connection = ConnectionThread::fromApplication( this ); if( !connection ) { return; } auto registry = new Registry( connection ); registry->create( connection ); connect(registry, &Registry::interfacesAnnounced, this, [registry, this] { const auto interface = registry->interface( Registry::Interface::Shadow ); if( interface.name != 0 ) { _shadowManager = registry->createShadowManager( interface.name, interface.version, registry ); } const auto shmInterface = registry->interface( Registry::Interface::Shm ); if( shmInterface.name != 0 ) { _shmPool = registry->createShmPool( shmInterface.name, shmInterface.version, registry ); } } ); registry->setup(); connection->roundtrip(); #endif } //______________________________________________ void ShadowHelper::reset() { #if BREEZE_HAVE_X11 if( Helper::isX11() ) { foreach( const quint32& value, _pixmaps ) xcb_free_pixmap( Helper::connection(), value ); } #endif _pixmaps.clear(); _shadowTiles = TileSet(); } //_______________________________________________________ bool ShadowHelper::registerWidget( QWidget* widget, bool force ) { // make sure widget is not already registered if( _widgets.contains( widget ) ) return false; // check if widget qualifies if( !( force || acceptWidget( widget ) ) ) { return false; } // try create shadow directly if( installShadows( widget ) ) _widgets.insert( widget, widget->winId() ); else _widgets.insert( widget, 0 ); // install event filter widget->removeEventFilter( this ); widget->installEventFilter( this ); // connect destroy signal connect( widget, SIGNAL(destroyed(QObject*)), SLOT(objectDeleted(QObject*)) ); return true; } //_______________________________________________________ void ShadowHelper::unregisterWidget( QWidget* widget ) { if( _widgets.remove( widget ) ) { uninstallShadows( widget ); } } //_______________________________________________________ void ShadowHelper::loadConfig() { // reset reset(); // update property for registered widgets for( QMap::const_iterator iter = _widgets.constBegin(); iter != _widgets.constEnd(); ++iter ) { installShadows( iter.key() ); } } //_______________________________________________________ bool ShadowHelper::eventFilter( QObject* object, QEvent* event ) { if( Helper::isWayland() ) { #if BREEZE_HAVE_KWAYLAND QWidget* widget( static_cast( object ) ); if( event->type() == QEvent::Paint ) { auto iter = _widgetSurfaces.constFind( widget ); if( iter == _widgetSurfaces.constEnd() ) { // install shadows and update winId installShadows( widget ); } } else if( event->type() == QEvent::Hide ) { auto iter = _widgetSurfaces.find( widget ); if( iter != _widgetSurfaces.end() ) { _widgetSurfaces.erase( iter ); } } #endif } else if( Helper::isX11() ) { // check event type if( event->type() != QEvent::WinIdChange ) return false; // cast widget QWidget* widget( static_cast( object ) ); // install shadows and update winId if( installShadows( widget ) ) { _widgets.insert( widget, widget->winId() ); } } return false; } //_______________________________________________________ TileSet ShadowHelper::shadowTiles() { - if( !_shadowTiles.isValid() ) + // metrics + const int shadowSize = ::shadowSize( StyleConfigData::shadowSize() ); + if( !shadowSize ) return TileSet(); + else if( !_shadowTiles.isValid() ) { const QPalette palette( QApplication::palette() ); const QColor shadowColor( StyleConfigData::shadowColor() ); - - // metrics - const int shadowSize = StyleConfigData::shadowSize()*12/16; const int shadowOffset = qMax( shadowSize/2, Metrics::Shadow_Overlap*2 ); const int shadowStrength = StyleConfigData::shadowStrength(); // pixmap QPixmap pixmap = _helper.highDpiPixmap( shadowSize*2 ); pixmap.fill( Qt::transparent ); // create gradient // gaussian delta function auto alpha = [](qreal x) { return std::exp( -x*x/0.15 ); }; // color calculation delta function auto gradientStopColor = [](QColor color, int alpha) { color.setAlpha(alpha); return color; }; QRadialGradient radialGradient( shadowSize, shadowSize, shadowSize); for( int i = 0; i < 10; ++i ) { const qreal x( qreal( i )/9 ); radialGradient.setColorAt(x, gradientStopColor(shadowColor, alpha(x)*shadowStrength)); } radialGradient.setColorAt(1, gradientStopColor( shadowColor, 0 ) ); // fill QPainter p(&pixmap); p.setRenderHint( QPainter::Antialiasing, true ); p.fillRect( pixmap.rect(), radialGradient); p.setPen( Qt::NoPen ); p.setBrush( Qt::black ); QRectF innerRect( shadowSize - Metrics::Shadow_Overlap, shadowSize - shadowOffset - Metrics::Shadow_Overlap, 2*Metrics::Shadow_Overlap,shadowOffset + 2*Metrics::Shadow_Overlap ); p.setCompositionMode(QPainter::CompositionMode_DestinationOut ); const qreal radius( _helper.frameRadius() ); p.drawRoundedRect( innerRect, radius, radius ); p.end(); // create tiles from pixmap _shadowTiles = TileSet( pixmap, shadowSize, shadowSize, 1, 1 ); } return _shadowTiles; } //_______________________________________________________ void ShadowHelper::objectDeleted( QObject* object ) { _widgets.remove( static_cast( object ) ); } //_______________________________________________________ bool ShadowHelper::isMenu( QWidget* widget ) const { return qobject_cast( widget ); } //_______________________________________________________ bool ShadowHelper::isToolTip( QWidget* widget ) const { return widget->inherits( "QTipLabel" ) || (widget->windowFlags() & Qt::WindowType_Mask) == Qt::ToolTip; } //_______________________________________________________ bool ShadowHelper::isDockWidget( QWidget* widget ) const { return qobject_cast( widget ); } //_______________________________________________________ bool ShadowHelper::isToolBar( QWidget* widget ) const { return qobject_cast( widget ); } //_______________________________________________________ bool ShadowHelper::acceptWidget( QWidget* widget ) const { // flags if( widget->property( PropertyNames::netWMSkipShadow ).toBool() ) return false; if( widget->property( PropertyNames::netWMForceShadow ).toBool() ) return true; // menus if( isMenu( widget ) ) return true; // combobox dropdown lists if( widget->inherits( "QComboBoxPrivateContainer" ) ) return true; // tooltips if( isToolTip( widget ) && !widget->inherits( "Plasma::ToolTip" ) ) { return true; } // detached widgets if( isDockWidget( widget ) || isToolBar( widget ) ) { return true; } // reject return false; } //______________________________________________ const QVector& ShadowHelper::createPixmapHandles() { /** shadow atom and property specification available at http://community.kde.org/KWin/Shadow */ // create atom #if BREEZE_HAVE_X11 if( !_atom && Helper::isX11() ) _atom = _helper.createAtom( QLatin1String( netWMShadowAtomName ) ); #endif shadowTiles(); // make sure size is valid if( _pixmaps.empty() && _shadowTiles.isValid() ) { _pixmaps.append( createPixmap( _shadowTiles.pixmap( 1 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 2 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 5 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 8 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 7 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 6 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 3 ) ) ); _pixmaps.append( createPixmap( _shadowTiles.pixmap( 0 ) ) ); } // return relevant list of pixmap handles return _pixmaps; } //______________________________________________ quint32 ShadowHelper::createPixmap( const QPixmap& source ) { // do nothing for invalid pixmaps if( source.isNull() ) return 0; if( !Helper::isX11() ) return 0; /* in some cases, pixmap handle is invalid. This is the case notably when Qt uses to RasterEngine. In this case, we create an X11 Pixmap explicitly and draw the source pixmap on it. */ #if BREEZE_HAVE_X11 const int width( source.width() ); const int height( source.height() ); // create X11 pixmap xcb_pixmap_t pixmap = xcb_generate_id( Helper::connection() ); xcb_create_pixmap( Helper::connection(), 32, pixmap, QX11Info::appRootWindow(), width, height ); // create gc if( !_gc ) { _gc = xcb_generate_id( Helper::connection() ); xcb_create_gc( Helper::connection(), _gc, pixmap, 0, 0x0 ); } // create image from QPixmap and assign to pixmap QImage image( source.toImage() ); xcb_put_image( Helper::connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, _gc, image.width(), image.height(), 0, 0, 0, 32, image.byteCount(), image.constBits()); return pixmap; #else return 0; #endif } //_______________________________________________________ bool ShadowHelper::installShadows( QWidget* widget ) { if( !widget ) return false; /* From bespin code. Supposibly prevent playing with some 'pseudo-widgets' that have winId matching some other -random- window */ if( !(widget->testAttribute(Qt::WA_WState_Created) && widget->internalWinId() )) { return false; } if( Helper::isX11() ) return installX11Shadows( widget ); if( Helper::isWayland() ) return installWaylandShadows( widget ); return false; } //_______________________________________________________ bool ShadowHelper::installX11Shadows( QWidget* widget ) { #if BREEZE_HAVE_X11 #ifndef QT_NO_XRENDER // create pixmap handles if needed const QVector& pixmaps( createPixmapHandles() ); if( pixmaps.size() != numPixmaps ) return false; // create data // add pixmap handles QVector data; foreach( const quint32& value, pixmaps ) { data.append( value ); } const QMargins margins = shadowMargins( widget ); const int topSize = margins.top(); const int bottomSize = margins.bottom(); const int leftSize( margins.left() ); const int rightSize( margins.right() ); // assign to data and xcb property data << topSize << rightSize << bottomSize << leftSize; xcb_change_property( Helper::connection(), XCB_PROP_MODE_REPLACE, widget->winId(), _atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData() ); xcb_flush( Helper::connection() ); return true; #endif #endif return false; } //_______________________________________________________ bool ShadowHelper::installWaylandShadows( QWidget* widget ) { #if BREEZE_HAVE_KWAYLAND if( widget->windowHandle()->parent() ) return false; if( !_shadowManager || !_shmPool ) return false; if( !_shadowTiles.isValid() ) return false; // create shadow using namespace KWayland::Client; auto s = Surface::fromWindow( widget->windowHandle() ); if( !s ) return false; auto shadow = _shadowManager->createShadow( s, widget ); if( !shadow->isValid() ) return false; // add the shadow elements shadow->attachTop( _shmPool->createBuffer( _shadowTiles.pixmap( 1 ).toImage() ) ); shadow->attachTopRight( _shmPool->createBuffer( _shadowTiles.pixmap( 2 ).toImage() ) ); shadow->attachRight( _shmPool->createBuffer( _shadowTiles.pixmap( 5 ).toImage() ) ); shadow->attachBottomRight( _shmPool->createBuffer( _shadowTiles.pixmap( 8 ).toImage() ) ); shadow->attachBottom( _shmPool->createBuffer( _shadowTiles.pixmap( 7 ).toImage() ) ); shadow->attachBottomLeft( _shmPool->createBuffer( _shadowTiles.pixmap( 6 ).toImage() ) ); shadow->attachLeft( _shmPool->createBuffer( _shadowTiles.pixmap( 3 ).toImage() ) ); shadow->attachTopLeft( _shmPool->createBuffer( _shadowTiles.pixmap( 0 ).toImage() ) ); shadow->setOffsets( shadowMargins( widget ) ); shadow->commit(); s->commit( Surface::CommitFlag::None ); _widgetSurfaces.insert(widget, s); return true; #else Q_UNUSED( widget ); #endif return false; } //_______________________________________________________ QMargins ShadowHelper::shadowMargins( QWidget* widget ) const { // get devicePixelRatio // for testing purposes only const qreal devicePixelRatio( _helper.devicePixelRatio( _shadowTiles.pixmap( 0 ) ) ); // metrics - const int shadowSize = StyleConfigData::shadowSize()*12/16; + const int shadowSize = ::shadowSize( StyleConfigData::shadowSize() ); + if( !shadowSize ) return QMargins(); const int shadowOffset = qMax( shadowSize/2, Metrics::Shadow_Overlap*2 ); // define shadows padding int size( shadowSize - Metrics::Shadow_Overlap ); int topSize = ( size - shadowOffset ) * devicePixelRatio; int bottomSize = size * devicePixelRatio; const int leftSize( size * devicePixelRatio ); const int rightSize( size * devicePixelRatio ); if( widget->inherits( "QBalloonTip" ) ) { // balloon tip needs special margins to deal with the arrow int top = 0; int bottom = 0; widget->getContentsMargins( nullptr, &top, nullptr, &bottom ); // also need to decrement default size further due to extra hard coded round corner size -= 2 * devicePixelRatio; // it seems arrow can be either to the top or the bottom. Adjust margins accordingly if( top > bottom ) topSize -= (top - bottom); else bottomSize -= (bottom - top ); } return QMargins( leftSize, topSize, rightSize, bottomSize ); } //_______________________________________________________ void ShadowHelper::uninstallShadows( QWidget* widget ) const { if( !( widget && widget->testAttribute(Qt::WA_WState_Created) ) ) return; if( Helper::isX11() ) uninstallX11Shadows( widget ); if( Helper::isWayland() ) uninstallWaylandShadows( widget ); } //_______________________________________________________ void ShadowHelper::uninstallX11Shadows( QWidget* widget ) const { #if BREEZE_HAVE_X11 xcb_delete_property( Helper::connection(), widget->winId(), _atom); #else Q_UNUSED( widget ) #endif } //_______________________________________________________ void ShadowHelper::uninstallWaylandShadows( QWidget* widget ) const { #if BREEZE_HAVE_KWAYLAND if( widget->windowHandle() && widget->windowHandle()->parent() ) return; if( !_shadowManager ) return; using namespace KWayland::Client; auto s = Surface::fromWindow( widget->windowHandle() ); if( !s ) return; _shadowManager->removeShadow( s ); s->commit( Surface::CommitFlag::None ); #else Q_UNUSED( widget ) #endif } }