init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
// 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/paint/arcs.h"
#include "ui/effects/animation_value.h"
#include "ui/effects/animation_value_f.h"
#include "ui/painter.h"
namespace Ui::Paint {
ArcsAnimation::ArcsAnimation(
const style::ArcsAnimation &st,
std::vector<float> thresholds,
float64 startValue,
Direction direction)
: _st(st)
, _direction(direction)
, _startAngle(16
* (st.deltaAngle
+ ((direction == Direction::Up)
? 90
: (direction == Direction::Down)
? 270
: (direction == Direction::Left)
? 180
: 0)))
, _spanAngle(-st.deltaAngle * 2 * 16)
, _emptyRect(computeArcRect(0))
, _currentValue(startValue) {
initArcs(std::move(thresholds));
}
void ArcsAnimation::initArcs(std::vector<float> thresholds) {
const auto count = thresholds.size();
_arcs.reserve(count);
for (auto i = 0; i < count; 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));
}
}
bool ArcsAnimation::isHorizontal() const {
return _direction == Direction::Left || _direction == Direction::Right;
}
QRectF ArcsAnimation::computeArcRect(int index) const {
const auto w = _st.startWidth + _st.deltaWidth * index;
const auto h = _st.startHeight + _st.deltaHeight * index;
if (isHorizontal()) {
auto rect = QRectF(0, -h / 2.0, w, h);
if (_direction == Direction::Right) {
rect.moveRight(index * _st.space);
} else {
rect.moveLeft(-index * _st.space);
}
return rect;
} else {
auto rect = QRectF(-w / 2.0, 0, w, h);
if (_direction == Direction::Up) {
rect.moveTop(-index * _st.space);
} else {
rect.moveBottom(index * _st.space);
}
return rect;
}
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;
}
}
float ArcsAnimation::width() const {
if (_arcs.empty()) {
return 0;
}
for (const auto &arc : ranges::views::reverse(_arcs)) {
if ((arc.progress != 1.)) {
return arc.rect.x() + arc.rect.width();
}
}
return 0;
}
float ArcsAnimation::finishedWidth() const {
if (_arcs.empty()) {
return 0;
}
for (const auto &arc : ranges::views::reverse(_arcs)) {
if (arc.threshold <= _currentValue) {
return arc.rect.x() + arc.rect.width();
}
}
return 0;
}
float ArcsAnimation::maxWidth() const {
if (_arcs.empty()) {
return 0;
}
const auto &r = _arcs.back().rect;
return r.x() + r.width();
}
float ArcsAnimation::height() const {
return _arcs.empty()
? 0
: _arcs.back().rect.height();
}
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(QPainter &p, std::optional<QColor> colorOverride) {
PainterHighQualityEnabler hq(p);
QPen pen;
if (_strokeRatio) {
pen.setWidthF(_st.stroke * _strokeRatio);
} else {
pen.setWidth(_st.stroke);
}
pen.setCapStyle(Qt::RoundCap);
pen.setColor(colorOverride ? (*colorOverride) : _st.fg->c);
p.setPen(pen);
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
: anim::interpolatedRectF(arc.rect, previousRect, progress);
p.drawArc(rect, _startAngle, _spanAngle);
}
p.setOpacity(1.);
}
void ArcsAnimation::setStrokeRatio(float ratio) {
_strokeRatio = ratio;
}
} // namespace Ui::Paint

View File

@@ -0,0 +1,85 @@
// 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
#include "styles/style_widgets.h"
class Painter;
namespace Ui::Paint {
class ArcsAnimation {
public:
enum class Direction {
Up,
Down,
Left,
Right,
};
ArcsAnimation(
const style::ArcsAnimation &st,
std::vector<float> thresholds,
float64 startValue,
Direction direction);
void paint(
QPainter &p,
std::optional<QColor> colorOverride = std::nullopt);
void setValue(float64 value);
rpl::producer<> startUpdateRequests();
rpl::producer<> stopUpdateRequests();
void update(crl::time now);
bool isFinished() const;
float width() const;
float maxWidth() const;
float finishedWidth() const;
float height() const;
void setStrokeRatio(float ratio);
private:
struct Arc {
QRectF rect;
float threshold;
crl::time startTime = 0;
float64 progress = 0.;
};
void initArcs(std::vector<float> thresholds);
QRectF computeArcRect(int index) const;
bool isHorizontal() const;
bool isArcFinished(const Arc &arc) const;
void updateArcStartTime(
Arc &arc,
float64 previousValue,
crl::time now);
const style::ArcsAnimation &_st;
const Direction _direction;
const int _startAngle;
const int _spanAngle;
const QRectF _emptyRect;
float64 _currentValue = 0.;
float _strokeRatio = 0.;
rpl::event_stream<> _startUpdateRequests;
rpl::event_stream<> _stopUpdateRequests;
std::vector<Arc> _arcs;
};
} // namespace Ui::Paint

