Added initial support of sync zoom to chart widget.

This commit is contained in:
23rd 2023-09-25 15:25:39 +03:00 committed by John Preston
parent 71b6a58683
commit 9051716172
2 changed files with 127 additions and 6 deletions

View File

@ -38,6 +38,22 @@ inline float64 InterpolationRatio(float64 from, float64 to, float64 result) {
return (result - from) / (to - from);
};
[[nodiscard]] QString HeaderRightInfo(
const Data::StatisticalChart &chartData,
const Limits &limits) {
return (limits.min == limits.max)
? chartData.getDayString(limits.min)
: chartData.getDayString(limits.min)
+ ' '
+ QChar(8212)
+ ' '
+ chartData.getDayString(limits.max);
}
[[nodiscard]] bool IsChartHasLocalZoom(ChartViewType type) {
return type == ChartViewType::StackLinear;
}
void PaintBottomLine(
QPainter &p,
const std::vector<ChartWidget::BottomCaptionLineData> &dates,
@ -1014,11 +1030,7 @@ void ChartWidget::updateHeader() {
return;
}
const auto indices = _animationController.currentXIndices();
_header->setRightInfo(_chartData.getDayString(indices.min)
+ ' '
+ QChar(8212)
+ ' '
+ _chartData.getDayString(indices.max));
_header->setRightInfo(HeaderRightInfo(_chartData, indices));
_header->update();
}
@ -1091,13 +1103,22 @@ void ChartWidget::setupDetails() {
}
return maxValue;
}();
if (hasLocalZoom()) {
_zoomEnabled = true;
}
_details.widget = base::make_unique_q<PointDetailsWidget>(
this,
_chartData,
maxAbsoluteValue,
_zoomEnabled);
_details.widget->setClickedCallback([=] {
if (const auto index = _details.widget->xIndex(); index >= 0) {
const auto index = _details.widget->xIndex();
if (index < 0) {
return;
}
if (hasLocalZoom()) {
processLocalZoom(index);
} else {
_zoomRequests.fire_copy(_chartData.x[index]);
}
});
@ -1178,6 +1199,103 @@ void ChartWidget::setupDetails() {
});
}
bool ChartWidget::hasLocalZoom() const {
return _chartData
&& _chartView->maybeLocalZoom({
_chartData,
AbstractChartView::LocalZoomArgs::Type::CheckAvailability,
}).hasZoom;
}
void ChartWidget::processLocalZoom(int xIndex) {
using Type = AbstractChartView::LocalZoomArgs::Type;
constexpr auto kFooterZoomDuration = crl::time(400);
const auto wasZoom = _footer->xPercentageLimits();
const auto header = Ui::CreateChild<Header>(this);
header->show();
_header->geometryValue(
) | rpl::start_with_next([=](const QRect &g) {
header->setGeometry(g);
}, header->lifetime());
header->setRightInfo(_chartData.getDayString(xIndex));
const auto zoomOutButton = Ui::CreateChild<Ui::RoundButton>(
header,
tr::lng_stats_zoom_out(),
st::statisticsHeaderButton);
zoomOutButton->show();
zoomOutButton->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
zoomOutButton->setClickedCallback([=] {
auto lifetime = std::make_shared<rpl::lifetime>();
const auto animation = lifetime->make_state<Ui::Animations::Simple>();
const auto currentXPercentage = _footer->xPercentageLimits();
animation->start([=](float64 value) {
_chartView->maybeLocalZoom({
_chartData,
Type::SkipCalculation,
value,
});
const auto p = value;
_footer->setXPercentageLimits({
anim::interpolateF(wasZoom.min, currentXPercentage.min, p),
anim::interpolateF(wasZoom.max, currentXPercentage.max, p),
});
if (value == 0.) {
if (lifetime) {
lifetime->destroy();
}
}
}, 1., 0., kFooterZoomDuration, anim::easeOutCirc);
Ui::Animations::HideWidgets({ header });
});
Ui::Animations::ShowWidgets({ header });
zoomOutButton->moveToLeft(0, 0);
const auto finish = [=](const Limits &zoomLimit) {
_footer->xPercentageLimitsChange(
) | rpl::start_with_next([=](const Limits &l) {
const auto result = FindNearestElements(
_chartData.xPercentage,
Limits{
anim::interpolateF(zoomLimit.min, zoomLimit.max, l.min),
anim::interpolateF(zoomLimit.min, zoomLimit.max, l.max),
});
header->setRightInfo(HeaderRightInfo(_chartData, result));
header->update();
}, header->lifetime());
};
{
auto lifetime = std::make_shared<rpl::lifetime>();
const auto animation = lifetime->make_state<Ui::Animations::Simple>();
_chartView->maybeLocalZoom({ _chartData, Type::Prepare });
animation->start([=](float64 value) {
const auto zoom = _chartView->maybeLocalZoom({
_chartData,
Type::Process,
value,
xIndex,
});
const auto result = Limits{
anim::interpolateF(wasZoom.min, zoom.range.min, value),
anim::interpolateF(wasZoom.max, zoom.range.max, value),
};
_footer->setXPercentageLimits(result);
if (value == 1.) {
if (lifetime) {
lifetime->destroy();
}
finish(zoom.limit);
}
}, 0., 1., kFooterZoomDuration, anim::easeOutCirc);
}
}
void ChartWidget::setupFilterButtons() {
if (!_chartData || (_chartData.lines.size() <= 1)) {
_filterButtons = nullptr;

View File

@ -134,6 +134,9 @@ private:
void updateChartFullWidth(int w);
[[nodiscard]] bool hasLocalZoom() const;
void processLocalZoom(int xIndex);
const base::unique_qptr<RpMouseWidget> _chartArea;
const std::unique_ptr<Header> _header;
const std::unique_ptr<Footer> _footer;