| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- // 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
|