View File

@@ -0,0 +1,244 @@
// 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/paint/blob.h"
#include "base/random.h"
#include "ui/painter.h"
#include <QtGui/QPainterPath>
#include <QtCore/QtMath>
namespace Ui::Paint {
namespace {
constexpr auto kMaxSpeed = 8.2;
constexpr auto kMinSpeed = 0.8;
constexpr auto kMinSegmentSpeed = 0.017;
constexpr auto kSegmentSpeedDiff = 0.003;
[[nodiscard]] float64 RandomAdditional() {
return (base::RandomValue<int>() % 100 / 100.);
}
} // namespace
Blob::Blob(int n, float minSpeed, float maxSpeed)
: _segmentsCount(n)
, _minSpeed(minSpeed ? minSpeed : kMinSpeed)
, _maxSpeed(maxSpeed ? maxSpeed : kMaxSpeed)
, _pen(Qt::NoBrush, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) {
}
void Blob::generateBlob() {
for (auto i = 0; i < _segmentsCount; i++) {
generateSingleValues(i);
// Fill nexts.
generateTwoValues(i);
// Fill currents.
generateTwoValues(i);
}
}
void Blob::generateSingleValues(int i) {
auto &segment = segmentAt(i);
segment.progress = 0.;
segment.speed = kMinSegmentSpeed
+ kSegmentSpeedDiff * std::abs(RandomAdditional());
}
void Blob::update(float level, float speedScale, float64 rate) {
for (auto i = 0; i < _segmentsCount; i++) {
auto &segment = segmentAt(i);
segment.progress += (_minSpeed + level * _maxSpeed * speedScale)
* segment.speed
* rate;
if (segment.progress >= 1) {
generateSingleValues(i);
generateTwoValues(i);
}
}
}
void Blob::setRadiuses(Radiuses values) {
_radiuses = values;
}
Blob::Radiuses Blob::radiuses() const {
return _radiuses;
}
RadialBlob::RadialBlob(int n, float minScale, float minSpeed, float maxSpeed)
: Blob(n, minSpeed, maxSpeed)
, _segmentLength((4.0 / 3.0) * std::tan(M_PI / (2 * n)))
, _minScale(minScale)
, _segmentAngle(360. / n)
, _angleDiff(_segmentAngle * 0.05)
, _segments(n) {
}
void RadialBlob::paint(QPainter &p, const QBrush &brush, float outerScale) {
auto path = QPainterPath();
auto m = QTransform();
const auto scale = (_minScale + (1. - _minScale) * _scale) * outerScale;
if (scale == 0.) {
return;
}
p.save();
if (scale != 1.) {
p.scale(scale, scale);
}
for (auto i = 0; i < _segmentsCount; i++) {
const auto &segment = _segments[i];
const auto nextIndex = i + 1 < _segmentsCount ? (i + 1) : 0;
const auto nextSegment = _segments[nextIndex];
const auto progress = segment.progress;
const auto progressNext = nextSegment.progress;
const auto r1 = segment.radius.current * (1. - progress)
+ segment.radius.next * progress;
const auto r2 = nextSegment.radius.current * (1. - progressNext)
+ nextSegment.radius.next * progressNext;
const auto angle1 = segment.angle.current * (1. - progress)
+ segment.angle.next * progress;
const auto angle2 = nextSegment.angle.current * (1. - progressNext)
+ nextSegment.angle.next * progressNext;
const auto l = _segmentLength * (std::min(r1, r2)
+ (std::max(r1, r2) - std::min(r1, r2)) / 2.);
m.reset();
m.rotate(angle1);
const auto pointStart1 = m.map(QPointF(0, -r1));
const auto pointStart2 = m.map(QPointF(l, -r1));
m.reset();
m.rotate(angle2);
const auto pointEnd1 = m.map(QPointF(0, -r2));
const auto pointEnd2 = m.map(QPointF(-l, -r2));
if (i == 0) {
path.moveTo(pointStart1);
}
path.cubicTo(pointStart2, pointEnd2, pointEnd1);
}
p.setBrush(Qt::NoBrush);
p.setPen(_pen);
p.fillPath(path, brush);
p.drawPath(path);
p.restore();
}
void RadialBlob::generateTwoValues(int i) {
auto &radius = _segments[i].radius;
auto &angle = _segments[i].angle;
const auto radDiff = _radiuses.max - _radiuses.min;
angle.setNext(_segmentAngle * i + RandomAdditional() * _angleDiff);
radius.setNext(_radiuses.min + std::abs(RandomAdditional()) * radDiff);
}
void RadialBlob::update(float level, float speedScale, float64 rate) {
_scale = level;
Blob::update(level, speedScale, rate);
}
Blob::Segment &RadialBlob::segmentAt(int i) {
return _segments[i];
};
LinearBlob::LinearBlob(
int n,
Direction direction,
float minSpeed,
float maxSpeed)
: Blob(n + 1)
, _topDown(direction == Direction::TopDown ? 1 : -1)
, _segments(_segmentsCount) {
}
void LinearBlob::paint(QPainter &p, const QBrush &brush, int width) {
if (!width) {
return;
}
auto path = QPainterPath();
const auto left = 0;
const auto right = width;
path.moveTo(right, 0);
path.lineTo(left, 0);
const auto n = float(_segmentsCount - 1);
p.save();
for (auto i = 0; i < _segmentsCount; i++) {
const auto &segment = _segments[i];
if (!i) {
const auto &progress = segment.progress;
const auto r1 = segment.radius.current * (1. - progress)
+ segment.radius.next * progress;
const auto y = r1 * _topDown;
path.lineTo(left, y);
} else {
const auto &prevSegment = _segments[i - 1];
const auto &progress = prevSegment.progress;
const auto r1 = prevSegment.radius.current * (1. - progress)
+ prevSegment.radius.next * progress;
const auto &progressNext = segment.progress;
const auto r2 = segment.radius.current * (1. - progressNext)
+ segment.radius.next * progressNext;
const auto x1 = (right - left) / n * (i - 1);
const auto x2 = (right - left) / n * i;
const auto cx = x1 + (x2 - x1) / 2;
const auto y1 = r1 * _topDown;
const auto y2 = r2 * _topDown;
path.cubicTo(
QPointF(cx, y1),
QPointF(cx, y2),
QPointF(x2, y2)
);
}
}
path.lineTo(right, 0);
p.setBrush(Qt::NoBrush);
p.setPen(_pen);
p.fillPath(path, brush);
p.drawPath(path);
p.restore();
}
void LinearBlob::generateTwoValues(int i) {
auto &radius = _segments[i].radius;
const auto radDiff = _radiuses.max - _radiuses.min;
radius.setNext(_radiuses.min + std::abs(RandomAdditional()) * radDiff);
}
Blob::Segment &LinearBlob::segmentAt(int i) {
return _segments[i];
};
} // namespace Ui::Paint

