Added masked shadow corners for special layers.

This commit is contained in:
23rd 2022-06-01 08:19:14 +03:00
parent 549edbe5c7
commit b9a702f6e0
6 changed files with 172 additions and 28 deletions

View file

@ -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

View file

@ -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<QImage, 4> 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>{
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<QImage, 4>{ {
processCorner(0),
processCorner(1),
processCorner(2),
processCorner(3),
} };
}
} // namespace
const std::array<QImage, 4> &SpecialLayerShadowCorners() {
static const auto custom = PrepareSpecialLayerShadowCorners();
return custom;
}
} // namespace Ui

View file

@ -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<QImage, 4> &SpecialLayerShadowCorners();
} // namespace Ui

View file

@ -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<void()> callback) {
_doneCallback = std::move(callback);
@ -63,7 +64,6 @@ private:
int _mainMenuCacheWidth = 0;
QPixmap _specialLayerCache;
QPixmap _layerCache;
RoundRect _roundRect;
Fn<void()> _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);
}
}

View file

@ -14,22 +14,42 @@
#include <QtGui/QtEvents>
namespace Ui {
namespace {
PlainShadow::PlainShadow(QWidget *parent)
: PlainShadow(parent, st::shadowFg) {
struct CustomShadowCorners {
struct Image {
public:
Image(const QImage &image)
: _image(image) {
}
PlainShadow::PlainShadow(QWidget *parent, style::color color)
: RpWidget(parent)
, _color(color) {
resize(st::lineWidth, st::lineWidth);
void paint(QPainter &p, int x, int y, int outerw) const {
p.drawImage(x, y, _image);
}
void PlainShadow::paintEvent(QPaintEvent *e) {
QPainter(this).fillRect(e->rect(), _color);
[[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 &top;
Image topRight;
const style::icon &right;
Image bottomRight;
const style::icon &bottom;
Image bottomLeft;
const style::margins &extend;
};
void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) {
template <typename Shadow>
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<style::Shadow>(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<QImage, 4> &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<CustomShadowCorners>(p, box, outerWidth, shadow, std::move(sides));
}
QPixmap Shadow::grab(
not_null<TWidget*> target,
const style::Shadow &shadow,

View file

@ -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<QImage, 4> &corners);
static QPixmap grab(
not_null<TWidget*> target,
const style::Shadow &shadow,