Slightly optimized footer in chart widget.

This commit is contained in:
23rd 2023-07-01 11:27:58 +03:00 committed by John Preston
parent c8d5a60c74
commit c5684e768a
1 changed files with 126 additions and 159 deletions

View File

@ -102,6 +102,104 @@ private:
};
ChartWidget::Footer::Footer(not_null<Ui::RpWidget*> parent)
: Ui::AbstractButton(parent)
, _left(Ui::CreateChild<Ui::AbstractButton>(this))
, _right(Ui::CreateChild<Ui::AbstractButton>(this)) {
sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
_left->resize(st::colorSliderWidth, s.height());
_right->resize(st::colorSliderWidth, s.height());
}, _left->lifetime());
_left->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(_left);
p.setOpacity(0.3);
p.fillRect(_left->rect(), st::boxTextFg);
}, _left->lifetime());
_right->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(_right);
p.setOpacity(0.3);
p.fillRect(_right->rect(), st::boxTextFg);
}, _right->lifetime());
_left->move(10, 0);
_right->move(50, 0);
const auto handleDrag = [&](
not_null<Ui::AbstractButton*> side,
Fn<int()> leftLimit,
Fn<int()> rightLimit) {
side->events(
) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::MouseButtonPress)
|| (e->type() == QEvent::MouseButtonRelease)
|| ((e->type() == QEvent::MouseMove) && side->isDown());
}) | rpl::start_with_next([=](not_null<QEvent*> e) {
const auto pos = static_cast<QMouseEvent*>(e.get())->pos();
switch (e->type()) {
case QEvent::MouseMove: {
const auto nowDiffXDirection = (pos.x() - _start.x) < 0;
const auto wasDiffXDirection = _start.diffX < 0;
if (base::IsCtrlPressed()) {
const auto diff = (pos.x() - _start.x);
_left->move(_left->x() + diff, side->y());
_right->move(_right->x() + diff, side->y());
} else {
_start.diffX = pos.x() - _start.x;
const auto nextX = std::clamp(
side->x() + (pos.x() - _start.x),
_start.leftLimit,
_start.rightLimit);
side->move(nextX, side->y());
}
_xPercentageLimitsChange.fire({
.min = _left->x() / float64(width()),
.max = rect::right(_right) / float64(width()),
});
if (nowDiffXDirection != wasDiffXDirection) {
_directionChanges.fire({});
}
} break;
case QEvent::MouseButtonPress: {
_start.x = pos.x();
_start.leftLimit = leftLimit();
_start.rightLimit = rightLimit();
} break;
case QEvent::MouseButtonRelease: {
_userInteractionFinished.fire({});
_xPercentageLimitsChange.fire({
.min = _left->x() / float64(width()),
.max = rect::right(_right) / float64(width()),
});
_start = {};
} break;
}
}, side->lifetime());
};
handleDrag(
_left,
[=] { return 0; },
[=] { return _right->x() - _left->width(); });
handleDrag(
_right,
[=] { return rect::right(_left); },
[=] { return width() - _right->width(); });
}
rpl::producer<Limits> ChartWidget::Footer::xPercentageLimitsChange() const {
return _xPercentageLimitsChange.events();
}
rpl::producer<> ChartWidget::Footer::userInteractionFinished() const {
return _userInteractionFinished.events();
}
rpl::producer<> ChartWidget::Footer::directionChanges() const {
return _directionChanges.events();
}
ChartWidget::ChartAnimationController::ChartAnimationController(
Fn<void()> &&updateCallback)
: _animation(std::move(updateCallback)) {
@ -120,67 +218,6 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
_animValueXMax.start(xPercentageLimits.max);
_lastUserInteracted = now;
{
auto minY = std::numeric_limits<float64>::max();
auto maxY = 0.;
auto minYIndex = 0;
auto maxYIndex = 0;
const auto tempXPercentage = Limits{
.min = *ranges::lower_bound(
chartData.xPercentage,
xPercentageLimits.min),
.max = *ranges::lower_bound(
chartData.xPercentage,
xPercentageLimits.max),
};
for (auto i = 0; i < chartData.xPercentage.size(); i++) {
if (chartData.xPercentage[i] == tempXPercentage.min) {
minYIndex = i;
}
if (chartData.xPercentage[i] == tempXPercentage.max) {
maxYIndex = i;
}
}
for (const auto &line : chartData.lines) {
for (auto i = minYIndex; i < maxYIndex; i++) {
if (line.y[i] > maxY) {
maxY = line.y[i];
}
if (line.y[i] < minY) {
minY = line.y[i];
}
}
}
_animValueYMin = anim::value(
_animValueYMin.current(),
minY);
_animValueYMax = anim::value(
_animValueYMax.current(),
maxY);
{
auto k = (_animValueYMax.current() - _animValueYMin.current())
/ float64(maxY - minY);
if (k > 1.) {
k = 1. / k;
}
constexpr auto kDtHeightSpeed1 = 0.03 / 2;
constexpr auto kDtHeightSpeed2 = 0.03 / 2;
constexpr auto kDtHeightSpeed3 = 0.045 / 2;
constexpr auto kDtHeightSpeedThreshold1 = 0.7;
constexpr auto kDtHeightSpeedThreshold2 = 0.1;
constexpr auto kDtHeightInstantThreshold = 0.97;
_dtYSpeed = (k > kDtHeightSpeedThreshold1)
? kDtHeightSpeed1
: (k < kDtHeightSpeedThreshold2)
? kDtHeightSpeed2
: kDtHeightSpeed3;
if (k < kDtHeightInstantThreshold) {
_dtCurrent = { 0., 0. };
}
}
}
{
const auto startXIndex = chartData.findStartIndex(
_animValueXMin.to());
@ -192,6 +229,34 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
float64(FindMaxValue(chartData, startXIndex, endXIndex)),
};
}
_animValueYMin = anim::value(
_animValueYMin.current(),
_finalHeightLimits.min);
_animValueYMax = anim::value(
_animValueYMax.current(),
_finalHeightLimits.max);
{
auto k = (_animValueYMax.current() - _animValueYMin.current())
/ float64(_finalHeightLimits.max - _finalHeightLimits.min);
if (k > 1.) {
k = 1. / k;
}
constexpr auto kDtHeightSpeed1 = 0.03 / 2;
constexpr auto kDtHeightSpeed2 = 0.03 / 2;
constexpr auto kDtHeightSpeed3 = 0.045 / 2;
constexpr auto kDtHeightSpeedThreshold1 = 0.7;
constexpr auto kDtHeightSpeedThreshold2 = 0.1;
constexpr auto kDtHeightInstantThreshold = 0.97;
_dtYSpeed = (k > kDtHeightSpeedThreshold1)
? kDtHeightSpeed1
: (k < kDtHeightSpeedThreshold2)
? kDtHeightSpeed2
: kDtHeightSpeed3;
if (k < kDtHeightInstantThreshold) {
_dtCurrent = { 0., 0. };
}
}
}
void ChartWidget::ChartAnimationController::start() {
@ -312,104 +377,6 @@ auto ChartWidget::ChartAnimationController::heightAnimationStarts() const
return _heightAnimationStarts.events();
}
ChartWidget::Footer::Footer(not_null<Ui::RpWidget*> parent)
: Ui::AbstractButton(parent)
, _left(Ui::CreateChild<Ui::AbstractButton>(this))
, _right(Ui::CreateChild<Ui::AbstractButton>(this)) {
sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
_left->resize(st::colorSliderWidth, s.height());
_right->resize(st::colorSliderWidth, s.height());
}, _left->lifetime());
_left->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(_left);
p.setOpacity(0.3);
p.fillRect(_left->rect(), st::boxTextFg);
}, _left->lifetime());
_right->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(_right);
p.setOpacity(0.3);
p.fillRect(_right->rect(), st::boxTextFg);
}, _right->lifetime());
_left->move(10, 0);
_right->move(50, 0);
const auto handleDrag = [&](
not_null<Ui::AbstractButton*> side,
Fn<int()> leftLimit,
Fn<int()> rightLimit) {
side->events(
) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::MouseButtonPress)
|| (e->type() == QEvent::MouseButtonRelease)
|| ((e->type() == QEvent::MouseMove) && side->isDown());
}) | rpl::start_with_next([=](not_null<QEvent*> e) {
const auto pos = static_cast<QMouseEvent*>(e.get())->pos();
switch (e->type()) {
case QEvent::MouseMove: {
const auto nowDiffXDirection = (pos.x() - _start.x) < 0;
const auto wasDiffXDirection = _start.diffX < 0;
if (base::IsCtrlPressed()) {
const auto diff = (pos.x() - _start.x);
_left->move(_left->x() + diff, side->y());
_right->move(_right->x() + diff, side->y());
} else {
_start.diffX = pos.x() - _start.x;
const auto nextX = std::clamp(
side->x() + (pos.x() - _start.x),
_start.leftLimit,
_start.rightLimit);
side->move(nextX, side->y());
}
_xPercentageLimitsChange.fire({
.min = _left->x() / float64(width()),
.max = rect::right(_right) / float64(width()),
});
if (nowDiffXDirection != wasDiffXDirection) {
_directionChanges.fire({});
}
} break;
case QEvent::MouseButtonPress: {
_start.x = pos.x();
_start.leftLimit = leftLimit();
_start.rightLimit = rightLimit();
} break;
case QEvent::MouseButtonRelease: {
_userInteractionFinished.fire({});
_xPercentageLimitsChange.fire({
.min = _left->x() / float64(width()),
.max = rect::right(_right) / float64(width()),
});
_start = {};
} break;
}
}, side->lifetime());
};
handleDrag(
_left,
[=] { return 0; },
[=] { return _right->x() - _left->width(); });
handleDrag(
_right,
[=] { return rect::right(_left); },
[=] { return width() - _right->width(); });
}
rpl::producer<Limits> ChartWidget::Footer::xPercentageLimitsChange() const {
return _xPercentageLimitsChange.events();
}
rpl::producer<> ChartWidget::Footer::userInteractionFinished() const {
return _userInteractionFinished.events();
}
rpl::producer<> ChartWidget::Footer::directionChanges() const {
return _directionChanges.events();
}
ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
: Ui::RpWidget(parent)
, _footer(std::make_unique<Footer>(this))