2023-07-06 11:09:16 +00:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
2023-10-11 19:42:23 +00:00
|
|
|
#include "statistics/widgets/point_details_widget.h"
|
2023-07-06 11:09:16 +00:00
|
|
|
|
|
|
|
#include "ui/cached_round_corners.h"
|
2023-10-05 15:45:48 +00:00
|
|
|
#include "statistics/statistics_common.h"
|
|
|
|
#include "statistics/view/stack_linear_chart_common.h"
|
2023-09-08 12:08:13 +00:00
|
|
|
#include "ui/effects/ripple_animation.h"
|
|
|
|
#include "ui/painter.h"
|
2023-07-06 11:09:16 +00:00
|
|
|
#include "ui/rect.h"
|
|
|
|
#include "styles/style_layers.h"
|
|
|
|
#include "styles/style_statistics.h"
|
|
|
|
|
2023-09-29 13:41:18 +00:00
|
|
|
#include <QtCore/QDateTime>
|
2023-10-13 06:04:29 +00:00
|
|
|
#include <QtCore/QLocale>
|
2023-09-29 13:41:18 +00:00
|
|
|
|
2023-07-06 11:09:16 +00:00
|
|
|
namespace Statistic {
|
2023-07-27 03:12:05 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
[[nodiscard]] QString FormatTimestamp(
|
|
|
|
float64 timestamp,
|
|
|
|
const QString &longFormat,
|
|
|
|
const QString &shortFormat) {
|
|
|
|
const auto dateTime = QDateTime::fromSecsSinceEpoch(timestamp / 1000);
|
|
|
|
if (dateTime.toUTC().time().hour() || dateTime.toUTC().time().minute()) {
|
|
|
|
return QLocale().toString(dateTime, longFormat);
|
|
|
|
} else {
|
|
|
|
return QLocale().toString(dateTime.date(), shortFormat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-11 04:03:49 +00:00
|
|
|
[[nodiscard]] QString FormatWeek(float64 timestamp) {
|
|
|
|
constexpr auto kSevenDays = 3600 * 24 * 7;
|
|
|
|
const auto leftFormatter = u"d MMM"_q;
|
|
|
|
const auto rightFormatter = u"d MMM yyyy"_q;
|
|
|
|
timestamp /= 1000;
|
|
|
|
return QLocale().toString(
|
|
|
|
QDateTime::fromSecsSinceEpoch(timestamp).date(),
|
|
|
|
leftFormatter)
|
|
|
|
+ ' '
|
|
|
|
+ QChar(8212)
|
|
|
|
+ ' '
|
|
|
|
+ QLocale().toString(
|
|
|
|
QDateTime::fromSecsSinceEpoch(timestamp + kSevenDays).date(),
|
|
|
|
rightFormatter);
|
|
|
|
}
|
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
void PaintShadow(QPainter &p, int radius, const QRect &r) {
|
|
|
|
constexpr auto kHorizontalOffset = 1;
|
|
|
|
constexpr auto kHorizontalOffset2 = 2;
|
|
|
|
constexpr auto kVerticalOffset = 2;
|
|
|
|
constexpr auto kVerticalOffset2 = 3;
|
|
|
|
constexpr auto kOpacityStep = 0.2;
|
|
|
|
constexpr auto kOpacityStep2 = 0.4;
|
|
|
|
const auto hOffset = style::ConvertScale(kHorizontalOffset);
|
|
|
|
const auto hOffset2 = style::ConvertScale(kHorizontalOffset2);
|
|
|
|
const auto vOffset = style::ConvertScale(kVerticalOffset);
|
|
|
|
const auto vOffset2 = style::ConvertScale(kVerticalOffset2);
|
|
|
|
const auto opacity = p.opacity();
|
|
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
|
|
|
|
|
|
p.setOpacity(opacity);
|
|
|
|
p.drawRoundedRect(r + QMarginsF(0, hOffset, 0, hOffset), radius, radius);
|
|
|
|
|
|
|
|
p.setOpacity(opacity * kOpacityStep);
|
|
|
|
p.drawRoundedRect(r + QMarginsF(hOffset, 0, hOffset, 0), radius, radius);
|
|
|
|
p.setOpacity(opacity * kOpacityStep2);
|
2023-10-12 13:54:24 +00:00
|
|
|
p.drawRoundedRect(
|
|
|
|
r + QMarginsF(hOffset2, 0, hOffset2, 0),
|
|
|
|
radius,
|
|
|
|
radius);
|
2023-10-03 02:19:41 +00:00
|
|
|
|
|
|
|
p.setOpacity(opacity * kOpacityStep);
|
|
|
|
p.drawRoundedRect(r + QMarginsF(0, 0, 0, vOffset), radius, radius);
|
|
|
|
p.setOpacity(opacity * kOpacityStep2);
|
|
|
|
p.drawRoundedRect(r + QMarginsF(0, 0, 0, vOffset2), radius, radius);
|
|
|
|
|
|
|
|
p.setOpacity(opacity);
|
|
|
|
}
|
|
|
|
|
2023-07-27 03:12:05 +00:00
|
|
|
} // namespace
|
2023-07-06 11:09:16 +00:00
|
|
|
|
2023-09-15 09:41:14 +00:00
|
|
|
void PaintDetails(
|
|
|
|
QPainter &p,
|
|
|
|
const Data::StatisticalChart::Line &line,
|
|
|
|
int absoluteValue,
|
|
|
|
const QRect &rect) {
|
|
|
|
auto name = Ui::Text::String(
|
|
|
|
st::statisticsDetailsPopupStyle,
|
|
|
|
line.name);
|
|
|
|
auto value = Ui::Text::String(
|
|
|
|
st::statisticsDetailsPopupStyle,
|
|
|
|
QString("%L1").arg(absoluteValue));
|
|
|
|
const auto nameWidth = name.maxWidth();
|
|
|
|
const auto valueWidth = value.maxWidth();
|
|
|
|
|
|
|
|
const auto width = valueWidth
|
|
|
|
+ rect::m::sum::h(st::statisticsDetailsPopupMargins)
|
|
|
|
+ rect::m::sum::h(st::statisticsDetailsPopupPadding)
|
|
|
|
+ st::statisticsDetailsPopupPadding.left() // Between strings.
|
|
|
|
+ nameWidth;
|
|
|
|
|
|
|
|
const auto height = st::statisticsDetailsPopupStyle.font->height
|
|
|
|
+ rect::m::sum::v(st::statisticsDetailsPopupMargins)
|
|
|
|
+ rect::m::sum::v(st::statisticsDetailsPopupPadding);
|
|
|
|
|
|
|
|
const auto fullRect = QRect(
|
|
|
|
rect.x() + rect.width() - width,
|
|
|
|
rect.y(),
|
|
|
|
width,
|
|
|
|
height);
|
|
|
|
|
|
|
|
const auto innerRect = fullRect - st::statisticsDetailsPopupPadding;
|
|
|
|
const auto textRect = innerRect - st::statisticsDetailsPopupMargins;
|
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
p.setBrush(st::shadowFg);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
PaintShadow(p, st::boxRadius, innerRect);
|
2023-09-15 09:41:14 +00:00
|
|
|
Ui::FillRoundRect(p, innerRect, st::boxBg, Ui::BoxCorners);
|
|
|
|
|
|
|
|
const auto lineY = textRect.y();
|
|
|
|
const auto valueContext = Ui::Text::PaintContext{
|
|
|
|
.position = QPoint(rect::right(textRect) - valueWidth, lineY),
|
2023-10-12 13:54:24 +00:00
|
|
|
.outerWidth = textRect.width(),
|
|
|
|
.availableWidth = valueWidth,
|
2023-09-15 09:41:14 +00:00
|
|
|
};
|
|
|
|
const auto nameContext = Ui::Text::PaintContext{
|
|
|
|
.position = QPoint(textRect.x(), lineY),
|
2023-10-12 13:54:24 +00:00
|
|
|
.outerWidth = textRect.width(),
|
|
|
|
.availableWidth = textRect.width() - valueWidth,
|
2023-09-15 09:41:14 +00:00
|
|
|
};
|
|
|
|
p.setPen(st::boxTextFg);
|
|
|
|
name.draw(p, nameContext);
|
|
|
|
p.setPen(line.color);
|
|
|
|
value.draw(p, valueContext);
|
|
|
|
}
|
|
|
|
|
2023-07-06 11:09:16 +00:00
|
|
|
PointDetailsWidget::PointDetailsWidget(
|
|
|
|
not_null<Ui::RpWidget*> parent,
|
2023-07-11 00:58:46 +00:00
|
|
|
const Data::StatisticalChart &chartData,
|
2023-07-27 04:05:33 +00:00
|
|
|
float64 maxAbsoluteValue,
|
|
|
|
bool zoomEnabled)
|
2023-10-03 02:19:41 +00:00
|
|
|
: Ui::AbstractButton(parent)
|
2023-07-27 04:05:33 +00:00
|
|
|
, _zoomEnabled(zoomEnabled)
|
2023-07-06 11:09:16 +00:00
|
|
|
, _chartData(chartData)
|
|
|
|
, _textStyle(st::statisticsDetailsPopupStyle)
|
2023-09-08 10:46:11 +00:00
|
|
|
, _headerStyle(st::statisticsDetailsPopupHeaderStyle)
|
2023-10-03 13:53:11 +00:00
|
|
|
, _longFormat(u"ddd, d MMM hh:mm"_q)
|
|
|
|
, _shortFormat(u"ddd, d MMM yyyy"_q) {
|
2023-09-08 12:08:13 +00:00
|
|
|
|
|
|
|
if (zoomEnabled) {
|
|
|
|
rpl::single(rpl::empty_value()) | rpl::then(
|
|
|
|
style::PaletteChanged()
|
|
|
|
) | rpl::start_with_next([=] {
|
|
|
|
const auto w = st::statisticsDetailsArrowShift;
|
|
|
|
const auto stroke = style::ConvertScaleExact(
|
|
|
|
st::statisticsDetailsArrowStroke);
|
|
|
|
_arrow = QImage(
|
|
|
|
QSize(w + stroke, w * 2 + stroke) * style::DevicePixelRatio(),
|
|
|
|
QImage::Format_ARGB32_Premultiplied);
|
2023-10-12 14:06:32 +00:00
|
|
|
_arrow.setDevicePixelRatio(style::DevicePixelRatio());
|
2023-09-08 12:08:13 +00:00
|
|
|
_arrow.fill(Qt::transparent);
|
|
|
|
{
|
|
|
|
auto p = QPainter(&_arrow);
|
|
|
|
|
|
|
|
const auto hq = PainterHighQualityEnabler(p);
|
|
|
|
const auto s = stroke / 2.;
|
|
|
|
|
|
|
|
p.setPen(QPen(st::windowSubTextFg, stroke));
|
|
|
|
p.drawLine(QLineF(s, s, w, w + s));
|
|
|
|
p.drawLine(QLineF(s, s + w * 2, w, w + s));
|
|
|
|
}
|
2023-10-03 02:19:41 +00:00
|
|
|
invalidateCache();
|
2023-09-08 12:08:13 +00:00
|
|
|
}, lifetime());
|
|
|
|
}
|
|
|
|
|
2023-10-05 15:45:48 +00:00
|
|
|
_maxPercentageWidth = [&] {
|
|
|
|
if (_chartData.hasPercentages) {
|
|
|
|
const auto maxPercentageText = Ui::Text::String(
|
|
|
|
_textStyle,
|
|
|
|
u"10000%"_q);
|
|
|
|
return maxPercentageText.maxWidth();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}();
|
|
|
|
|
2023-07-11 00:58:46 +00:00
|
|
|
const auto calculatedWidth = [&]{
|
|
|
|
const auto maxValueText = Ui::Text::String(
|
|
|
|
_textStyle,
|
|
|
|
QString("%L1").arg(maxAbsoluteValue));
|
|
|
|
const auto maxValueTextWidth = maxValueText.maxWidth();
|
|
|
|
|
|
|
|
auto maxNameTextWidth = 0;
|
|
|
|
for (const auto &dataLine : _chartData.lines) {
|
|
|
|
const auto maxNameText = Ui::Text::String(
|
|
|
|
_textStyle,
|
|
|
|
dataLine.name);
|
|
|
|
maxNameTextWidth = std::max(
|
|
|
|
maxNameText.maxWidth(),
|
|
|
|
maxNameTextWidth);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto maxHeaderText = Ui::Text::String(
|
|
|
|
_headerStyle,
|
2023-10-11 04:03:49 +00:00
|
|
|
_chartData.weekFormat
|
|
|
|
? FormatWeek(_chartData.x.front())
|
|
|
|
: FormatTimestamp(
|
|
|
|
_chartData.x.front(),
|
|
|
|
_longFormat,
|
|
|
|
_shortFormat));
|
2023-07-11 00:58:46 +00:00
|
|
|
maxNameTextWidth = std::max(
|
|
|
|
maxHeaderText.maxWidth()
|
|
|
|
+ st::statisticsDetailsPopupPadding.left(),
|
|
|
|
maxNameTextWidth);
|
|
|
|
}
|
|
|
|
return maxValueTextWidth
|
|
|
|
+ rect::m::sum::h(st::statisticsDetailsPopupMargins)
|
|
|
|
+ rect::m::sum::h(st::statisticsDetailsPopupPadding)
|
|
|
|
+ st::statisticsDetailsPopupPadding.left() // Between strings.
|
2023-10-05 15:45:48 +00:00
|
|
|
+ maxNameTextWidth
|
|
|
|
+ _maxPercentageWidth;
|
2023-07-11 00:58:46 +00:00
|
|
|
}();
|
2023-07-06 11:09:16 +00:00
|
|
|
sizeValue(
|
|
|
|
) | rpl::start_with_next([=](const QSize &s) {
|
|
|
|
const auto fullRect = s.isNull()
|
2023-07-11 00:58:46 +00:00
|
|
|
? Rect(Size(calculatedWidth))
|
2023-07-06 11:09:16 +00:00
|
|
|
: Rect(s);
|
|
|
|
_innerRect = fullRect - st::statisticsDetailsPopupPadding;
|
|
|
|
_textRect = _innerRect - st::statisticsDetailsPopupMargins;
|
2023-10-03 02:19:41 +00:00
|
|
|
invalidateCache();
|
2023-07-06 11:09:16 +00:00
|
|
|
}, lifetime());
|
|
|
|
|
2023-07-14 16:01:33 +00:00
|
|
|
resize(calculatedWidth, height());
|
|
|
|
resizeHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointDetailsWidget::setLineAlpha(int lineId, float64 alpha) {
|
|
|
|
for (auto &line : _lines) {
|
|
|
|
if (line.id == lineId) {
|
2023-10-03 02:19:41 +00:00
|
|
|
if (line.alpha != alpha) {
|
|
|
|
line.alpha = alpha;
|
|
|
|
resizeHeight();
|
|
|
|
invalidateCache();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
return;
|
2023-07-14 16:01:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointDetailsWidget::resizeHeight() {
|
2023-07-06 11:09:16 +00:00
|
|
|
resize(
|
2023-07-14 16:01:33 +00:00
|
|
|
width(),
|
|
|
|
lineYAt(_chartData.lines.size())
|
2023-07-06 11:09:16 +00:00
|
|
|
+ st::statisticsDetailsPopupMargins.bottom());
|
|
|
|
}
|
|
|
|
|
2023-07-07 08:28:50 +00:00
|
|
|
int PointDetailsWidget::xIndex() const {
|
|
|
|
return _xIndex;
|
|
|
|
}
|
|
|
|
|
2023-07-06 11:09:16 +00:00
|
|
|
void PointDetailsWidget::setXIndex(int xIndex) {
|
2023-07-07 08:28:50 +00:00
|
|
|
_xIndex = xIndex;
|
2023-07-07 11:41:30 +00:00
|
|
|
if (xIndex < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2023-10-03 13:53:11 +00:00
|
|
|
{
|
|
|
|
constexpr auto kOneDay = 3600 * 24 * 1000;
|
|
|
|
const auto timestamp = _chartData.x[xIndex];
|
|
|
|
_header.setText(
|
|
|
|
_headerStyle,
|
|
|
|
(timestamp < kOneDay)
|
|
|
|
? _chartData.getDayString(xIndex)
|
2023-10-11 04:03:49 +00:00
|
|
|
: _chartData.weekFormat
|
|
|
|
? FormatWeek(timestamp)
|
2023-10-03 13:53:11 +00:00
|
|
|
: FormatTimestamp(timestamp, _longFormat, _shortFormat));
|
|
|
|
}
|
2023-07-06 11:09:16 +00:00
|
|
|
|
|
|
|
_lines.clear();
|
|
|
|
_lines.reserve(_chartData.lines.size());
|
2023-07-27 04:05:33 +00:00
|
|
|
auto hasPositiveValues = false;
|
2023-10-05 15:45:48 +00:00
|
|
|
const auto parts = _maxPercentageWidth
|
2023-10-11 15:12:11 +00:00
|
|
|
? PiePartsPercentageByIndices(
|
2023-10-05 15:45:48 +00:00
|
|
|
_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];
|
2023-07-06 11:09:16 +00:00
|
|
|
auto textLine = Line();
|
2023-07-14 16:01:33 +00:00
|
|
|
textLine.id = dataLine.id;
|
2023-10-05 15:45:48 +00:00
|
|
|
if (_maxPercentageWidth) {
|
|
|
|
textLine.percentage.setText(_textStyle, parts[i].percentageText);
|
|
|
|
}
|
2023-07-06 11:09:16 +00:00
|
|
|
textLine.name.setText(_textStyle, dataLine.name);
|
|
|
|
textLine.value.setText(
|
|
|
|
_textStyle,
|
2023-07-11 00:58:46 +00:00
|
|
|
QString("%L1").arg(dataLine.y[xIndex]));
|
2023-07-27 04:05:33 +00:00
|
|
|
hasPositiveValues |= (dataLine.y[xIndex] > 0);
|
2023-07-06 11:09:16 +00:00
|
|
|
textLine.valueColor = QColor(dataLine.color);
|
|
|
|
_lines.push_back(std::move(textLine));
|
|
|
|
}
|
2023-07-27 04:05:33 +00:00
|
|
|
const auto clickable = _zoomEnabled && hasPositiveValues;
|
|
|
|
setAttribute(
|
|
|
|
Qt::WA_TransparentForMouseEvents,
|
|
|
|
!clickable);
|
2023-10-03 02:19:41 +00:00
|
|
|
invalidateCache();
|
2023-07-06 11:09:16 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 11:41:30 +00:00
|
|
|
void PointDetailsWidget::setAlpha(float64 alpha) {
|
|
|
|
_alpha = alpha;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2023-09-05 14:03:08 +00:00
|
|
|
float64 PointDetailsWidget::alpha() const {
|
|
|
|
return _alpha;
|
|
|
|
}
|
|
|
|
|
2023-07-14 16:01:33 +00:00
|
|
|
int PointDetailsWidget::lineYAt(int index) const {
|
|
|
|
auto linesHeight = 0.;
|
|
|
|
for (auto i = 0; i < index; i++) {
|
|
|
|
const auto alpha = (i >= _lines.size()) ? 1. : _lines[i].alpha;
|
|
|
|
linesHeight += alpha
|
|
|
|
* (_textStyle.font->height
|
|
|
|
+ st::statisticsDetailsPopupMidLineSpace);
|
|
|
|
}
|
|
|
|
|
2023-07-06 11:09:16 +00:00
|
|
|
return _textRect.y()
|
|
|
|
+ _headerStyle.font->height
|
2023-09-08 10:46:11 +00:00
|
|
|
+ st::statisticsDetailsPopupMargins.bottom() / 2
|
2023-07-14 16:01:33 +00:00
|
|
|
+ std::ceil(linesHeight);
|
2023-07-06 11:09:16 +00:00
|
|
|
}
|
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
void PointDetailsWidget::invalidateCache() {
|
|
|
|
_cache = QImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointDetailsWidget::mousePressEvent(QMouseEvent *e) {
|
|
|
|
AbstractButton::mousePressEvent(e);
|
|
|
|
const auto position = e->pos() - _innerRect.topLeft();
|
|
|
|
if (!_ripple) {
|
|
|
|
_ripple = std::make_unique<Ui::RippleAnimation>(
|
|
|
|
st::defaultRippleAnimation,
|
|
|
|
Ui::RippleAnimation::RoundRectMask(
|
|
|
|
_innerRect.size(),
|
|
|
|
st::boxRadius),
|
|
|
|
[=] { update(); });
|
|
|
|
}
|
|
|
|
_ripple->add(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointDetailsWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|
|
|
AbstractButton::mouseReleaseEvent(e);
|
|
|
|
if (_ripple) {
|
|
|
|
_ripple->lastStop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-06 11:09:16 +00:00
|
|
|
void PointDetailsWidget::paintEvent(QPaintEvent *e) {
|
2023-10-03 02:19:41 +00:00
|
|
|
auto painter = QPainter(this);
|
2023-07-06 11:09:16 +00:00
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
if (_cache.isNull()) {
|
|
|
|
_cache = QImage(
|
|
|
|
size() * style::DevicePixelRatio(),
|
|
|
|
QImage::Format_ARGB32_Premultiplied);
|
2023-10-12 14:06:32 +00:00
|
|
|
_cache.setDevicePixelRatio(style::DevicePixelRatio());
|
2023-10-03 02:19:41 +00:00
|
|
|
_cache.fill(Qt::transparent);
|
2023-07-07 11:41:30 +00:00
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
auto p = QPainter(&_cache);
|
2023-07-06 11:09:16 +00:00
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
p.setBrush(st::shadowFg);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
PaintShadow(p, st::boxRadius, _innerRect);
|
|
|
|
Ui::FillRoundRect(p, _innerRect, st::boxBg, Ui::BoxCorners);
|
|
|
|
|
|
|
|
if (_ripple) {
|
|
|
|
_ripple->paint(p, _innerRect.left(), _innerRect.top(), width());
|
|
|
|
if (_ripple->empty()) {
|
|
|
|
_ripple.reset();
|
|
|
|
}
|
|
|
|
}
|
2023-07-06 11:09:16 +00:00
|
|
|
|
|
|
|
p.setPen(st::boxTextFg);
|
2023-10-03 02:19:41 +00:00
|
|
|
const auto headerContext = Ui::Text::PaintContext{
|
|
|
|
.position = _textRect.topLeft(),
|
|
|
|
.availableWidth = _textRect.width(),
|
|
|
|
};
|
|
|
|
_header.draw(p, headerContext);
|
|
|
|
for (auto i = 0; i < _lines.size(); i++) {
|
|
|
|
const auto &line = _lines[i];
|
|
|
|
const auto lineY = lineYAt(i);
|
|
|
|
const auto valueWidth = line.value.maxWidth();
|
|
|
|
const auto valueContext = Ui::Text::PaintContext{
|
|
|
|
.position = QPoint(
|
|
|
|
rect::right(_textRect) - valueWidth,
|
|
|
|
lineY),
|
|
|
|
.outerWidth = _textRect.width(),
|
|
|
|
.availableWidth = valueWidth,
|
|
|
|
};
|
|
|
|
const auto nameContext = Ui::Text::PaintContext{
|
2023-10-05 15:45:48 +00:00
|
|
|
.position = QPoint(
|
|
|
|
_textRect.x() + _maxPercentageWidth,
|
|
|
|
lineY),
|
2023-10-03 02:19:41 +00:00
|
|
|
.outerWidth = _textRect.width(),
|
|
|
|
.availableWidth = _textRect.width() - valueWidth,
|
|
|
|
};
|
|
|
|
p.setOpacity(line.alpha * line.alpha);
|
|
|
|
p.setPen(st::boxTextFg);
|
2023-10-05 15:45:48 +00:00
|
|
|
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);
|
|
|
|
}
|
2023-10-03 02:19:41 +00:00
|
|
|
line.name.draw(p, nameContext);
|
|
|
|
p.setPen(line.valueColor);
|
|
|
|
line.value.draw(p, valueContext);
|
|
|
|
}
|
2023-09-08 12:08:13 +00:00
|
|
|
|
2023-10-03 02:19:41 +00:00
|
|
|
if (_zoomEnabled) {
|
|
|
|
const auto s = _arrow.size() / style::DevicePixelRatio();
|
|
|
|
const auto x = rect::right(_textRect) - s.width();
|
|
|
|
const auto y = _textRect.y()
|
|
|
|
+ (_headerStyle.font->height - s.height()) / 2.;
|
|
|
|
p.drawImage(x, y, _arrow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_alpha < 1.) {
|
|
|
|
painter.setOpacity(_alpha);
|
|
|
|
}
|
|
|
|
painter.drawImage(0, 0, _cache);
|
|
|
|
if (_ripple) {
|
|
|
|
invalidateCache();
|
2023-09-08 12:08:13 +00:00
|
|
|
}
|
2023-07-06 11:09:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Statistic
|