Slightly optimized footer in chart widget.
This commit is contained in:
parent
c8d5a60c74
commit
c5684e768a
|
@ -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(
|
ChartWidget::ChartAnimationController::ChartAnimationController(
|
||||||
Fn<void()> &&updateCallback)
|
Fn<void()> &&updateCallback)
|
||||||
: _animation(std::move(updateCallback)) {
|
: _animation(std::move(updateCallback)) {
|
||||||
|
@ -120,67 +218,6 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
|
||||||
_animValueXMax.start(xPercentageLimits.max);
|
_animValueXMax.start(xPercentageLimits.max);
|
||||||
_lastUserInteracted = now;
|
_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(
|
const auto startXIndex = chartData.findStartIndex(
|
||||||
_animValueXMin.to());
|
_animValueXMin.to());
|
||||||
|
@ -192,6 +229,34 @@ void ChartWidget::ChartAnimationController::setXPercentageLimits(
|
||||||
float64(FindMaxValue(chartData, startXIndex, endXIndex)),
|
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() {
|
void ChartWidget::ChartAnimationController::start() {
|
||||||
|
@ -312,104 +377,6 @@ auto ChartWidget::ChartAnimationController::heightAnimationStarts() const
|
||||||
return _heightAnimationStarts.events();
|
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)
|
ChartWidget::ChartWidget(not_null<Ui::RpWidget*> parent)
|
||||||
: Ui::RpWidget(parent)
|
: Ui::RpWidget(parent)
|
||||||
, _footer(std::make_unique<Footer>(this))
|
, _footer(std::make_unique<Footer>(this))
|
||||||
|
|
Loading…
Reference in New Issue