Add general RoundAreaWithShadow helper.
This commit is contained in:
parent
a096628b4c
commit
eed9293c80
7 changed files with 569 additions and 27 deletions
|
|
@ -61,6 +61,8 @@ PRIVATE
|
|||
ui/effects/radial_animation.h
|
||||
ui/effects/ripple_animation.cpp
|
||||
ui/effects/ripple_animation.h
|
||||
ui/effects/round_area_with_shadow.cpp
|
||||
ui/effects/round_area_with_shadow.h
|
||||
ui/effects/show_animation.cpp
|
||||
ui/effects/show_animation.h
|
||||
ui/effects/slide_animation.cpp
|
||||
|
|
|
|||
|
|
@ -361,30 +361,64 @@ void PanelAnimation::start() {
|
|||
checkCorner(_bottomRight);
|
||||
}
|
||||
|
||||
void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity) {
|
||||
auto PanelAnimation::computeState(float64 dt, float64 opacity) const
|
||||
-> PaintState {
|
||||
auto &transition = anim::easeOutCirc;
|
||||
if (dt < _alphaDuration) {
|
||||
opacity *= transition(1., dt / _alphaDuration);
|
||||
}
|
||||
const auto widthProgress = (_startWidth < 0 || dt >= _widthDuration)
|
||||
? 1.
|
||||
: transition(1., dt / _widthDuration);
|
||||
const auto heightProgress = (_startHeight < 0 || dt >= _heightDuration)
|
||||
? 1.
|
||||
: transition(1., dt / _heightDuration);
|
||||
auto frameWidth = (widthProgress < 1.)
|
||||
? anim::interpolate(_startWidth, _finalInnerWidth, widthProgress)
|
||||
: _finalInnerWidth;
|
||||
auto frameHeight = (heightProgress < 1.)
|
||||
? anim::interpolate(_startHeight, _finalInnerHeight, heightProgress)
|
||||
: _finalInnerHeight;
|
||||
if (auto decrease = (frameWidth % style::DevicePixelRatio())) {
|
||||
frameWidth -= decrease;
|
||||
}
|
||||
if (auto decrease = (frameHeight % style::DevicePixelRatio())) {
|
||||
frameHeight -= decrease;
|
||||
}
|
||||
return {
|
||||
.opacity = opacity,
|
||||
.widthProgress = widthProgress,
|
||||
.heightProgress = heightProgress,
|
||||
.fade = transition(1., dt),
|
||||
.width = frameWidth,
|
||||
.height = frameHeight,
|
||||
};
|
||||
}
|
||||
|
||||
auto PanelAnimation::paintFrame(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
float64 dt,
|
||||
float64 opacity)
|
||||
-> PaintState {
|
||||
Assert(started());
|
||||
Assert(dt >= 0.);
|
||||
|
||||
const auto pixelRatio = style::DevicePixelRatio();
|
||||
|
||||
auto &transition = anim::easeOutCirc;
|
||||
if (dt < _alphaDuration) opacity *= transition(1., dt / _alphaDuration);
|
||||
const auto state = computeState(dt, opacity);
|
||||
opacity = state.opacity;
|
||||
_frameAlpha = anim::interpolate(1, 256, opacity);
|
||||
|
||||
auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration));
|
||||
auto frameHeight = (_startHeight < 0 || dt >= _heightDuration) ? _finalInnerHeight : anim::interpolate(_startHeight, _finalInnerHeight, transition(1., dt / _heightDuration));
|
||||
if (auto decrease = (frameWidth % pixelRatio)) {
|
||||
frameWidth -= decrease;
|
||||
}
|
||||
if (auto decrease = (frameHeight % pixelRatio)) {
|
||||
frameHeight -= decrease;
|
||||
}
|
||||
const auto frameWidth = state.width;
|
||||
const auto frameHeight = state.height;
|
||||
auto frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth);
|
||||
auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight);
|
||||
auto frameRight = frameLeft + frameWidth;
|
||||
auto frameBottom = frameTop + frameHeight;
|
||||
|
||||
auto fadeTop = (_fadeHeight > 0) ? std::clamp(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight;
|
||||
auto fadeTop = (_fadeHeight > 0) ? std::clamp(anim::interpolate(_startFadeTop, _finalInnerHeight, state.fade), 0, frameHeight) : frameHeight;
|
||||
if (auto decrease = (fadeTop % pixelRatio)) {
|
||||
fadeTop -= decrease;
|
||||
}
|
||||
|
|
@ -503,6 +537,8 @@ void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float
|
|||
//}
|
||||
|
||||
p.drawImage(style::rtlpoint(x + (outerLeft / pixelRatio), y + (outerTop / pixelRatio), outerWidth), _frame, QRect(outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop));
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
|||
|
|
@ -79,11 +79,27 @@ public:
|
|||
PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) {
|
||||
}
|
||||
|
||||
struct PaintState {
|
||||
float64 opacity = 0.;
|
||||
float64 widthProgress = 0.;
|
||||
float64 heightProgress = 0.;
|
||||
float64 fade = 0.;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
void setFinalImage(QImage &&finalImage, QRect inner);
|
||||
void setSkipShadow(bool skipShadow);
|
||||
|
||||
void start();
|
||||
void paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity);
|
||||
[[nodiscard]] PaintState computeState(float64 dt, float64 opacity) const;
|
||||
PaintState paintFrame(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
float64 dt,
|
||||
float64 opacity);
|
||||
|
||||
private:
|
||||
void setStartWidth();
|
||||
|
|
|
|||
388
ui/effects/round_area_with_shadow.cpp
Normal file
388
ui/effects/round_area_with_shadow.cpp
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
// 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/effects/round_area_with_shadow.h"
|
||||
|
||||
#include "ui/style/style_core.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/painter.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kBgCacheIndex = 0;
|
||||
constexpr auto kShadowCacheIndex = 0;
|
||||
constexpr auto kOverlayMaskCacheIndex = 0;
|
||||
constexpr auto kOverlayShadowCacheIndex = 1;
|
||||
constexpr auto kOverlayCacheColumsCount = 2;
|
||||
constexpr auto kDivider = 4;
|
||||
|
||||
} // namespace
|
||||
|
||||
[[nodiscard]] QImage RoundAreaWithShadow::PrepareImage(QSize size) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto result = QImage(
|
||||
size * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(ratio);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage RoundAreaWithShadow::PrepareFramesCache(
|
||||
QSize frame,
|
||||
int columns) {
|
||||
static_assert(!(kFramesCount % kDivider));
|
||||
|
||||
return PrepareImage(QSize(
|
||||
frame.width() * kDivider * columns,
|
||||
frame.height() * kFramesCount / kDivider));
|
||||
}
|
||||
|
||||
[[nodiscard]] QRect RoundAreaWithShadow::FrameCacheRect(
|
||||
int frameIndex,
|
||||
int column,
|
||||
QSize frame) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto origin = QPoint(
|
||||
frame.width() * (kDivider * column + (frameIndex % kDivider)),
|
||||
frame.height() * (frameIndex / kDivider));
|
||||
return QRect(ratio * origin, ratio * frame);
|
||||
}
|
||||
|
||||
RoundAreaWithShadow::RoundAreaWithShadow(
|
||||
QSize inner,
|
||||
QMargins shadow,
|
||||
int twiceRadiusMax)
|
||||
: _inner({}, inner)
|
||||
, _outer(_inner.marginsAdded(shadow).size())
|
||||
, _overlay(QRect(
|
||||
0,
|
||||
0,
|
||||
std::max(inner.width(), twiceRadiusMax),
|
||||
std::max(inner.height(), twiceRadiusMax)).marginsAdded(shadow).size())
|
||||
, _cacheBg(PrepareFramesCache(_outer))
|
||||
, _shadowParts(PrepareFramesCache(_outer))
|
||||
, _overlayCacheParts(PrepareFramesCache(_overlay, kOverlayCacheColumsCount))
|
||||
, _overlayMaskScaled(PrepareImage(_overlay))
|
||||
, _overlayShadowScaled(PrepareImage(_overlay))
|
||||
, _shadowBuffer(PrepareImage(_outer)) {
|
||||
_inner.translate(QRect({}, _outer).center() - _inner.center());
|
||||
}
|
||||
|
||||
ImageSubrect RoundAreaWithShadow::validateOverlayMask(
|
||||
int frameIndex,
|
||||
QSize innerSize,
|
||||
float64 radius,
|
||||
int twiceRadius,
|
||||
float64 scale) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto cached = (scale == 1.);
|
||||
const auto full = cached
|
||||
? FrameCacheRect(frameIndex, kOverlayMaskCacheIndex, _overlay)
|
||||
: QRect(QPoint(), _overlay * ratio);
|
||||
|
||||
const auto minWidth = twiceRadius + _outer.width() - _inner.width();
|
||||
const auto minHeight = twiceRadius + _outer.height() - _inner.height();
|
||||
const auto maskSize = QSize(
|
||||
std::max(_outer.width(), minWidth),
|
||||
std::max(_outer.height(), minHeight));
|
||||
|
||||
const auto result = ImageSubrect{
|
||||
cached ? &_overlayCacheParts : &_overlayMaskScaled,
|
||||
QRect(full.topLeft(), maskSize * ratio),
|
||||
};
|
||||
if (cached && _validOverlayMask[frameIndex]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto p = QPainter(result.image.get());
|
||||
const auto position = full.topLeft() / ratio;
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(QRect(position, maskSize), Qt::transparent);
|
||||
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto inner = QRect(position + _inner.topLeft(), innerSize);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(Qt::white);
|
||||
if (scale != 1.) {
|
||||
const auto center = inner.center();
|
||||
p.save();
|
||||
p.translate(center);
|
||||
p.scale(scale, scale);
|
||||
p.translate(-center);
|
||||
}
|
||||
p.drawRoundedRect(inner, radius, radius);
|
||||
if (scale != 1.) {
|
||||
p.restore();
|
||||
}
|
||||
|
||||
if (cached) {
|
||||
_validOverlayMask[frameIndex] = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ImageSubrect RoundAreaWithShadow::validateOverlayShadow(
|
||||
int frameIndex,
|
||||
QSize innerSize,
|
||||
float64 radius,
|
||||
int twiceRadius,
|
||||
float64 scale,
|
||||
const ImageSubrect &mask) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto cached = (scale == 1.);
|
||||
const auto full = cached
|
||||
? FrameCacheRect(frameIndex, kOverlayShadowCacheIndex, _overlay)
|
||||
: QRect(QPoint(), _overlay * ratio);
|
||||
|
||||
const auto minWidth = twiceRadius + _outer.width() - _inner.width();
|
||||
const auto minHeight = twiceRadius + _outer.height() - _inner.height();
|
||||
const auto maskSize = QSize(
|
||||
std::max(_outer.width(), minWidth),
|
||||
std::max(_outer.height(), minHeight));
|
||||
|
||||
const auto result = ImageSubrect{
|
||||
cached ? &_overlayCacheParts : &_overlayShadowScaled,
|
||||
QRect(full.topLeft(), maskSize * ratio),
|
||||
};
|
||||
if (cached && _validOverlayShadow[frameIndex]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto position = full.topLeft() / ratio;
|
||||
|
||||
_overlayShadowScaled.fill(Qt::transparent);
|
||||
const auto inner = QRect(_inner.topLeft(), innerSize);
|
||||
const auto add = style::ConvertScale(2.5);
|
||||
const auto shift = style::ConvertScale(0.5);
|
||||
const auto extended = QRectF(inner).marginsAdded({ add, add, add, add });
|
||||
{
|
||||
auto p = QPainter(&_overlayShadowScaled);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_shadow);
|
||||
if (scale != 1.) {
|
||||
const auto center = inner.center();
|
||||
p.translate(center);
|
||||
p.scale(scale, scale);
|
||||
p.translate(-center);
|
||||
}
|
||||
p.drawRoundedRect(extended.translated(0, shift), radius, radius);
|
||||
p.end();
|
||||
}
|
||||
|
||||
_overlayShadowScaled = Images::Blur(std::move(_overlayShadowScaled));
|
||||
|
||||
auto q = Painter(result.image);
|
||||
if (result.image != &_overlayShadowScaled) {
|
||||
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
q.drawImage(
|
||||
QRect(position, maskSize),
|
||||
_overlayShadowScaled,
|
||||
QRect(QPoint(), maskSize * ratio));
|
||||
}
|
||||
q.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
||||
q.drawImage(QRect(position, maskSize), *mask.image, mask.rect);
|
||||
|
||||
if (cached) {
|
||||
_validOverlayShadow[frameIndex] = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RoundAreaWithShadow::overlayExpandedBorder(
|
||||
QPainter &p,
|
||||
QSize size,
|
||||
float64 expandRatio,
|
||||
float64 radius,
|
||||
float64 scale) {
|
||||
const auto progress = expandRatio;
|
||||
const auto frame = int(base::SafeRound(progress * (kFramesCount - 1)));
|
||||
const auto twiceRadius = int(base::SafeRound(radius * 2));
|
||||
const auto innerSize = QSize(
|
||||
std::max(_inner.width(), twiceRadius),
|
||||
std::max(_inner.height(), twiceRadius));
|
||||
|
||||
const auto overlayMask = validateOverlayMask(
|
||||
frame,
|
||||
innerSize,
|
||||
radius,
|
||||
twiceRadius,
|
||||
scale);
|
||||
const auto overlayShadow = validateOverlayShadow(
|
||||
frame,
|
||||
innerSize,
|
||||
radius,
|
||||
twiceRadius,
|
||||
scale,
|
||||
overlayMask);
|
||||
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
FillWithImage(p, QRect(QPoint(), size), overlayMask);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
FillWithImage(p, QRect(QPoint(), size), overlayShadow);
|
||||
}
|
||||
|
||||
void RoundAreaWithShadow::FillWithImage(
|
||||
QPainter &p,
|
||||
QRect geometry,
|
||||
const ImageSubrect &pattern) {
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
const auto &image = *pattern.image;
|
||||
const auto source = pattern.rect;
|
||||
const auto sourceWidth = (source.width() / factor);
|
||||
const auto sourceHeight = (source.height() / factor);
|
||||
if (geometry.width() == sourceWidth) {
|
||||
const auto part = (sourceHeight / 2) - 1;
|
||||
const auto fill = geometry.height() - 2 * part;
|
||||
const auto half = part * factor;
|
||||
const auto top = source.height() - half;
|
||||
p.drawImage(
|
||||
geometry.topLeft(),
|
||||
image,
|
||||
QRect(source.x(), source.y(), source.width(), half));
|
||||
if (fill > 0) {
|
||||
p.drawImage(
|
||||
QRect(
|
||||
geometry.topLeft() + QPoint(0, part),
|
||||
QSize(sourceWidth, fill)),
|
||||
image,
|
||||
QRect(
|
||||
source.x(),
|
||||
source.y() + half,
|
||||
source.width(),
|
||||
top - half));
|
||||
}
|
||||
p.drawImage(
|
||||
geometry.topLeft() + QPoint(0, part + fill),
|
||||
image,
|
||||
QRect(source.x(), source.y() + top, source.width(), half));
|
||||
} else if (geometry.height() == sourceHeight) {
|
||||
const auto part = (sourceWidth / 2) - 1;
|
||||
const auto fill = geometry.width() - 2 * part;
|
||||
const auto half = part * factor;
|
||||
const auto left = source.width() - half;
|
||||
p.drawImage(
|
||||
geometry.topLeft(),
|
||||
image,
|
||||
QRect(source.x(), source.y(), half, source.height()));
|
||||
if (fill > 0) {
|
||||
p.drawImage(
|
||||
QRect(
|
||||
geometry.topLeft() + QPoint(part, 0),
|
||||
QSize(fill, sourceHeight)),
|
||||
image,
|
||||
QRect(
|
||||
source.x() + half,
|
||||
source.y(),
|
||||
left - half,
|
||||
source.height()));
|
||||
}
|
||||
p.drawImage(
|
||||
geometry.topLeft() + QPoint(part + fill, 0),
|
||||
image,
|
||||
QRect(source.x() + left, source.y(), half, source.height()));
|
||||
} else {
|
||||
Unexpected("Values in RoundAreaWithShadow::fillWithImage.");
|
||||
}
|
||||
}
|
||||
|
||||
void RoundAreaWithShadow::setShadowColor(const QColor &shadow) {
|
||||
if (_shadow == shadow) {
|
||||
return;
|
||||
}
|
||||
_shadow = shadow;
|
||||
ranges::fill(_validBg, false);
|
||||
ranges::fill(_validShadow, false);
|
||||
ranges::fill(_validOverlayShadow, false);
|
||||
}
|
||||
|
||||
QRect RoundAreaWithShadow::validateShadow(
|
||||
int frameIndex,
|
||||
float64 scale,
|
||||
float64 radius) {
|
||||
const auto rect = FrameCacheRect(frameIndex, kShadowCacheIndex, _outer);
|
||||
if (_validShadow[frameIndex]) {
|
||||
return rect;
|
||||
}
|
||||
|
||||
_shadowBuffer.fill(Qt::transparent);
|
||||
auto p = QPainter(&_shadowBuffer);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto center = _inner.center();
|
||||
const auto add = style::ConvertScale(2.5);
|
||||
const auto shift = style::ConvertScale(0.5);
|
||||
const auto big = QRectF(_inner).marginsAdded({ add, add, add, add });
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_shadow);
|
||||
if (scale != 1.) {
|
||||
p.translate(center);
|
||||
p.scale(scale, scale);
|
||||
p.translate(-center);
|
||||
}
|
||||
p.drawRoundedRect(big.translated(0, shift), radius, radius);
|
||||
p.end();
|
||||
_shadowBuffer = Images::Blur(std::move(_shadowBuffer));
|
||||
|
||||
auto q = QPainter(&_shadowParts);
|
||||
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
q.drawImage(rect.topLeft() / style::DevicePixelRatio(), _shadowBuffer);
|
||||
|
||||
_validShadow[frameIndex] = true;
|
||||
return rect;
|
||||
}
|
||||
|
||||
void RoundAreaWithShadow::setBackgroundColor(const QColor &background) {
|
||||
if (_background == background) {
|
||||
return;
|
||||
}
|
||||
_background = background;
|
||||
ranges::fill(_validBg, false);
|
||||
}
|
||||
|
||||
ImageSubrect RoundAreaWithShadow::validateFrame(
|
||||
int frameIndex,
|
||||
float64 scale) {
|
||||
const auto result = ImageSubrect{
|
||||
&_cacheBg,
|
||||
FrameCacheRect(frameIndex, kBgCacheIndex, _outer)
|
||||
};
|
||||
if (_validBg[frameIndex]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto position = result.rect.topLeft() / style::DevicePixelRatio();
|
||||
const auto inner = _inner.translated(position);
|
||||
const auto radius = inner.height() / 2.;
|
||||
|
||||
const auto shadowSource = validateShadow(frameIndex, scale, radius);
|
||||
|
||||
auto p = QPainter(&_cacheBg);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.drawImage(position, _shadowParts, shadowSource);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_background);
|
||||
if (scale != 1.) {
|
||||
const auto center = inner.center();
|
||||
p.save();
|
||||
p.translate(center);
|
||||
p.scale(scale, scale);
|
||||
p.translate(-center);
|
||||
}
|
||||
p.drawRoundedRect(inner, radius, radius);
|
||||
if (scale != 1.) {
|
||||
p.restore();
|
||||
}
|
||||
|
||||
_validBg[frameIndex] = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
87
ui/effects/round_area_with_shadow.h
Normal file
87
ui/effects/round_area_with_shadow.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// 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 {
|
||||
|
||||
struct ImageSubrect {
|
||||
not_null<QImage*> image;
|
||||
QRect rect;
|
||||
};
|
||||
|
||||
class RoundAreaWithShadow final {
|
||||
public:
|
||||
static constexpr auto kFramesCount = 32;
|
||||
|
||||
[[nodiscard]] static QImage PrepareImage(QSize size);
|
||||
[[nodiscard]] static QImage PrepareFramesCache(
|
||||
QSize frame,
|
||||
int columns = 1);
|
||||
[[nodiscard]] static QRect FrameCacheRect(
|
||||
int frameIndex,
|
||||
int column,
|
||||
QSize frame);
|
||||
|
||||
static void FillWithImage(
|
||||
QPainter &p,
|
||||
QRect geometry,
|
||||
const ImageSubrect &pattern);
|
||||
|
||||
RoundAreaWithShadow(QSize inner, QMargins shadow, int twiceRadiusMax);
|
||||
|
||||
void setBackgroundColor(const QColor &background);
|
||||
void setShadowColor(const QColor &shadow);
|
||||
|
||||
[[nodiscard]] ImageSubrect validateFrame(int frameIndex, float64 scale);
|
||||
[[nodiscard]] ImageSubrect validateOverlayMask(
|
||||
int frameIndex,
|
||||
QSize innerSize,
|
||||
float64 radius,
|
||||
int twiceRadius,
|
||||
float64 scale);
|
||||
[[nodiscard]] ImageSubrect validateOverlayShadow(
|
||||
int frameIndex,
|
||||
QSize innerSize,
|
||||
float64 radius,
|
||||
int twiceRadius,
|
||||
float64 scale,
|
||||
const ImageSubrect &mask);
|
||||
|
||||
void overlayExpandedBorder(
|
||||
QPainter &p,
|
||||
QSize size,
|
||||
float64 expandRatio,
|
||||
float64 radius,
|
||||
float64 scale);
|
||||
|
||||
private:
|
||||
[[nodiscard]] QRect validateShadow(
|
||||
int frameIndex,
|
||||
float64 scale,
|
||||
float64 radius);
|
||||
|
||||
QRect _inner;
|
||||
QSize _outer;
|
||||
QSize _overlay;
|
||||
|
||||
std::array<bool, kFramesCount> _validBg = { { false } };
|
||||
std::array<bool, kFramesCount> _validShadow = { { false } };
|
||||
std::array<bool, kFramesCount> _validOverlayMask = { { false } };
|
||||
std::array<bool, kFramesCount> _validOverlayShadow = { { false } };
|
||||
QColor _background;
|
||||
QColor _gradient;
|
||||
QColor _shadow;
|
||||
QImage _cacheBg;
|
||||
QImage _shadowParts;
|
||||
QImage _overlayCacheParts;
|
||||
QImage _overlayMaskScaled;
|
||||
QImage _overlayShadowScaled;
|
||||
QImage _shadowBuffer;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
|
@ -430,12 +430,15 @@ void PopupMenu::paintEvent(QPaintEvent *e) {
|
|||
if (_a_show.animating()) {
|
||||
const auto opacity = _a_opacity.value(_hiding ? 0. : 1.);
|
||||
const auto progress = _a_show.value(1.);
|
||||
if (opacity) {
|
||||
_showAnimation->paintFrame(p, 0, 0, width(), progress, opacity);
|
||||
}
|
||||
const auto state = (opacity > 0.)
|
||||
? _showAnimation->paintFrame(p, 0, 0, width(), progress, opacity)
|
||||
: PanelAnimation::PaintState();
|
||||
_showStateChanges.fire({
|
||||
.opacity = opacity,
|
||||
.progress = progress,
|
||||
.opacity = state.opacity,
|
||||
.widthProgress = state.widthProgress,
|
||||
.heightProgress = state.heightProgress,
|
||||
.appearingWidth = state.width,
|
||||
.appearingHeight = state.height,
|
||||
.appearing = true,
|
||||
});
|
||||
} else if (_a_opacity.animating()) {
|
||||
|
|
@ -703,11 +706,7 @@ void PopupMenu::prepareCache() {
|
|||
_showAnimation = base::take(showAnimationData);
|
||||
_a_show = base::take(showAnimation);
|
||||
if (_a_show.animating()) {
|
||||
_showStateChanges.fire({
|
||||
.opacity = _a_opacity.value(1.),
|
||||
.progress = _a_show.value(1.),
|
||||
.appearing = true,
|
||||
});
|
||||
fireCurrentShowState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -773,9 +772,19 @@ void PopupMenu::startShowAnimation() {
|
|||
}
|
||||
hideChildren();
|
||||
_a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration);
|
||||
fireCurrentShowState();
|
||||
}
|
||||
|
||||
void PopupMenu::fireCurrentShowState() {
|
||||
const auto state = _showAnimation->computeState(
|
||||
_a_show.value(1.),
|
||||
_a_opacity.value(1.));
|
||||
_showStateChanges.fire({
|
||||
.opacity = _a_opacity.value(1.),
|
||||
.progress = _a_show.value(1.),
|
||||
.opacity = state.opacity,
|
||||
.widthProgress = state.widthProgress,
|
||||
.heightProgress = state.heightProgress,
|
||||
.appearingWidth = state.width,
|
||||
.appearingHeight = state.height,
|
||||
.appearing = true,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,10 @@ public:
|
|||
|
||||
struct ShowState {
|
||||
float64 opacity = 1.;
|
||||
float64 progress = 1.;
|
||||
float64 widthProgress = 1.;
|
||||
float64 heightProgress = 1.;
|
||||
int appearingWidth = 0;
|
||||
int appearingHeight = 0;
|
||||
bool appearing = false;
|
||||
bool toggling = false;
|
||||
};
|
||||
|
|
@ -123,6 +126,7 @@ private:
|
|||
|
||||
void hideFinished();
|
||||
void showStarted();
|
||||
void fireCurrentShowState();
|
||||
|
||||
using TriggeredSource = Menu::TriggeredSource;
|
||||
void validateCompositingSupport();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue