diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index dc8b1608ce..c6722f4236 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -8,6 +8,7 @@ /** @file graph_gui.cpp GUI that shows performance graphs. */ #include "stdafx.h" +#include #include "misc/history_func.hpp" #include "graph_gui.h" #include "window_gui.h" @@ -174,6 +175,7 @@ protected: static const int GRAPH_PAYMENT_RATE_STEPS = 20; ///< Number of steps on Payment rate graph. static const int PAYMENT_GRAPH_X_STEP_DAYS = 10; ///< X-axis step label for cargo payment rates "Days in transit". static const int PAYMENT_GRAPH_X_STEP_SECONDS = 20; ///< X-axis step label for cargo payment rates "Seconds in transit". + static const int ECONOMY_YEAR_MINUTES = 12; ///< Minutes per economic year. static const int ECONOMY_QUARTER_MINUTES = 3; ///< Minutes per economic quarter. static const int ECONOMY_MONTH_MINUTES = 1; ///< Minutes per economic month. @@ -182,6 +184,24 @@ protected: static const int MIN_GRAPH_NUM_LINES_Y = 9; ///< Minimal number of horizontal lines to draw. static const int MIN_GRID_PIXEL_SIZE = 20; ///< Minimum distance between graph lines. + struct GraphScale { + StringID label = STR_NULL; + uint8_t month_increment = 0; + int16_t x_values_increment = 0; + }; + + static inline constexpr GraphScale MONTHLY_SCALE_WALLCLOCK[] = { + {STR_GRAPH_LAST_24_MINUTES_TIME_LABEL, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES}, + {STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES}, + {STR_GRAPH_LAST_288_MINUTES_TIME_LABEL, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES}, + }; + + static inline constexpr GraphScale MONTHLY_SCALE_CALENDAR[] = { + {STR_GRAPH_LAST_24_MONTHS, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES}, + {STR_GRAPH_LAST_24_QUARTERS, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES}, + {STR_GRAPH_LAST_24_YEARS, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES}, + }; + uint64_t excluded_data = 0; ///< bitmask of datasets hidden by the player. uint64_t excluded_range = 0; ///< bitmask of ranges hidden by the player. uint64_t masked_range = 0; ///< bitmask of ranges that are not available for the current data. @@ -211,7 +231,9 @@ protected: }; std::vector data{}; - std::span ranges = {}; + std::span ranges{}; + std::span scales{}; + uint8_t selected_scale = 0; uint8_t highlight_data = UINT8_MAX; ///< Data set that should be highlighted, or UINT8_MAX for none. uint8_t highlight_range = UINT8_MAX; ///< Data range that should be highlighted, or UINT8_MAX for none. @@ -607,24 +629,34 @@ protected: this->SetDirty(); }}; + void UpdateMatrixSize(WidgetID widget, Dimension &size, Dimension &resize, auto labels) + { + size = {}; + for (const StringID &str : labels) { + size = maxdim(size, GetStringBoundingBox(str, FS_SMALL)); + } + + size.width += WidgetDimensions::scaled.framerect.Horizontal(); + size.height += WidgetDimensions::scaled.framerect.Vertical(); + + /* Set fixed height for number of ranges. */ + size.height *= static_cast(std::size(labels)); + + resize.width = 0; + resize.height = 0; + this->GetWidget(widget)->SetMatrixDimension(1, ClampTo(std::size(labels))); + } + public: void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { case WID_GRAPH_RANGE_MATRIX: - for (const StringID &str : this->ranges) { - size = maxdim(size, GetStringBoundingBox(str, FS_SMALL)); - } + this->UpdateMatrixSize(widget, size, resize, this->ranges); + break; - size.width += WidgetDimensions::scaled.framerect.Horizontal(); - size.height += WidgetDimensions::scaled.framerect.Vertical(); - - /* Set fixed height for number of ranges. */ - size.height *= static_cast(std::size(this->ranges)); - - resize.width = 0; - resize.height = 0; - this->GetWidget(WID_GRAPH_RANGE_MATRIX)->SetMatrixDimension(1, ClampTo(std::size(this->ranges))); + case WID_GRAPH_SCALE_MATRIX: + this->UpdateMatrixSize(widget, size, resize, this->scales | std::views::transform(&GraphScale::label)); break; case WID_GRAPH_GRAPH: { @@ -691,6 +723,21 @@ public: break; } + case WID_GRAPH_SCALE_MATRIX: { + uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical(); + uint8_t selected_month_increment = this->scales[this->selected_scale].month_increment; + Rect line = r.WithHeight(line_height); + for (const auto &scale : this->scales) { + /* Redraw frame if selected */ + if (selected_month_increment == scale.month_increment) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered); + + DrawString(line.Shrink(WidgetDimensions::scaled.framerect), scale.label, TC_BLACK, SA_CENTER, false, FS_SMALL); + + line = line.Translate(0, line_height); + } + break; + } + default: break; } } @@ -712,6 +759,18 @@ public: break; } + case WID_GRAPH_SCALE_MATRIX: { + int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical()); + const auto &scale = this->scales[row]; + if (this->selected_scale != row) { + this->selected_scale = row; + this->month_increment = scale.month_increment; + this->x_values_increment = scale.x_values_increment; + this->InvalidateData(); + } + break; + } + default: break; } } @@ -1636,6 +1695,13 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow { this->InitializeWindow(window_number, STR_GRAPH_LAST_24_MINUTES_TIME_LABEL); } + void OnInit() override + { + this->BaseCargoGraphWindow::OnInit(); + + this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR; + } + CargoTypes GetCargoTypes(WindowNumber window_number) const override { CargoTypes cargo_types{}; @@ -1699,7 +1765,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow { transported.range_bit = 1; transported.dash = 2; - FillFromHistory(p.history, 0, Filler{produced, &Industry::ProducedHistory::production}, Filler{transported, &Industry::ProducedHistory::transported}); + FillFromHistory(p.history, this->selected_scale, Filler{produced, &Industry::ProducedHistory::production}, Filler{transported, &Industry::ProducedHistory::transported}); } for (const auto &a : i->accepted) { @@ -1720,7 +1786,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow { waiting.range_bit = 3; waiting.dash = 4; - FillFromHistory(a.history, 0, Filler{accepted, &Industry::AcceptedHistory::accepted}, Filler{waiting, &Industry::AcceptedHistory::waiting}); + FillFromHistory(a.history, this->selected_scale, Filler{accepted, &Industry::AcceptedHistory::accepted}, Filler{waiting, &Industry::AcceptedHistory::waiting}); } this->SetDirty(); @@ -1740,7 +1806,7 @@ static constexpr NWidgetPart _nested_industry_production_widgets[] = { NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1), NWidget(NWID_VERTICAL), NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1), - NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO), + NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_TOGGLE_RANGE), NWidget(NWID_SPACER), SetMinimalSize(0, 4), NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0), @@ -1749,6 +1815,8 @@ static constexpr NWidgetPart _nested_industry_production_widgets[] = { NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_MATRIX), SetFill(1, 0), SetResize(0, 2), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO), SetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_GRAPH_MATRIX_SCROLLBAR), EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 4), + NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_SELECT_SCALE), NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1), EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1), diff --git a/src/lang/english.txt b/src/lang/english.txt index 1f1e5d2aa8..e8e9ff278c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -621,6 +621,14 @@ STR_GRAPH_COMPANY_VALUES_CAPTION :{WHITE}Company STR_GRAPH_LAST_24_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}Last 24 minutes STR_GRAPH_LAST_72_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}Last 72 minutes +STR_GRAPH_LAST_288_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}Last 288 minutes + +STR_GRAPH_LAST_24_MONTHS :{TINY_FONT}{BLACK}2 years (monthly) +STR_GRAPH_LAST_24_QUARTERS :{TINY_FONT}{BLACK}6 years (quarterly) +STR_GRAPH_LAST_24_YEARS :{TINY_FONT}{BLACK}24 years (yearly) + +STR_GRAPH_TOGGLE_RANGE :Toggle graph for this data range +STR_GRAPH_SELECT_SCALE :Change horizontal scale of graph STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION :{WHITE}Cargo Payment Rates STR_GRAPH_CARGO_PAYMENT_RATES_DAYS :{TINY_FONT}{BLACK}Days in transit diff --git a/src/widgets/graph_widget.h b/src/widgets/graph_widget.h index 496410eb8a..f6c1ae56bc 100644 --- a/src/widgets/graph_widget.h +++ b/src/widgets/graph_widget.h @@ -37,6 +37,7 @@ enum GraphWidgets : WidgetID { WID_GRAPH_MATRIX_SCROLLBAR,///< Cargo list scrollbar. WID_GRAPH_RANGE_MATRIX, ///< Range list. + WID_GRAPH_SCALE_MATRIX, ///< Horizontal axis scale list. WID_PHG_DETAILED_PERFORMANCE, ///< Detailed performance. };