From ddda92c777b89e84603699952c84864202690fe5 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 17 Jan 2021 23:08:21 +0300 Subject: [PATCH] Added animation support to arc painter. --- ui/paint/arcs.cpp | 130 ++++++++++++++++++++++++++++++++++++--- ui/paint/arcs.h | 32 +++++++++- ui/widgets/widgets.style | 1 + 3 files changed, 150 insertions(+), 13 deletions(-) diff --git a/ui/paint/arcs.cpp b/ui/paint/arcs.cpp index 9538b99..bcc6e23 100644 --- a/ui/paint/arcs.cpp +++ b/ui/paint/arcs.cpp @@ -6,13 +6,30 @@ // #include "ui/paint/arcs.h" +#include "ui/effects/animation_value.h" #include "ui/painter.h" namespace Ui::Paint { +namespace { + +inline float64 InterpolateF(float a, float b, float64 b_ratio) { + return a + float64(b - a) * b_ratio; +}; + +QRectF InterpolatedRect(const QRectF &r1, const QRectF &r2, float64 ratio) { + return QRectF( + InterpolateF(r1.x(), r2.x(), ratio), + InterpolateF(r1.y(), r2.y(), ratio), + InterpolateF(r1.width(), r2.width(), ratio), + InterpolateF(r1.height(), r2.height(), ratio)); +} + +} // namespace ArcsAnimation::ArcsAnimation( const style::ArcsAnimation &st, - int count, + std::vector thresholds, + float64 startValue, VerticalDirection direction, int centerX, int startY) @@ -23,13 +40,16 @@ ArcsAnimation::ArcsAnimation( , _verticalDirection(direction) , _startAngle((st.deltaAngle + ((direction == VerticalDirection::Up) ? 90 : 270)) * 16) -, _spanAngle(-st.deltaAngle * 2 * 16) { - initArcs(count); +, _spanAngle(-st.deltaAngle * 2 * 16) +, _emptyRect(computeArcRect(0)) +, _currentValue(startValue) { + initArcs(std::move(thresholds)); } ArcsAnimation::ArcsAnimation( const style::ArcsAnimation &st, - int count, + std::vector thresholds, + float64 startValue, HorizontalDirection direction, int startX, int centerY) @@ -40,15 +60,24 @@ ArcsAnimation::ArcsAnimation( , _verticalDirection(VerticalDirection::None) , _startAngle((st.deltaAngle + ((direction == HorizontalDirection::Left) ? 180 : 0)) * 16) -, _spanAngle(-st.deltaAngle * 2 * 16) { - initArcs(count); +, _spanAngle(-st.deltaAngle * 2 * 16) +, _emptyRect(computeArcRect(0)) +, _currentValue(startValue) { + initArcs(std::move(thresholds)); } -void ArcsAnimation::initArcs(int count) { +void ArcsAnimation::initArcs(std::vector thresholds) { + const auto count = thresholds.size(); _arcs.reserve(count); for (auto i = 0; i < count; i++) { - auto arc = Arc{ .rect = computeArcRect(i) }; + const auto threshold = thresholds[i]; + const auto progress = (threshold > _currentValue) ? 1. : 0.; + auto arc = Arc{ + .rect = computeArcRect(i + 1), + .threshold = threshold, + .progress = progress, + }; _arcs.push_back(std::move(arc)); } } @@ -76,6 +105,77 @@ QRectF ArcsAnimation::computeArcRect(int index) const { return QRectF(); } +void ArcsAnimation::update(crl::time now) { + for (auto &arc : _arcs) { + if (!isArcFinished(arc)) { + const auto progress = std::clamp( + (now - arc.startTime) / float64(_st.duration), + 0., + 1.); + arc.progress = (arc.threshold > _currentValue) + ? progress + : (1. - progress); + } + } + if (isFinished()) { + _stopUpdateRequests.fire({}); + } +} + + +void ArcsAnimation::setValue(float64 value) { + if (_currentValue == value) { + return; + } + const auto previousValue = _currentValue; + _currentValue = value; + if (!isFinished()) { + const auto now = crl::now(); + _startUpdateRequests.fire({}); + for (auto &arc : _arcs) { + updateArcStartTime(arc, previousValue, now); + } + } +} + +void ArcsAnimation::updateArcStartTime( + Arc &arc, + float64 previousValue, + crl::time now) { + if ((arc.progress == 0.) || (arc.progress == 1.)) { + arc.startTime = isArcFinished(arc) ? 0 : now; + return; + } + const auto isPreviousToHide = (arc.threshold <= previousValue); // 0 -> 1 + const auto isCurrentToHide = (arc.threshold <= _currentValue); + if (isPreviousToHide != isCurrentToHide) { + const auto passedTime = _st.duration * arc.progress; + const auto newDelta = isCurrentToHide + ? (_st.duration - passedTime) + : passedTime; + arc.startTime = now - newDelta; + } +} + +rpl::producer<> ArcsAnimation::startUpdateRequests() { + return _startUpdateRequests.events(); +} + +rpl::producer<> ArcsAnimation::stopUpdateRequests() { + return _stopUpdateRequests.events(); +} + +bool ArcsAnimation::isFinished() const { + return ranges::all_of( + _arcs, + [=](const Arc &arc) { return isArcFinished(arc); }); +} + +bool ArcsAnimation::isArcFinished(const Arc &arc) const { + return ((arc.threshold > _currentValue) && (arc.progress == 1.)) + || ((arc.threshold <= _currentValue) && (arc.progress == 0.)); +} + void ArcsAnimation::paint(Painter &p, std::optional colorOverride) { PainterHighQualityEnabler hq(p); QPen pen; @@ -83,8 +183,18 @@ void ArcsAnimation::paint(Painter &p, std::optional colorOverride) { pen.setCapStyle(Qt::RoundCap); pen.setColor(colorOverride ? (*colorOverride) : _st.fg->c); p.setPen(pen); - for (const auto &arc : _arcs) { - p.drawArc(arc.rect, _startAngle, _spanAngle); + for (auto i = 0; i < _arcs.size(); i++) { + const auto &arc = _arcs[i]; + const auto previousRect = (!i) ? _emptyRect : _arcs[i - 1].rect; + const auto progress = arc.progress; + const auto opactity = (1. - progress); + p.setOpacity(opactity * opactity); + const auto rect = (progress == 0.) + ? arc.rect + : (progress == 1.) + ? previousRect + : InterpolatedRect(arc.rect, previousRect, progress); + p.drawArc(rect, _startAngle, _spanAngle); } } diff --git a/ui/paint/arcs.h b/ui/paint/arcs.h index 1c4988e..03f42e3 100644 --- a/ui/paint/arcs.h +++ b/ui/paint/arcs.h @@ -29,14 +29,16 @@ public: ArcsAnimation( const style::ArcsAnimation &st, - int count, + std::vector thresholds, + float64 startValue, VerticalDirection direction, int centerX, int startY); ArcsAnimation( const style::ArcsAnimation &st, - int count, + std::vector thresholds, + float64 startValue, HorizontalDirection direction, int startX, int centerY); @@ -45,14 +47,32 @@ public: Painter &p, std::optional colorOverride = std::nullopt); + void setValue(float64 value); + + rpl::producer<> startUpdateRequests(); + rpl::producer<> stopUpdateRequests(); + + void update(crl::time now); + + bool isFinished() const; + private: struct Arc { QRectF rect; + float threshold; + crl::time startTime = 0; + float64 progress = 0.; }; - void initArcs(int count); + void initArcs(std::vector thresholds); QRectF computeArcRect(int index) const; + bool isArcFinished(const Arc &arc) const; + void updateArcStartTime( + Arc &arc, + float64 previousValue, + crl::time now); + const style::ArcsAnimation &_st; const int _center; const int _start; @@ -60,6 +80,12 @@ private: const VerticalDirection _verticalDirection; const int _startAngle; const int _spanAngle; + const QRectF _emptyRect; + + float64 _currentValue = 0.; + + rpl::event_stream<> _startUpdateRequests; + rpl::event_stream<> _stopUpdateRequests; std::vector _arcs; diff --git a/ui/widgets/widgets.style b/ui/widgets/widgets.style index 36f0e4d..6da3006 100644 --- a/ui/widgets/widgets.style +++ b/ui/widgets/widgets.style @@ -414,6 +414,7 @@ ArcsAnimation { fg: color; stroke: pixels; space: pixels; + duration: int; deltaAngle: int; deltaHeight: pixels; deltaWidth: pixels;