diff --git a/kdecoration/CMakeLists.txt b/kdecoration/CMakeLists.txt --- a/kdecoration/CMakeLists.txt +++ b/kdecoration/CMakeLists.txt @@ -28,6 +28,10 @@ ################# configuration ################# configure_file(config-breeze.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-breeze.h ) +################# includes ################# +include_directories(${CMAKE_SOURCE_DIR}/libbreezecommon) +include_directories(${CMAKE_BINARY_DIR}/libbreezecommon) + ################# newt target ################# ### plugin classes set(breezedecoration_SRCS @@ -71,6 +75,7 @@ Qt5::Gui Qt5::DBus PRIVATE + breezecommon KDecoration2::KDecoration KF5::ConfigCore KF5::CoreAddons diff --git a/kdecoration/breezedecoration.cpp b/kdecoration/breezedecoration.cpp --- a/kdecoration/breezedecoration.cpp +++ b/kdecoration/breezedecoration.cpp @@ -29,6 +29,8 @@ #include "breezebutton.h" #include "breezesizegrip.h" +#include "breezeboxshadowhelper.h" + #include #include #include @@ -39,7 +41,9 @@ #include #include +#include #include +#include #include #include @@ -57,6 +61,123 @@ registerPlugin(QStringLiteral("kcmodule")); ) +namespace +{ + struct ShadowParam { + // Offset relative to decoration. + QPoint offset; + + struct { + // Offset relative to a box. + QPoint offset; + + // Blur radius. + int radius; + + // Shadow opacity(1 is opaque, 0 is transparent). + qreal opacity; + } shadow1 + , shadow2; + + bool isNone() const { + return qMax(shadow1.radius, shadow2.radius) == 0; + } + }; + + // TODO: adjust existing params. + const ShadowParam s_shadowParams[] = { + { + // None + .offset = QPoint(0, 0), + .shadow1 = { + .offset = QPoint(0, 0), + .radius = 0, + .opacity = 0 + }, + .shadow2 = { + .offset = QPoint(0, 0), + .radius = 0, + .opacity = 0 + } + }, + { + // Small + .offset = QPoint(0, 6), + .shadow1 = { + .offset = QPoint(0, 0), + .radius = 16, + .opacity = 1.0 + }, + .shadow2 = { + .offset = QPoint(0, 0), + .radius = 4, + .opacity = 0.1 + } + }, + { + // Medium + .offset = QPoint(0, 12), + .shadow1 = { + .offset = QPoint(0, 0), + .radius = 32, + .opacity = 1.0 + }, + .shadow2 = { + .offset = QPoint(0, 0), + .radius = 16, + .opacity = 0.1 + } + }, + { + // Large + .offset = QPoint(0, 20), + .shadow1 = { + .offset = QPoint(0, 0), + .radius = 64, + .opacity = 1.0 + }, + .shadow2 = { + .offset = QPoint(0, 0), + .radius = 24, + .opacity = 0.1 + } + }, + { + // Very large + .offset = QPoint(0, 34), + .shadow1 = { + .offset = QPoint(0, 0), + .radius = 96, + .opacity = 1.0 + }, + .shadow2 = { + .offset = QPoint(0, 0), + .radius = 28, + .opacity = 0.1 + } + } + }; + + inline ShadowParam 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 { @@ -66,7 +187,7 @@ //________________________________________________________________ static int g_sDecoCount = 0; static int g_shadowSizeEnum = InternalSettings::ShadowLarge; - static int g_shadowStrength = 90; + static int g_shadowStrength = 216; static QColor g_shadowColor = Qt::black; static QSharedPointer g_sShadow; @@ -622,105 +743,96 @@ //________________________________________________________________ void Decoration::createShadow() { - - // assign global shadow if exists and parameters match - if( - !g_sShadow || - g_shadowSizeEnum != m_internalSettings->shadowSize() || - g_shadowStrength != m_internalSettings->shadowStrength() || - g_shadowColor != m_internalSettings->shadowColor() - ) + if (!g_sShadow + ||g_shadowSizeEnum != m_internalSettings->shadowSize() + || g_shadowStrength != m_internalSettings->shadowStrength() + || g_shadowColor != m_internalSettings->shadowColor()) { - // assign parameters g_shadowSizeEnum = m_internalSettings->shadowSize(); g_shadowStrength = m_internalSettings->shadowStrength(); g_shadowColor = m_internalSettings->shadowColor(); - // 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; + const ShadowParam params = lookupShadowParams(g_shadowSizeEnum); + if (params.isNone()) { + g_sShadow.clear(); + setShadow(g_sShadow); + return; } - // offset - int shadowOffset = (g_shadowSizeEnum == InternalSettings::ShadowNone) ? 0 : qMax( 6*shadowSize/16, Metrics::Shadow_Overlap*2 ); - - // create image - QImage image(2*shadowSize, 2*shadowSize, QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - - // painter - QPainter painter(&image); - painter.setRenderHint( QPainter::Antialiasing, true ); - - // color calculation delta function - auto gradientStopColor = [](QColor color, int alpha) - { - color.setAlpha(alpha); - return color; + auto withOpacity = [](const QColor &color, qreal opacity) -> QColor { + QColor c(color); + c.setAlphaF(opacity); + return c; }; - // create gradient - if( g_shadowSizeEnum != InternalSettings::ShadowNone ) - { - - // gaussian lambda function - auto alpha = [](qreal x) { return std::exp( -x*x/0.15 ); }; - - 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( - shadowSize - Metrics::Shadow_Overlap, shadowSize - shadowOffset - Metrics::Shadow_Overlap, - 2*Metrics::Shadow_Overlap, shadowOffset + 2*Metrics::Shadow_Overlap ); - - 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 ); + // Calculate the scaling factor by ourselves because devicePixelRatio() is not + // available inside of KWin. + const qreal dpr = QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96.0; + + // In order to properly render a box shadow with a given radius `shadowSize`, + // the box size should be at least `2 * QSize(shadowSize, shadowSize)`. + const int shadowSize = qMax(params.shadow1.radius, params.shadow2.radius); + const QRect box(shadowSize, shadowSize, 2 * shadowSize + 1, 2 * shadowSize + 1); + const QRect rect = box.adjusted(-shadowSize, -shadowSize, shadowSize, shadowSize); + + QImage shadow(rect.size() * dpr, QImage::Format_ARGB32_Premultiplied); + shadow.setDevicePixelRatio(dpr); + shadow.fill(Qt::transparent); + + QPainter painter(&shadow); + painter.setRenderHint(QPainter::Antialiasing); + + const qreal strength = static_cast(g_shadowStrength) / 255.0; + + // Draw the "shape" shadow. + BoxShadowHelper::boxShadow( + &painter, + box, + params.shadow1.offset, + params.shadow1.radius, + withOpacity(g_shadowColor, params.shadow1.opacity * strength)); + + // Draw the "contrast" shadow. + BoxShadowHelper::boxShadow( + &painter, + box, + params.shadow2.offset, + params.shadow2.radius, + withOpacity(g_shadowColor, params.shadow2.opacity * strength)); + + // Mask out inner rect. + const QMargins padding = 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()); + + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::black); + painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); + painter.drawRoundedRect( + rect - padding, + Metrics::Frame_FrameRadius + 0.5, + Metrics::Frame_FrameRadius + 0.5); + + // Draw pixel contrast. + painter.setPen(withOpacity(g_shadowColor, 0.2 * strength)); + painter.setBrush(Qt::NoBrush); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawRoundedRect( + rect - padding, + Metrics::Frame_FrameRadius - 0.5, + Metrics::Frame_FrameRadius - 0.5); painter.end(); g_sShadow = QSharedPointer::create(); - g_sShadow->setPadding( QMargins( - shadowSize - Metrics::Shadow_Overlap, - shadowSize - shadowOffset - Metrics::Shadow_Overlap, - shadowSize - Metrics::Shadow_Overlap, - shadowSize - Metrics::Shadow_Overlap ) ); - - g_sShadow->setInnerShadowRect(QRect( shadowSize, shadowSize, 1, 1) ); - - // assign image - g_sShadow->setShadow(image); - + g_sShadow->setPadding(padding * dpr); + g_sShadow->setInnerShadowRect(QRect(shadow.rect().center(), QSize(1, 1))); + g_sShadow->setShadow(shadow); } setShadow(g_sShadow); - } //_________________________________________________________________ diff --git a/kdecoration/breezesettingsdata.kcfg b/kdecoration/breezesettingsdata.kcfg --- a/kdecoration/breezesettingsdata.kcfg +++ b/kdecoration/breezesettingsdata.kcfg @@ -7,7 +7,7 @@ - 90 + 216 25 255