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
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:
220
Telegram/lib_ui/ui/paint/arcs.cpp
Normal file
220
Telegram/lib_ui/ui/paint/arcs.cpp
Normal 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
|
||||
85
Telegram/lib_ui/ui/paint/arcs.h
Normal file
85
Telegram/lib_ui/ui/paint/arcs.h
Normal 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
|
||||
244
Telegram/lib_ui/ui/paint/blob.cpp
Normal file
244
Telegram/lib_ui/ui/paint/blob.cpp
Normal 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
|
||||
113
Telegram/lib_ui/ui/paint/blob.h
Normal file
113
Telegram/lib_ui/ui/paint/blob.h
Normal 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
|
||||
113
Telegram/lib_ui/ui/paint/blobs.cpp
Normal file
113
Telegram/lib_ui/ui/paint/blobs.cpp
Normal 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
|
||||
64
Telegram/lib_ui/ui/paint/blobs.h
Normal file
64
Telegram/lib_ui/ui/paint/blobs.h
Normal 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
|
||||
117
Telegram/lib_ui/ui/paint/blobs_linear.cpp
Normal file
117
Telegram/lib_ui/ui/paint/blobs_linear.cpp
Normal 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
|
||||
63
Telegram/lib_ui/ui/paint/blobs_linear.h
Normal file
63
Telegram/lib_ui/ui/paint/blobs_linear.h
Normal 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
|
||||
Reference in New Issue
Block a user