View File

@@ -0,0 +1,113 @@
// 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
class Painter;
namespace Ui::Paint {
class Blob {
public:
struct Radiuses {
float min = 0.;
float max = 0.;
};
Blob(int n, float minSpeed = 0, float maxSpeed = 0);
virtual ~Blob() = default;
void update(float level, float speedScale, float64 rate);
void generateBlob();
void setRadiuses(Radiuses values);
[[nodiscard]] Radiuses radiuses() const;
protected:
struct TwoValues {
float current = 0.;
float next = 0.;
void setNext(float v) {
current = next;
next = v;
}
};
struct Segment {
float progress = 0.;
float speed = 0.;
};
void generateSingleValues(int i);
virtual void generateTwoValues(int i) = 0;
virtual Segment &segmentAt(int i) = 0;
const int _segmentsCount;
const float _minSpeed;
const float _maxSpeed;
const QPen _pen;
Radiuses _radiuses;
};
class RadialBlob final : public Blob {
public:
RadialBlob(int n, float minScale, float minSpeed = 0, float maxSpeed = 0);
void paint(QPainter &p, const QBrush &brush, float outerScale = 1.);
void update(float level, float speedScale, float64 rate);
private:
struct Segment : Blob::Segment {
Blob::TwoValues radius;
Blob::TwoValues angle;
};
void generateTwoValues(int i) override;
Blob::Segment &segmentAt(int i) override;
const float64 _segmentLength;
const float _minScale;
const float _segmentAngle;
const float _angleDiff;
std::vector<Segment> _segments;
float64 _scale = 0;
};
class LinearBlob final : public Blob {
public:
enum class Direction {
TopDown,
BottomUp,
};
LinearBlob(
int n,
Direction direction = Direction::TopDown,
float minSpeed = 0,
float maxSpeed = 0);
void paint(QPainter &p, const QBrush &brush, int width);
private:
struct Segment : Blob::Segment {
Blob::TwoValues radius;
};
void generateTwoValues(int i) override;
Blob::Segment &segmentAt(int i) override;
const int _topDown;
std::vector<Segment> _segments;
};
} // namespace Ui::Paint

