diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 3e43ec10a..19202d5c3 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1302,6 +1302,8 @@ PRIVATE statistics/view/chart_view_factory.h statistics/view/linear_chart_view.cpp statistics/view/linear_chart_view.h + statistics/view/stack_chart_common.cpp + statistics/view/stack_chart_common.h statistics/view/stack_chart_view.cpp statistics/view/stack_chart_view.h statistics/view/stack_linear_chart_view.cpp diff --git a/Telegram/SourceFiles/statistics/view/stack_chart_common.cpp b/Telegram/SourceFiles/statistics/view/stack_chart_common.cpp new file mode 100644 index 000000000..38e93a2b6 --- /dev/null +++ b/Telegram/SourceFiles/statistics/view/stack_chart_common.cpp @@ -0,0 +1,33 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "statistics/view/stack_chart_common.h" + +#include "data/data_statistics.h" +#include "statistics/statistics_common.h" + +namespace Statistic { + +LeftStartAndStep ComputeLeftStartAndStep( + const Data::StatisticalChart &chartData, + const Limits &xPercentageLimits, + const QRect &rect, + float64 xIndexStart) { + const auto fullWidth = rect.width() + / (xPercentageLimits.max - xPercentageLimits.min); + const auto offset = fullWidth * xPercentageLimits.min; + const auto p = (chartData.xPercentage.size() < 2) + ? 1. + : chartData.xPercentage[1] * fullWidth; + const auto w = chartData.xPercentage[1] * (fullWidth - p); + const auto leftStart = rect.x() + + chartData.xPercentage[xIndexStart] * (fullWidth - p) + - offset; + return { leftStart, w }; +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/view/stack_chart_common.h b/Telegram/SourceFiles/statistics/view/stack_chart_common.h new file mode 100644 index 000000000..fde77b85a --- /dev/null +++ b/Telegram/SourceFiles/statistics/view/stack_chart_common.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Data { +struct StatisticalChart; +} // namespace Data + +namespace Statistic { + +struct Limits; + +struct LeftStartAndStep final { + float64 start = 0.; + float64 step = 0.; +}; + +[[nodiscard]] LeftStartAndStep ComputeLeftStartAndStep( + const Data::StatisticalChart &chartData, + const Limits &xPercentageLimits, + const QRect &rect, + float64 xIndexStart); + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp b/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp index 96da19619..317e10639 100644 --- a/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/view/stack_chart_view.cpp @@ -7,8 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "statistics/view/stack_chart_view.h" -#include "ui/effects/animation_value_f.h" #include "data/data_statistics.h" +#include "statistics/view/stack_chart_common.h" +#include "ui/effects/animation_value_f.h" #include "ui/painter.h" namespace Statistic { @@ -16,29 +17,6 @@ namespace { constexpr auto kAlphaDuration = float64(200); -struct LeftStartAndStep final { - float64 start = 0.; - float64 step = 0.; -}; - -[[nodiscard]] LeftStartAndStep ComputeLeftStartAndStep( - const Data::StatisticalChart &chartData, - const Limits &xPercentageLimits, - const QRect &rect, - float64 xIndexStart) { - const auto fullWidth = rect.width() - / (xPercentageLimits.max - xPercentageLimits.min); - const auto offset = fullWidth * xPercentageLimits.min; - const auto p = (chartData.xPercentage.size() < 2) - ? 1. - : chartData.xPercentage[1] * fullWidth; - const auto w = chartData.xPercentage[1] * (fullWidth - p); - const auto leftStart = rect.x() - + chartData.xPercentage[xIndexStart] * (fullWidth - p) - - offset; - return { leftStart, w }; -} - } // namespace StackChartView::StackChartView() = default; diff --git a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp index bafc42314..a6a9d861a 100644 --- a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_statistics.h" #include "statistics/point_details_widget.h" +#include "statistics/view/stack_chart_common.h" #include "ui/effects/animation_value_f.h" #include "ui/painter.h" #include "ui/rect.h" @@ -151,8 +152,12 @@ void StackLinearChartView::paint( rect, footer }; - if (_transitionProgress == 1) { - return paintZoomed(p, context); + if (_transitionProgress == 1.) { + if (footer) { + return paintZoomedFooter(p, context); + } else { + return paintZoomed(p, context); + } } const auto &[localStart, localEnd] = _lastPaintedXIndices; _skipPoints = std::vector(chartData.lines.size(), false); @@ -389,17 +394,18 @@ void StackLinearChartView::paint( p.setClipPath(ovalPath); } + const auto opacity = footer ? (1. - _transitionProgress) : 1.; for (auto k = int(chartData.lines.size() - 1); k >= 0; k--) { if (paths[k].isEmpty()) { continue; } const auto &line = chartData.lines[k]; - p.setOpacity(alpha(line.id)); + p.setOpacity(alpha(line.id) * opacity); p.setPen(Qt::NoPen); p.fillPath(paths[k], line.color); } - p.setOpacity(1.); - { + p.setOpacity(opacity); + if (!footer) { constexpr auto kAlphaTextPart = 0.6; const auto progress = std::clamp( (_transitionProgress - kAlphaTextPart) / (1. - kAlphaTextPart), @@ -409,6 +415,8 @@ void StackLinearChartView::paint( auto o = ScopedPainterOpacity(p, progress); paintPieText(p, context); } + } else { + paintZoomedFooter(p, context); } // Fix ugly outline. @@ -423,6 +431,7 @@ void StackLinearChartView::paintZoomed(QPainter &p, const PaintContext &c) { if (c.footer) { return; } + p.fillRect(c.rect, st::boxBg); const auto center = QPointF(c.rect.center()); const auto side = (c.rect.width() / 2.) * kCircleSizeRatio; const auto rectF = QRectF( @@ -472,6 +481,55 @@ void StackLinearChartView::paintZoomed(QPainter &p, const PaintContext &c) { } } +void StackLinearChartView::paintZoomedFooter( + QPainter &p, + const PaintContext &c) { + if (!c.footer) { + return; + } + auto o = ScopedPainterOpacity(p, _transitionProgress); + auto hq = PainterHighQualityEnabler(p); + const auto &[localStart, localEnd] = _lastPaintedXIndices; + const auto &[leftStart, w] = ComputeLeftStartAndStep( + c.chartData, + c.xPercentageLimits, + c.rect, + localStart); + for (auto i = localStart; i <= localEnd; i++) { + auto sum = 0.; + auto lastEnabledId = int(0); + for (const auto &line : c.chartData.lines) { + if (!isEnabled(line.id)) { + continue; + } + sum += line.y[i] * alpha(line.id); + lastEnabledId = line.id; + } + + auto stack = 0.; + for (auto k = int(c.chartData.lines.size() - 1); k >= 0; k--) { + const auto &line = c.chartData.lines[k]; + if (!isEnabled(line.id)) { + continue; + } + const auto visibleHeight = c.rect.height() * (line.y[i] / sum); + const auto height = (line.id == lastEnabledId) + ? c.rect.height() + : visibleHeight; + + const auto column = QRectF( + leftStart + (i - localStart) * w, + stack, + w, + height); + + p.setPen(Qt::NoPen); + p.fillRect(column, line.color); + stack += visibleHeight; + } + } +} + void StackLinearChartView::paintPieText(QPainter &p, const PaintContext &c) { const auto center = QPointF(c.rect.center()); const auto side = (c.rect.width() / 2.) * kCircleSizeRatio; diff --git a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.h b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.h index 92f5b8487..efcf04e70 100644 --- a/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.h +++ b/Telegram/SourceFiles/statistics/view/stack_linear_chart_view.h @@ -86,6 +86,7 @@ private: bool footer); void paintZoomed(QPainter &p, const PaintContext &context); + void paintZoomedFooter(QPainter &p, const PaintContext &context); void paintPieText(QPainter &p, const PaintContext &context); [[nodiscard]] bool skipSelectedTranslation() const;