From b9a702f6e0aaf2a4e0c006a264a23d1f38347fb8 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 1 Jun 2022 08:19:14 +0300 Subject: [PATCH] Added masked shadow corners for special layers. --- CMakeLists.txt | 2 + ui/cached_special_layer_shadow_corners.cpp | 71 +++++++++++++++++ ui/cached_special_layer_shadow_corners.h | 13 ++++ ui/layers/layer_widget.cpp | 17 +---- ui/widgets/shadow.cpp | 89 ++++++++++++++++++---- ui/widgets/shadow.h | 8 ++ 6 files changed, 172 insertions(+), 28 deletions(-) create mode 100644 ui/cached_special_layer_shadow_corners.cpp create mode 100644 ui/cached_special_layer_shadow_corners.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 00ef694..6d1ddab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,6 +227,8 @@ PRIVATE ui/abstract_button.h ui/basic_click_handlers.cpp ui/basic_click_handlers.h + ui/cached_special_layer_shadow_corners.cpp + ui/cached_special_layer_shadow_corners.h ui/click_handler.cpp ui/click_handler.h ui/delayed_activation.cpp diff --git a/ui/cached_special_layer_shadow_corners.cpp b/ui/cached_special_layer_shadow_corners.cpp new file mode 100644 index 0000000..c60c359 --- /dev/null +++ b/ui/cached_special_layer_shadow_corners.cpp @@ -0,0 +1,71 @@ +// This file is part of Desktop App Toolkit, +// a set of libraries for developing nice desktop applications. +// +// For license and copyright information please follow this link: +// https://github.com/desktop-app/legal/blob/master/LEGAL +// +#include "ui/cached_special_layer_shadow_corners.h" + +#include "ui/effects/ripple_animation.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +[[nodiscard]] std::array PrepareSpecialLayerShadowCorners() { + const auto &st = st::boxRoundShadow; + + const auto s = QSize( + st::boxRadius * 2 + st.extend.left(), + st::boxRadius * 2 + st.extend.right()); + const auto mask = Ui::RippleAnimation::maskByDrawer(s, false, [&]( + QPainter &p) { + p.drawRoundedRect(QRect(QPoint(), s), st::boxRadius, st::boxRadius); + }); + struct Corner { + const style::icon &icon; + QPoint factor; + }; + const auto corners = std::vector{ + Corner{ st.topLeft, QPoint(1, 1) }, + Corner{ st.bottomLeft, QPoint(1, 0) }, + Corner{ st.topRight, QPoint(0, 1) }, + Corner{ st.bottomRight, QPoint(0, 0) }, + }; + const auto processCorner = [&](int i) { + const auto &corner = corners[i]; + auto result = QImage( + corner.icon.size() * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(style::DevicePixelRatio()); + result.fill(Qt::transparent); + + { + QPainter p(&result); + corner.icon.paint(p, 0, 0, corner.icon.width()); + p.setCompositionMode(QPainter::CompositionMode_DestinationOut); + p.drawImage( + corner.icon.width() * corner.factor.x() + - mask.width() / style::DevicePixelRatio() / 2, + corner.icon.height() * corner.factor.y() + - mask.height() / style::DevicePixelRatio() / 2, + mask); + } + return result; + }; + return std::array{ { + processCorner(0), + processCorner(1), + processCorner(2), + processCorner(3), + } }; +} + +} // namespace + +const std::array &SpecialLayerShadowCorners() { + static const auto custom = PrepareSpecialLayerShadowCorners(); + return custom; +} + +} // namespace Ui diff --git a/ui/cached_special_layer_shadow_corners.h b/ui/cached_special_layer_shadow_corners.h new file mode 100644 index 0000000..10955d4 --- /dev/null +++ b/ui/cached_special_layer_shadow_corners.h @@ -0,0 +1,13 @@ +// This file is part of Desktop App Toolkit, +// a set of libraries for developing nice desktop applications. +// +// For license and copyright information please follow this link: +// https://github.com/desktop-app/legal/blob/master/LEGAL +// +#pragma once + +namespace Ui { + +[[nodiscard]] const std::array &SpecialLayerShadowCorners(); + +} // namespace Ui diff --git a/ui/layers/layer_widget.cpp b/ui/layers/layer_widget.cpp index 2b5731d..5f07694 100644 --- a/ui/layers/layer_widget.cpp +++ b/ui/layers/layer_widget.cpp @@ -6,6 +6,7 @@ // #include "ui/layers/layer_widget.h" +#include "ui/cached_special_layer_shadow_corners.h" #include "ui/layers/box_layer_widget.h" #include "ui/widgets/shadow.h" #include "ui/image/image_prepare.h" @@ -21,7 +22,7 @@ namespace Ui { class LayerStackWidget::BackgroundWidget : public TWidget { public: - explicit BackgroundWidget(QWidget *parent); + using TWidget::TWidget; void setDoneCallback(Fn callback) { _doneCallback = std::move(callback); @@ -63,7 +64,6 @@ private: int _mainMenuCacheWidth = 0; QPixmap _specialLayerCache; QPixmap _layerCache; - RoundRect _roundRect; Fn _doneCallback; @@ -84,11 +84,6 @@ private: }; -LayerStackWidget::BackgroundWidget::BackgroundWidget(QWidget *parent) -: TWidget(parent) -, _roundRect(st::boxRadius, st::boxBg) { -} - void LayerStackWidget::BackgroundWidget::setCacheImages( QPixmap &&bodyCache, QPixmap &&mainMenuCache, @@ -262,15 +257,9 @@ void LayerStackWidget::BackgroundWidget::paintEvent(QPaintEvent *e) { if (topCorners || bottomCorners) { p.setClipRegion(QRegion(rect()) - specialLayerBox.marginsRemoved(QMargins(st::boxRadius, 0, st::boxRadius, 0)) - specialLayerBox.marginsRemoved(QMargins(0, st::boxRadius, 0, st::boxRadius))); } - Ui::Shadow::paint(p, specialLayerBox, width(), st::boxRoundShadow, sides); - + Ui::Shadow::paint(p, specialLayerBox, width(), st::boxRoundShadow, sides, Ui::SpecialLayerShadowCorners()); if (topCorners || bottomCorners) { - // In case of painting the shadow above the special layer we get - // glitches in the corners, so we need to paint the corners once more. p.setClipping(false); - auto parts = (topCorners ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) - | (bottomCorners ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None); - _roundRect.paint(p, specialLayerBox, parts); } } diff --git a/ui/widgets/shadow.cpp b/ui/widgets/shadow.cpp index 08198a8..6d790ec 100644 --- a/ui/widgets/shadow.cpp +++ b/ui/widgets/shadow.cpp @@ -14,22 +14,42 @@ #include namespace Ui { +namespace { -PlainShadow::PlainShadow(QWidget *parent) -: PlainShadow(parent, st::shadowFg) { -} +struct CustomShadowCorners { + struct Image { + public: + Image(const QImage &image) + : _image(image) { + } + void paint(QPainter &p, int x, int y, int outerw) const { + p.drawImage(x, y, _image); + } + [[nodiscard]] bool empty() const { + return _image.isNull(); + } + [[nodiscard]] int width() const { + return _image.width() / style::DevicePixelRatio(); + } + [[nodiscard]] int height() const { + return _image.height() / style::DevicePixelRatio(); + } + private: + const QImage &_image; + }; + const style::icon &left; + Image topLeft; + const style::icon ⊤ + Image topRight; + const style::icon &right; + Image bottomRight; + const style::icon ⊥ + Image bottomLeft; + const style::margins &extend; +}; -PlainShadow::PlainShadow(QWidget *parent, style::color color) -: RpWidget(parent) -, _color(color) { - resize(st::lineWidth, st::lineWidth); -} - -void PlainShadow::paintEvent(QPaintEvent *e) { - QPainter(this).fillRect(e->rect(), _color); -} - -void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) { +template +void ShadowPaint(QPainter &p, const QRect &box, int outerWidth, const Shadow &st, RectParts sides) { auto left = (sides & RectPart::Left); auto top = (sides & RectPart::Top); auto right = (sides & RectPart::Right); @@ -84,6 +104,47 @@ void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::S } } +} // namespace + +PlainShadow::PlainShadow(QWidget *parent) +: PlainShadow(parent, st::shadowFg) { +} + +PlainShadow::PlainShadow(QWidget *parent, style::color color) +: RpWidget(parent) +, _color(color) { + resize(st::lineWidth, st::lineWidth); +} + +void PlainShadow::paintEvent(QPaintEvent *e) { + QPainter(this).fillRect(e->rect(), _color); +} + +void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) { + ShadowPaint(p, box, outerWidth, st, std::move(sides)); +} + +void Shadow::paint( + QPainter &p, + const QRect &box, + int outerWidth, + const style::Shadow &st, + RectParts sides, + const std::array &corners) { + const auto shadow = CustomShadowCorners{ + .left = st.left, + .topLeft = CustomShadowCorners::Image(corners[0]), + .top = st.top, + .topRight = CustomShadowCorners::Image(corners[2]), + .right = st.right, + .bottomRight = CustomShadowCorners::Image(corners[3]), + .bottom = st.bottom, + .bottomLeft = CustomShadowCorners::Image(corners[1]), + .extend = st.extend, + }; + ShadowPaint(p, box, outerWidth, shadow, std::move(sides)); +} + QPixmap Shadow::grab( not_null target, const style::Shadow &shadow, diff --git a/ui/widgets/shadow.h b/ui/widgets/shadow.h index c6709af..b2ee435 100644 --- a/ui/widgets/shadow.h +++ b/ui/widgets/shadow.h @@ -46,6 +46,14 @@ public: const style::Shadow &st, RectParts sides = RectPart::AllSides); + static void paint( + QPainter &p, + const QRect &box, + int outerWidth, + const style::Shadow &st, + RectParts sides, + const std::array &corners); + static QPixmap grab( not_null target, const style::Shadow &shadow,