OpenTTD Source 20260218-master-g2123fca5ea
graph_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include <ranges>
12#include "misc/history_func.hpp"
13#include "graph_gui.h"
14#include "window_gui.h"
15#include "company_base.h"
16#include "company_gui.h"
17#include "economy_func.h"
18#include "cargotype.h"
19#include "strings_func.h"
20#include "window_func.h"
21#include "sound_func.h"
22#include "gfx_func.h"
24#include "currency.h"
25#include "timer/timer.h"
26#include "timer/timer_window.h"
29#include "zoom_func.h"
30#include "industry.h"
31#include "town.h"
32
34
35#include "table/strings.h"
36#include "table/sprites.h"
37
38#include "safeguards.h"
39
40/* Bitmasks of company and cargo indices that shouldn't be drawn. */
41static CompanyMask _legend_excluded_companies;
42
43/* Apparently these don't play well with enums. */
44static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn.
45static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn.
46
47constexpr double INT64_MAX_IN_DOUBLE = static_cast<double>(INT64_MAX - 512);
48static_assert(static_cast<int64_t>(INT64_MAX_IN_DOUBLE) < INT64_MAX);
49
50/****************/
51/* GRAPH LEGEND */
52/****************/
53
54struct GraphLegendWindow : Window {
55 GraphLegendWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
56 {
57 this->InitNested(window_number);
58
59 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
60 if (!_legend_excluded_companies.Test(c)) this->LowerWidget(WID_GL_FIRST_COMPANY + c);
61
62 this->OnInvalidateData(c.base());
63 }
64 }
65
66 void DrawWidget(const Rect &r, WidgetID widget) const override
67 {
68 if (!IsInsideMM(widget, WID_GL_FIRST_COMPANY, WID_GL_FIRST_COMPANY + MAX_COMPANIES)) return;
69
70 CompanyID cid = (CompanyID)(widget - WID_GL_FIRST_COMPANY);
71
72 if (!Company::IsValidID(cid)) return;
73
74 bool rtl = _current_text_dir == TD_RTL;
75
76 const Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
77 Dimension d = GetSpriteSize(SPR_COMPANY_ICON);
78 DrawCompanyIcon(cid, rtl ? ir.right - d.width : ir.left, CentreBounds(ir.top, ir.bottom, d.height));
79
80 const Rect tr = ir.Indent(d.width + WidgetDimensions::scaled.hsep_normal, rtl);
81 DrawString(tr.left, tr.right, CentreBounds(tr.top, tr.bottom, GetCharacterHeight(FS_NORMAL)), GetString(STR_COMPANY_NAME_COMPANY_NUM, cid, cid), _legend_excluded_companies.Test(cid) ? TC_BLACK : TC_WHITE);
82 }
83
84 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
85 {
86 if (!IsInsideMM(widget, WID_GL_FIRST_COMPANY, WID_GL_FIRST_COMPANY + MAX_COMPANIES)) return;
87
88 _legend_excluded_companies.Flip(static_cast<CompanyID>(widget - WID_GL_FIRST_COMPANY));
89 this->ToggleWidgetLoweredState(widget);
90 this->SetDirty();
96
98 }
99
105 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
106 {
107 if (!gui_scope) return;
108 if (Company::IsValidID(data)) return;
109
110 _legend_excluded_companies.Set(static_cast<CompanyID>(data));
111 this->RaiseWidget(data + WID_GL_FIRST_COMPANY);
112 }
113};
114
119static std::unique_ptr<NWidgetBase> MakeNWidgetCompanyLines()
120{
121 auto vert = std::make_unique<NWidgetVertical>(NWidContainerFlag::EqualSize);
122 vert->SetPadding(2, 2, 2, 2);
123 uint sprite_height = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZoomLevel::Normal).height;
124
125 for (WidgetID widnum = WID_GL_FIRST_COMPANY; widnum <= WID_GL_LAST_COMPANY; widnum++) {
126 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
127 panel->SetMinimalSize(246, sprite_height + WidgetDimensions::unscaled.framerect.Vertical());
128 panel->SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical(), FS_NORMAL);
129 panel->SetFill(1, 1);
130 panel->SetToolTip(STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP);
131 vert->Add(std::move(panel));
132 }
133 return vert;
134}
135
136static constexpr std::initializer_list<NWidgetPart> _nested_graph_legend_widgets = {
138 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
139 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_KEY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
140 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
141 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
142 EndContainer(),
143 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GL_BACKGROUND),
145 EndContainer(),
146};
147
148static WindowDesc _graph_legend_desc(
149 WDP_AUTO, "graph_legend", 0, 0,
151 {},
152 _nested_graph_legend_widgets
153);
154
155static void ShowGraphLegend()
156{
157 AllocateWindowDescFront<GraphLegendWindow>(_graph_legend_desc, 0);
158}
159
162 OverflowSafeInt64 highest;
163 OverflowSafeInt64 lowest;
164};
165
166/******************/
167/* BASE OF GRAPHS */
168/*****************/
169
170struct BaseGraphWindow : Window {
171protected:
172 static const int GRAPH_MAX_DATASETS = 64;
173 static constexpr PixelColour GRAPH_BASE_COLOUR = GREY_SCALE(2);
174 static constexpr PixelColour GRAPH_GRID_COLOUR = GREY_SCALE(3);
175 static constexpr PixelColour GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1);
176 static constexpr PixelColour GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8);
177 static constexpr PixelColour GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5);
178 static const int GRAPH_NUM_MONTHS = 24;
179 static const int GRAPH_PAYMENT_RATE_STEPS = 20;
180 static const int PAYMENT_GRAPH_X_STEP_DAYS = 10;
181 static const int PAYMENT_GRAPH_X_STEP_SECONDS = 20;
182 static const int ECONOMY_YEAR_MINUTES = 12;
183 static const int ECONOMY_QUARTER_MINUTES = 3;
184 static const int ECONOMY_MONTH_MINUTES = 1;
185
186 static const TextColour GRAPH_AXIS_LABEL_COLOUR = TC_BLACK;
187
188 static const int MIN_GRAPH_NUM_LINES_Y = 9;
189 static const int MIN_GRID_PIXEL_SIZE = 20;
190
191 struct GraphScale {
192 StringID label = STR_NULL;
193 uint8_t month_increment = 0;
194 int16_t x_values_increment = 0;
195 const HistoryRange *history_range = nullptr;
196 };
197
198 static inline constexpr GraphScale MONTHLY_SCALE_WALLCLOCK[] = {
199 {STR_GRAPH_LAST_24_MINUTES_TIME_LABEL, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES, &HISTORY_MONTH},
200 {STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES, &HISTORY_QUARTER},
201 {STR_GRAPH_LAST_288_MINUTES_TIME_LABEL, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES, &HISTORY_YEAR},
202 };
203
204 static inline constexpr GraphScale MONTHLY_SCALE_CALENDAR[] = {
205 {STR_GRAPH_LAST_24_MONTHS, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES, &HISTORY_MONTH},
206 {STR_GRAPH_LAST_24_QUARTERS, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES, &HISTORY_QUARTER},
207 {STR_GRAPH_LAST_24_YEARS, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES, &HISTORY_YEAR},
208 };
209
210 uint64_t excluded_data = 0;
211 uint64_t excluded_range = 0;
212 uint64_t masked_range = 0;
213 uint8_t num_on_x_axis = 0;
214 uint8_t num_vert_lines = GRAPH_NUM_MONTHS;
215
216 /* The starting month and year that values are plotted against. */
218 TimerGameEconomy::Year year{};
219 uint8_t month_increment = 3;
220
221 bool draw_dates = true;
222
223 /* These values are used if the graph is being plotted against values
224 * rather than the dates specified by month and year. */
225 bool x_values_reversed = true;
226 int16_t x_values_increment = ECONOMY_QUARTER_MINUTES;
227
228 StringID format_str_y_axis{};
229
230 struct DataSet {
231 std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values;
232 PixelColour colour;
233 uint8_t exclude_bit;
234 uint8_t range_bit;
235 uint8_t dash;
236 };
237 std::vector<DataSet> data{};
238
239 std::span<const StringID> ranges{};
240 std::span<const GraphScale> scales{};
241 uint8_t selected_scale = 0;
242
243 uint8_t highlight_data = UINT8_MAX;
244 uint8_t highlight_range = UINT8_MAX;
245 bool highlight_state = false;
246
247 struct BaseFiller {
249
250 inline void MakeZero(uint i) const { this->dataset.values[i] = 0; }
251 inline void MakeInvalid(uint i) const { this->dataset.values[i] = INVALID_DATAPOINT; }
252 };
253
254 template <typename Tprojection>
256 Tprojection proj;
257
258 inline void Fill(uint i, const auto &data) const { this->dataset.values[i] = std::invoke(this->proj, data); }
259 };
260
266 std::span<const OverflowSafeInt64> GetDataSetRange(const DataSet &dataset) const
267 {
268 return {std::begin(dataset.values), std::begin(dataset.values) + this->num_on_x_axis};
269 }
270
277 ValuesInterval GetValuesInterval(int num_hori_lines) const
278 {
279 assert(num_hori_lines > 0);
280
281 ValuesInterval current_interval;
282 current_interval.highest = INT64_MIN;
283 current_interval.lowest = INT64_MAX;
284
285 for (const DataSet &dataset : this->data) {
286 if (HasBit(this->excluded_data, dataset.exclude_bit)) continue;
287 if (HasBit(this->excluded_range, dataset.range_bit)) continue;
288
289 for (const OverflowSafeInt64 &datapoint : this->GetDataSetRange(dataset)) {
290 if (datapoint != INVALID_DATAPOINT) {
291 current_interval.highest = std::max(current_interval.highest, datapoint);
292 current_interval.lowest = std::min(current_interval.lowest, datapoint);
293 }
294 }
295 }
296
297 /* Always include zero in the shown range. */
298 double abs_lower = (current_interval.lowest > 0) ? 0 : (double)abs(current_interval.lowest);
299 double abs_higher = (current_interval.highest < 0) ? 0 : (double)current_interval.highest;
300
301 /* Prevent showing values too close to the graph limits. */
302 abs_higher = (11.0 * abs_higher) / 10.0;
303 abs_lower = (11.0 * abs_lower) / 10.0;
304
305 int num_pos_grids;
306 OverflowSafeInt64 grid_size;
307
308 if (abs_lower != 0 || abs_higher != 0) {
309 /* The number of grids to reserve for the positive part is: */
310 num_pos_grids = (int)floor(0.5 + num_hori_lines * abs_higher / (abs_higher + abs_lower));
311
312 /* If there are any positive or negative values, force that they have at least one grid. */
313 if (num_pos_grids == 0 && abs_higher != 0) num_pos_grids++;
314 if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--;
315
316 /* Get the required grid size for each side and use the maximum one. */
317
318 OverflowSafeInt64 grid_size_higher = 0;
319 if (abs_higher > 0) {
320 grid_size_higher = abs_higher > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_higher);
321 grid_size_higher = (grid_size_higher + num_pos_grids - 1) / num_pos_grids;
322 }
323
324 OverflowSafeInt64 grid_size_lower = 0;
325 if (abs_lower > 0) {
326 grid_size_lower = abs_lower > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_lower);
327 grid_size_lower = (grid_size_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids);
328 }
329
330 grid_size = std::max(grid_size_higher, grid_size_lower);
331 } else {
332 /* If both values are zero, show an empty graph. */
333 num_pos_grids = num_hori_lines / 2;
334 grid_size = 1;
335 }
336
337 current_interval.highest = num_pos_grids * grid_size;
338 current_interval.lowest = -(num_hori_lines - num_pos_grids) * grid_size;
339 return current_interval;
340 }
341
348 uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
349 {
350 /* draw text strings on the y axis */
351 int64_t y_label = current_interval.highest;
352 int64_t y_label_separation = (current_interval.highest - current_interval.lowest) / num_hori_lines;
353
354 uint max_width = 0;
355
356 for (int i = 0; i < (num_hori_lines + 1); i++) {
357 Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label));
358 if (d.width > max_width) max_width = d.width;
359
360 y_label -= y_label_separation;
361 }
362
363 return max_width;
364 }
365
370 void DrawGraph(Rect r) const
371 {
372 uint x, y;
373 ValuesInterval interval;
374 int x_axis_offset;
375
376 /* the colours and cost array of GraphDrawer must accommodate
377 * both values for cargo and companies. So if any are higher, quit */
378 static_assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_COMPANIES);
379 assert(this->num_vert_lines > 0);
380
381 bool rtl = _current_text_dir == TD_RTL;
382
383 /* Rect r will be adjusted to contain just the graph, with labels being
384 * placed outside the area. */
385 r.top += ScaleGUITrad(5) + GetCharacterHeight(FS_SMALL) / 2;
386 r.bottom -= (this->draw_dates ? 2 : 1) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4);
387 r.left += ScaleGUITrad(rtl ? 5 : 9);
388 r.right -= ScaleGUITrad(rtl ? 9 : 5);
389
390 /* Initial number of horizontal lines. */
391 int num_hori_lines = 160 / ScaleGUITrad(MIN_GRID_PIXEL_SIZE);
392 /* For the rest of the height, the number of horizontal lines will increase more slowly. */
393 int resize = (r.bottom - r.top - 160) / (2 * ScaleGUITrad(MIN_GRID_PIXEL_SIZE));
394 if (resize > 0) num_hori_lines += resize;
395
396 interval = GetValuesInterval(num_hori_lines);
397
398 int label_width = GetYLabelWidth(interval, num_hori_lines);
399
400 if (rtl) {
401 r.right -= label_width;
402 } else {
403 r.left += label_width;
404 }
405
406 int x_sep = (r.right - r.left) / this->num_vert_lines;
407 int y_sep = (r.bottom - r.top) / num_hori_lines;
408
409 /* Redetermine right and bottom edge of graph to fit with the integer
410 * separation values. */
411 if (rtl) {
412 r.left = r.right - x_sep * this->num_vert_lines;
413 } else {
414 r.right = r.left + x_sep * this->num_vert_lines;
415 }
416 r.bottom = r.top + y_sep * num_hori_lines;
417
418 OverflowSafeInt64 interval_size = interval.highest + abs(interval.lowest);
419 /* Where to draw the X axis. Use floating point to avoid overflowing and results of zero. */
420 x_axis_offset = (int)((r.bottom - r.top) * (double)interval.highest / (double)interval_size);
421
422 /* Draw the background of the graph itself. */
423 GfxFillRect(r.left, r.top, r.right, r.bottom, GRAPH_BASE_COLOUR);
424
425 /* Draw the grid lines. */
426 int gridline_width = WidgetDimensions::scaled.bevel.top;
427 PixelColour grid_colour = GRAPH_GRID_COLOUR;
428
429 /* Don't draw the first line, as that's where the axis will be. */
430 if (rtl) {
431 x_sep = -x_sep;
432 x = r.right + x_sep;
433 } else {
434 x = r.left + x_sep;
435 }
436
437 for (int i = 1; i < this->num_vert_lines + 1; i++) {
438 /* If using wallclock units, we separate periods with a lighter line. */
440 grid_colour = (i % 4 == 0) ? GRAPH_YEAR_LINE_COLOUR : GRAPH_GRID_COLOUR;
441 }
442 GfxFillRect(x, r.top, x + gridline_width - 1, r.bottom, grid_colour);
443 x += x_sep;
444 }
445
446 /* Draw the horizontal grid lines. */
447 y = r.bottom;
448
449 for (int i = 0; i < (num_hori_lines + 1); i++) {
450 if (rtl) {
451 GfxFillRect(r.right + 1, y, r.right + ScaleGUITrad(3), y + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
452 } else {
453 GfxFillRect(r.left - ScaleGUITrad(3), y, r.left - 1, y + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
454 }
455 GfxFillRect(r.left, y, r.right + gridline_width - 1, y + gridline_width - 1, GRAPH_GRID_COLOUR);
456 y -= y_sep;
457 }
458
459 /* Draw the y axis. */
460 GfxFillRect(r.left, r.top, r.left + gridline_width - 1, r.bottom + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
461
462 /* Draw the x axis. */
463 y = x_axis_offset + r.top;
464 GfxFillRect(r.left, y, r.right + gridline_width - 1, y + gridline_width - 1, GRAPH_ZERO_LINE_COLOUR);
465
466 /* Find the largest value that will be drawn. */
467 if (this->num_on_x_axis == 0) return;
468
469 assert(this->num_on_x_axis > 0);
470
471 /* draw text strings on the y axis */
472 int64_t y_label = interval.highest;
473 int64_t y_label_separation = abs(interval.highest - interval.lowest) / num_hori_lines;
474
475 y = r.top - GetCharacterHeight(FS_SMALL) / 2;
476
477 for (int i = 0; i < (num_hori_lines + 1); i++) {
478 if (rtl) {
479 DrawString(r.right + ScaleGUITrad(4), r.right + label_width + ScaleGUITrad(4), y,
480 GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label),
482 } else {
483 DrawString(r.left - label_width - ScaleGUITrad(4), r.left - ScaleGUITrad(4), y,
484 GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label),
486 }
487
488 y_label -= y_label_separation;
489 y += y_sep;
490 }
491
492 x = rtl ? r.right : r.left;
493 y = r.bottom + ScaleGUITrad(2);
494
495 /* if there are not enough datapoints to fill the graph, align to the right */
496 x += (this->num_vert_lines - this->num_on_x_axis) * x_sep;
497
498 /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
499 if (this->draw_dates) {
500 TimerGameEconomy::Month mo = this->month;
501 TimerGameEconomy::Year yr = this->year;
502 for (int i = 0; i < this->num_on_x_axis; i++) {
503 if (rtl) {
504 DrawStringMultiLineWithClipping(x + x_sep, x, y, this->height,
505 GetString(mo == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + mo, yr),
507 } else {
508 DrawStringMultiLineWithClipping(x, x + x_sep, y, this->height,
509 GetString(mo == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + mo, yr),
511 }
512
513 mo += this->month_increment;
514 if (mo >= 12) {
515 mo = 0;
516 yr++;
517
518 /* Draw a lighter grid line between years. Top and bottom adjustments ensure we don't draw over top and bottom horizontal grid lines. */
519 GfxFillRect(x + x_sep, r.top + gridline_width, x + x_sep + gridline_width - 1, r.bottom - 1, GRAPH_YEAR_LINE_COLOUR);
520 }
521 x += x_sep;
522 }
523 } else {
524 /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates, and all graphs when using wallclock units). */
525 int16_t iterator;
526 uint16_t label;
527 if (this->x_values_reversed) {
528 label = this->x_values_increment * this->num_on_x_axis;
529 iterator = -this->x_values_increment;
530 } else {
531 label = this->x_values_increment;
532 iterator = this->x_values_increment;
533 }
534
535 for (int i = 0; i < this->num_on_x_axis; i++) {
536 if (rtl) {
537 DrawString(x + x_sep + 1, x - 1, y, GetString(STR_GRAPH_Y_LABEL_NUMBER, label), GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
538 } else {
539 DrawString(x + 1, x + x_sep - 1, y, GetString(STR_GRAPH_Y_LABEL_NUMBER, label), GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
540 }
541
542 label += iterator;
543 x += x_sep;
544 }
545 }
546
547 /* Draw lines and dots. */
548 uint linewidth = ScaleGUITrad(_settings_client.gui.graph_line_thickness);
549 uint pointwidth = ScaleGUITrad(_settings_client.gui.graph_line_thickness + 1);
550 uint pointoffs1 = pointwidth / 2;
551 uint pointoffs2 = pointwidth - pointoffs1;
552
553 auto draw_dataset = [&](const DataSet &dataset, PixelColour colour) {
554 if (HasBit(this->excluded_data, dataset.exclude_bit)) return;
555 if (HasBit(this->excluded_range, dataset.range_bit)) return;
556
557 /* Centre the dot between the grid lines. */
558 if (rtl) {
559 x = r.right + (x_sep / 2);
560 } else {
561 x = r.left + (x_sep / 2);
562 }
563
564 /* if there are not enough datapoints to fill the graph, align to the right */
565 x += (this->num_vert_lines - this->num_on_x_axis) * x_sep;
566
567 uint prev_x = INVALID_DATAPOINT_POS;
568 uint prev_y = INVALID_DATAPOINT_POS;
569
570 const uint dash = ScaleGUITrad(dataset.dash);
571 for (OverflowSafeInt64 datapoint : this->GetDataSetRange(dataset)) {
572 if (datapoint != INVALID_DATAPOINT) {
573 /*
574 * Check whether we need to reduce the 'accuracy' of the
575 * datapoint value and the highest value to split overflows.
576 * And when 'drawing' 'one million' or 'one million and one'
577 * there is no significant difference, so the least
578 * significant bits can just be removed.
579 *
580 * If there are more bits needed than would fit in a 32 bits
581 * integer, so at about 31 bits because of the sign bit, the
582 * least significant bits are removed.
583 */
584 int mult_range = FindLastBit<uint32_t>(x_axis_offset) + FindLastBit<uint64_t>(abs(datapoint));
585 int reduce_range = std::max(mult_range - 31, 0);
586
587 /* Handle negative values differently (don't shift sign) */
588 if (datapoint < 0) {
589 datapoint = -(abs(datapoint) >> reduce_range);
590 } else {
591 datapoint >>= reduce_range;
592 }
593 y = r.top + x_axis_offset - ((r.bottom - r.top) * datapoint) / (interval_size >> reduce_range);
594
595 /* Draw the point. */
596 GfxFillRect(x - pointoffs1, y - pointoffs1, x + pointoffs2, y + pointoffs2, colour);
597
598 /* Draw the line connected to the previous point. */
599 if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour, linewidth, dash);
600
601 prev_x = x;
602 prev_y = y;
603 } else {
604 prev_x = INVALID_DATAPOINT_POS;
605 prev_y = INVALID_DATAPOINT_POS;
606 }
607
608 x += x_sep;
609 }
610 };
611
612 /* Draw unhighlighted datasets. */
613 for (const DataSet &dataset : this->data) {
614 if (dataset.exclude_bit != this->highlight_data && dataset.range_bit != this->highlight_range) {
615 draw_dataset(dataset, dataset.colour);
616 }
617 }
618
619 /* If any dataset or range is highlighted, draw separately after the rest so they appear on top of all other
620 * data. Highlighted data is only drawn when highlight_state is set, otherwise it is invisible. */
621 if (this->highlight_state && (this->highlight_data != UINT8_MAX || this->highlight_range != UINT8_MAX)) {
622 for (const DataSet &dataset : this->data) {
623 if (dataset.exclude_bit == this->highlight_data || dataset.range_bit == this->highlight_range) {
624 draw_dataset(dataset, PC_WHITE);
625 }
626 }
627 }
628 }
629
630 BaseGraphWindow(WindowDesc &desc, StringID format_str_y_axis) :
631 Window(desc),
632 format_str_y_axis(format_str_y_axis)
633 {
635 }
636
637 const IntervalTimer<TimerWindow> blink_interval = {TIMER_BLINK_INTERVAL, [this](auto) {
638 /* If nothing is highlighted then no redraw is needed. */
639 if (this->highlight_data == UINT8_MAX && this->highlight_range == UINT8_MAX) return;
640
641 /* Toggle the highlight state and redraw. */
642 this->highlight_state = !this->highlight_state;
643 this->SetDirty();
644 }};
645
646 void UpdateMatrixSize(WidgetID widget, Dimension &size, Dimension &resize, auto labels)
647 {
648 size = {};
649 for (const StringID &str : labels) {
650 size = maxdim(size, GetStringBoundingBox(str, FS_SMALL));
651 }
652
655
656 /* Set fixed height for number of ranges. */
657 size.height *= static_cast<uint>(std::size(labels));
658
659 resize.width = 0;
660 resize.height = 0;
661 this->GetWidget<NWidgetCore>(widget)->SetMatrixDimension(1, ClampTo<uint32_t>(std::size(labels)));
662 }
663
664public:
665 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
666 {
667 switch (widget) {
669 this->UpdateMatrixSize(widget, size, resize, this->ranges);
670 break;
671
673 this->UpdateMatrixSize(widget, size, resize, this->scales | std::views::transform(&GraphScale::label));
674 break;
675
676 case WID_GRAPH_GRAPH: {
677 uint x_label_width = 0;
678
679 /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
680 if (this->draw_dates) {
681 uint yr = GetParamMaxValue(this->year.base(), 4, FS_SMALL);
682 for (uint mo = 0; mo < 12; ++mo) {
683 x_label_width = std::max(x_label_width, GetStringBoundingBox(GetString(mo == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + mo, yr)).width);
684 }
685 } else {
686 /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates). */
687 uint64_t max_value = GetParamMaxValue((this->num_on_x_axis + 1) * this->x_values_increment, 0, FS_SMALL);
688 x_label_width = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL_NUMBER, max_value)).width;
689 }
690
691 uint y_label_width = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, INT64_MAX)).width;
692
693 size.width = std::max<uint>(size.width, ScaleGUITrad(5) + y_label_width + this->num_vert_lines * (x_label_width + ScaleGUITrad(5)) + ScaleGUITrad(9));
694 size.height = std::max<uint>(size.height, ScaleGUITrad(5) + (1 + MIN_GRAPH_NUM_LINES_Y * 2 + (this->draw_dates ? 3 : 1)) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4));
695 size.height = std::max<uint>(size.height, size.width / 3);
696 break;
697 }
698
699 default: break;
700 }
701 }
702
703 void DrawWidget(const Rect &r, WidgetID widget) const override
704 {
705 switch (widget) {
706 case WID_GRAPH_GRAPH:
707 this->DrawGraph(r);
708 break;
709
711 uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
712 uint index = 0;
713 Rect line = r.WithHeight(line_height);
714 for (const auto &str : this->ranges) {
715 bool lowered = !HasBit(this->excluded_range, index) && !HasBit(this->masked_range, index);
716
717 /* Redraw frame if lowered */
718 if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
719
720 const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
721 DrawString(text, str, (this->highlight_state && this->highlight_range == index) ? TC_WHITE : TC_BLACK, SA_CENTER, false, FS_SMALL);
722
723 if (HasBit(this->masked_range, index)) {
724 GfxFillRect(line.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_BROWN, SHADE_DARKER), FILLRECT_CHECKER);
725 }
726
727 line = line.Translate(0, line_height);
728 ++index;
729 }
730 break;
731 }
732
734 uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
735 uint8_t selected_month_increment = this->scales[this->selected_scale].month_increment;
736 Rect line = r.WithHeight(line_height);
737 for (const auto &scale : this->scales) {
738 /* Redraw frame if selected */
739 if (selected_month_increment == scale.month_increment) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
740
741 DrawString(line.Shrink(WidgetDimensions::scaled.framerect), scale.label, TC_BLACK, SA_CENTER, false, FS_SMALL);
742
743 line = line.Translate(0, line_height);
744 }
745 break;
746 }
747
748 default: break;
749 }
750 }
751
752 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
753 {
754 /* Clicked on legend? */
755 switch (widget) {
757 ShowGraphLegend();
758 break;
759
761 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
762
763 if (HasBit(this->masked_range, row)) break;
764 ToggleBit(this->excluded_range, row);
765 SndClickBeep();
766 this->SetDirty();
767 break;
768 }
769
771 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
772 const auto &scale = this->scales[row];
773 if (this->selected_scale != row) {
774 this->selected_scale = row;
775 this->month_increment = scale.month_increment;
776 this->x_values_increment = scale.x_values_increment;
777 this->InvalidateData();
778 }
779 break;
780 }
781
782 default: break;
783 }
784 }
785
786 void OnMouseOver(Point pt, WidgetID widget) override
787 {
788 /* Test if a range should be highlighted. */
789 uint8_t new_highlight_range = UINT8_MAX;
790 if (widget == WID_GRAPH_RANGE_MATRIX) {
791 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
792 if (!HasBit(this->excluded_range, row)) new_highlight_range = static_cast<uint8_t>(row);
793 }
794
795 /* Test if a dataset should be highlighted. */
796 uint8_t new_highlight_data = UINT8_MAX;
797 if (widget == WID_GRAPH_MATRIX) {
798 auto dataset_index = this->GetDatasetIndex(pt.y);
799 if (dataset_index.has_value() && !HasBit(this->excluded_data, *dataset_index)) new_highlight_data = *dataset_index;
800 }
801
802 if (this->highlight_data == new_highlight_data && this->highlight_range == new_highlight_range) return;
803
804 /* Range or data set highlight has changed, set and redraw. */
805 this->highlight_data = new_highlight_data;
806 this->highlight_range = new_highlight_range;
807 this->highlight_state = true;
808 this->SetDirty();
809 }
810
811 void OnGameTick() override
812 {
813 this->UpdateStatistics(false);
814 }
815
821 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
822 {
823 if (!gui_scope) return;
824 this->UpdateStatistics(true);
825 }
826
831 virtual void UpdateStatistics(bool initialize) = 0;
832
838 virtual std::optional<uint8_t> GetDatasetIndex([[maybe_unused]] int y) { return std::nullopt; }
839};
840
841class BaseCompanyGraphWindow : public BaseGraphWindow {
842public:
843 BaseCompanyGraphWindow(WindowDesc &desc, StringID format_str_y_axis) : BaseGraphWindow(desc, format_str_y_axis) {}
844
845 void InitializeWindow(WindowNumber number)
846 {
847 /* Initialise the dataset */
848 this->UpdateStatistics(true);
849
850 this->CreateNestedTree();
851
852 auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
853 wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? STR_GRAPH_LAST_72_MINUTES_TIME_LABEL : STR_EMPTY);
854
855 this->FinishInitNested(number);
856 }
857
858 void UpdateStatistics(bool initialize) override
859 {
860 CompanyMask excluded_companies = _legend_excluded_companies;
861
862 /* Exclude the companies which aren't valid */
863 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
864 if (!Company::IsValidID(c)) excluded_companies.Set(c);
865 }
866
867 uint8_t nums = 0;
868 for (const Company *c : Company::Iterate()) {
869 nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
870 }
871
872 int mo = (TimerGameEconomy::month / this->month_increment - nums) * this->month_increment;
873 auto yr = TimerGameEconomy::year;
874 while (mo < 0) {
875 yr--;
876 mo += 12;
877 }
878
879 if (!initialize && this->excluded_data == excluded_companies.base() && this->num_on_x_axis == nums &&
880 this->year == yr && this->month == mo) {
881 /* There's no reason to get new stats */
882 return;
883 }
884
885 this->excluded_data = excluded_companies.base();
886 this->num_on_x_axis = nums;
887 this->year = yr;
888 this->month = mo;
889
890 this->data.clear();
891 for (CompanyID k = CompanyID::Begin(); k < MAX_COMPANIES; ++k) {
892 const Company *c = Company::GetIfValid(k);
893 if (c == nullptr) continue;
894
895 DataSet &dataset = this->data.emplace_back();
896 dataset.colour = GetColourGradient(c->colour, SHADE_LIGHTER);
897 dataset.exclude_bit = k.base();
898
899 for (int j = this->num_on_x_axis, i = 0; --j >= 0;) {
900 if (j >= c->num_valid_stat_ent) {
901 dataset.values[i] = INVALID_DATAPOINT;
902 } else {
903 /* Ensure we never assign INVALID_DATAPOINT, as that has another meaning.
904 * Instead, use the value just under it. Hopefully nobody will notice. */
905 dataset.values[i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1);
906 }
907 i++;
908 }
909 }
910 }
911
918 virtual OverflowSafeInt64 GetGraphData(const Company *c, int j) = 0;
919};
920
921
922/********************/
923/* OPERATING PROFIT */
924/********************/
925
926struct OperatingProfitGraphWindow : BaseCompanyGraphWindow {
927 OperatingProfitGraphWindow(WindowDesc &desc, WindowNumber window_number) :
928 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
929 {
930 this->num_on_x_axis = GRAPH_NUM_MONTHS;
931 this->num_vert_lines = GRAPH_NUM_MONTHS;
932 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
933
934 this->InitializeWindow(window_number);
935 }
936
937 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
938 {
939 return c->old_economy[j].income + c->old_economy[j].expenses;
940 }
941};
942
943static constexpr std::initializer_list<NWidgetPart> _nested_operating_profit_widgets = {
945 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
946 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_OPERATING_PROFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
947 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
948 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
949 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
950 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
951 EndContainer(),
952 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
953 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 160), SetFill(1, 1), SetResize(1, 1),
955 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
956 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
957 EndContainer(),
958 EndContainer(),
959};
960
961static WindowDesc _operating_profit_desc(
962 WDP_AUTO, "graph_operating_profit", 0, 0,
964 {},
965 _nested_operating_profit_widgets
966);
967
968
969void ShowOperatingProfitGraph()
970{
972}
973
974
975/****************/
976/* INCOME GRAPH */
977/****************/
978
979struct IncomeGraphWindow : BaseCompanyGraphWindow {
980 IncomeGraphWindow(WindowDesc &desc, WindowNumber window_number) :
981 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
982 {
983 this->num_on_x_axis = GRAPH_NUM_MONTHS;
984 this->num_vert_lines = GRAPH_NUM_MONTHS;
985 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
986
987 this->InitializeWindow(window_number);
988 }
989
990 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
991 {
992 return c->old_economy[j].income;
993 }
994};
995
996static constexpr std::initializer_list<NWidgetPart> _nested_income_graph_widgets = {
998 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
999 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_INCOME_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1000 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1001 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1002 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1003 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1004 EndContainer(),
1005 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1006 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
1008 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1009 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1010 EndContainer(),
1011 EndContainer(),
1012};
1013
1014static WindowDesc _income_graph_desc(
1015 WDP_AUTO, "graph_income", 0, 0,
1017 {},
1018 _nested_income_graph_widgets
1019);
1020
1021void ShowIncomeGraph()
1022{
1023 AllocateWindowDescFront<IncomeGraphWindow>(_income_graph_desc, 0);
1024}
1025
1026/*******************/
1027/* DELIVERED CARGO */
1028/*******************/
1029
1030struct DeliveredCargoGraphWindow : BaseCompanyGraphWindow {
1031 DeliveredCargoGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1032 BaseCompanyGraphWindow(desc, STR_JUST_COMMA)
1033 {
1034 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1035 this->num_vert_lines = GRAPH_NUM_MONTHS;
1036 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1037
1038 this->InitializeWindow(window_number);
1039 }
1040
1041 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1042 {
1043 return c->old_economy[j].delivered_cargo.GetSum<OverflowSafeInt64>();
1044 }
1045};
1046
1047static constexpr std::initializer_list<NWidgetPart> _nested_delivered_cargo_graph_widgets = {
1049 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1050 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_CARGO_DELIVERED_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1051 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1052 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1053 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1054 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1055 EndContainer(),
1056 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1057 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
1059 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1060 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1061 EndContainer(),
1062 EndContainer(),
1063};
1064
1065static WindowDesc _delivered_cargo_graph_desc(
1066 WDP_AUTO, "graph_delivered_cargo", 0, 0,
1068 {},
1069 _nested_delivered_cargo_graph_widgets
1070);
1071
1072void ShowDeliveredCargoGraph()
1073{
1074 AllocateWindowDescFront<DeliveredCargoGraphWindow>(_delivered_cargo_graph_desc, 0);
1075}
1076
1077/***********************/
1078/* PERFORMANCE HISTORY */
1079/***********************/
1080
1081struct PerformanceHistoryGraphWindow : BaseCompanyGraphWindow {
1082 PerformanceHistoryGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1083 BaseCompanyGraphWindow(desc, STR_JUST_COMMA)
1084 {
1085 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1086 this->num_vert_lines = GRAPH_NUM_MONTHS;
1087 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1088
1089 this->InitializeWindow(window_number);
1090 }
1091
1092 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1093 {
1094 return c->old_economy[j].performance_history;
1095 }
1096
1097 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1098 {
1099 if (widget == WID_PHG_DETAILED_PERFORMANCE) ShowPerformanceRatingDetail();
1100 this->BaseGraphWindow::OnClick(pt, widget, click_count);
1101 }
1102};
1103
1104static constexpr std::initializer_list<NWidgetPart> _nested_performance_history_widgets = {
1106 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1107 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_COMPANY_PERFORMANCE_RATINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1108 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_PHG_DETAILED_PERFORMANCE), SetMinimalSize(50, 0), SetStringTip(STR_PERFORMANCE_DETAIL_KEY, STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP),
1109 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1110 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1111 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1112 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1113 EndContainer(),
1114 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1115 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
1117 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1118 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1119 EndContainer(),
1120 EndContainer(),
1121};
1122
1123static WindowDesc _performance_history_desc(
1124 WDP_AUTO, "graph_performance", 0, 0,
1126 {},
1127 _nested_performance_history_widgets
1128);
1129
1130void ShowPerformanceHistoryGraph()
1131{
1132 AllocateWindowDescFront<PerformanceHistoryGraphWindow>(_performance_history_desc, 0);
1133}
1134
1135/*****************/
1136/* COMPANY VALUE */
1137/*****************/
1138
1139struct CompanyValueGraphWindow : BaseCompanyGraphWindow {
1140 CompanyValueGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1141 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
1142 {
1143 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1144 this->num_vert_lines = GRAPH_NUM_MONTHS;
1145 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1146
1147 this->InitializeWindow(window_number);
1148 }
1149
1150 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1151 {
1152 return c->old_economy[j].company_value;
1153 }
1154};
1155
1156static constexpr std::initializer_list<NWidgetPart> _nested_company_value_graph_widgets = {
1158 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1159 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_COMPANY_VALUES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1160 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1161 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1162 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1163 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1164 EndContainer(),
1165 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1166 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
1168 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1169 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1170 EndContainer(),
1171 EndContainer(),
1172};
1173
1174static WindowDesc _company_value_graph_desc(
1175 WDP_AUTO, "graph_company_value", 0, 0,
1177 {},
1178 _nested_company_value_graph_widgets
1179);
1180
1181void ShowCompanyValueGraph()
1182{
1183 AllocateWindowDescFront<CompanyValueGraphWindow>(_company_value_graph_desc, 0);
1184}
1185
1186struct BaseCargoGraphWindow : BaseGraphWindow {
1187 Scrollbar *vscroll = nullptr;
1188 uint line_height = 0;
1189 uint legend_width = 0;
1190
1191 CargoTypes cargo_types{};
1192
1193 BaseCargoGraphWindow(WindowDesc &desc, StringID format_str_y_axis) : BaseGraphWindow(desc, format_str_y_axis) {}
1194
1195 void InitializeWindow(WindowNumber number, StringID footer_wallclock = STR_NULL, StringID footer_calendar = STR_NULL)
1196 {
1197 this->CreateNestedTree();
1198
1199 this->excluded_range = this->masked_range;
1200 this->cargo_types = this->GetCargoTypes(number);
1201
1203 this->vscroll->SetCount(CountBits(this->cargo_types));
1204
1205 auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
1206 wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? footer_wallclock : footer_calendar);
1207
1208 this->FinishInitNested(number);
1209
1210 /* Initialise the dataset */
1211 this->InvalidateData();
1212 }
1213
1219 virtual CargoTypes GetCargoTypes(WindowNumber number) const = 0;
1220
1225 virtual CargoTypes &GetExcludedCargoTypes() const = 0;
1226
1227 std::optional<uint8_t> GetDatasetIndex(int y) override
1228 {
1229 int row = this->vscroll->GetScrolledRowFromWidget(y, this, WID_GRAPH_MATRIX);
1230 if (row >= this->vscroll->GetCount()) return std::nullopt;
1231
1232 for (const CargoSpec *cs : _sorted_cargo_specs) {
1233 if (!HasBit(this->cargo_types, cs->Index())) continue;
1234 if (row-- > 0) continue;
1235
1236 return cs->Index();
1237 }
1238
1239 return std::nullopt;
1240 }
1241
1242 void OnInit() override
1243 {
1244 /* Width of the legend blob. */
1245 this->legend_width = GetCharacterHeight(FS_SMALL) * 9 / 6;
1246 }
1247
1248 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1249 {
1250 if (widget != WID_GRAPH_MATRIX) {
1251 BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
1252 return;
1253 }
1254
1255 size.height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
1256
1257 for (CargoType cargo_type : SetCargoBitIterator(this->cargo_types)) {
1258 const CargoSpec *cs = CargoSpec::Get(cargo_type);
1259
1260 Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
1261 d.width += this->legend_width + WidgetDimensions::scaled.hsep_normal; // colour field
1262 d.width += WidgetDimensions::scaled.framerect.Horizontal();
1263 d.height += WidgetDimensions::scaled.framerect.Vertical();
1264 size = maxdim(d, size);
1265 }
1266
1267 this->line_height = size.height;
1268 size.height = this->line_height * 11; /* Default number of cargo types in most climates. */
1269 resize.width = 0;
1270 fill.height = resize.height = this->line_height;
1271 }
1272
1273 void DrawWidget(const Rect &r, WidgetID widget) const override
1274 {
1275 if (widget != WID_GRAPH_MATRIX) {
1276 BaseGraphWindow::DrawWidget(r, widget);
1277 return;
1278 }
1279
1280 bool rtl = _current_text_dir == TD_RTL;
1281
1282 int pos = this->vscroll->GetPosition();
1283 int max = pos + this->vscroll->GetCapacity();
1284
1285 Rect line = r.WithHeight(this->line_height);
1286
1287 for (const CargoSpec *cs : _sorted_cargo_specs) {
1288 if (!HasBit(this->cargo_types, cs->Index())) continue;
1289
1290 if (pos-- > 0) continue;
1291 if (--max < 0) break;
1292
1293 bool lowered = !HasBit(this->excluded_data, cs->Index());
1294
1295 /* Redraw frame if lowered */
1296 if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
1297
1298 const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
1299
1300 /* Cargo-colour box with outline */
1301 const Rect cargo = text.WithWidth(this->legend_width, rtl);
1302 GfxFillRect(cargo, PC_BLACK);
1303 PixelColour pc = cs->legend_colour;
1304 if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK;
1306
1307 /* Cargo name */
1308 DrawString(text.Indent(this->legend_width + WidgetDimensions::scaled.hsep_normal, rtl), GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
1309
1310 line = line.Translate(0, this->line_height);
1311 }
1312 }
1313
1314 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1315 {
1316 switch (widget) {
1318 /* Remove all cargoes from the excluded lists. */
1319 this->GetExcludedCargoTypes() = {};
1320 this->excluded_data = this->GetExcludedCargoTypes();
1321 this->SetDirty();
1322 break;
1323
1325 /* Add all cargoes to the excluded lists. */
1326 this->GetExcludedCargoTypes() = this->cargo_types;
1327 this->excluded_data = this->GetExcludedCargoTypes();
1328 this->SetDirty();
1329 break;
1330 }
1331
1332 case WID_GRAPH_MATRIX: {
1333 int row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GRAPH_MATRIX);
1334 if (row >= this->vscroll->GetCount()) return;
1335
1336 SndClickBeep();
1337
1338 for (const CargoSpec *cs : _sorted_cargo_specs) {
1339 if (!HasBit(this->cargo_types, cs->Index())) continue;
1340 if (row-- > 0) continue;
1341
1342 ToggleBit(this->GetExcludedCargoTypes(), cs->Index());
1343 this->excluded_data = this->GetExcludedCargoTypes();
1344 this->SetDirty();
1345 break;
1346 }
1347 break;
1348 }
1349
1350 default:
1351 this->BaseGraphWindow::OnClick(pt, widget, click_count);
1352 break;
1353 }
1354 }
1355
1356 void OnResize() override
1357 {
1358 this->vscroll->SetCapacityFromWidget(this, WID_GRAPH_MATRIX);
1359 }
1360};
1361
1362/*****************/
1363/* PAYMENT RATES */
1364/*****************/
1365
1366struct PaymentRatesGraphWindow : BaseCargoGraphWindow {
1367 static inline CargoTypes excluded_cargo_types{};
1368
1369 PaymentRatesGraphWindow(WindowDesc &desc, WindowNumber window_number) : BaseCargoGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
1370 {
1371 this->num_on_x_axis = GRAPH_PAYMENT_RATE_STEPS;
1372 this->num_vert_lines = GRAPH_PAYMENT_RATE_STEPS;
1373 this->draw_dates = false;
1374
1375 this->x_values_reversed = false;
1376 /* The x-axis is labeled in either seconds or days. A day is two seconds, so we adjust the label if needed. */
1378
1379 this->InitializeWindow(window_number, STR_GRAPH_CARGO_PAYMENT_RATES_SECONDS, STR_GRAPH_CARGO_PAYMENT_RATES_DAYS);
1380 }
1381
1382 CargoTypes GetCargoTypes(WindowNumber) const override
1383 {
1384 return _standard_cargo_mask;
1385 }
1386
1387 CargoTypes &GetExcludedCargoTypes() const override
1388 {
1389 return PaymentRatesGraphWindow::excluded_cargo_types;
1390 }
1391
1397 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1398 {
1399 if (!gui_scope) return;
1400 this->UpdatePaymentRates();
1401 }
1402
1404 const IntervalTimer<TimerWindow> update_payment_interval = {std::chrono::seconds(3), [this](auto) {
1405 this->UpdatePaymentRates();
1406 }};
1407
1408 void UpdateStatistics(bool) override {}
1409
1414 {
1415 this->excluded_data = this->GetExcludedCargoTypes();
1416
1417 this->data.clear();
1418 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1419 DataSet &dataset = this->data.emplace_back();
1420 dataset.colour = cs->legend_colour;
1421 dataset.exclude_bit = cs->Index();
1422
1423 for (uint j = 0; j != this->num_on_x_axis; j++) {
1424 dataset.values[j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
1425 }
1426 }
1427 }
1428};
1429
1430static constexpr std::initializer_list<NWidgetPart> _nested_cargo_payment_rates_widgets = {
1432 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1433 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1434 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1435 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1436 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1437 EndContainer(),
1438 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1439 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_HEADER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetStringTip(STR_GRAPH_CARGO_PAYMENT_RATES_TITLE), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1441 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1443 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1444 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1445 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1448 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),
1450 EndContainer(),
1451 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1452 EndContainer(),
1453 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1454 EndContainer(),
1456 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1457 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1458 EndContainer(),
1459 EndContainer(),
1460};
1461
1462static WindowDesc _cargo_payment_rates_desc(
1463 WDP_AUTO, "graph_cargo_payment_rates", 0, 0,
1465 {},
1466 _nested_cargo_payment_rates_widgets
1467);
1468
1469
1470void ShowCargoPaymentRates()
1471{
1472 AllocateWindowDescFront<PaymentRatesGraphWindow>(_cargo_payment_rates_desc, 0);
1473}
1474
1475/*****************************/
1476/* PERFORMANCE RATING DETAIL */
1477/*****************************/
1478
1479struct PerformanceRatingDetailWindow : Window {
1480 static CompanyID company;
1481 int timeout = 0;
1482 uint score_info_left = 0;
1483 uint score_info_right = 0;
1484 uint bar_left = 0;
1485 uint bar_right = 0;
1486 uint bar_width = 0;
1487 uint bar_height = 0;
1488 uint score_detail_left = 0;
1489 uint score_detail_right = 0;
1490
1491 PerformanceRatingDetailWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
1492 {
1493 this->UpdateCompanyStats();
1494
1495 this->InitNested(window_number);
1496 this->OnInvalidateData(CompanyID::Invalid().base());
1497 }
1498
1499 void UpdateCompanyStats()
1500 {
1501 /* Update all company stats with the current data
1502 * (this is because _score_info is not saved to a savegame) */
1503 for (Company *c : Company::Iterate()) {
1505 }
1506
1507 this->timeout = Ticks::DAY_TICKS * 5;
1508 }
1509
1510 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1511 {
1512 switch (widget) {
1514 this->bar_height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.fullbevel.Vertical();
1515 size.height = this->bar_height + WidgetDimensions::scaled.matrix.Vertical();
1516
1517 uint score_info_width = 0;
1518 for (ScoreID i = ScoreID::Begin; i < ScoreID::End; i++) {
1519 score_info_width = std::max(score_info_width, GetStringBoundingBox(STR_PERFORMANCE_DETAIL_VEHICLES + to_underlying(i)).width);
1520 }
1521 score_info_width += GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxValue(1000))).width + WidgetDimensions::scaled.hsep_wide;
1522
1523 this->bar_width = GetStringBoundingBox(GetString(STR_PERFORMANCE_DETAIL_PERCENT, GetParamMaxValue(100))).width + WidgetDimensions::scaled.hsep_indent * 2; // Wide bars!
1524
1525 /* At this number we are roughly at the max; it can become wider,
1526 * but then you need at 1000 times more money. At that time you're
1527 * not that interested anymore in the last few digits anyway.
1528 * The 500 is because 999 999 500 to 999 999 999 are rounded to
1529 * 1 000 M, and not 999 999 k. Use negative numbers to account for
1530 * the negative income/amount of money etc. as well. */
1531 int max = -(999999999 - 500);
1532
1533 /* Scale max for the display currency. Prior to rendering the value
1534 * is converted into the display currency, which may cause it to
1535 * raise significantly. We need to compensate for that since {{CURRCOMPACT}}
1536 * is used, which can produce quite short renderings of very large
1537 * values. Otherwise the calculated width could be too narrow.
1538 * Note that it doesn't work if there was a currency with an exchange
1539 * rate greater than max.
1540 * When the currency rate is more than 1000, the 999 999 k becomes at
1541 * least 999 999 M which roughly is equally long. Furthermore if the
1542 * exchange rate is that high, 999 999 k is usually not enough anymore
1543 * to show the different currency numbers. */
1544 if (GetCurrency().rate < 1000) max /= GetCurrency().rate;
1545 uint score_detail_width = GetStringBoundingBox(GetString(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, max, max)).width;
1546
1547 size.width = WidgetDimensions::scaled.frametext.Horizontal() + score_info_width + WidgetDimensions::scaled.hsep_wide + this->bar_width + WidgetDimensions::scaled.hsep_wide + score_detail_width;
1548 uint left = WidgetDimensions::scaled.frametext.left;
1549 uint right = size.width - WidgetDimensions::scaled.frametext.right;
1550
1551 bool rtl = _current_text_dir == TD_RTL;
1552 this->score_info_left = rtl ? right - score_info_width : left;
1553 this->score_info_right = rtl ? right : left + score_info_width;
1554
1555 this->score_detail_left = rtl ? left : right - score_detail_width;
1556 this->score_detail_right = rtl ? left + score_detail_width : right;
1557
1558 this->bar_left = left + (rtl ? score_detail_width : score_info_width) + WidgetDimensions::scaled.hsep_wide;
1559 this->bar_right = this->bar_left + this->bar_width - 1;
1560 break;
1561 }
1562 }
1563
1564 void DrawWidget(const Rect &r, WidgetID widget) const override
1565 {
1566 /* No need to draw when there's nothing to draw */
1567 if (this->company == CompanyID::Invalid()) return;
1568
1570 if (this->IsWidgetDisabled(widget)) return;
1571 CompanyID cid = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1572 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
1573 DrawCompanyIcon(cid, CentreBounds(r.left, r.right, sprite_size.width), CentreBounds(r.top, r.bottom, sprite_size.height));
1574 return;
1575 }
1576
1577 if (!IsInsideMM(widget, WID_PRD_SCORE_FIRST, WID_PRD_SCORE_LAST + 1)) return;
1578
1579 ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
1580
1581 /* The colours used to show how the progress is going */
1582 PixelColour colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
1583 PixelColour colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
1584
1585 /* Draw all the score parts */
1586 int64_t val = _score_part[company][score_type];
1587 int64_t needed = _score_info[score_type].needed;
1588 int score = _score_info[score_type].score;
1589
1590 /* ScoreID::Total has its own rules ;) */
1591 if (score_type == ScoreID::Total) {
1592 for (ScoreID i = ScoreID::Begin; i < ScoreID::End; i++) score += _score_info[i].score;
1593 needed = SCORE_MAX;
1594 }
1595
1596 uint bar_top = CentreBounds(r.top, r.bottom, this->bar_height);
1597 uint text_top = CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
1598
1599 DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + to_underlying(score_type));
1600
1601 /* Draw the score */
1602 DrawString(this->score_info_left, this->score_info_right, text_top, GetString(STR_JUST_COMMA, score), TC_BLACK, SA_RIGHT);
1603
1604 /* Calculate the %-bar */
1605 uint x = Clamp<int64_t>(val, 0, needed) * this->bar_width / needed;
1606 bool rtl = _current_text_dir == TD_RTL;
1607 if (rtl) {
1608 x = this->bar_right - x;
1609 } else {
1610 x = this->bar_left + x;
1611 }
1612
1613 /* Draw the bar */
1614 if (x != this->bar_left) GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height - 1, rtl ? colour_notdone : colour_done);
1615 if (x != this->bar_right) GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height - 1, rtl ? colour_done : colour_notdone);
1616
1617 /* Draw it */
1618 DrawString(this->bar_left, this->bar_right, text_top, GetString(STR_PERFORMANCE_DETAIL_PERCENT, Clamp<int64_t>(val, 0, needed) * 100 / needed), TC_FROMSTRING, SA_HOR_CENTER);
1619
1620 /* ScoreID::Loan is inverted */
1621 if (score_type == ScoreID::Loan) val = needed - val;
1622
1623 /* Draw the amount we have against what is needed
1624 * For some of them it is in currency format */
1625 switch (score_type) {
1626 case ScoreID::MinProfit:
1627 case ScoreID::MinIncome:
1628 case ScoreID::MaxIncome:
1629 case ScoreID::Money:
1630 case ScoreID::Loan:
1631 DrawString(this->score_detail_left, this->score_detail_right, text_top, GetString(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, val, needed));
1632 break;
1633 default:
1634 DrawString(this->score_detail_left, this->score_detail_right, text_top, GetString(STR_PERFORMANCE_DETAIL_AMOUNT_INT, val, needed));
1635 break;
1636 }
1637 }
1638
1639 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1640 {
1641 /* Check which button is clicked */
1643 /* Is it no on disable? */
1644 if (!this->IsWidgetDisabled(widget)) {
1645 this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1646 this->company = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1647 this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1648 this->SetDirty();
1649 }
1650 }
1651 }
1652
1653 void OnGameTick() override
1654 {
1655 /* Update the company score every 5 days */
1656 if (--this->timeout == 0) {
1657 this->UpdateCompanyStats();
1658 this->SetDirty();
1659 }
1660 }
1661
1667 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1668 {
1669 if (!gui_scope) return;
1670 /* Disable the companies who are not active */
1671 for (CompanyID i = CompanyID::Begin(); i < MAX_COMPANIES; ++i) {
1673 }
1674
1675 /* Check if the currently selected company is still active. */
1676 if (this->company != CompanyID::Invalid() && !Company::IsValidID(this->company)) {
1677 /* Raise the widget for the previous selection. */
1678 this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1679 this->company = CompanyID::Invalid();
1680 }
1681
1682 if (this->company == CompanyID::Invalid()) {
1683 for (const Company *c : Company::Iterate()) {
1684 this->company = c->index;
1685 break;
1686 }
1687 }
1688
1689 /* Make sure the widget is lowered */
1690 if (this->company != CompanyID::Invalid()) {
1691 this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1692 }
1693 }
1694};
1695
1696CompanyID PerformanceRatingDetailWindow::company = CompanyID::Invalid();
1697
1698/*******************************/
1699/* INDUSTRY PRODUCTION HISTORY */
1700/*******************************/
1701
1702struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
1703 static inline constexpr StringID RANGE_LABELS[] = {
1704 STR_GRAPH_INDUSTRY_RANGE_PRODUCED,
1705 STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED,
1706 STR_GRAPH_INDUSTRY_RANGE_DELIVERED,
1707 STR_GRAPH_INDUSTRY_RANGE_WAITING,
1708 };
1709
1710 static inline CargoTypes excluded_cargo_types{};
1711
1712 IndustryProductionGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1713 BaseCargoGraphWindow(desc, STR_JUST_COMMA)
1714 {
1715 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1716 this->num_vert_lines = GRAPH_NUM_MONTHS;
1717 this->month_increment = 1;
1718 this->x_values_increment = ECONOMY_MONTH_MINUTES;
1719 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1720 this->ranges = RANGE_LABELS;
1721
1723 if (!i->IsCargoProduced()) this->masked_range = (1U << 0) | (1U << 1);
1724 if (!i->IsCargoAccepted()) this->masked_range = (1U << 2) | (1U << 3);
1725
1726 this->InitializeWindow(window_number);
1727 }
1728
1729 void OnInit() override
1730 {
1732
1733 this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
1734 }
1735
1736 CargoTypes GetCargoTypes(WindowNumber window_number) const override
1737 {
1738 CargoTypes cargo_types{};
1740 for (const auto &a : i->accepted) {
1741 if (IsValidCargoType(a.cargo)) SetBit(cargo_types, a.cargo);
1742 }
1743 for (const auto &p : i->produced) {
1744 if (IsValidCargoType(p.cargo)) SetBit(cargo_types, p.cargo);
1745 }
1746 return cargo_types;
1747 }
1748
1749 CargoTypes &GetExcludedCargoTypes() const override
1750 {
1751 return IndustryProductionGraphWindow::excluded_cargo_types;
1752 }
1753
1754 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1755 {
1756 if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_INDUSTRY_CAPTION, this->window_number);
1757
1758 return this->Window::GetWidgetString(widget, stringid);
1759 }
1760
1761 void UpdateStatistics(bool initialize) override
1762 {
1763 int mo = (TimerGameEconomy::month / this->month_increment - this->num_vert_lines) * this->month_increment;
1764 auto yr = TimerGameEconomy::year;
1765 while (mo < 0) {
1766 yr--;
1767 mo += 12;
1768 }
1769
1770 if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1771 /* There's no reason to get new stats */
1772 return;
1773 }
1774
1775 this->excluded_data = this->GetExcludedCargoTypes();
1776 this->year = yr;
1777 this->month = mo;
1778
1779 const Industry *i = Industry::Get(this->window_number);
1780
1781 this->data.clear();
1782 this->data.reserve(
1783 2 * std::ranges::count_if(i->produced, &IsValidCargoType, &Industry::ProducedCargo::cargo) +
1784 2 * std::ranges::count_if(i->accepted, &IsValidCargoType, &Industry::AcceptedCargo::cargo));
1785
1786 for (const auto &p : i->produced) {
1787 if (!IsValidCargoType(p.cargo)) continue;
1788 const CargoSpec *cs = CargoSpec::Get(p.cargo);
1789
1790 DataSet &produced = this->data.emplace_back();
1791 produced.colour = cs->legend_colour;
1792 produced.exclude_bit = cs->Index();
1793 produced.range_bit = 0;
1794
1795 DataSet &transported = this->data.emplace_back();
1796 transported.colour = cs->legend_colour;
1797 transported.exclude_bit = cs->Index();
1798 transported.range_bit = 1;
1799 transported.dash = 2;
1800
1801 FillFromHistory<GRAPH_NUM_MONTHS>(p.history, i->valid_history, *this->scales[this->selected_scale].history_range,
1804 }
1805
1806 for (const auto &a : i->accepted) {
1807 if (!IsValidCargoType(a.cargo)) continue;
1808 const CargoSpec *cs = CargoSpec::Get(a.cargo);
1809
1810 DataSet &accepted = this->data.emplace_back();
1811 accepted.colour = cs->legend_colour;
1812 accepted.exclude_bit = cs->Index();
1813 accepted.range_bit = 2;
1814 accepted.dash = 1;
1815
1816 DataSet &waiting = this->data.emplace_back();
1817 waiting.colour = cs->legend_colour;
1818 waiting.exclude_bit = cs->Index();
1819 waiting.range_bit = 3;
1820 waiting.dash = 4;
1821
1822 FillFromHistory<GRAPH_NUM_MONTHS>(a.history.get(), i->valid_history, *this->scales[this->selected_scale].history_range,
1823 Filler{{accepted}, &Industry::AcceptedHistory::accepted},
1825 }
1826
1827 this->SetDirty();
1828 }
1829};
1830
1831static constexpr std::initializer_list<NWidgetPart> _nested_industry_production_widgets = {
1833 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1834 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION),
1835 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1836 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1837 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1838 EndContainer(),
1839 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1841 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1843 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1844 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_TOGGLE_RANGE),
1846 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1847 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1850 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),
1852 EndContainer(),
1854 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_SELECT_SCALE),
1855 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1856 EndContainer(),
1857 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1858 EndContainer(),
1860 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1861 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1862 EndContainer(),
1863 EndContainer(),
1864};
1865
1866static WindowDesc _industry_production_desc(
1867 WDP_AUTO, "graph_industry_production", 0, 0,
1869 {},
1870 _nested_industry_production_widgets
1871);
1872
1873void ShowIndustryProductionGraph(WindowNumber window_number)
1874{
1875 AllocateWindowDescFront<IndustryProductionGraphWindow>(_industry_production_desc, window_number);
1876}
1877
1878struct TownCargoGraphWindow : BaseCargoGraphWindow {
1879 static inline constexpr StringID RANGE_LABELS[] = {
1880 STR_GRAPH_TOWN_RANGE_PRODUCED,
1881 STR_GRAPH_TOWN_RANGE_TRANSPORTED,
1882 };
1883
1884 static inline CargoTypes excluded_cargo_types{};
1885
1886 TownCargoGraphWindow(WindowDesc &desc, WindowNumber window_number) : BaseCargoGraphWindow(desc, STR_JUST_COMMA)
1887 {
1888 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1889 this->num_vert_lines = GRAPH_NUM_MONTHS;
1890 this->month_increment = 1;
1891 this->x_values_increment = ECONOMY_MONTH_MINUTES;
1892 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1893 this->ranges = RANGE_LABELS;
1894
1895 this->InitializeWindow(window_number);
1896 }
1897
1898 void OnInit() override
1899 {
1901
1902 this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
1903 }
1904
1905 CargoTypes GetCargoTypes(WindowNumber window_number) const override
1906 {
1907 CargoTypes cargo_types{};
1908 const Town *t = Town::Get(window_number);
1909 for (const auto &s : t->supplied) {
1910 if (IsValidCargoType(s.cargo)) SetBit(cargo_types, s.cargo);
1911 }
1912 return cargo_types;
1913 }
1914
1915 CargoTypes &GetExcludedCargoTypes() const override
1916 {
1917 return TownCargoGraphWindow::excluded_cargo_types;
1918 }
1919
1920 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1921 {
1922 if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_TOWN_CARGO_CAPTION, this->window_number);
1923
1924 return this->Window::GetWidgetString(widget, stringid);
1925 }
1926
1927 void UpdateStatistics(bool initialize) override
1928 {
1929 int mo = (TimerGameEconomy::month / this->month_increment - this->num_vert_lines) * this->month_increment;
1930 auto yr = TimerGameEconomy::year;
1931 while (mo < 0) {
1932 yr--;
1933 mo += 12;
1934 }
1935
1936 if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1937 /* There's no reason to get new stats */
1938 return;
1939 }
1940
1941 this->excluded_data = this->GetExcludedCargoTypes();
1942 this->year = yr;
1943 this->month = mo;
1944
1945 const Town *t = Town::Get(this->window_number);
1946
1947 this->data.clear();
1948 this->data.reserve(
1949 2 * std::ranges::count_if(t->supplied, &IsValidCargoType, &Town::SuppliedCargo::cargo));
1950
1951 for (const auto &s : t->supplied) {
1952 if (!IsValidCargoType(s.cargo)) continue;
1953 const CargoSpec *cs = CargoSpec::Get(s.cargo);
1954
1955 DataSet &produced = this->data.emplace_back();
1956 produced.colour = cs->legend_colour;
1957 produced.exclude_bit = cs->Index();
1958 produced.range_bit = 0;
1959
1960 DataSet &transported = this->data.emplace_back();
1961 transported.colour = cs->legend_colour;
1962 transported.exclude_bit = cs->Index();
1963 transported.range_bit = 1;
1964 transported.dash = 2;
1965
1966 FillFromHistory<GRAPH_NUM_MONTHS>(s.history, t->valid_history, *this->scales[this->selected_scale].history_range,
1969 }
1970
1971 this->SetDirty();
1972 }
1973};
1974
1975static constexpr std::initializer_list<NWidgetPart> _nested_town_cargo_graph_widgets = {
1977 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1978 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION),
1979 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1980 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1981 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1982 EndContainer(),
1983 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1985 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1987 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1988 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
1990 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1991 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1994 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),
1996 EndContainer(),
1998 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
1999 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
2000 EndContainer(),
2001 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
2002 EndContainer(),
2004 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
2005 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
2006 EndContainer(),
2007 EndContainer(),
2008};
2009
2010static WindowDesc _town_cargo_graph_desc(
2011 WDP_AUTO, "graph_town_cargo", 0, 0,
2013 {},
2014 _nested_town_cargo_graph_widgets
2015);
2016
2017void ShowTownCargoGraph(WindowNumber window_number)
2018{
2019 AllocateWindowDescFront<TownCargoGraphWindow>(_town_cargo_graph_desc, window_number);
2020}
2021
2026static std::unique_ptr<NWidgetBase> MakePerformanceDetailPanels()
2027{
2028 auto realtime = TimerGameEconomy::UsingWallclockUnits();
2029 const StringID performance_tips[] = {
2030 realtime ? STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_YEARS,
2031 STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
2032 realtime ? STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_YEARS,
2033 STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
2034 STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
2035 STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
2036 STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
2037 STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
2038 STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
2039 STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
2040 };
2041
2042 static_assert(lengthof(performance_tips) == to_underlying(ScoreID::End));
2043
2044 auto vert = std::make_unique<NWidgetVertical>(NWidContainerFlag::EqualSize);
2045 for (WidgetID widnum = WID_PRD_SCORE_FIRST; widnum <= WID_PRD_SCORE_LAST; widnum++) {
2046 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
2047 panel->SetFill(1, 1);
2048 panel->SetToolTip(performance_tips[widnum - WID_PRD_SCORE_FIRST]);
2049 vert->Add(std::move(panel));
2050 }
2051 return vert;
2052}
2053
2055std::unique_ptr<NWidgetBase> MakeCompanyButtonRowsGraphGUI()
2056{
2057 return MakeCompanyButtonRows(WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST, COLOUR_BROWN, 8, STR_PERFORMANCE_DETAIL_SELECT_COMPANY_TOOLTIP);
2058}
2059
2060static constexpr std::initializer_list<NWidgetPart> _nested_performance_rating_detail_widgets = {
2062 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
2063 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_PERFORMANCE_DETAIL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2064 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
2065 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
2066 EndContainer(),
2067 NWidget(WWT_PANEL, COLOUR_BROWN),
2069 EndContainer(),
2071};
2072
2073static WindowDesc _performance_rating_detail_desc(
2074 WDP_AUTO, "league_details", 0, 0,
2076 {},
2077 _nested_performance_rating_detail_widgets
2078);
2079
2080void ShowPerformanceRatingDetail()
2081{
2082 AllocateWindowDescFront<PerformanceRatingDetailWindow>(_performance_rating_detail_desc, 0);
2083}
2084
2085void InitializeGraphGui()
2086{
2087 _legend_excluded_companies = CompanyMask{};
2088 PaymentRatesGraphWindow::excluded_cargo_types = {};
2089 IndustryProductionGraphWindow::excluded_cargo_types = {};
2090}
constexpr uint8_t FindLastBit(T x)
Search the last set bit in a value.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:108
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
CargoTypes _standard_cargo_mask
Bitmask of real cargo types available.
Definition cargotype.cpp:36
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
Types/functions related to cargoes.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr Timpl & Set()
Set all bits.
void UpdateStatistics(bool initialize) override
Update the statistics.
virtual OverflowSafeInt64 GetGraphData(const Company *c, int j)=0
Get the data to show in the graph for a given company at a location along the X-axis.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2426
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition widget.cpp:2500
size_type GetCount() const
Gets the number of elements in the list.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static Year year
Current year, starting at 0.
static Month month
Current month (0..11).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
Definition of stuff that is very close to a company, like the company struct itself.
void DrawCompanyIcon(CompanyID c, int x, int y)
Draw the icon of a company.
GUI Functions related to companies.
Functions to handle different currencies.
const CurrencySpec & GetCurrency()
Get the currently selected currency.
Definition currency.h:119
int UpdateCompanyRatingAndValue(Company *c, bool update)
if update is set to true, the economy is updated with this score (also the house is updated,...
Definition economy.cpp:202
const EnumClassIndexContainer< std::array< ScoreInfo, to_underlying(ScoreID::End)>, ScoreID > _score_info
Score info, values used for computing the detailed performance rating.
Definition economy.cpp:91
Functions related to the economy.
ScoreID
Score categories in the detailed performance rating.
@ Begin
The lowest valid value.
@ MinIncome
Income in the quater with the lowest profit of the last 12 quaters.
@ End
Score ID end marker.
@ Total
Total points out of possible points ,must always be the last entry.
@ Loan
The amount of money company can take as a loan.
@ MaxIncome
Income in the quater with the highest profit of the last 12 quaters.
@ MinProfit
The profit of the vehicle with the lowest income.
@ Money
Amount of money company has in the bank.
static constexpr int SCORE_MAX
The max score that can be in the performance history.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:972
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:900
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:669
bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw a multiline string, possibly over multiple lines, if the region is within the current display cl...
Definition gfx.cpp:873
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
Functions related to the gfx engine.
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_LEFT
Left align the text.
Definition gfx_type.h:388
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:400
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:346
constexpr double INT64_MAX_IN_DOUBLE
The biggest double that when cast to int64_t still fits in a int64_t.
Definition graph_gui.cpp:47
static std::unique_ptr< NWidgetBase > MakeNWidgetCompanyLines()
Construct a vertical list of buttons, one for each company.
std::unique_ptr< NWidgetBase > MakeCompanyButtonRowsGraphGUI()
Make a number of rows with buttons for each company for the performance rating detail window.
static std::unique_ptr< NWidgetBase > MakePerformanceDetailPanels()
Make a vertical list of panels for outputting score details.
Graph GUI functions.
Types related to the graph widgets.
@ WID_GRAPH_FOOTER
Footer.
@ WID_GRAPH_RESIZE
Resize button.
@ WID_GRAPH_BACKGROUND
Background of the window.
@ WID_GRAPH_SCALE_MATRIX
Horizontal axis scale list.
@ WID_GRAPH_GRAPH
Graph itself.
@ WID_PHG_DETAILED_PERFORMANCE
Detailed performance.
@ WID_GRAPH_HEADER
Header.
@ WID_GRAPH_MATRIX_SCROLLBAR
Cargo list scrollbar.
@ WID_GRAPH_DISABLE_CARGOES
Disable cargoes button.
@ WID_GRAPH_CAPTION
Caption.
@ WID_GRAPH_KEY_BUTTON
Key button.
@ WID_GRAPH_MATRIX
Cargo list.
@ WID_GRAPH_ENABLE_CARGOES
Enable cargoes button.
@ WID_GRAPH_RANGE_MATRIX
Range list.
@ WID_GL_FIRST_COMPANY
First company in the legend.
@ WID_GL_LAST_COMPANY
Last company in the legend.
@ WID_GL_BACKGROUND
Background of the window.
@ WID_PRD_COMPANY_FIRST
First company.
@ WID_PRD_SCORE_FIRST
First entry in the score list.
@ WID_PRD_SCORE_LAST
Last entry in the score list.
@ WID_PRD_COMPANY_LAST
Last company.
constexpr NWidgetPart SetMatrixDataTip(uint32_t cols, uint32_t rows, StringID tip={})
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetTextStyle(TextColour colour, FontSize size=FS_NORMAL)
Widget part function for setting the text style.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetResizeWidgetTypeTip(ResizeWidgetValues widget_type, StringID tip)
Widget part function for setting the resize widget type and tooltip.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:967
Functions for storing historical data.
void FillFromHistory(const HistoryData< T > &history, ValidHistoryMask valid_history, const HistoryRange &hr, Tfillers &&... fillers)
Fill some data with historical data.
Base of all industries.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
constexpr PixelColour GREY_SCALE(uint8_t level)
Return the colour for a particular greyscale level.
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_WHITE
White palette colour.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:253
Functions related to sound.
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:236
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
uint legend_width
Width of legend 'blob'.
virtual CargoTypes & GetExcludedCargoTypes() const =0
Get a reference to the cargo types that should not be shown.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnInit() override
Notification that the nested widget tree gets initialized.
std::optional< uint8_t > GetDatasetIndex(int y) override
Get the dataset associated with a given Y-location within WID_GRAPH_MATRIX.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
virtual CargoTypes GetCargoTypes(WindowNumber number) const =0
Get the CargoTypes to show in this window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
CargoTypes cargo_types
Cargo types that can be selected.
void OnResize() override
Called after the window got resized.
Scrollbar * vscroll
Cargo list scrollbar.
uint line_height
Pixel height of each cargo type row.
DataSet & dataset
Dataset to fill.
Tprojection proj
Projection to apply.
uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
Get width for Y labels.
void OnGameTick() override
Called once per (game) tick.
virtual void UpdateStatistics(bool initialize)=0
Update the statistics.
static const int MIN_GRID_PIXEL_SIZE
Minimum distance between graph lines.
static const int GRAPH_NUM_MONTHS
Number of months displayed in the graph.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
static const int ECONOMY_QUARTER_MINUTES
Minutes per economic quarter.
static const int GRAPH_PAYMENT_RATE_STEPS
Number of steps on Payment rate graph.
void OnMouseOver(Point pt, WidgetID widget) override
The mouse is currently moving over the window or has just moved outside of the window.
std::span< const OverflowSafeInt64 > GetDataSetRange(const DataSet &dataset) const
Get appropriate part of dataset values for the current number of horizontal points.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
static const int ECONOMY_MONTH_MINUTES
Minutes per economic month.
uint64_t excluded_data
bitmask of datasets hidden by the player.
bool draw_dates
Should we draw months and years on the time axis?
static const int MIN_GRAPH_NUM_LINES_Y
Minimal number of horizontal lines to draw.
uint64_t excluded_range
bitmask of ranges hidden by the player.
uint8_t highlight_range
Data range that should be highlighted, or UINT8_MAX for none.
bool highlight_state
Current state of highlight, toggled every TIMER_BLINK_INTERVAL period.
uint8_t month_increment
month increment between vertical lines. must be divisor of 12.
virtual std::optional< uint8_t > GetDatasetIndex(int y)
Get the dataset associated with a given Y-location within WID_GRAPH_MATRIX.
static const int PAYMENT_GRAPH_X_STEP_DAYS
X-axis step label for cargo payment rates "Days in transit".
static const int ECONOMY_YEAR_MINUTES
Minutes per economic year.
ValuesInterval GetValuesInterval(int num_hori_lines) const
Get the interval that contains the graph's data.
uint64_t masked_range
bitmask of ranges that are not available for the current data.
static const TextColour GRAPH_AXIS_LABEL_COLOUR
colour of the graph axis label.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void DrawGraph(Rect r) const
Actually draw the graph.
static const int PAYMENT_GRAPH_X_STEP_SECONDS
X-axis step label for cargo payment rates "Seconds in transit".
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
uint8_t highlight_data
Data set that should be highlighted, or UINT8_MAX for none.
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:138
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
StringID name
Name of this type of cargo.
Definition cargotype.h:91
Colours colour
Company colour.
std::array< CompanyEconomyEntry, MAX_HISTORY_QUARTERS > old_economy
Economic data of the company of the last MAX_HISTORY_QUARTERS quarters.
uint8_t num_valid_stat_ent
Number of valid statistical entries in old_economy.
OverflowSafeInt64 GetGraphData(const Company *c, int j) override
Get the data to show in the graph for a given company at a location along the X-axis.
T y
Y coordinate.
uint16_t rate
The conversion rate compared to the base currency.
Definition currency.h:78
OverflowSafeInt64 GetGraphData(const Company *c, int j) override
Get the data to show in the graph for a given company at a location along the X-axis.
Dimensions (a width and height) of a rectangle in 2D.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition graph_gui.cpp:84
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition graph_gui.cpp:66
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
OverflowSafeInt64 GetGraphData(const Company *c, int j) override
Get the data to show in the graph for a given company at a location along the X-axis.
void UpdateStatistics(bool initialize) override
Update the statistics.
CargoTypes GetCargoTypes(WindowNumber window_number) const override
Get the CargoTypes to show in this window.
CargoTypes & GetExcludedCargoTypes() const override
Get a reference to the cargo types that should not be shown.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnInit() override
Notification that the nested widget tree gets initialized.
CargoType cargo
Cargo type.
Definition industry.h:86
uint16_t waiting
Total accepted.
Definition industry.h:82
CargoType cargo
Cargo type.
Definition industry.h:74
uint16_t transported
Total transported.
Definition industry.h:65
uint16_t production
Total produced.
Definition industry.h:64
Defines the internal data of a functional industry.
Definition industry.h:62
bool IsCargoAccepted() const
Test if this industry accepts any cargo.
Definition industry.h:223
ValidHistoryMask valid_history
Mask of valid history records.
Definition industry.h:109
ProducedCargoes produced
produced cargo slots
Definition industry.h:110
AcceptedCargoes accepted
accepted cargo slots
Definition industry.h:111
bool IsCargoProduced() const
Test if this industry produces any cargo.
Definition industry.h:229
OverflowSafeInt64 GetGraphData(const Company *c, int j) override
Get the data to show in the graph for a given company at a location along the X-axis.
void UpdateStatistics(bool) override
Update the statistics.
void UpdatePaymentRates()
Update the payment rates according to the latest information.
CargoTypes & GetExcludedCargoTypes() const override
Get a reference to the cargo types that should not be shown.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
const IntervalTimer< TimerWindow > update_payment_interval
Update the payment rates on a regular interval.
CargoTypes GetCargoTypes(WindowNumber) const override
Get the CargoTypes to show in this window.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
OverflowSafeInt64 GetGraphData(const Company *c, int j) override
Get the data to show in the graph for a given company at a location along the X-axis.
void OnGameTick() override
Called once per (game) tick.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Colour for pixel/line drawing.
Definition gfx_type.h:405
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static Industry * Get(auto index)
static Company * GetIfValid(auto index)
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
CargoTypes & GetExcludedCargoTypes() const override
Get a reference to the cargo types that should not be shown.
CargoTypes GetCargoTypes(WindowNumber window_number) const override
Get the CargoTypes to show in this window.
void UpdateStatistics(bool initialize) override
Update the statistics.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnInit() override
Notification that the nested widget tree gets initialized.
uint32_t transported
Total transported.
Definition town.h:90
uint32_t production
Total produced.
Definition town.h:89
Town data structure.
Definition town.h:63
SuppliedCargoes supplied
Cargo statistics about supplied cargo.
Definition town.h:109
ValidHistoryMask valid_history
Mask of valid history records.
Definition town.h:112
Contains the interval of a graph's data.
OverflowSafeInt64 lowest
Lowest value of this interval. Must be zero or less.
OverflowSafeInt64 highest
Highest value of this interval. Must be zero or greater.
High level window description.
Definition window_gui.h:168
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:274
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1809
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3247
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:470
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:505
ResizeInfo resize
Resize information.
Definition window_gui.h:315
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:305
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1799
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition window_gui.h:411
int left
x position of left edge of the window
Definition window_gui.h:310
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1832
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:210
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:986
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:461
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1822
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:314
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
int height
Height of the window (number of pixels down in y direction).
Definition window_gui.h:313
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
Definition window_gui.h:451
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
Definition of Interval and OneShot timers.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Definition of the Window system.
static constexpr std::chrono::milliseconds TIMER_BLINK_INTERVAL
Interval used by blinking interface elements.
Base of the town class.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:289
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
std::unique_ptr< NWidgetBase > MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
Make a number of rows with button-like graphics, for enabling/disabling each company.
Definition widget.cpp:3436
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:70
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:49
@ EqualSize
Containers should keep all their (resizing) children equally large.
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition widget_type.h:29
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3307
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3185
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed).
Definition window_gui.h:27
Twindow * AllocateWindowDescFront(WindowDesc &desc, WindowNumber window_number, Targs... extra_arguments)
Open a new window.
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
int WidgetID
Widget ID.
Definition window_type.h:20
@ WC_PERFORMANCE_HISTORY
Performance history graph; Window numbers:
@ WC_PERFORMANCE_DETAIL
Performance detail window; Window numbers:
@ WC_PAYMENT_RATES
Payment rates graph; Window numbers:
@ WC_GRAPH_LEGEND
Legend for graphs; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
@ WC_OPERATING_PROFIT
Operating profit graph; Window numbers:
@ WC_INDUSTRY_PRODUCTION
Industry production history graph; Window numbers:
@ WC_TOWN_VIEW
Town view; Window numbers:
@ WC_INDUSTRY_VIEW
Industry view; Window numbers:
@ WC_INCOME_GRAPH
Income graph; Window numbers:
@ WC_DELIVERED_CARGO
Delivered cargo graph; Window numbers:
@ WC_COMPANY_VALUE
Company value graph; Window numbers:
@ WC_TOWN_CARGO_GRAPH
Town cargo history graph; Window numbers:
Functions related to zooming.
@ Normal
The normal zoom level.
Definition zoom_type.h:26