diff --git a/kdecoration/CMakeLists.txt b/kdecoration/CMakeLists.txt --- a/kdecoration/CMakeLists.txt +++ b/kdecoration/CMakeLists.txt @@ -37,6 +37,7 @@ set(breezedecoration_SRCS breezebutton.cpp breezedecoration.cpp + breezedecorationshadow.cpp breezeexceptionlist.cpp breezesettingsprovider.cpp breezesizegrip.cpp) @@ -84,6 +85,8 @@ KF5::I18n KF5::WindowSystem) + +set_target_properties(breezedecoration PROPERTIES CXX_STANDARD 17) if(BREEZE_HAVE_X11) target_link_libraries(breezedecoration PUBLIC diff --git a/kdecoration/breezedecoration.cpp b/kdecoration/breezedecoration.cpp --- a/kdecoration/breezedecoration.cpp +++ b/kdecoration/breezedecoration.cpp @@ -28,6 +28,7 @@ #include "config/breezeconfigwidget.h" #include "breezebutton.h" +#include "breezedecorationshadow.h" #include "breezesizegrip.h" #include "breezeboxshadowrenderer.h" @@ -61,121 +62,23 @@ registerPlugin(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 g_sShadow; - //________________________________________________________________ Decoration::Decoration(QObject *parent, const QVariantList &args) : KDecoration2::Decoration(parent, args) , m_animation( new QVariantAnimation( this ) ) { - g_sDecoCount++; } //________________________________________________________________ Decoration::~Decoration() { - g_sDecoCount--; - if (g_sDecoCount == 0) { - // last deco destroyed, clean up shadow - g_sShadow.clear(); - } - deleteSizeGrip(); - } //________________________________________________________________ @@ -434,7 +337,9 @@ extSides = extSize; } - + if(auto breezeShadow = qobject_cast(shadow())){ + breezeShadow->setEdges(isRightEdge(), isLeftEdge(), isBottomEdge(), isTopEdge()); + } setResizeOnlyBorders(QMargins(extSides, 0, extSides, extBottom)); } @@ -711,86 +616,9 @@ //________________________________________________________________ void Decoration::createShadow() { - if (!g_sShadow - ||g_shadowSizeEnum != m_internalSettings->shadowSize() - || g_shadowStrength != m_internalSettings->shadowStrength() - || g_shadowColor != m_internalSettings->shadowColor()) - { - g_shadowSizeEnum = m_internalSettings->shadowSize(); - g_shadowStrength = m_internalSettings->shadowStrength(); - g_shadowColor = m_internalSettings->shadowColor(); - - const CompositeShadowParams params = lookupShadowParams(g_shadowSizeEnum); - if (params.isNone()) { - g_sShadow.clear(); - setShadow(g_sShadow); - return; - } - - auto withOpacity = [](const QColor &color, qreal opacity) -> QColor { - QColor c(color); - c.setAlphaF(opacity); - return c; - }; - - const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius) - .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); - - BoxShadowRenderer shadowRenderer; - shadowRenderer.setBorderRadius(Metrics::Frame_FrameRadius + 0.5); - shadowRenderer.setBoxSize(boxSize); - shadowRenderer.setDevicePixelRatio(1.0); // TODO: Create HiDPI shadows? - - const qreal strength = static_cast(g_shadowStrength) / 255.0; - shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius, - withOpacity(g_shadowColor, params.shadow1.opacity * strength)); - shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius, - withOpacity(g_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, - Metrics::Frame_FrameRadius + 0.5, - Metrics::Frame_FrameRadius + 0.5); - - // Draw outline. - painter.setPen(withOpacity(g_shadowColor, 0.2 * strength)); - painter.setBrush(Qt::NoBrush); - painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - painter.drawRoundedRect( - innerRect, - Metrics::Frame_FrameRadius - 0.5, - Metrics::Frame_FrameRadius - 0.5); - - painter.end(); - - g_sShadow = QSharedPointer::create(); - g_sShadow->setPadding(padding); - g_sShadow->setInnerShadowRect(QRect(outerRect.center(), QSize(1, 1))); - g_sShadow->setShadow(shadowTexture); - } - - setShadow(g_sShadow); + setShadow(QSharedPointer(new Shadow(static_cast(internalSettings()->shadowSize()), + internalSettings()->shadowStrength(), internalSettings()->shadowColor()))); + qobject_cast(shadow())->setEdges(isRightEdge(), isLeftEdge(), isBottomEdge(), isTopEdge()); } //_________________________________________________________________ diff --git a/kdecoration/breezedecorationshadow.h b/kdecoration/breezedecorationshadow.h new file mode 100644 --- /dev/null +++ b/kdecoration/breezedecorationshadow.h @@ -0,0 +1,48 @@ +/* +* Copyright 2020 David Redondo +* +* 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 . +*/ + +#ifndef BREEZE_DECORATION_SHADOW_H +#define BREEZE_DECORATION_SHADOW_H + +#include + +#include "breezedecoration.h" +#include "breezesettingsprovider.h" + +namespace Breeze { + +class Shadow : public KDecoration2::DecorationShadow { + Q_OBJECT +public: + Shadow(InternalSettings::EnumShadowSize size, int strength, QColor color); + void setEdges(bool isRightEdge, bool isLeftEdge, bool isBottomEdge, bool isTopEdge); +private: + + static void createShadowImage(InternalSettings::EnumShadowSize size, int strength, QColor color); + + static inline QImage shadowImage; + static inline int s_size = InternalSettings::ShadowLarge; + static inline int s_strength = 255; + static inline QColor s_color = Qt::black; + static inline QMargins padding; +}; + +} +#endif diff --git a/kdecoration/breezedecorationshadow.cpp b/kdecoration/breezedecorationshadow.cpp new file mode 100644 --- /dev/null +++ b/kdecoration/breezedecorationshadow.cpp @@ -0,0 +1,226 @@ +/* +* Copyright 2014 Martin Gräßlin +* Copyright 2014 Hugo Pereira Da Costa +* Copyright 2018 Vlad Zahorodnii +* Copyright 2020 David Redondo +* +* 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 "breezeboxshadowrenderer.h" +#include "breezedecorationshadow.h" + +#include + +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]; + } +}; +} + +using namespace Breeze; + +void Shadow::createShadowImage(InternalSettings::EnumShadowSize size, int strength, QColor color) +{ + if (shadowImage.isNull() + || s_size != size + || s_strength != strength + || s_color != color) + { + s_size = size; + s_strength = strength; + s_color = color; + + const CompositeShadowParams params = lookupShadowParams(size); + if (params.isNone()) { + shadowImage = QImage(); + return; + } + + auto withOpacity = [](const QColor &color, qreal opacity) -> QColor { + QColor c(color); + c.setAlphaF(opacity); + return c; + }; + + const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius) + .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius)); + + BoxShadowRenderer shadowRenderer; + shadowRenderer.setBorderRadius(Metrics::Frame_FrameRadius + 0.5); + shadowRenderer.setBoxSize(boxSize); + shadowRenderer.setDevicePixelRatio(1.0); // TODO: Create HiDPI shadows? + + const qreal strength = static_cast(s_strength) / 255.0; + shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius, + withOpacity(s_color, params.shadow1.opacity * strength)); + shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius, + withOpacity(s_color, params.shadow2.opacity * strength)); + + shadowImage = shadowRenderer.render(); + + QPainter painter(&shadowImage); + painter.setRenderHint(QPainter::Antialiasing); + + const QRect outerRect = shadowImage.rect(); + + QRect boxRect(QPoint(0, 0), boxSize); + boxRect.moveCenter(outerRect.center()); + + // Mask out inner rect. + 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()); + qDebug() << padding; + const QRect innerRect = outerRect - padding; + + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::black); + painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); + painter.drawRoundedRect( + innerRect, + Metrics::Frame_FrameRadius + 0.5, + Metrics::Frame_FrameRadius + 0.5); + + // Draw outline. + painter.setPen(withOpacity(s_color, 0.2 * strength)); + painter.setBrush(Qt::NoBrush); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawRoundedRect( + innerRect, + Metrics::Frame_FrameRadius - 0.5, + Metrics::Frame_FrameRadius - 0.5); + + painter.end(); + } +} + +Shadow::Shadow(InternalSettings::EnumShadowSize size, int strength, QColor color) +{ + createShadowImage(size, strength, color); + setPadding(padding); + setInnerShadowRect(QRect(shadowImage.rect().center(), QSize(1, 1))); + setShadow(shadowImage); +} + +void Shadow::setEdges(bool isRightEdge, bool isLeftEdge, bool isBottomEdge, bool isTopEdge) +{ + QRect innerRect = QRect(shadowImage.rect().center(), QSize(1, 1)); + QRect shadowRect(shadowImage.rect()); + QMargins padding = Shadow::padding; + if (isRightEdge) { + shadowRect.setRight(innerRect.right()); + padding.setRight(0); + } + if (isLeftEdge) { + shadowRect.setLeft(innerRect.left()); + innerRect.moveLeft(0); + padding.setLeft(0); + } + if (isBottomEdge) { + shadowRect.setBottom(innerRect.bottom()); + padding.setBottom(0); + } + if (isTopEdge) { + shadowRect.setTop(innerRect.top()); + innerRect.moveTop(0); + padding.setTop(0); + } + setInnerShadowRect(innerRect); + setPadding(padding); + setShadow(shadowImage.copy(shadowRect)); +} + +