View File

@@ -0,0 +1,113 @@
// 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/paint/blobs.h"
#include "ui/painter.h"
namespace Ui::Paint {
namespace {
constexpr auto kRateLimitF = 1000. / 60.;
constexpr auto kRateLimit = int(kRateLimitF + 0.5); // Round.
} // namespace
Blobs::Blobs(
std::vector<BlobData> blobDatas,
float levelDuration,
float maxLevel)
: _maxLevel(maxLevel)
, _blobDatas(std::move(blobDatas))
, _levelValue(levelDuration) {
init();
}
void Blobs::init() {
for (const auto &data : _blobDatas) {
auto blob = Paint::RadialBlob(
data.segmentsCount,
data.minScale,
data.minSpeed,
data.maxSpeed);
blob.setRadiuses({ data.minRadius, data.maxRadius });
blob.generateBlob();
_blobs.push_back(std::move(blob));
}
}
float Blobs::maxRadius() const {
const auto maxOfRadiuses = [](const BlobData data) {
return std::max(data.maxRadius, data.minRadius);
};
const auto max = *ranges::max_element(
_blobDatas,
std::less<>(),
maxOfRadiuses);
return maxOfRadiuses(max);
}
int Blobs::size() const {
return _blobs.size();
}
void Blobs::setRadiusesAt(
rpl::producer<Blob::Radiuses> &&radiuses,
int index) {
Expects(index >= 0 && index < size());
std::move(
radiuses
) | rpl::on_next([=](Blob::Radiuses r) {
_blobs[index].setRadiuses(std::move(r));
}, _lifetime);
}
Blob::Radiuses Blobs::radiusesAt(int index) {
Expects(index >= 0 && index < size());
return _blobs[index].radiuses();
}
void Blobs::setLevel(float value) {
const auto to = std::min(_maxLevel, value) / _maxLevel;
_levelValue.start(to);
}
void Blobs::resetLevel() {
_levelValue.reset();
}
void Blobs::paint(QPainter &p, const QBrush &brush, float outerScale) {
const auto opacity = p.opacity();
for (auto i = 0; i < _blobs.size(); i++) {
const auto alpha = _blobDatas[i].alpha;
if (alpha != 1.) {
p.setOpacity(opacity * alpha);
}
_blobs[i].paint(p, brush, outerScale);
if (alpha != 1.) {
p.setOpacity(opacity);
}
}
}
void Blobs::updateLevel(crl::time dt) {
const auto limitedDt = (dt > 20) ? kRateLimit : dt;
_levelValue.update(limitedDt);
for (auto i = 0; i < _blobs.size(); i++) {
_blobs[i].update(
_levelValue.current(),
_blobDatas[i].speedScale,
limitedDt / kRateLimitF);
}
}
float64 Blobs::currentLevel() const {
return _levelValue.current();
}
} // namespace Ui::Paint

View File

@@ -0,0 +1,64 @@
// 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
#include "ui/effects/animation_value.h"
#include "ui/paint/blob.h"
class Painter;
namespace Ui::Paint {
class Blobs final {
public:
struct BlobData {
int segmentsCount = 0;
float minScale = 0;
float minRadius = 0;
float maxRadius = 0;
float speedScale = 0;
float alpha = 0;
float minSpeed = 0;
float maxSpeed = 0;
};
Blobs(
std::vector<BlobData> blobDatas,
float levelDuration,
float maxLevel);
void setRadiusesAt(
rpl::producer<Blob::Radiuses> &&radiuses,
int index);
Blob::Radiuses radiusesAt(int index);
void setLevel(float value);
void resetLevel();
void paint(QPainter &p, const QBrush &brush, float outerScale = 1.);
void updateLevel(crl::time dt);
[[nodiscard]] float maxRadius() const;
[[nodiscard]] int size() const;
[[nodiscard]] float64 currentLevel() const;
static constexpr auto kHideBlobsDuration = 2000;
private:
void init();
const float _maxLevel;
std::vector<BlobData> _blobDatas;
std::vector<RadialBlob> _blobs;
anim::continuous_value _levelValue;
rpl::lifetime _lifetime;
};
} // namespace Ui::Paint

