diff --git a/kstyle/CMakeLists.txt b/kstyle/CMakeLists.txt --- a/kstyle/CMakeLists.txt +++ b/kstyle/CMakeLists.txt @@ -82,6 +82,9 @@ debug ) +include_directories(${CMAKE_SOURCE_DIR}/libbreezecommon) +include_directories(${CMAKE_BINARY_DIR}/libbreezecommon) + ################# configuration ################# configure_file(config-breeze.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-breeze.h ) @@ -139,6 +142,7 @@ kde4_add_kcfg_files(breeze_PART_SRCS breezestyleconfigdata.kcfgc) kde4_add_plugin(breeze ${breeze_PART_SRCS} kstylekde4compat.cpp) target_link_libraries(breeze ${KDE4_KDEUI_LIBS}) + target_link_libraries(breeze breezecommon4) if(BREEZE_HAVE_X11) target_link_libraries(breeze ${X11_XCB_LIBRARIES}) @@ -153,6 +157,7 @@ add_library(breeze MODULE ${breeze_PART_SRCS}) target_link_libraries(breeze Qt5::Core Qt5::Gui Qt5::Widgets Qt5::DBus Qt5::Quick) target_link_libraries(breeze KF5::ConfigCore KF5::ConfigWidgets KF5::GuiAddons KF5::WindowSystem) + target_link_libraries(breeze breezecommon) if( KF5FrameworkIntegration_FOUND ) target_link_libraries(breeze KF5::Style) diff --git a/kstyle/breeze.kcfg b/kstyle/breeze.kcfg --- a/kstyle/breeze.kcfg +++ b/kstyle/breeze.kcfg @@ -10,7 +10,7 @@ - 90 + 255 25 255 diff --git a/kstyle/breezemdiwindowshadow.cpp b/kstyle/breezemdiwindowshadow.cpp --- a/kstyle/breezemdiwindowshadow.cpp +++ b/kstyle/breezemdiwindowshadow.cpp @@ -47,13 +47,15 @@ if( !_widget ) return; // metrics - const int shadowSize = ShadowHelper::shadowSize( StyleConfigData::shadowSize() ); - const int shadowOffset = qMax( shadowSize/2, Metrics::Shadow_Overlap*2 ); + const CompositeShadowParams params = ShadowHelper::lookupShadowParams( StyleConfigData::shadowSize() ); + if( params.isNone() ) return; + + const int shadowSize = qMax( params.shadow1.radius, params.shadow2.radius ); const int size( shadowSize - Metrics::Shadow_Overlap ); - const int topSize( size - shadowOffset ); - const int bottomSize( size ); - const int leftSize( size ); - const int rightSize( size ); + const int topSize( size - params.offset.y() ); + const int bottomSize( size + params.offset.y() ); + const int leftSize( size - params.offset.x() ); + const int rightSize( size + params.offset.x() ); // get tileSet rect auto hole = _widget->frameGeometry(); diff --git a/kstyle/breezeshadowhelper.h b/kstyle/breezeshadowhelper.h --- a/kstyle/breezeshadowhelper.h +++ b/kstyle/breezeshadowhelper.h @@ -50,6 +50,43 @@ //* forward declaration class Helper; + 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; + }; + //* handle shadow pixmaps passed to window manager via X property class ShadowHelper: public QObject { @@ -73,8 +110,8 @@ bool isSupported() const { return _supported; } - //* shadow size from enum - static int shadowSize( int shadowSizeEnum ); + //* shadow params from size enum + static CompositeShadowParams lookupShadowParams( int shadowSizeEnum ); //* reset void reset(); diff --git a/kstyle/breezeshadowhelper.cpp b/kstyle/breezeshadowhelper.cpp --- a/kstyle/breezeshadowhelper.cpp +++ b/kstyle/breezeshadowhelper.cpp @@ -20,6 +20,7 @@ #include "breezeshadowhelper.h" #include "breeze.h" +#include "breezeboxshadowhelper.h" #include "breezehelper.h" #include "breezepropertynames.h" #include "breezestyleconfigdata.h" @@ -46,28 +47,64 @@ #include #endif +namespace +{ + using Breeze::CompositeShadowParams; + using Breeze::ShadowParams; + + const CompositeShadowParams s_shadowParams[] = { + // None + CompositeShadowParams(), + // Small + CompositeShadowParams( + QPoint(0, 6), + ShadowParams(QPoint(0, 0), 12, 0.2), + ShadowParams(QPoint(0, -3), 6, 0.16)), + // Medium + CompositeShadowParams( + QPoint(0, 8), + ShadowParams(QPoint(0, 0), 16, 0.21), + ShadowParams(QPoint(0, -4), 6, 0.14)), + // Large + CompositeShadowParams( + QPoint(0, 10), + ShadowParams(QPoint(0, 0), 20, 0.23), + ShadowParams(QPoint(0, -5), 8, 0.12)), + // Very Large + CompositeShadowParams( + QPoint(0, 12), + ShadowParams(QPoint(0, 0), 24, 0.26), + ShadowParams(QPoint(0, -5), 10, 0.12)) + }; +} + namespace Breeze { const char ShadowHelper::netWMShadowAtomName[] ="_KDE_NET_WM_SHADOW"; //_____________________________________________________ - int ShadowHelper::shadowSize( int shadowSizeEnum ) + CompositeShadowParams ShadowHelper::lookupShadowParams(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; + switch (shadowSizeEnum) { + case StyleConfigData::ShadowNone: + return s_shadowParams[0]; + case StyleConfigData::ShadowSmall: + return s_shadowParams[1]; + case StyleConfigData::ShadowMedium: + return s_shadowParams[2]; + case StyleConfigData::ShadowLarge: + return s_shadowParams[3]; + case StyleConfigData::ShadowVeryLarge: + return s_shadowParams[4]; + default: + // Fallback to the Large size. + return s_shadowParams[3]; } } - //_____________________________________________________ ShadowHelper::ShadowHelper( QObject* parent, Helper& helper ): QObject( parent ), @@ -237,69 +274,84 @@ //_______________________________________________________ TileSet ShadowHelper::shadowTiles() { - // metrics - const int shadowSize = this->shadowSize( StyleConfigData::shadowSize() ); - if( !shadowSize ) return TileSet(); - else if( !_shadowTiles.isValid() ) - { - - const QPalette palette( QApplication::palette() ); - const QColor shadowColor( StyleConfigData::shadowColor() ); - 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); + const CompositeShadowParams params = lookupShadowParams(StyleConfigData::shadowSize()); - 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 ); + if (params.isNone()) { + return TileSet(); + } else if (_shadowTiles.isValid()) { + return _shadowTiles; + } - const qreal radius( _helper.frameRadius() ); - p.drawRoundedRect( innerRect, radius, radius ); - p.end(); + auto withOpacity = [](const QColor &color, qreal opacity) -> QColor { + QColor c(color); + c.setAlphaF(opacity); + return c; + }; + + const int shadowSize = qMax(params.shadow1.radius, params.shadow2.radius); + const QColor color = StyleConfigData::shadowColor(); + const qreal strength = static_cast(StyleConfigData::shadowStrength()) / 255.0; + + const QRect box( + shadowSize, + shadowSize, + 2 * shadowSize + 1, + 2 * shadowSize + 1); + const QRect outerRect = box.adjusted(-shadowSize, -shadowSize, shadowSize, shadowSize); + + QPixmap shadow = _helper.highDpiPixmap(outerRect.width(), outerRect.height()); + shadow.fill(Qt::transparent); + + QPainter painter(&shadow); + painter.setRenderHint(QPainter::Antialiasing); + + // Draw the "shape" shadow. + BoxShadowHelper::boxShadow( + &painter, + box, + params.shadow1.offset, + params.shadow1.radius, + withOpacity(color, params.shadow1.opacity * strength)); + + // Draw the "contrast" shadow. + BoxShadowHelper::boxShadow( + &painter, + box, + params.shadow2.offset, + params.shadow2.radius, + withOpacity(color, params.shadow2.opacity * strength)); + + // Mask out inner rect. + const QMargins margins = QMargins( + shadowSize - Metrics::Shadow_Overlap - params.offset.x(), + shadowSize - Metrics::Shadow_Overlap - params.offset.y(), + shadowSize - Metrics::Shadow_Overlap + params.offset.x(), + shadowSize - Metrics::Shadow_Overlap + params.offset.y()); + const qreal frameRadius = _helper.frameRadius(); + + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::black); + painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); + painter.drawRoundedRect( +#if BREEZE_USE_KDE4 + outerRect.adjusted(margins.left(), margins.top(), -margins.right(), -margins.bottom()), +#else + outerRect - margins, +#endif + frameRadius, + frameRadius); - // create tiles from pixmap - _shadowTiles = TileSet( pixmap, - shadowSize, - shadowSize, - 1, 1 ); + // We're done. + painter.end(); - } + const QPoint innerRectTopLeft = outerRect.center(); + _shadowTiles = TileSet( + shadow, + innerRectTopLeft.x(), + innerRectTopLeft.y(), + 1, 1); return _shadowTiles; - } @@ -520,40 +572,54 @@ //_______________________________________________________ QMargins ShadowHelper::shadowMargins( QWidget* widget ) const { - // get devicePixelRatio - // for testing purposes only - const qreal devicePixelRatio( _helper.devicePixelRatio( _shadowTiles.pixmap( 0 ) ) ); - - // metrics - const int shadowSize = this->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" ) ) - { + const CompositeShadowParams params = lookupShadowParams(StyleConfigData::shadowSize()); + if (params.isNone()) { + return QMargins(); + } - // balloon tip needs special margins to deal with the arrow + const int shadowSize = qMax(params.shadow1.radius, params.shadow2.radius); + QMargins margins( + shadowSize - Metrics::Shadow_Overlap - params.offset.x(), + shadowSize - Metrics::Shadow_Overlap - params.offset.y(), + shadowSize - Metrics::Shadow_Overlap + params.offset.x(), + shadowSize - Metrics::Shadow_Overlap + params.offset.y()); + + 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 ); + widget->getContentsMargins(nullptr, &top, nullptr, &bottom); + + // Need to decrement default size further due to extra hard coded round corner. +#if BREEZE_USE_KDE4 + margins.setLeft(margins.left() - 1); + margins.setTop(margins.top() - 1); + margins.setRight(margins.right() - 1); + margins.setBottom(margins.bottom() - 1); +#else + margins -= 1; +#endif + // Arrow can be either to the top or the bottom. Adjust margins accordingly. + const int diff = qAbs(top - bottom); + if (top > bottom) { + margins.setTop(margins.top() - diff); + } else { + margins.setBottom(margins.bottom() - diff); + } } - return QMargins( leftSize, topSize, rightSize, bottomSize ); +#if BREEZE_USE_KDE4 + const qreal dpr = _helper.devicePixelRatio(_shadowTiles.pixmap(0)); + margins.setLeft(margins.left() * dpr); + margins.setTop(margins.top() * dpr); + margins.setRight(margins.right() * dpr); + margins.setBottom(margins.bottom() * dpr); +#else + margins *= _helper.devicePixelRatio(_shadowTiles.pixmap(0)); +#endif + + return margins; } //_______________________________________________________