From 845f985a1338e9452ab17ee85f6830d852987a14 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 22 Feb 2025 14:55:16 +0100 Subject: [PATCH] snap in marker if mouse near data point --- client/mainmenu/CStatisticScreen.cpp | 54 ++++++++++++++++++++-------- client/mainmenu/CStatisticScreen.h | 6 ++++ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/client/mainmenu/CStatisticScreen.cpp b/client/mainmenu/CStatisticScreen.cpp index 50afea2a7..b7d8a21f4 100644 --- a/client/mainmenu/CStatisticScreen.cpp +++ b/client/mainmenu/CStatisticScreen.cpp @@ -438,7 +438,7 @@ int computeGridStep(int maxAmount, int linesLimit) } LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons, float maxY) - : CIntObject(), maxVal(0), maxDay(0) + : CIntObject(), maxVal(0), maxDay(0), data(data) { OBJECT_CONSTRUCTION; @@ -474,13 +474,6 @@ LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons, niceMaxVal = gridStep * std::ceil(maxVal / gridStep); niceMaxVal = std::max(1, niceMaxVal); // avoid zero size Y axis (if all values are 0) - // calculate points in chart - auto getPoint = [this](int i, std::vector data){ - float x = (static_cast(chartArea.w) / static_cast(maxDay - 1)) * static_cast(i); - float y = static_cast(chartArea.h) - (static_cast(chartArea.h) / niceMaxVal) * data[i]; - return Point(x, y); - }; - // draw grid (vertical lines) int dayGridInterval = maxDay < 700 ? 7 : 28; if(maxDay > 1) @@ -541,18 +534,51 @@ LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons, layout.emplace_back(std::make_shared(p.x, p.y, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.64"))); } +Point LineChart::getPoint(int i, std::vector data) +{ + float x = (static_cast(chartArea.w) / static_cast(maxDay - 1)) * static_cast(i); + float y = static_cast(chartArea.h) - (static_cast(chartArea.h) / niceMaxVal) * data[i]; + return Point(x, y); +}; + void LineChart::updateStatusBar(const Point & cursorPosition) { - statusBar->moveTo(cursorPosition + Point(-statusBar->pos.w / 2, 20)); - statusBar->fitToRect(pos, 10); + OBJECT_CONSTRUCTION; + Rect r(pos.x + chartArea.x, pos.y + chartArea.y, chartArea.w, chartArea.h); - statusBar->setEnabled(r.isInside(cursorPosition)); - if(r.isInside(cursorPosition)) + Point curPos = cursorPosition; + if(r.isInside(curPos)) { - float x = (static_cast(maxDay - 1) / static_cast(chartArea.w)) * (static_cast(cursorPosition.x) - static_cast(r.x)) + 1.0f; - float y = niceMaxVal - (niceMaxVal / static_cast(chartArea.h)) * (static_cast(cursorPosition.y) - static_cast(r.y)); + std::vector> points; + for(const auto & line : data) + { + for(int i = 0; i < line.second.size(); i++) + { + Point p = getPoint(i, line.second) + chartArea.topLeft(); + int len = Point(curPos.x - p.x - pos.x, curPos.y - p.y - pos.y).length(); + points.push_back(std::make_pair(len, p)); + } + } + std::sort(points.begin(), points.end(), [](const auto &a, const auto &b) { return a.first < b.first; }); + if(points.size() && points[0].first < 15) + { + // Snap in with marker for nearest point + hoverMarker = std::make_shared(Rect(points[0].second - Point(3, 3), Point(6, 6)), Colors::ORANGE); + curPos = points[0].second + pos; + } + else + hoverMarker.reset(); + + float x = (static_cast(maxDay - 1) / static_cast(chartArea.w)) * (static_cast(curPos.x) - static_cast(r.x)) + 1.0f; + float y = niceMaxVal - (niceMaxVal / static_cast(chartArea.h)) * (static_cast(curPos.y) - static_cast(r.y)); + statusBar->write(CGI->generaltexth->translate("core.genrltxt.64") + ": " + CStatisticScreen::getDay(x) + " " + CGI->generaltexth->translate("vcmi.statisticWindow.value") + ": " + (static_cast(y) > 0 ? std::to_string(static_cast(y)) : std::to_string(y))); } + + statusBar->setEnabled(r.resize(1).isInside(curPos)); + statusBar->moveTo(curPos + Point(-statusBar->pos.w / 2, 20)); + statusBar->fitToRect(pos, 10); + setRedrawParent(true); redraw(); } diff --git a/client/mainmenu/CStatisticScreen.h b/client/mainmenu/CStatisticScreen.h index 1c28da504..5224a67f4 100644 --- a/client/mainmenu/CStatisticScreen.h +++ b/client/mainmenu/CStatisticScreen.h @@ -20,6 +20,7 @@ class ComboBox; class CSlider; class IImage; class CPicture; +class TransparentFilledRectangle; using TData = std::vector>>; using TIcons = std::vector, std::string>>; // Color, Day, Image, Helptext @@ -118,13 +119,18 @@ class LineChart : public CIntObject std::vector> layout; std::shared_ptr statusBar; std::vector> pictures; + std::shared_ptr hoverMarker; Rect chartArea; float maxVal; int niceMaxVal; int maxDay; + TData data; + void updateStatusBar(const Point & cursorPosition); + // calculate points in chart + Point getPoint(int i, std::vector data); public: LineChart(Rect position, std::string title, TData data, TIcons icons, float maxY);