From fa1ddbd9e34dd07e84a0533b5aa19a3d33a3abe3 Mon Sep 17 00:00:00 2001 From: Peter Ryszkiewicz Date: Sat, 28 Mar 2020 05:03:10 -0500 Subject: [PATCH] Show hovered values on finance charts (#10925) --- distribution/changelog.txt | 1 + src/openrct2-ui/interface/Graph.cpp | 121 +++++++++++++++++++++++++++- src/openrct2-ui/interface/Graph.h | 3 +- src/openrct2/drawing/Drawing.h | 5 +- src/openrct2/drawing/NewDrawing.cpp | 35 ++++++++ src/openrct2/drawing/Text.cpp | 11 +-- 6 files changed, 168 insertions(+), 8 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 3ef5757088..2009f868b2 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.2.5+ (in development) ------------------------------------------------------------------------ +- Feature: [#10925] Show hovered values on finance charts. - Feature: [#11013] Ctrl+C copies input dialog text to clipboard. - Fix: [#475] Water sides drawn incorrectly (original bug). - Fix: [#6123, #7907, #9472, #11028] Cannot build some track designs with 4 stations (original bug). diff --git a/src/openrct2-ui/interface/Graph.cpp b/src/openrct2-ui/interface/Graph.cpp index 8203908f44..6ed0dbd484 100644 --- a/src/openrct2-ui/interface/Graph.cpp +++ b/src/openrct2-ui/interface/Graph.cpp @@ -8,6 +8,7 @@ *****************************************************************************/ #include +#include #include #include @@ -182,10 +183,128 @@ static void graph_draw_line_b_money32( } } +struct FinancialTooltipInfo +{ + const ScreenCoordsXY coords; + const money32 money{}; +}; + +struct ScreenRect +{ + const ScreenCoordsXY LeftTop; + const ScreenCoordsXY RightBottom; + + int32_t GetLeft() const + { + return LeftTop.x; + } + int32_t GetTop() const + { + return LeftTop.y; + } + int32_t GetRight() const + { + return RightBottom.x; + } + int32_t GetBottom() const + { + return RightBottom.y; + } + int32_t GetWidth() const + { + return RightBottom.x - LeftTop.x; + } + int32_t GetHeight() const + { + return RightBottom.y - LeftTop.y; + } + bool Contains(const ScreenCoordsXY& coords) const + { + return coords.x >= GetLeft() && coords.x <= GetRight() && coords.y >= GetTop() && coords.y <= GetBottom(); + } +}; + +static constexpr auto CHART_MAX_DATA_COUNT = 64; +static constexpr auto CHART_MAX_INDEX = CHART_MAX_DATA_COUNT - 1; +static constexpr auto CHART_DATA_WIDTH = 6; +static constexpr auto CHART_MAX_WIDTH = CHART_MAX_INDEX * CHART_DATA_WIDTH; +static constexpr auto CHART_MAX_HEIGHT = 164; +static constexpr auto CURSOR_X_OFFSET = 3; +static constexpr auto DEFAULT_DASHED_LENGTH = 2; + +static int32_t IndexForCursorAndHistory(const int32_t historyCount, const int32_t cursorX, const int32_t chartX) +{ + const auto offsettedCursorX = cursorX + CURSOR_X_OFFSET; + return (historyCount - 1) - (offsettedCursorX - chartX) / CHART_DATA_WIDTH; +} + +static const ScreenCoordsXY ScreenCoordsForHistoryIndex( + const int32_t index, const money32* history, const int32_t chartX, const int32_t chartY, const int32_t modifier, + const int32_t offset) +{ + ScreenCoordsXY coords; + coords.x = chartX + CHART_DATA_WIDTH * (CHART_MAX_INDEX - index); + coords.y = chartY + CHART_MAX_HEIGHT - ((((history[index] >> modifier) + offset) * 170) / 256); + return coords; +} + +static const FinancialTooltipInfo finance_tooltip_info_from_money( + const money32* history, const int32_t historyCount, const int32_t modifier, const int32_t offset, + const ScreenRect& chartFrame, const ScreenCoordsXY& cursorPosition) +{ + if (!chartFrame.Contains(cursorPosition)) + { + return { {}, MONEY32_UNDEFINED }; + } + + const auto historyIndex = IndexForCursorAndHistory(historyCount, cursorPosition.x, chartFrame.GetLeft()); + const auto coords = ScreenCoordsForHistoryIndex( + historyIndex, history, chartFrame.GetLeft(), chartFrame.GetTop(), modifier, offset); + + return { { coords.x, coords.y }, history[historyIndex] }; +} + +static void graph_draw_hovered_value( + rct_drawpixelinfo* dpi, const money32* history, const int32_t historyCount, const int32_t baseX, const int32_t baseY, + const int32_t modifier, const int32_t offset) +{ + const auto cursorPosition = context_get_cursor_position_scaled(); + const ScreenRect chartFrame{ { baseX, baseY }, { baseX + CHART_MAX_WIDTH, baseY + CHART_MAX_HEIGHT } }; + + if (!chartFrame.Contains(cursorPosition)) + { + return; + } + + const auto info = finance_tooltip_info_from_money( + history, CHART_MAX_DATA_COUNT, modifier, offset, chartFrame, cursorPosition); + + if (info.money == MONEY32_UNDEFINED) + { + return; + } + + gfx_draw_dashed_line(dpi, info.coords.x, chartFrame.GetTop(), info.coords.x, info.coords.y, DEFAULT_DASHED_LENGTH, 0); + gfx_draw_dashed_line(dpi, chartFrame.GetLeft() - 10, info.coords.y, info.coords.x, info.coords.y, DEFAULT_DASHED_LENGTH, 0); + + if (cursorPosition.y > info.coords.y) + { + gfx_draw_dashed_line(dpi, info.coords.x, info.coords.y, info.coords.x, cursorPosition.y, DEFAULT_DASHED_LENGTH, 0); + } + + gfx_draw_string_centred( + dpi, STR_FINANCES_SUMMARY_EXPENDITURE_VALUE, info.coords.x, info.coords.y - 16, COLOUR_BLACK, &info.money); + + gfx_fill_rect(dpi, info.coords.x - 2, info.coords.y - 2, info.coords.x + 2, info.coords.y + 2, PALETTE_INDEX_10); + gfx_fill_rect(dpi, info.coords.x - 1, info.coords.y - 1, info.coords.x + 1, info.coords.y + 1, PALETTE_INDEX_21); +} + void graph_draw_money32( - rct_drawpixelinfo* dpi, money32* history, int32_t count, int32_t baseX, int32_t baseY, int32_t modifier, int32_t offset) + rct_drawpixelinfo* dpi, const money32* history, const int32_t count, const int32_t baseX, const int32_t baseY, + const int32_t modifier, const int32_t offset) { graph_draw_months_money32(dpi, history, count, baseX, baseY); graph_draw_line_a_money32(dpi, history, count, baseX, baseY, modifier, offset); graph_draw_line_b_money32(dpi, history, count, baseX, baseY, modifier, offset); + graph_draw_hovered_value(dpi, history, count, baseX, baseY, modifier, offset); } diff --git a/src/openrct2-ui/interface/Graph.h b/src/openrct2-ui/interface/Graph.h index 288aa871cb..be00d47d08 100644 --- a/src/openrct2-ui/interface/Graph.h +++ b/src/openrct2-ui/interface/Graph.h @@ -15,6 +15,7 @@ void graph_draw_uint8_t(rct_drawpixelinfo* dpi, uint8_t* history, int32_t count, int32_t baseX, int32_t baseY); void graph_draw_money32( - rct_drawpixelinfo* dpi, money32* history, int32_t count, int32_t baseX, int32_t baseY, int32_t modifier, int32_t offset); + rct_drawpixelinfo* dpi, const money32* history, const int32_t count, const int32_t baseX, const int32_t baseY, + const int32_t modifier, const int32_t offset); #endif diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index 133d8efb7a..9e99b228c8 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -465,6 +465,8 @@ void gfx_draw_pickedup_peep(rct_drawpixelinfo* dpi); // line void gfx_draw_line(rct_drawpixelinfo* dpi, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t colour); void gfx_draw_line_software(rct_drawpixelinfo* dpi, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t colour); +void gfx_draw_dashed_line( + rct_drawpixelinfo* dpi, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t dashedLineSegmentLength, int32_t colour); // rect void gfx_fill_rect(rct_drawpixelinfo* dpi, int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t colour); @@ -512,7 +514,8 @@ void FASTCALL void gfx_draw_string(rct_drawpixelinfo* dpi, const_utf8string buffer, uint8_t colour, int32_t x, int32_t y); void gfx_draw_string_left(rct_drawpixelinfo* dpi, rct_string_id format, void* args, uint8_t colour, int32_t x, int32_t y); -void gfx_draw_string_centred(rct_drawpixelinfo* dpi, rct_string_id format, int32_t x, int32_t y, uint8_t colour, void* args); +void gfx_draw_string_centred( + rct_drawpixelinfo* dpi, rct_string_id format, int32_t x, int32_t y, uint8_t colour, const void* args); void gfx_draw_string_right(rct_drawpixelinfo* dpi, rct_string_id format, void* args, uint8_t colour, int32_t x, int32_t y); void draw_string_left_underline(rct_drawpixelinfo* dpi, rct_string_id format, void* args, uint8_t colour, int32_t x, int32_t y); diff --git a/src/openrct2/drawing/NewDrawing.cpp b/src/openrct2/drawing/NewDrawing.cpp index caf66f57e9..a546fa68ca 100644 --- a/src/openrct2/drawing/NewDrawing.cpp +++ b/src/openrct2/drawing/NewDrawing.cpp @@ -19,6 +19,8 @@ #include "IDrawingContext.h" #include "IDrawingEngine.h" +#include + using namespace OpenRCT2; using namespace OpenRCT2::Drawing; using namespace OpenRCT2::Paint; @@ -209,6 +211,39 @@ void gfx_draw_line(rct_drawpixelinfo* dpi, int32_t x1, int32_t y1, int32_t x2, i } } +void gfx_draw_dashed_line( + rct_drawpixelinfo* dpi, const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2, + const int32_t dashedLineSegmentLength, const int32_t colour) +{ + assert(dashedLineSegmentLength > 0); + + const auto drawingEngine = dpi->DrawingEngine; + if (drawingEngine != nullptr) + { + constexpr int32_t precisionFactor = 1000; + + const int32_t dashedLineLength = std::hypot(x2 - x1, y2 - y1); + const int32_t lineSegmentCount = dashedLineLength / dashedLineSegmentLength / 2; + if (lineSegmentCount == 0) + { + return; + } + + const int32_t lineXDist = std::abs(x2 - x1); + const int32_t lineYDist = std::abs(y2 - y1); + const int32_t dxPrecise = precisionFactor * lineXDist / lineSegmentCount / 2; + const int32_t dyPrecise = precisionFactor * lineYDist / lineSegmentCount / 2; + IDrawingContext* dc = drawingEngine->GetDrawingContext(dpi); + + for (int32_t i = 0, x, y; i < lineSegmentCount; ++i) + { + x = x1 + dxPrecise * i * 2 / precisionFactor; + y = y1 + dyPrecise * i * 2 / precisionFactor; + dc->DrawLine(colour, x, y, x + dxPrecise / precisionFactor, y + dyPrecise / precisionFactor); + } + } +} + void FASTCALL gfx_draw_sprite(rct_drawpixelinfo* dpi, int32_t image, int32_t x, int32_t y, uint32_t tertiary_colour) { auto drawingEngine = dpi->DrawingEngine; diff --git a/src/openrct2/drawing/Text.cpp b/src/openrct2/drawing/Text.cpp index 07ba4a506b..70ef28f372 100644 --- a/src/openrct2/drawing/Text.cpp +++ b/src/openrct2/drawing/Text.cpp @@ -15,7 +15,7 @@ static TextPaint _legacyPaint; static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* paint, const_utf8string text); -static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* paint, rct_string_id format, void* args); +static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* paint, rct_string_id format, const void* args); StaticLayout::StaticLayout(utf8string source, TextPaint paint, int32_t width) { @@ -105,7 +105,7 @@ static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* pa } } -static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* paint, rct_string_id format, void* args) +static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* paint, rct_string_id format, const void* args) { utf8 buffer[512]; format_string(buffer, sizeof(buffer), format, args); @@ -113,8 +113,8 @@ static void DrawText(rct_drawpixelinfo* dpi, int32_t x, int32_t y, TextPaint* pa } static void DrawTextCompat( - rct_drawpixelinfo* dpi, int32_t x, int32_t y, rct_string_id format, void* args, uint8_t colour, TextAlignment alignment, - bool underline = false) + rct_drawpixelinfo* dpi, int32_t x, int32_t y, rct_string_id format, const void* args, uint8_t colour, + TextAlignment alignment, bool underline = false) { _legacyPaint.UnderlineText = underline; _legacyPaint.Colour = colour; @@ -156,7 +156,8 @@ void gfx_draw_string_left(rct_drawpixelinfo* dpi, rct_string_id format, void* ar DrawTextCompat(dpi, x, y, format, args, colour, TextAlignment::LEFT); } -void gfx_draw_string_centred(rct_drawpixelinfo* dpi, rct_string_id format, int32_t x, int32_t y, uint8_t colour, void* args) +void gfx_draw_string_centred( + rct_drawpixelinfo* dpi, rct_string_id format, int32_t x, int32_t y, uint8_t colour, const void* args) { DrawTextCompat(dpi, x, y, format, args, colour, TextAlignment::CENTRE); }