From 54d5358b75ca17e712f47115dcd214f3c1c40ee9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 5 Sep 2023 17:03:08 +0300 Subject: [PATCH] Fixed position of selected X in linear chart animations. --- .../SourceFiles/statistics/chart_widget.cpp | 98 +++++++++---------- .../SourceFiles/statistics/chart_widget.h | 7 +- .../statistics/point_details_widget.cpp | 6 +- .../statistics/point_details_widget.h | 1 + .../statistics/view/abstract_chart_view.h | 3 +- .../statistics/view/linear_chart_view.cpp | 23 ++++- .../statistics/view/linear_chart_view.h | 4 +- .../statistics/view/stack_chart_view.cpp | 3 +- .../statistics/view/stack_chart_view.h | 3 +- 9 files changed, 83 insertions(+), 65 deletions(-) diff --git a/Telegram/SourceFiles/statistics/chart_widget.cpp b/Telegram/SourceFiles/statistics/chart_widget.cpp index 961018351..9f6dcb5fe 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.cpp +++ b/Telegram/SourceFiles/statistics/chart_widget.cpp @@ -830,20 +830,6 @@ Limits ChartWidget::ChartAnimationController::finalHeightLimits() const { return _finalHeightLimits; } -float64 ChartWidget::ChartAnimationController::detailsProgress( - crl::time now, - const Limits &appearedOnXLimits) const { - const auto xLimitsChanged = false - || (appearedOnXLimits.min != _animationValueXMin.to()) - || (appearedOnXLimits.max != _animationValueXMax.to()); - return (_animation.animating() && xLimitsChanged) - ? std::clamp( - (now - _animation.started()) / float64(kExpandingDelay), - 0., - 1.) - : 0.; -} - bool ChartWidget::ChartAnimationController::animating() const { return _animation.animating(); } @@ -964,35 +950,6 @@ void ChartWidget::setupChartArea() { PaintHorizontalLines(p, horizontalLine, chartRect); } - const auto detailsAlpha = 1. - - _animationController.detailsProgress( - now, - _details.appearedOnXLimits); - - if (_details.widget) { - if (!detailsAlpha && _details.currentX) { - _details.widget->hide(); - _details.widget->setXIndex(-1); - _details.currentX = 0; - _details.appearedOnXLimits = {}; - } - if (_details.currentX) { - const auto lineRect = QRectF( - _details.currentX - (st::lineWidth / 2.), - 0, - st::lineWidth, - _chartArea->height()); - const auto opacity = ScopedPainterOpacity(p, detailsAlpha); - p.fillRect(lineRect, st::windowSubTextFg); - _details.widget->setAlpha(detailsAlpha); - for (const auto &line : _chartData.lines) { - _details.widget->setLineAlpha( - line.id, - _chartView->alpha(line.id)); - } - } - } - if (_chartData) { // p.setRenderHint( // QPainter::Antialiasing, @@ -1020,16 +977,22 @@ void ChartWidget::setupChartArea() { QRect(bottom.x(), bottom.y(), bottom.width(), st::lineWidth), st::windowSubTextFg); } - if (_details.widget && (detailsAlpha > 0.)) { - auto hq = PainterHighQualityEnabler(p); - auto o = ScopedPainterOpacity(p, detailsAlpha); + if (_details.widget) { + const auto detailsAlpha = _details.widget->alpha(); + + for (const auto &line : _chartData.lines) { + _details.widget->setLineAlpha( + line.id, + _chartView->alpha(line.id)); + } _chartView->paintSelectedXIndex( p, _chartData, _animationController.currentXLimits(), _animationController.currentHeightLimits(), chartRect, - _details.widget->xIndex()); + _details.widget->xIndex(), + detailsAlpha); } p.setPen(st::windowSubTextFg); @@ -1158,6 +1121,12 @@ void ChartWidget::setupFooter() { _footer->xPercentageLimitsChange( ) | rpl::start_with_next([=](Limits xPercentageLimits) { const auto now = crl::now(); + if (_details.widget + && (_details.widget->xIndex() >= 0) + && !_details.animation.animating()) { + _details.hideOnAnimationEnd = true; + _details.animation.start(); + } _animationController.setXPercentageLimits( _chartData, xPercentageLimits, @@ -1176,7 +1145,7 @@ void ChartWidget::setupFooter() { void ChartWidget::setupDetails() { if (!_chartData) { - _details = {}; + _details.widget = nullptr; _chartArea->update(); return; } @@ -1229,23 +1198,26 @@ void ChartWidget::setupDetails() { const auto nearestXIndex = std::distance( begin(_chartData.xPercentage), nearestXPercentageIt); - _details.currentX = 0 + const auto currentX = 0 + chartRect.width() * InterpolationRatio( currentXLimits.min, currentXLimits.max, *nearestXPercentageIt); - _details.appearedOnXLimits = currentXLimits; - const auto xLeft = _details.currentX + const auto xLeft = currentX - _details.widget->width(); const auto x = (xLeft >= 0) ? xLeft - : ((_details.currentX + : ((currentX + _details.widget->width() - _chartArea->width()) > 0) ? 0 - : _details.currentX; + : currentX; _details.widget->moveToLeft(x, _chartArea->y()); _details.widget->setXIndex(nearestXIndex); + if (_details.widget->isHidden()) { + _details.hideOnAnimationEnd = false; + _details.animation.start(); + } _details.widget->show(); _chartArea->update(); } break; @@ -1253,6 +1225,26 @@ void ChartWidget::setupDetails() { } break; } }, _details.widget->lifetime()); + + _details.animation.init([=](crl::time now) { + const auto value = std::clamp( + (now - _details.animation.started()) / float64(200), + 0., + 1.); + const auto alpha = _details.hideOnAnimationEnd ? (1. - value) : value; + _chartArea->update(); + if (_details.widget) { + _details.widget->setAlpha(alpha); + _details.widget->update(); + } + if (value >= 1.) { + if (_details.hideOnAnimationEnd && _details.widget) { + _details.widget->hide(); + _details.widget->setXIndex(-1); + } + _details.animation.stop(); + } + }); } void ChartWidget::setupFilterButtons() { diff --git a/Telegram/SourceFiles/statistics/chart_widget.h b/Telegram/SourceFiles/statistics/chart_widget.h index 4216a3e5a..323f67a4f 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.h +++ b/Telegram/SourceFiles/statistics/chart_widget.h @@ -75,9 +75,6 @@ private: [[nodiscard]] Limits currentHeightLimits() const; [[nodiscard]] Limits currentFooterHeightLimits() const; [[nodiscard]] Limits finalHeightLimits() const; - [[nodiscard]] float64 detailsProgress( - crl::time now, - const Limits &appearedOnXLimits) const; [[nodiscard]] bool animating() const; [[nodiscard]] bool footerAnimating() const; [[nodiscard]] bool isFPSSlow() const; @@ -146,8 +143,8 @@ private: struct { base::unique_qptr widget; - float64 currentX = 0; - Limits appearedOnXLimits; + Ui::Animations::Basic animation; + bool hideOnAnimationEnd = false; } _details; struct { diff --git a/Telegram/SourceFiles/statistics/point_details_widget.cpp b/Telegram/SourceFiles/statistics/point_details_widget.cpp index 0b412de86..fe2926ab1 100644 --- a/Telegram/SourceFiles/statistics/point_details_widget.cpp +++ b/Telegram/SourceFiles/statistics/point_details_widget.cpp @@ -143,6 +143,10 @@ void PointDetailsWidget::setAlpha(float64 alpha) { update(); } +float64 PointDetailsWidget::alpha() const { + return _alpha; +} + int PointDetailsWidget::lineYAt(int index) const { auto linesHeight = 0.; for (auto i = 0; i < index; i++) { @@ -186,7 +190,7 @@ void PointDetailsWidget::paintEvent(QPaintEvent *e) { .outerWidth = _textRect.width() - valueWidth, .availableWidth = _textRect.width(), }; - p.setOpacity(line.alpha * line.alpha); + p.setOpacity(line.alpha * line.alpha * _alpha); p.setPen(st::boxTextFg); line.name.draw(p, nameContext); p.setPen(line.valueColor); diff --git a/Telegram/SourceFiles/statistics/point_details_widget.h b/Telegram/SourceFiles/statistics/point_details_widget.h index fc27008ea..bfc1abfcd 100644 --- a/Telegram/SourceFiles/statistics/point_details_widget.h +++ b/Telegram/SourceFiles/statistics/point_details_widget.h @@ -23,6 +23,7 @@ public: [[nodiscard]] int xIndex() const; void setXIndex(int xIndex); void setAlpha(float64 alpha); + [[nodiscard]] float64 alpha() const; void setLineAlpha(int lineId, float64 alpha); protected: diff --git a/Telegram/SourceFiles/statistics/view/abstract_chart_view.h b/Telegram/SourceFiles/statistics/view/abstract_chart_view.h index bb06a19db..67cbda885 100644 --- a/Telegram/SourceFiles/statistics/view/abstract_chart_view.h +++ b/Telegram/SourceFiles/statistics/view/abstract_chart_view.h @@ -34,7 +34,8 @@ public: const Limits &xPercentageLimits, const Limits &heightLimits, const QRect &rect, - int selectedXIndex) = 0; + int selectedXIndex, + float64 progress) = 0; virtual void setEnabled(int id, bool enabled, crl::time now) = 0; [[nodiscard]] virtual bool isEnabled(int id) const = 0; diff --git a/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp b/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp index 4fc702231..9936f9219 100644 --- a/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/view/linear_chart_view.cpp @@ -135,16 +135,22 @@ void LinearChartView::paintSelectedXIndex( const Limits &xPercentageLimits, const Limits &heightLimits, const QRect &rect, - int selectedXIndex) { + int selectedXIndex, + float64 progress) { if (selectedXIndex < 0) { return; } + auto hq = PainterHighQualityEnabler(p); + auto o = ScopedPainterOpacity(p, progress); p.setBrush(st::boxBg); const auto r = st::statisticsDetailsDotRadius; const auto i = selectedXIndex; const auto isSameToken = (_selectedPoints.lastXIndex == selectedXIndex) && (_selectedPoints.lastHeightLimits.min == heightLimits.min) - && (_selectedPoints.lastHeightLimits.max == heightLimits.max); + && (_selectedPoints.lastHeightLimits.max == heightLimits.max) + && (_selectedPoints.lastXLimits.min == xPercentageLimits.min) + && (_selectedPoints.lastXLimits.max == xPercentageLimits.max); + auto linePainted = false; for (const auto &line : chartData.lines) { const auto lineAlpha = alpha(line.id); const auto useCache = isSameToken @@ -161,6 +167,18 @@ void LinearChartView::paintSelectedXIndex( + rect.topLeft(); } + if (!linePainted) { + const auto lineRect = QRectF( + rect.x() + + begin(_selectedPoints.points)->second.x() + - (st::lineWidth / 2.), + rect.y(), + st::lineWidth, + rect.height()); + p.fillRect(lineRect, st::windowSubTextFg); + linePainted = true; + } + // Paint. auto o = ScopedPainterOpacity(p, lineAlpha * p.opacity()); p.setPen(QPen(line.color, st::statisticsChartLineWidth)); @@ -168,6 +186,7 @@ void LinearChartView::paintSelectedXIndex( } _selectedPoints.lastXIndex = selectedXIndex; _selectedPoints.lastHeightLimits = heightLimits; + _selectedPoints.lastXLimits = xPercentageLimits; } void LinearChartView::setEnabled(int id, bool enabled, crl::time now) { diff --git a/Telegram/SourceFiles/statistics/view/linear_chart_view.h b/Telegram/SourceFiles/statistics/view/linear_chart_view.h index 9ff75de2f..74b69d653 100644 --- a/Telegram/SourceFiles/statistics/view/linear_chart_view.h +++ b/Telegram/SourceFiles/statistics/view/linear_chart_view.h @@ -38,7 +38,8 @@ public: const Limits &xPercentageLimits, const Limits &heightLimits, const QRect &rect, - int selectedXIndex) override; + int selectedXIndex, + float64 progress) override; void setEnabled(int id, bool enabled, crl::time now) override; [[nodiscard]] bool isEnabled(int id) const override; @@ -97,6 +98,7 @@ private: struct SelectedPoints final { int lastXIndex = -1; Limits lastHeightLimits; + Limits lastXLimits; base::flat_map points; }; SelectedPoints _selectedPoints; diff --git a/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp b/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp index 43a811275..65200bfbf 100644 --- a/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp @@ -94,7 +94,8 @@ void StackChartView::paintSelectedXIndex( const Limits &xPercentageLimits, const Limits &heightLimits, const QRect &rect, - int selectedXIndex) { + int selectedXIndex, + float64 progress) { } void StackChartView::setEnabled(int id, bool enabled, crl::time now) { diff --git a/Telegram/SourceFiles/statistics/view/stack_chart_view.h b/Telegram/SourceFiles/statistics/view/stack_chart_view.h index 33bd5f7a2..95eb55876 100644 --- a/Telegram/SourceFiles/statistics/view/stack_chart_view.h +++ b/Telegram/SourceFiles/statistics/view/stack_chart_view.h @@ -39,7 +39,8 @@ public: const Limits &xPercentageLimits, const Limits &heightLimits, const QRect &rect, - int selectedXIndex) override; + int selectedXIndex, + float64 progress) override; void setEnabled(int id, bool enabled, crl::time now) override; [[nodiscard]] bool isEnabled(int id) const override;