View File

@@ -0,0 +1,117 @@
// 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/paint/blobs_linear.h"
#include "ui/painter.h"
namespace Ui::Paint {
namespace {
constexpr auto kRateLimitF = 1000. / 60.;
constexpr auto kRateLimit = int(kRateLimitF + 0.5); // Round.
} // namespace
LinearBlobs::LinearBlobs(
std::vector<BlobData> blobDatas,
float levelDuration,
float maxLevel,
LinearBlob::Direction direction)
: _maxLevel(maxLevel)
, _direction(direction)
, _blobDatas(std::move(blobDatas))
, _levelValue(levelDuration) {
init();
}
void LinearBlobs::init() {
for (const auto &data : _blobDatas) {
auto blob = Paint::LinearBlob(
data.segmentsCount,
_direction,
data.minSpeed,
data.maxSpeed);
blob.setRadiuses({ data.minRadius, data.idleRadius });
blob.generateBlob();
_blobs.push_back(std::move(blob));
}
}
float LinearBlobs::maxRadius() const {
const auto maxOfRadiuses = [](const BlobData &d) {
return std::max(d.idleRadius, std::max(d.maxRadius, d.minRadius));
};
const auto max = *ranges::max_element(
_blobDatas,
std::less<>(),
maxOfRadiuses);
return maxOfRadiuses(max);
}
int LinearBlobs::size() const {
return _blobs.size();
}
void LinearBlobs::setRadiusesAt(
rpl::producer<Blob::Radiuses> &&radiuses,
int index) {
Expects(index >= 0 && index < size());
std::move(
radiuses
) | rpl::on_next([=](Blob::Radiuses r) {
_blobs[index].setRadiuses(std::move(r));
}, _lifetime);
}
Blob::Radiuses LinearBlobs::radiusesAt(int index) {
Expects(index >= 0 && index < size());
return _blobs[index].radiuses();
}
void LinearBlobs::setLevel(float value) {
const auto to = std::min(_maxLevel, value) / _maxLevel;
_levelValue.start(to);
}
void LinearBlobs::paint(QPainter &p, const QBrush &brush, int width) {
PainterHighQualityEnabler hq(p);
const auto opacity = p.opacity();
for (auto i = 0; i < _blobs.size(); i++) {
const auto alpha = _blobDatas[i].alpha;
if (alpha != 1.) {
p.setOpacity(opacity * alpha);
}
_blobs[i].paint(p, brush, width);
if (alpha != 1.) {
p.setOpacity(opacity);
}
}
}
void LinearBlobs::updateLevel(crl::time dt) {
const auto limitedDt = (dt > 20) ? kRateLimit : dt;
_levelValue.update(limitedDt);
const auto level = (float)currentLevel();
for (auto i = 0; i < _blobs.size(); i++) {
const auto &data = _blobDatas[i];
_blobs[i].setRadiuses({
data.minRadius,
data.idleRadius + (data.maxRadius - data.idleRadius) * level });
_blobs[i].update(
_levelValue.current(),
data.speedScale,
limitedDt / kRateLimitF);
}
}
float64 LinearBlobs::currentLevel() const {
return _levelValue.current();
}
} // namespace Ui::Paint

View File

@@ -0,0 +1,63 @@
// 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
#include "ui/effects/animation_value.h"
#include "ui/paint/blob.h"
namespace Ui::Paint {
class LinearBlobs final {
public:
struct BlobData {
int segmentsCount = 0;
float minRadius = 0;
float maxRadius = 0;
float idleRadius = 0;
float speedScale = 0;
float alpha = 0;
float minSpeed = 0;
float maxSpeed = 0;
};
LinearBlobs(
std::vector<BlobData> blobDatas,
float levelDuration,
float maxLevel,
LinearBlob::Direction direction);
void setRadiusesAt(
rpl::producer<Blob::Radiuses> &&radiuses,
int index);
Blob::Radiuses radiusesAt(int index);
void setLevel(float value);
void paint(QPainter &p, const QBrush &brush, int width);
void updateLevel(crl::time dt);
[[nodiscard]] float maxRadius() const;
[[nodiscard]] int size() const;
[[nodiscard]] float64 currentLevel() const;
static constexpr auto kHideBlobsDuration = 2000;
private:
void init();
const float _maxLevel;
const LinearBlob::Direction _direction;
std::vector<BlobData> _blobDatas;
std::vector<LinearBlob> _blobs;
anim::continuous_value _levelValue;
rpl::lifetime _lifetime;
};
} // namespace Ui::Paint