mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added support of percentages display to details widget.
This commit is contained in:
parent
515850ec9b
commit
fc3acff5d6
9 changed files with 187 additions and 80 deletions
|
@ -63,6 +63,7 @@ struct StatisticalChart {
|
||||||
float64 timeStep = 0.;
|
float64 timeStep = 0.;
|
||||||
|
|
||||||
bool isFooterHidden = false;
|
bool isFooterHidden = false;
|
||||||
|
bool hasPercentages = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "statistics/point_details_widget.h"
|
#include "statistics/point_details_widget.h"
|
||||||
|
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
|
#include "statistics/statistics_common.h"
|
||||||
|
#include "statistics/view/stack_linear_chart_common.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
|
@ -155,6 +157,16 @@ PointDetailsWidget::PointDetailsWidget(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_maxPercentageWidth = [&] {
|
||||||
|
if (_chartData.hasPercentages) {
|
||||||
|
const auto maxPercentageText = Ui::Text::String(
|
||||||
|
_textStyle,
|
||||||
|
u"10000%"_q);
|
||||||
|
return maxPercentageText.maxWidth();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}();
|
||||||
|
|
||||||
const auto calculatedWidth = [&]{
|
const auto calculatedWidth = [&]{
|
||||||
const auto maxValueText = Ui::Text::String(
|
const auto maxValueText = Ui::Text::String(
|
||||||
_textStyle,
|
_textStyle,
|
||||||
|
@ -186,7 +198,8 @@ PointDetailsWidget::PointDetailsWidget(
|
||||||
+ rect::m::sum::h(st::statisticsDetailsPopupMargins)
|
+ rect::m::sum::h(st::statisticsDetailsPopupMargins)
|
||||||
+ rect::m::sum::h(st::statisticsDetailsPopupPadding)
|
+ rect::m::sum::h(st::statisticsDetailsPopupPadding)
|
||||||
+ st::statisticsDetailsPopupPadding.left() // Between strings.
|
+ st::statisticsDetailsPopupPadding.left() // Between strings.
|
||||||
+ maxNameTextWidth;
|
+ maxNameTextWidth
|
||||||
|
+ _maxPercentageWidth;
|
||||||
}();
|
}();
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::start_with_next([=](const QSize &s) {
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
|
@ -245,9 +258,19 @@ void PointDetailsWidget::setXIndex(int xIndex) {
|
||||||
_lines.clear();
|
_lines.clear();
|
||||||
_lines.reserve(_chartData.lines.size());
|
_lines.reserve(_chartData.lines.size());
|
||||||
auto hasPositiveValues = false;
|
auto hasPositiveValues = false;
|
||||||
for (const auto &dataLine : _chartData.lines) {
|
const auto parts = _maxPercentageWidth
|
||||||
|
? PiePartsPercentage(
|
||||||
|
_chartData,
|
||||||
|
nullptr,
|
||||||
|
{ float64(xIndex), float64(xIndex) }).parts
|
||||||
|
: std::vector<PiePartData::Part>();
|
||||||
|
for (auto i = 0; i < _chartData.lines.size(); i++) {
|
||||||
|
const auto &dataLine = _chartData.lines[i];
|
||||||
auto textLine = Line();
|
auto textLine = Line();
|
||||||
textLine.id = dataLine.id;
|
textLine.id = dataLine.id;
|
||||||
|
if (_maxPercentageWidth) {
|
||||||
|
textLine.percentage.setText(_textStyle, parts[i].percentageText);
|
||||||
|
}
|
||||||
textLine.name.setText(_textStyle, dataLine.name);
|
textLine.name.setText(_textStyle, dataLine.name);
|
||||||
textLine.value.setText(
|
textLine.value.setText(
|
||||||
_textStyle,
|
_textStyle,
|
||||||
|
@ -353,12 +376,22 @@ void PointDetailsWidget::paintEvent(QPaintEvent *e) {
|
||||||
.availableWidth = valueWidth,
|
.availableWidth = valueWidth,
|
||||||
};
|
};
|
||||||
const auto nameContext = Ui::Text::PaintContext{
|
const auto nameContext = Ui::Text::PaintContext{
|
||||||
.position = QPoint(_textRect.x(), lineY),
|
.position = QPoint(
|
||||||
|
_textRect.x() + _maxPercentageWidth,
|
||||||
|
lineY),
|
||||||
.outerWidth = _textRect.width(),
|
.outerWidth = _textRect.width(),
|
||||||
.availableWidth = _textRect.width() - valueWidth,
|
.availableWidth = _textRect.width() - valueWidth,
|
||||||
};
|
};
|
||||||
p.setOpacity(line.alpha * line.alpha);
|
p.setOpacity(line.alpha * line.alpha);
|
||||||
p.setPen(st::boxTextFg);
|
p.setPen(st::boxTextFg);
|
||||||
|
if (_maxPercentageWidth) {
|
||||||
|
const auto percentageContext = Ui::Text::PaintContext{
|
||||||
|
.position = QPoint(_textRect.x(), lineY),
|
||||||
|
.outerWidth = _textRect.width(),
|
||||||
|
.availableWidth = _textRect.width() - valueWidth,
|
||||||
|
};
|
||||||
|
line.percentage.draw(p, percentageContext);
|
||||||
|
}
|
||||||
line.name.draw(p, nameContext);
|
line.name.draw(p, nameContext);
|
||||||
p.setPen(line.valueColor);
|
p.setPen(line.valueColor);
|
||||||
line.value.draw(p, valueContext);
|
line.value.draw(p, valueContext);
|
||||||
|
|
|
@ -60,10 +60,13 @@ private:
|
||||||
int id = 0;
|
int id = 0;
|
||||||
Ui::Text::String name;
|
Ui::Text::String name;
|
||||||
Ui::Text::String value;
|
Ui::Text::String value;
|
||||||
|
Ui::Text::String percentage;
|
||||||
QColor valueColor;
|
QColor valueColor;
|
||||||
float64 alpha = 1.;
|
float64 alpha = 1.;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int _maxPercentageWidth = 0;
|
||||||
|
|
||||||
QRect _innerRect;
|
QRect _innerRect;
|
||||||
QRect _textRect;
|
QRect _textRect;
|
||||||
QImage _arrow;
|
QImage _arrow;
|
||||||
|
|
|
@ -109,6 +109,15 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) {
|
||||||
result.defaultZoomXIndex.min = std::min(min, max);
|
result.defaultZoomXIndex.min = std::min(min, max);
|
||||||
result.defaultZoomXIndex.max = std::max(min, max);
|
result.defaultZoomXIndex.max = std::max(min, max);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto percentageShowIt = root.constFind(u"percentage"_q);
|
||||||
|
if (percentageShowIt != root.constEnd()) {
|
||||||
|
if (percentageShowIt->isBool()) {
|
||||||
|
result.hasPercentages = (percentageShowIt->toBool());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const auto colors = root.value(u"colors"_q).toObject();
|
const auto colors = root.value(u"colors"_q).toObject();
|
||||||
const auto names = root.value(u"names"_q).toObject();
|
const auto names = root.value(u"names"_q).toObject();
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
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_linear_chart_common.h"
|
||||||
|
|
||||||
|
#include "data/data_statistics_chart.h"
|
||||||
|
#include "statistics/chart_lines_filter_controller.h"
|
||||||
|
#include "statistics/statistics_common.h"
|
||||||
|
|
||||||
|
namespace Statistic {
|
||||||
|
|
||||||
|
PiePartData PiePartsPercentage(
|
||||||
|
const Data::StatisticalChart &chartData,
|
||||||
|
const std::shared_ptr<LinesFilterController> &linesFilter,
|
||||||
|
const Limits &xIndices) {
|
||||||
|
auto result = PiePartData();
|
||||||
|
result.parts.reserve(chartData.lines.size());
|
||||||
|
auto sums = std::vector<float64>();
|
||||||
|
sums.reserve(chartData.lines.size());
|
||||||
|
auto totalSum = 0.;
|
||||||
|
for (const auto &line : chartData.lines) {
|
||||||
|
auto sum = 0;
|
||||||
|
for (auto i = xIndices.min; i <= xIndices.max; i++) {
|
||||||
|
sum += line.y[i];
|
||||||
|
}
|
||||||
|
if (linesFilter) {
|
||||||
|
sum *= linesFilter->alpha(line.id);
|
||||||
|
}
|
||||||
|
totalSum += sum;
|
||||||
|
sums.push_back(sum);
|
||||||
|
}
|
||||||
|
auto stackedPercentage = 0.;
|
||||||
|
|
||||||
|
auto sumPercDiffs = 0.;
|
||||||
|
auto maxPercDiff = 0.;
|
||||||
|
auto minPercDiff = 0.;
|
||||||
|
auto maxPercDiffIndex = int(-1);
|
||||||
|
auto minPercDiffIndex = int(-1);
|
||||||
|
auto roundedPercentagesSum = 0.;
|
||||||
|
|
||||||
|
result.pieHasSinglePart = false;
|
||||||
|
constexpr auto kPerChar = '%';
|
||||||
|
for (auto k = 0; k < sums.size(); k++) {
|
||||||
|
const auto rawPercentage = totalSum ? (sums[k] / totalSum) : 0.;
|
||||||
|
const auto rounded = 0.01 * std::round(rawPercentage * 100.);
|
||||||
|
roundedPercentagesSum += rounded;
|
||||||
|
const auto diff = rawPercentage - rounded;
|
||||||
|
sumPercDiffs += diff;
|
||||||
|
const auto diffAbs = std::abs(diff);
|
||||||
|
if (maxPercDiff < diffAbs) {
|
||||||
|
maxPercDiff = diffAbs;
|
||||||
|
maxPercDiffIndex = k;
|
||||||
|
}
|
||||||
|
if (minPercDiff < diffAbs) {
|
||||||
|
minPercDiff = diffAbs;
|
||||||
|
minPercDiffIndex = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
stackedPercentage += rounded;
|
||||||
|
result.parts.push_back({
|
||||||
|
rounded,
|
||||||
|
stackedPercentage * 360. - 180.,
|
||||||
|
QString::number(int(rounded * 100)) + kPerChar,
|
||||||
|
});
|
||||||
|
result.pieHasSinglePart |= (rounded == 1.);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto index = (roundedPercentagesSum > 1.)
|
||||||
|
? maxPercDiffIndex
|
||||||
|
: minPercDiffIndex;
|
||||||
|
if (index >= 0) {
|
||||||
|
result.parts[index].roundedPercentage += sumPercDiffs;
|
||||||
|
result.parts[index].percentageText = QString::number(
|
||||||
|
int(result.parts[index].roundedPercentage * 100)) + kPerChar;
|
||||||
|
const auto angleShrink = (sumPercDiffs) * 360.;
|
||||||
|
for (auto &part : result.parts) {
|
||||||
|
part.stackedAngle += angleShrink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Statistic
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
class LinesFilterController;
|
||||||
|
|
||||||
|
struct PiePartData final {
|
||||||
|
struct Part final {
|
||||||
|
float64 roundedPercentage = 0; // 0.XX.
|
||||||
|
float64 stackedAngle = 0.;
|
||||||
|
QString percentageText;
|
||||||
|
};
|
||||||
|
std::vector<Part> parts;
|
||||||
|
bool pieHasSinglePart = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] PiePartData PiePartsPercentage(
|
||||||
|
const Data::StatisticalChart &chartData,
|
||||||
|
const std::shared_ptr<LinesFilterController> &linesFilter,
|
||||||
|
const Limits &xIndices);
|
||||||
|
|
||||||
|
} // namespace Statistic
|
|
@ -166,7 +166,8 @@ void StackLinearChartView::prepareZoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackLinearChartView::applyParts(const std::vector<PiePartData> &parts) {
|
void StackLinearChartView::applyParts(
|
||||||
|
const std::vector<PiePartData::Part> &parts) {
|
||||||
for (auto k = 0; k < parts.size(); k++) {
|
for (auto k = 0; k < parts.size(); k++) {
|
||||||
_transition.lines[k].angle = parts[k].stackedAngle;
|
_transition.lines[k].angle = parts[k].stackedAngle;
|
||||||
}
|
}
|
||||||
|
@ -184,72 +185,12 @@ void StackLinearChartView::saveZoomRange(const PaintContext &c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackLinearChartView::savePieTextParts(const PaintContext &c) {
|
void StackLinearChartView::savePieTextParts(const PaintContext &c) {
|
||||||
_transition.textParts = partsPercentage(
|
auto data = PiePartsPercentage(
|
||||||
c.chartData,
|
c.chartData,
|
||||||
|
linesFilterController(),
|
||||||
_transition.zoomedInRangeXIndices);
|
_transition.zoomedInRangeXIndices);
|
||||||
}
|
_transition.textParts = std::move(data.parts);
|
||||||
|
_pieHasSinglePart = data.pieHasSinglePart;
|
||||||
auto StackLinearChartView::partsPercentage(
|
|
||||||
const Data::StatisticalChart &chartData,
|
|
||||||
const Limits &xIndices) -> std::vector<PiePartData> {
|
|
||||||
auto result = std::vector<PiePartData>();
|
|
||||||
result.reserve(chartData.lines.size());
|
|
||||||
auto sums = std::vector<float64>();
|
|
||||||
sums.reserve(chartData.lines.size());
|
|
||||||
auto totalSum = 0.;
|
|
||||||
const auto &linesFilter = linesFilterController();
|
|
||||||
for (const auto &line : chartData.lines) {
|
|
||||||
auto sum = 0;
|
|
||||||
for (auto i = xIndices.min; i <= xIndices.max; i++) {
|
|
||||||
sum += line.y[i];
|
|
||||||
}
|
|
||||||
sum *= linesFilter->alpha(line.id);
|
|
||||||
totalSum += sum;
|
|
||||||
sums.push_back(sum);
|
|
||||||
}
|
|
||||||
auto stackedPercentage = 0.;
|
|
||||||
|
|
||||||
auto sumPercDiffs = 0.;
|
|
||||||
auto maxPercDiff = 0.;
|
|
||||||
auto minPercDiff = 0.;
|
|
||||||
auto maxPercDiffIndex = int(-1);
|
|
||||||
auto minPercDiffIndex = int(-1);
|
|
||||||
auto roundedPercentagesSum = 0.;
|
|
||||||
|
|
||||||
_pieHasSinglePart = false;
|
|
||||||
for (auto k = 0; k < sums.size(); k++) {
|
|
||||||
const auto rawPercentage = totalSum ? (sums[k] / totalSum) : 0.;
|
|
||||||
const auto rounded = 0.01 * std::round(rawPercentage * 100.);
|
|
||||||
roundedPercentagesSum += rounded;
|
|
||||||
const auto diff = rawPercentage - rounded;
|
|
||||||
sumPercDiffs += diff;
|
|
||||||
const auto diffAbs = std::abs(diff);
|
|
||||||
if (maxPercDiff < diffAbs) {
|
|
||||||
maxPercDiff = diffAbs;
|
|
||||||
maxPercDiffIndex = k;
|
|
||||||
}
|
|
||||||
if (minPercDiff < diffAbs) {
|
|
||||||
minPercDiff = diffAbs;
|
|
||||||
minPercDiffIndex = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
stackedPercentage += rounded;
|
|
||||||
result.push_back({ rounded, stackedPercentage * 360. - 180. });
|
|
||||||
_pieHasSinglePart |= (rounded == 1.);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const auto index = (roundedPercentagesSum > 1.)
|
|
||||||
? maxPercDiffIndex
|
|
||||||
: minPercDiffIndex;
|
|
||||||
if (index >= 0) {
|
|
||||||
result[index].roundedPercentage += sumPercDiffs;
|
|
||||||
const auto angleShrink = (sumPercDiffs) * 360.;
|
|
||||||
for (auto i = index; i < result.size(); i++) {
|
|
||||||
result[i].stackedAngle += angleShrink;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackLinearChartView::paintChartOrZoomAnimation(
|
void StackLinearChartView::paintChartOrZoomAnimation(
|
||||||
|
@ -564,9 +505,12 @@ void StackLinearChartView::paintZoomed(QPainter &p, const PaintContext &c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
saveZoomRange(c);
|
saveZoomRange(c);
|
||||||
const auto parts = partsPercentage(
|
const auto partsData = PiePartsPercentage(
|
||||||
c.chartData,
|
c.chartData,
|
||||||
|
linesFilterController(),
|
||||||
_transition.zoomedInRangeXIndices);
|
_transition.zoomedInRangeXIndices);
|
||||||
|
_pieHasSinglePart = partsData.pieHasSinglePart;
|
||||||
|
const auto &parts = partsData.parts;
|
||||||
applyParts(parts);
|
applyParts(parts);
|
||||||
|
|
||||||
p.fillRect(c.rect + QMargins(0, 0, 0, st::lineWidth), st::boxBg);
|
p.fillRect(c.rect + QMargins(0, 0, 0, st::lineWidth), st::boxBg);
|
||||||
|
@ -726,7 +670,7 @@ void StackLinearChartView::paintPieText(QPainter &p, const PaintContext &c) {
|
||||||
const auto scale = (maxScale == minScale)
|
const auto scale = (maxScale == minScale)
|
||||||
? 0.
|
? 0.
|
||||||
: (minScale) + percentage * (maxScale - minScale);
|
: (minScale) + percentage * (maxScale - minScale);
|
||||||
const auto text = QString::number(int(percentage * 100)) + u"%"_q;
|
const auto text = parts[k].percentageText;
|
||||||
const auto textW = font->width(text);
|
const auto textW = font->width(text);
|
||||||
const auto textH = font->height;
|
const auto textH = font->height;
|
||||||
const auto textXShift = textW / 2.;
|
const auto textXShift = textW / 2.;
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "statistics/segment_tree.h"
|
#include "statistics/segment_tree.h"
|
||||||
#include "statistics/statistics_common.h"
|
#include "statistics/statistics_common.h"
|
||||||
#include "statistics/view/abstract_chart_view.h"
|
#include "statistics/view/abstract_chart_view.h"
|
||||||
|
#include "statistics/view/stack_linear_chart_common.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/effects/animation_value.h"
|
#include "ui/effects/animation_value.h"
|
||||||
|
|
||||||
|
@ -65,19 +66,11 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] bool skipSelectedTranslation() const;
|
[[nodiscard]] bool skipSelectedTranslation() const;
|
||||||
|
|
||||||
struct PiePartData {
|
|
||||||
float64 roundedPercentage = 0; // 0.XX.
|
|
||||||
float64 stackedAngle = 0.;
|
|
||||||
};
|
|
||||||
|
|
||||||
void prepareZoom(const PaintContext &c, TransitionStep step);
|
void prepareZoom(const PaintContext &c, TransitionStep step);
|
||||||
|
|
||||||
void saveZoomRange(const PaintContext &c);
|
void saveZoomRange(const PaintContext &c);
|
||||||
void savePieTextParts(const PaintContext &c);
|
void savePieTextParts(const PaintContext &c);
|
||||||
void applyParts(const std::vector<PiePartData> &parts);
|
void applyParts(const std::vector<PiePartData::Part> &parts);
|
||||||
[[nodiscard]] std::vector<PiePartData> partsPercentage(
|
|
||||||
const Data::StatisticalChart &chartData,
|
|
||||||
const Limits &xIndices);
|
|
||||||
|
|
||||||
struct SelectedPoints final {
|
struct SelectedPoints final {
|
||||||
int lastXIndex = -1;
|
int lastXIndex = -1;
|
||||||
|
@ -107,7 +100,7 @@ private:
|
||||||
Limits zoomedInRange;
|
Limits zoomedInRange;
|
||||||
Limits zoomedInRangeXIndices;
|
Limits zoomedInRangeXIndices;
|
||||||
|
|
||||||
std::vector<PiePartData> textParts;
|
std::vector<PiePartData::Part> textParts;
|
||||||
} _transition;
|
} _transition;
|
||||||
|
|
||||||
std::vector<bool> _skipPoints;
|
std::vector<bool> _skipPoints;
|
||||||
|
|
|
@ -189,6 +189,8 @@ PRIVATE
|
||||||
statistics/view/stack_chart_common.h
|
statistics/view/stack_chart_common.h
|
||||||
statistics/view/stack_chart_view.cpp
|
statistics/view/stack_chart_view.cpp
|
||||||
statistics/view/stack_chart_view.h
|
statistics/view/stack_chart_view.h
|
||||||
|
statistics/view/stack_linear_chart_common.cpp
|
||||||
|
statistics/view/stack_linear_chart_common.h
|
||||||
statistics/view/stack_linear_chart_view.cpp
|
statistics/view/stack_linear_chart_view.cpp
|
||||||
statistics/view/stack_linear_chart_view.h
|
statistics/view/stack_linear_chart_view.h
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue