OpenTTD Source 20260311-master-g511d3794ce
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
42
43/* Apparently these don't play well with enums. */
44static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX);
45static const uint INVALID_DATAPOINT_POS = UINT_MAX;
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) {
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
217 TimerGameEconomy::Year year{};
218 uint8_t month_increment = 3;
219
220 bool draw_dates = true;
221
227 bool x_values_reversed = true;
230
231 StringID format_str_y_axis{};
232
233 struct DataSet {
234 std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values;
235 PixelColour colour;
236 uint8_t exclude_bit;
237 uint8_t range_bit;
238 uint8_t dash;
239 };
240 std::vector<DataSet> data{};
241
242 std::span<const StringID> ranges{};
243 std::span<const GraphScale> scales{};
244 uint8_t selected_scale = 0;
245
246 uint8_t highlight_data = UINT8_MAX;
247 uint8_t highlight_range = UINT8_MAX;
248 bool highlight_state = false;
249
250 struct BaseFiller {
252
253 inline void MakeZero(uint i) const { this->dataset.values[i] = 0; }
254 inline void MakeInvalid(uint i) const { this->dataset.values[i] = INVALID_DATAPOINT; }
255 };
256
257 template <typename Tprojection>
259 Tprojection proj;
260
261 inline void Fill(uint i, const auto &data) const { this->dataset.values[i] = std::invoke(this->proj, data); }
262 };
263
269 std::span<const OverflowSafeInt64> GetDataSetRange(const DataSet &dataset) const
270 {
271 return {std::begin(dataset.values), std::begin(dataset.values) + this->num_on_x_axis};
272 }
273
280 ValuesInterval GetValuesInterval(int num_hori_lines) const
281 {
282 assert(num_hori_lines > 0);
283
284 ValuesInterval current_interval;
285 current_interval.highest = INT64_MIN;
286 current_interval.lowest = INT64_MAX;
287
288 for (const DataSet &dataset : this->data) {
289 if (HasBit(this->excluded_data, dataset.exclude_bit)) continue;
290 if (HasBit(this->excluded_range, dataset.range_bit)) continue;
291
292 for (const OverflowSafeInt64 &datapoint : this->GetDataSetRange(dataset)) {
293 if (datapoint != INVALID_DATAPOINT) {
294 current_interval.highest = std::max(current_interval.highest, datapoint);
295 current_interval.lowest = std::min(current_interval.lowest, datapoint);
296 }
297 }
298 }
299
300 /* Always include zero in the shown range. */
301 double abs_lower = (current_interval.lowest > 0) ? 0 : (double)abs(current_interval.lowest);
302 double abs_higher = (current_interval.highest < 0) ? 0 : (double)current_interval.highest;
303
304 /* Prevent showing values too close to the graph limits. */
305 abs_higher = (11.0 * abs_higher) / 10.0;
306 abs_lower = (11.0 * abs_lower) / 10.0;
307
308 int num_pos_grids;
309 OverflowSafeInt64 grid_size;
310
311 if (abs_lower != 0 || abs_higher != 0) {
312 /* The number of grids to reserve for the positive part is: */
313 num_pos_grids = (int)floor(0.5 + num_hori_lines * abs_higher / (abs_higher + abs_lower));
314
315 /* If there are any positive or negative values, force that they have at least one grid. */
316 if (num_pos_grids == 0 && abs_higher != 0) num_pos_grids++;
317 if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--;
318
319 /* Get the required grid size for each side and use the maximum one. */
320
321 OverflowSafeInt64 grid_size_higher = 0;
322 if (abs_higher > 0) {
323 grid_size_higher = abs_higher > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_higher);
324 grid_size_higher = (grid_size_higher + num_pos_grids - 1) / num_pos_grids;
325 }
326
327 OverflowSafeInt64 grid_size_lower = 0;
328 if (abs_lower > 0) {
329 grid_size_lower = abs_lower > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_lower);
330 grid_size_lower = (grid_size_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids);
331 }
332
333 grid_size = std::max(grid_size_higher, grid_size_lower);
334 } else {
335 /* If both values are zero, show an empty graph. */
336 num_pos_grids = num_hori_lines / 2;
337 grid_size = 1;
338 }
339
340 current_interval.highest = num_pos_grids * grid_size;
341 current_interval.lowest = -(num_hori_lines - num_pos_grids) * grid_size;
342 return current_interval;
343 }
344
351 uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
352 {
353 /* draw text strings on the y axis */
354 int64_t y_label = current_interval.highest;
355 int64_t y_label_separation = (current_interval.highest - current_interval.lowest) / num_hori_lines;
356
357 uint max_width = 0;
358
359 for (int i = 0; i < (num_hori_lines + 1); i++) {
360 Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label));
361 if (d.width > max_width) max_width = d.width;
362
363 y_label -= y_label_separation;
364 }
365
366 return max_width;
367 }
368
373 void DrawGraph(Rect r) const
374 {
375 uint x, y;
376 ValuesInterval interval;
377 int x_axis_offset;
378
379 /* the colours and cost array of GraphDrawer must accommodate
380 * both values for cargo and companies. So if any are higher, quit */
381 static_assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_COMPANIES);
382 assert(this->num_vert_lines > 0);
383
384 bool rtl = _current_text_dir == TD_RTL;
385
386 /* Rect r will be adjusted to contain just the graph, with labels being
387 * placed outside the area. */
388 r.top += ScaleGUITrad(5) + GetCharacterHeight(FS_SMALL) / 2;
389 r.bottom -= (this->draw_dates ? 2 : 1) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4);
390 r.left += ScaleGUITrad(rtl ? 5 : 9);
391 r.right -= ScaleGUITrad(rtl ? 9 : 5);
392
393 /* Initial number of horizontal lines. */
394 int num_hori_lines = 160 / ScaleGUITrad(MIN_GRID_PIXEL_SIZE);
395 /* For the rest of the height, the number of horizontal lines will increase more slowly. */
396 int resize = (r.bottom - r.top - 160) / (2 * ScaleGUITrad(MIN_GRID_PIXEL_SIZE));
397 if (resize > 0) num_hori_lines += resize;
398
399 interval = GetValuesInterval(num_hori_lines);
400
401 int label_width = GetYLabelWidth(interval, num_hori_lines);
402
403 if (rtl) {
404 r.right -= label_width;
405 } else {
406 r.left += label_width;
407 }
408
409 int x_sep = (r.right - r.left) / this->num_vert_lines;
410 int y_sep = (r.bottom - r.top) / num_hori_lines;
411
412 /* Redetermine right and bottom edge of graph to fit with the integer
413 * separation values. */
414 if (rtl) {
415 r.left = r.right - x_sep * this->num_vert_lines;
416 } else {
417 r.right = r.left + x_sep * this->num_vert_lines;
418 }
419 r.bottom = r.top + y_sep * num_hori_lines;
420
421 OverflowSafeInt64 interval_size = interval.highest + abs(interval.lowest);
422 /* Where to draw the X axis. Use floating point to avoid overflowing and results of zero. */
423 x_axis_offset = (int)((r.bottom - r.top) * (double)interval.highest / (double)interval_size);
424
425 /* Draw the background of the graph itself. */
426 GfxFillRect(r.left, r.top, r.right, r.bottom, GRAPH_BASE_COLOUR);
427
428 /* Draw the grid lines. */
429 int gridline_width = WidgetDimensions::scaled.bevel.top;
430 PixelColour grid_colour = GRAPH_GRID_COLOUR;
431
432 /* Don't draw the first line, as that's where the axis will be. */
433 if (rtl) {
434 x_sep = -x_sep;
435 x = r.right + x_sep;
436 } else {
437 x = r.left + x_sep;
438 }
439
440 for (int i = 1; i < this->num_vert_lines + 1; i++) {
441 /* If using wallclock units, we separate periods with a lighter line. */
443 grid_colour = (i % 4 == 0) ? GRAPH_YEAR_LINE_COLOUR : GRAPH_GRID_COLOUR;
444 }
445 GfxFillRect(x, r.top, x + gridline_width - 1, r.bottom, grid_colour);
446 x += x_sep;
447 }
448
449 /* Draw the horizontal grid lines. */
450 y = r.bottom;
451
452 for (int i = 0; i < (num_hori_lines + 1); i++) {
453 if (rtl) {
454 GfxFillRect(r.right + 1, y, r.right + ScaleGUITrad(3), y + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
455 } else {
456 GfxFillRect(r.left - ScaleGUITrad(3), y, r.left - 1, y + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
457 }
458 GfxFillRect(r.left, y, r.right + gridline_width - 1, y + gridline_width - 1, GRAPH_GRID_COLOUR);
459 y -= y_sep;
460 }
461
462 /* Draw the y axis. */
463 GfxFillRect(r.left, r.top, r.left + gridline_width - 1, r.bottom + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
464
465 /* Draw the x axis. */
466 y = x_axis_offset + r.top;
467 GfxFillRect(r.left, y, r.right + gridline_width - 1, y + gridline_width - 1, GRAPH_ZERO_LINE_COLOUR);
468
469 /* Find the largest value that will be drawn. */
470 if (this->num_on_x_axis == 0) return;
471
472 assert(this->num_on_x_axis > 0);
473
474 /* draw text strings on the y axis */
475 int64_t y_label = interval.highest;
476 int64_t y_label_separation = abs(interval.highest - interval.lowest) / num_hori_lines;
477
478 y = r.top - GetCharacterHeight(FS_SMALL) / 2;
479
480 for (int i = 0; i < (num_hori_lines + 1); i++) {
481 if (rtl) {
482 DrawString(r.right + ScaleGUITrad(4), r.right + label_width + ScaleGUITrad(4), y,
483 GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label),
485 } else {
486 DrawString(r.left - label_width - ScaleGUITrad(4), r.left - ScaleGUITrad(4), y,
487 GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label),
489 }
490
491 y_label -= y_label_separation;
492 y += y_sep;
493 }
494
495 x = rtl ? r.right : r.left;
496 y = r.bottom + ScaleGUITrad(2);
497
498 /* if there are not enough datapoints to fill the graph, align to the right */
499 x += (this->num_vert_lines - this->num_on_x_axis) * x_sep;
500
501 /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
502 if (this->draw_dates) {
503 TimerGameEconomy::Month mo = this->month;
504 TimerGameEconomy::Year yr = this->year;
505 for (int i = 0; i < this->num_on_x_axis; i++) {
506 if (rtl) {
507 DrawStringMultiLineWithClipping(x + x_sep, x, y, this->height,
508 GetString(mo == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + mo, yr),
510 } else {
511 DrawStringMultiLineWithClipping(x, x + x_sep, y, this->height,
512 GetString(mo == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + mo, yr),
514 }
515
516 mo += this->month_increment;
517 if (mo >= 12) {
518 mo = 0;
519 yr++;
520
521 /* Draw a lighter grid line between years. Top and bottom adjustments ensure we don't draw over top and bottom horizontal grid lines. */
522 GfxFillRect(x + x_sep, r.top + gridline_width, x + x_sep + gridline_width - 1, r.bottom - 1, GRAPH_YEAR_LINE_COLOUR);
523 }
524 x += x_sep;
525 }
526 } else {
527 /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates, and all graphs when using wallclock units). */
528 int16_t iterator;
529 uint16_t label;
530 if (this->x_values_reversed) {
531 label = this->x_values_increment * this->num_on_x_axis;
532 iterator = -this->x_values_increment;
533 } else {
534 label = this->x_values_increment;
535 iterator = this->x_values_increment;
536 }
537
538 for (int i = 0; i < this->num_on_x_axis; i++) {
539 if (rtl) {
540 DrawString(x + x_sep + 1, x - 1, y, GetString(STR_GRAPH_Y_LABEL_NUMBER, label), GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
541 } else {
542 DrawString(x + 1, x + x_sep - 1, y, GetString(STR_GRAPH_Y_LABEL_NUMBER, label), GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
543 }
544
545 label += iterator;
546 x += x_sep;
547 }
548 }
549
550 /* Draw lines and dots. */
551 uint linewidth = ScaleGUITrad(_settings_client.gui.graph_line_thickness);
552 uint pointwidth = ScaleGUITrad(_settings_client.gui.graph_line_thickness + 1);
553 uint pointoffs1 = pointwidth / 2;
554 uint pointoffs2 = pointwidth - pointoffs1;
555
556 auto draw_dataset = [&](const DataSet &dataset, PixelColour colour) {
557 if (HasBit(this->excluded_data, dataset.exclude_bit)) return;
558 if (HasBit(this->excluded_range, dataset.range_bit)) return;
559
560 /* Centre the dot between the grid lines. */
561 if (rtl) {
562 x = r.right + (x_sep / 2);
563 } else {
564 x = r.left + (x_sep / 2);
565 }
566
567 /* if there are not enough datapoints to fill the graph, align to the right */
568 x += (this->num_vert_lines - this->num_on_x_axis) * x_sep;
569
570 uint prev_x = INVALID_DATAPOINT_POS;
571 uint prev_y = INVALID_DATAPOINT_POS;
572
573 const uint dash = ScaleGUITrad(dataset.dash);
574 for (OverflowSafeInt64 datapoint : this->GetDataSetRange(dataset)) {
575 if (datapoint != INVALID_DATAPOINT) {
576 /*
577 * Check whether we need to reduce the 'accuracy' of the
578 * datapoint value and the highest value to split overflows.
579 * And when 'drawing' 'one million' or 'one million and one'
580 * there is no significant difference, so the least
581 * significant bits can just be removed.
582 *
583 * If there are more bits needed than would fit in a 32 bits
584 * integer, so at about 31 bits because of the sign bit, the
585 * least significant bits are removed.
586 */
587 int mult_range = FindLastBit<uint32_t>(x_axis_offset) + FindLastBit<uint64_t>(abs(datapoint));
588 int reduce_range = std::max(mult_range - 31, 0);
589
590 /* Handle negative values differently (don't shift sign) */
591 if (datapoint < 0) {
592 datapoint = -(abs(datapoint) >> reduce_range);
593 } else {
594 datapoint >>= reduce_range;
595 }
596 y = r.top + x_axis_offset - ((r.bottom - r.top) * datapoint) / (interval_size >> reduce_range);
597
598 /* Draw the point. */
599 GfxFillRect(x - pointoffs1, y - pointoffs1, x + pointoffs2, y + pointoffs2, colour);
600
601 /* Draw the line connected to the previous point. */
602 if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour, linewidth, dash);
603
604 prev_x = x;
605 prev_y = y;
606 } else {
607 prev_x = INVALID_DATAPOINT_POS;
608 prev_y = INVALID_DATAPOINT_POS;
609 }
610
611 x += x_sep;
612 }
613 };
614
615 /* Draw unhighlighted datasets. */
616 for (const DataSet &dataset : this->data) {
617 if (dataset.exclude_bit != this->highlight_data && dataset.range_bit != this->highlight_range) {
618 draw_dataset(dataset, dataset.colour);
619 }
620 }
621
622 /* If any dataset or range is highlighted, draw separately after the rest so they appear on top of all other
623 * data. Highlighted data is only drawn when highlight_state is set, otherwise it is invisible. */
624 if (this->highlight_state && (this->highlight_data != UINT8_MAX || this->highlight_range != UINT8_MAX)) {
625 for (const DataSet &dataset : this->data) {
626 if (dataset.exclude_bit == this->highlight_data || dataset.range_bit == this->highlight_range) {
627 draw_dataset(dataset, PC_WHITE);
628 }
629 }
630 }
631 }
632
633 BaseGraphWindow(WindowDesc &desc, StringID format_str_y_axis) :
634 Window(desc),
635 format_str_y_axis(format_str_y_axis)
636 {
638 }
639
640 const IntervalTimer<TimerWindow> blink_interval = {TIMER_BLINK_INTERVAL, [this](auto) {
641 /* If nothing is highlighted then no redraw is needed. */
642 if (this->highlight_data == UINT8_MAX && this->highlight_range == UINT8_MAX) return;
643
644 /* Toggle the highlight state and redraw. */
645 this->highlight_state = !this->highlight_state;
646 this->SetDirty();
647 }};
648
649 void UpdateMatrixSize(WidgetID widget, Dimension &size, Dimension &resize, auto labels)
650 {
651 size = {};
652 for (const StringID &str : labels) {
653 size = maxdim(size, GetStringBoundingBox(str, FS_SMALL));
654 }
655
658
659 /* Set fixed height for number of ranges. */
660 size.height *= static_cast<uint>(std::size(labels));
661
662 resize.width = 0;
663 resize.height = 0;
664 this->GetWidget<NWidgetCore>(widget)->SetMatrixDimension(1, ClampTo<uint32_t>(std::size(labels)));
665 }
666
667public:
668 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
669 {
670 switch (widget) {
672 this->UpdateMatrixSize(widget, size, resize, this->ranges);
673 break;
674
676 this->UpdateMatrixSize(widget, size, resize, this->scales | std::views::transform(&GraphScale::label));
677 break;
678
679 case WID_GRAPH_GRAPH: {
680 uint x_label_width = 0;
681
682 /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
683 if (this->draw_dates) {
684 uint yr = GetParamMaxValue(this->year.base(), 4, FS_SMALL);
685 for (uint mo = 0; mo < 12; ++mo) {
686 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);
687 }
688 } else {
689 /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates). */
690 uint64_t max_value = GetParamMaxValue((this->num_on_x_axis + 1) * this->x_values_increment, 0, FS_SMALL);
691 x_label_width = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL_NUMBER, max_value)).width;
692 }
693
694 uint y_label_width = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, INT64_MAX)).width;
695
696 size.width = std::max<uint>(size.width, ScaleGUITrad(5) + y_label_width + this->num_vert_lines * (x_label_width + ScaleGUITrad(5)) + ScaleGUITrad(9));
697 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));
698 size.height = std::max<uint>(size.height, size.width / 3);
699 break;
700 }
701
702 default: break;
703 }
704 }
705
706 void DrawWidget(const Rect &r, WidgetID widget) const override
707 {
708 switch (widget) {
709 case WID_GRAPH_GRAPH:
710 this->DrawGraph(r);
711 break;
712
714 uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
715 uint index = 0;
716 Rect line = r.WithHeight(line_height);
717 for (const auto &str : this->ranges) {
718 bool lowered = !HasBit(this->excluded_range, index) && !HasBit(this->masked_range, index);
719
720 /* Redraw frame if lowered */
721 if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
722
723 const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
724 DrawString(text, str, (this->highlight_state && this->highlight_range == index) ? TC_WHITE : TC_BLACK, SA_CENTER, false, FS_SMALL);
725
726 if (HasBit(this->masked_range, index)) {
727 GfxFillRect(line.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_BROWN, SHADE_DARKER), FILLRECT_CHECKER);
728 }
729
730 line = line.Translate(0, line_height);
731 ++index;
732 }
733 break;
734 }
735
737 uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
738 uint8_t selected_month_increment = this->scales[this->selected_scale].month_increment;
739 Rect line = r.WithHeight(line_height);
740 for (const auto &scale : this->scales) {
741 /* Redraw frame if selected */
742 if (selected_month_increment == scale.month_increment) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
743
744 DrawString(line.Shrink(WidgetDimensions::scaled.framerect), scale.label, TC_BLACK, SA_CENTER, false, FS_SMALL);
745
746 line = line.Translate(0, line_height);
747 }
748 break;
749 }
750
751 default: break;
752 }
753 }
754
755 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
756 {
757 /* Clicked on legend? */
758 switch (widget) {
760 ShowGraphLegend();
761 break;
762
764 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
765
766 if (HasBit(this->masked_range, row)) break;
767 ToggleBit(this->excluded_range, row);
768 SndClickBeep();
769 this->SetDirty();
770 break;
771 }
772
774 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
775 const auto &scale = this->scales[row];
776 if (this->selected_scale != row) {
777 this->selected_scale = row;
778 this->month_increment = scale.month_increment;
779 this->x_values_increment = scale.x_values_increment;
780 this->InvalidateData();
781 }
782 break;
783 }
784
785 default: break;
786 }
787 }
788
789 void OnMouseOver(Point pt, WidgetID widget) override
790 {
791 /* Test if a range should be highlighted. */
792 uint8_t new_highlight_range = UINT8_MAX;
793 if (widget == WID_GRAPH_RANGE_MATRIX) {
794 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
795 if (!HasBit(this->excluded_range, row)) new_highlight_range = static_cast<uint8_t>(row);
796 }
797
798 /* Test if a dataset should be highlighted. */
799 uint8_t new_highlight_data = UINT8_MAX;
800 if (widget == WID_GRAPH_MATRIX) {
801 auto dataset_index = this->GetDatasetIndex(pt.y);
802 if (dataset_index.has_value() && !HasBit(this->excluded_data, *dataset_index)) new_highlight_data = *dataset_index;
803 }
804
805 if (this->highlight_data == new_highlight_data && this->highlight_range == new_highlight_range) return;
806
807 /* Range or data set highlight has changed, set and redraw. */
808 this->highlight_data = new_highlight_data;
809 this->highlight_range = new_highlight_range;
810 this->highlight_state = true;
811 this->SetDirty();
812 }
813
814 void OnGameTick() override
815 {
816 this->UpdateStatistics(false);
817 }
818
824 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
825 {
826 if (!gui_scope) return;
827 this->UpdateStatistics(true);
828 }
829
834 virtual void UpdateStatistics(bool initialize) = 0;
835
841 virtual std::optional<uint8_t> GetDatasetIndex([[maybe_unused]] int y) { return std::nullopt; }
842};
843
844class BaseCompanyGraphWindow : public BaseGraphWindow {
845public:
846 BaseCompanyGraphWindow(WindowDesc &desc, StringID format_str_y_axis) : BaseGraphWindow(desc, format_str_y_axis) {}
847
848 void InitializeWindow(WindowNumber number)
849 {
850 /* Initialise the dataset */
851 this->UpdateStatistics(true);
852
853 this->CreateNestedTree();
854
855 auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
856 wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? STR_GRAPH_LAST_72_MINUTES_TIME_LABEL : STR_EMPTY);
857
858 this->FinishInitNested(number);
859 }
860
861 void UpdateStatistics(bool initialize) override
862 {
863 CompanyMask excluded_companies = _legend_excluded_companies;
864
865 /* Exclude the companies which aren't valid */
866 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
867 if (!Company::IsValidID(c)) excluded_companies.Set(c);
868 }
869
870 uint8_t nums = 0;
871 for (const Company *c : Company::Iterate()) {
872 nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
873 }
874
875 int mo = (TimerGameEconomy::month / this->month_increment - nums) * this->month_increment;
876 auto yr = TimerGameEconomy::year;
877 while (mo < 0) {
878 yr--;
879 mo += 12;
880 }
881
882 if (!initialize && this->excluded_data == excluded_companies.base() && this->num_on_x_axis == nums &&
883 this->year == yr && this->month == mo) {
884 /* There's no reason to get new stats */
885 return;
886 }
887
888 this->excluded_data = excluded_companies.base();
889 this->num_on_x_axis = nums;
890 this->year = yr;
891 this->month = mo;
892
893 this->data.clear();
894 for (CompanyID k = CompanyID::Begin(); k < MAX_COMPANIES; ++k) {
895 const Company *c = Company::GetIfValid(k);
896 if (c == nullptr) continue;
897
898 DataSet &dataset = this->data.emplace_back();
899 dataset.colour = GetColourGradient(c->colour, SHADE_LIGHTER);
900 dataset.exclude_bit = k.base();
901
902 for (int j = this->num_on_x_axis, i = 0; --j >= 0;) {
903 if (j >= c->num_valid_stat_ent) {
904 dataset.values[i] = INVALID_DATAPOINT;
905 } else {
906 /* Ensure we never assign INVALID_DATAPOINT, as that has another meaning.
907 * Instead, use the value just under it. Hopefully nobody will notice. */
908 dataset.values[i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1);
909 }
910 i++;
911 }
912 }
913 }
914
921 virtual OverflowSafeInt64 GetGraphData(const Company *c, int j) = 0;
922};
923
924
925/********************/
926/* OPERATING PROFIT */
927/********************/
928
929struct OperatingProfitGraphWindow : BaseCompanyGraphWindow {
930 OperatingProfitGraphWindow(WindowDesc &desc, WindowNumber window_number) :
931 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
932 {
933 this->num_on_x_axis = GRAPH_NUM_MONTHS;
934 this->num_vert_lines = GRAPH_NUM_MONTHS;
935 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
936
937 this->InitializeWindow(window_number);
938 }
939
940 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
941 {
942 return c->old_economy[j].income + c->old_economy[j].expenses;
943 }
944};
945
946static constexpr std::initializer_list<NWidgetPart> _nested_operating_profit_widgets = {
948 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
949 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_OPERATING_PROFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
950 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
951 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
952 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
953 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
954 EndContainer(),
955 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
956 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 160), SetFill(1, 1), SetResize(1, 1),
958 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),
959 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
960 EndContainer(),
961 EndContainer(),
962};
963
964static WindowDesc _operating_profit_desc(
965 WDP_AUTO, "graph_operating_profit", 0, 0,
967 {},
968 _nested_operating_profit_widgets
969);
970
971
972void ShowOperatingProfitGraph()
973{
975}
976
977
978/****************/
979/* INCOME GRAPH */
980/****************/
981
982struct IncomeGraphWindow : BaseCompanyGraphWindow {
983 IncomeGraphWindow(WindowDesc &desc, WindowNumber window_number) :
984 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
985 {
986 this->num_on_x_axis = GRAPH_NUM_MONTHS;
987 this->num_vert_lines = GRAPH_NUM_MONTHS;
988 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
989
990 this->InitializeWindow(window_number);
991 }
992
993 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
994 {
995 return c->old_economy[j].income;
996 }
997};
998
999static constexpr std::initializer_list<NWidgetPart> _nested_income_graph_widgets = {
1001 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1002 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_INCOME_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1003 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1004 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1005 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1006 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1007 EndContainer(),
1008 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1009 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
1011 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),
1012 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1013 EndContainer(),
1014 EndContainer(),
1015};
1016
1017static WindowDesc _income_graph_desc(
1018 WDP_AUTO, "graph_income", 0, 0,
1020 {},
1021 _nested_income_graph_widgets
1022);
1023
1024void ShowIncomeGraph()
1025{
1026 AllocateWindowDescFront<IncomeGraphWindow>(_income_graph_desc, 0);
1027}
1028
1029/*******************/
1030/* DELIVERED CARGO */
1031/*******************/
1032
1033struct DeliveredCargoGraphWindow : BaseCompanyGraphWindow {
1034 DeliveredCargoGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1035 BaseCompanyGraphWindow(desc, STR_JUST_COMMA)
1036 {
1037 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1038 this->num_vert_lines = GRAPH_NUM_MONTHS;
1039 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1040
1041 this->InitializeWindow(window_number);
1042 }
1043
1044 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1045 {
1046 return c->old_economy[j].delivered_cargo.GetSum<OverflowSafeInt64>();
1047 }
1048};
1049
1050static constexpr std::initializer_list<NWidgetPart> _nested_delivered_cargo_graph_widgets = {
1052 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1053 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_CARGO_DELIVERED_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1054 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1055 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1056 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1057 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1058 EndContainer(),
1059 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1060 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
1062 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),
1063 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1064 EndContainer(),
1065 EndContainer(),
1066};
1067
1068static WindowDesc _delivered_cargo_graph_desc(
1069 WDP_AUTO, "graph_delivered_cargo", 0, 0,
1071 {},
1072 _nested_delivered_cargo_graph_widgets
1073);
1074
1075void ShowDeliveredCargoGraph()
1076{
1077 AllocateWindowDescFront<DeliveredCargoGraphWindow>(_delivered_cargo_graph_desc, 0);
1078}
1079
1080/***********************/
1081/* PERFORMANCE HISTORY */
1082/***********************/
1083
1084struct PerformanceHistoryGraphWindow : BaseCompanyGraphWindow {
1085 PerformanceHistoryGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1086 BaseCompanyGraphWindow(desc, STR_JUST_COMMA)
1087 {
1088 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1089 this->num_vert_lines = GRAPH_NUM_MONTHS;
1090 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1091
1092 this->InitializeWindow(window_number);
1093 }
1094
1095 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1096 {
1097 return c->old_economy[j].performance_history;
1098 }
1099
1100 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1101 {
1102 if (widget == WID_PHG_DETAILED_PERFORMANCE) ShowPerformanceRatingDetail();
1103 this->BaseGraphWindow::OnClick(pt, widget, click_count);
1104 }
1105};
1106
1107static constexpr std::initializer_list<NWidgetPart> _nested_performance_history_widgets = {
1109 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1110 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_COMPANY_PERFORMANCE_RATINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1111 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_PHG_DETAILED_PERFORMANCE), SetMinimalSize(50, 0), SetStringTip(STR_PERFORMANCE_DETAIL_KEY, STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP),
1112 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1113 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1114 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1115 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1116 EndContainer(),
1117 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1118 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
1120 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),
1121 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1122 EndContainer(),
1123 EndContainer(),
1124};
1125
1126static WindowDesc _performance_history_desc(
1127 WDP_AUTO, "graph_performance", 0, 0,
1129 {},
1130 _nested_performance_history_widgets
1131);
1132
1133void ShowPerformanceHistoryGraph()
1134{
1135 AllocateWindowDescFront<PerformanceHistoryGraphWindow>(_performance_history_desc, 0);
1136}
1137
1138/*****************/
1139/* COMPANY VALUE */
1140/*****************/
1141
1142struct CompanyValueGraphWindow : BaseCompanyGraphWindow {
1143 CompanyValueGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1144 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
1145 {
1146 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1147 this->num_vert_lines = GRAPH_NUM_MONTHS;
1148 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1149
1150 this->InitializeWindow(window_number);
1151 }
1152
1153 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1154 {
1155 return c->old_economy[j].company_value;
1156 }
1157};
1158
1159static constexpr std::initializer_list<NWidgetPart> _nested_company_value_graph_widgets = {
1161 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1162 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_COMPANY_VALUES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1163 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1164 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1165 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1166 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1167 EndContainer(),
1168 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1169 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
1171 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),
1172 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1173 EndContainer(),
1174 EndContainer(),
1175};
1176
1177static WindowDesc _company_value_graph_desc(
1178 WDP_AUTO, "graph_company_value", 0, 0,
1180 {},
1181 _nested_company_value_graph_widgets
1182);
1183
1184void ShowCompanyValueGraph()
1185{
1186 AllocateWindowDescFront<CompanyValueGraphWindow>(_company_value_graph_desc, 0);
1187}
1188
1189struct BaseCargoGraphWindow : BaseGraphWindow {
1190 Scrollbar *vscroll = nullptr;
1191 uint line_height = 0;
1192 uint legend_width = 0;
1193
1194 CargoTypes cargo_types{};
1195
1196 BaseCargoGraphWindow(WindowDesc &desc, StringID format_str_y_axis) : BaseGraphWindow(desc, format_str_y_axis) {}
1197
1198 void InitializeWindow(WindowNumber number, StringID footer_wallclock = STR_NULL, StringID footer_calendar = STR_NULL)
1199 {
1200 this->CreateNestedTree();
1201
1202 this->excluded_range = this->masked_range;
1203 this->cargo_types = this->GetCargoTypes(number);
1204
1206 this->vscroll->SetCount(CountBits(this->cargo_types));
1207
1208 auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
1209 wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? footer_wallclock : footer_calendar);
1210
1211 this->FinishInitNested(number);
1212
1213 /* Initialise the dataset */
1214 this->InvalidateData();
1215 }
1216
1222 virtual CargoTypes GetCargoTypes(WindowNumber number) const = 0;
1223
1228 virtual CargoTypes &GetExcludedCargoTypes() const = 0;
1229
1230 std::optional<uint8_t> GetDatasetIndex(int y) override
1231 {
1232 int row = this->vscroll->GetScrolledRowFromWidget(y, this, WID_GRAPH_MATRIX);
1233 if (row >= this->vscroll->GetCount()) return std::nullopt;
1234
1235 for (const CargoSpec *cs : _sorted_cargo_specs) {
1236 if (!HasBit(this->cargo_types, cs->Index())) continue;
1237 if (row-- > 0) continue;
1238
1239 return cs->Index();
1240 }
1241
1242 return std::nullopt;
1243 }
1244
1245 void OnInit() override
1246 {
1247 /* Width of the legend blob. */
1248 this->legend_width = GetCharacterHeight(FS_SMALL) * 9 / 6;
1249 }
1250
1251 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1252 {
1253 if (widget != WID_GRAPH_MATRIX) {
1254 BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
1255 return;
1256 }
1257
1258 size.height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
1259
1260 for (CargoType cargo_type : SetCargoBitIterator(this->cargo_types)) {
1261 const CargoSpec *cs = CargoSpec::Get(cargo_type);
1262
1263 Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
1264 d.width += this->legend_width + WidgetDimensions::scaled.hsep_normal; // colour field
1265 d.width += WidgetDimensions::scaled.framerect.Horizontal();
1266 d.height += WidgetDimensions::scaled.framerect.Vertical();
1267 size = maxdim(d, size);
1268 }
1269
1270 this->line_height = size.height;
1271 size.height = this->line_height * 11; /* Default number of cargo types in most climates. */
1272 resize.width = 0;
1273 fill.height = resize.height = this->line_height;
1274 }
1275
1276 void DrawWidget(const Rect &r, WidgetID widget) const override
1277 {
1278 if (widget != WID_GRAPH_MATRIX) {
1279 BaseGraphWindow::DrawWidget(r, widget);
1280 return;
1281 }
1282
1283 bool rtl = _current_text_dir == TD_RTL;
1284
1285 int pos = this->vscroll->GetPosition();
1286 int max = pos + this->vscroll->GetCapacity();
1287
1288 Rect line = r.WithHeight(this->line_height);
1289
1290 for (const CargoSpec *cs : _sorted_cargo_specs) {
1291 if (!HasBit(this->cargo_types, cs->Index())) continue;
1292
1293 if (pos-- > 0) continue;
1294 if (--max < 0) break;
1295
1296 bool lowered = !HasBit(this->excluded_data, cs->Index());
1297
1298 /* Redraw frame if lowered */
1299 if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
1300
1301 const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
1302
1303 /* Cargo-colour box with outline */
1304 const Rect cargo = text.WithWidth(this->legend_width, rtl);
1305 GfxFillRect(cargo, PC_BLACK);
1306 PixelColour pc = cs->legend_colour;
1307 if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK;
1309
1310 /* Cargo name */
1311 DrawString(text.Indent(this->legend_width + WidgetDimensions::scaled.hsep_normal, rtl), GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
1312
1313 line = line.Translate(0, this->line_height);
1314 }
1315 }
1316
1317 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1318 {
1319 switch (widget) {
1321 /* Remove all cargoes from the excluded lists. */
1322 this->GetExcludedCargoTypes() = {};
1323 this->excluded_data = this->GetExcludedCargoTypes();
1324 this->SetDirty();
1325 break;
1326
1328 /* Add all cargoes to the excluded lists. */
1329 this->GetExcludedCargoTypes() = this->cargo_types;
1330 this->excluded_data = this->GetExcludedCargoTypes();
1331 this->SetDirty();
1332 break;
1333 }
1334
1335 case WID_GRAPH_MATRIX: {
1336 int row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GRAPH_MATRIX);
1337 if (row >= this->vscroll->GetCount()) return;
1338
1339 SndClickBeep();
1340
1341 for (const CargoSpec *cs : _sorted_cargo_specs) {
1342 if (!HasBit(this->cargo_types, cs->Index())) continue;
1343 if (row-- > 0) continue;
1344
1345 ToggleBit(this->GetExcludedCargoTypes(), cs->Index());
1346 this->excluded_data = this->GetExcludedCargoTypes();
1347 this->SetDirty();
1348 break;
1349 }
1350 break;
1351 }
1352
1353 default:
1354 this->BaseGraphWindow::OnClick(pt, widget, click_count);
1355 break;
1356 }
1357 }
1358
1359 void OnResize() override
1360 {
1361 this->vscroll->SetCapacityFromWidget(this, WID_GRAPH_MATRIX);
1362 }
1363};
1364
1365/*****************/
1366/* PAYMENT RATES */
1367/*****************/
1368
1369struct PaymentRatesGraphWindow : BaseCargoGraphWindow {
1370 static inline CargoTypes excluded_cargo_types{};
1371
1372 PaymentRatesGraphWindow(WindowDesc &desc, WindowNumber window_number) : BaseCargoGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
1373 {
1374 this->num_on_x_axis = GRAPH_PAYMENT_RATE_STEPS;
1375 this->num_vert_lines = GRAPH_PAYMENT_RATE_STEPS;
1376 this->draw_dates = false;
1377
1378 this->x_values_reversed = false;
1379 /* The x-axis is labeled in either seconds or days. A day is two seconds, so we adjust the label if needed. */
1381
1382 this->InitializeWindow(window_number, STR_GRAPH_CARGO_PAYMENT_RATES_SECONDS, STR_GRAPH_CARGO_PAYMENT_RATES_DAYS);
1383 }
1384
1385 CargoTypes GetCargoTypes(WindowNumber) const override
1386 {
1387 return _standard_cargo_mask;
1388 }
1389
1390 CargoTypes &GetExcludedCargoTypes() const override
1391 {
1392 return PaymentRatesGraphWindow::excluded_cargo_types;
1393 }
1394
1400 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1401 {
1402 if (!gui_scope) return;
1403 this->UpdatePaymentRates();
1404 }
1405
1407 const IntervalTimer<TimerWindow> update_payment_interval = {std::chrono::seconds(3), [this](auto) {
1408 this->UpdatePaymentRates();
1409 }};
1410
1411 void UpdateStatistics(bool) override {}
1412
1417 {
1418 this->excluded_data = this->GetExcludedCargoTypes();
1419
1420 this->data.clear();
1421 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1422 DataSet &dataset = this->data.emplace_back();
1423 dataset.colour = cs->legend_colour;
1424 dataset.exclude_bit = cs->Index();
1425
1426 for (uint j = 0; j != this->num_on_x_axis; j++) {
1427 dataset.values[j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
1428 }
1429 }
1430 }
1431};
1432
1433static constexpr std::initializer_list<NWidgetPart> _nested_cargo_payment_rates_widgets = {
1435 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1436 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1437 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1438 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1439 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1440 EndContainer(),
1441 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1442 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),
1444 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1446 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1447 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1448 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1451 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),
1453 EndContainer(),
1454 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1455 EndContainer(),
1456 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1457 EndContainer(),
1459 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),
1460 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1461 EndContainer(),
1462 EndContainer(),
1463};
1464
1465static WindowDesc _cargo_payment_rates_desc(
1466 WDP_AUTO, "graph_cargo_payment_rates", 0, 0,
1468 {},
1469 _nested_cargo_payment_rates_widgets
1470);
1471
1472
1473void ShowCargoPaymentRates()
1474{
1475 AllocateWindowDescFront<PaymentRatesGraphWindow>(_cargo_payment_rates_desc, 0);
1476}
1477
1478/*****************************/
1479/* PERFORMANCE RATING DETAIL */
1480/*****************************/
1481
1482struct PerformanceRatingDetailWindow : Window {
1483 static CompanyID company;
1484 int timeout = 0;
1485 uint score_info_left = 0;
1486 uint score_info_right = 0;
1487 uint bar_left = 0;
1488 uint bar_right = 0;
1489 uint bar_width = 0;
1490 uint bar_height = 0;
1491 uint score_detail_left = 0;
1492 uint score_detail_right = 0;
1493
1494 PerformanceRatingDetailWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
1495 {
1496 this->UpdateCompanyStats();
1497
1498 this->InitNested(window_number);
1499 this->OnInvalidateData(CompanyID::Invalid().base());
1500 }
1501
1502 void UpdateCompanyStats()
1503 {
1504 /* Update all company stats with the current data
1505 * (this is because _score_info is not saved to a savegame) */
1506 for (Company *c : Company::Iterate()) {
1508 }
1509
1510 this->timeout = Ticks::DAY_TICKS * 5;
1511 }
1512
1513 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1514 {
1515 switch (widget) {
1517 this->bar_height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.fullbevel.Vertical();
1518 size.height = this->bar_height + WidgetDimensions::scaled.matrix.Vertical();
1519
1520 uint score_info_width = 0;
1521 for (ScoreID i = ScoreID::Begin; i < ScoreID::End; i++) {
1522 score_info_width = std::max(score_info_width, GetStringBoundingBox(STR_PERFORMANCE_DETAIL_VEHICLES + to_underlying(i)).width);
1523 }
1524 score_info_width += GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxValue(1000))).width + WidgetDimensions::scaled.hsep_wide;
1525
1526 this->bar_width = GetStringBoundingBox(GetString(STR_PERFORMANCE_DETAIL_PERCENT, GetParamMaxValue(100))).width + WidgetDimensions::scaled.hsep_indent * 2; // Wide bars!
1527
1528 /* At this number we are roughly at the max; it can become wider,
1529 * but then you need at 1000 times more money. At that time you're
1530 * not that interested anymore in the last few digits anyway.
1531 * The 500 is because 999 999 500 to 999 999 999 are rounded to
1532 * 1 000 M, and not 999 999 k. Use negative numbers to account for
1533 * the negative income/amount of money etc. as well. */
1534 int max = -(999999999 - 500);
1535
1536 /* Scale max for the display currency. Prior to rendering the value
1537 * is converted into the display currency, which may cause it to
1538 * raise significantly. We need to compensate for that since {{CURRCOMPACT}}
1539 * is used, which can produce quite short renderings of very large
1540 * values. Otherwise the calculated width could be too narrow.
1541 * Note that it doesn't work if there was a currency with an exchange
1542 * rate greater than max.
1543 * When the currency rate is more than 1000, the 999 999 k becomes at
1544 * least 999 999 M which roughly is equally long. Furthermore if the
1545 * exchange rate is that high, 999 999 k is usually not enough anymore
1546 * to show the different currency numbers. */
1547 if (GetCurrency().rate < 1000) max /= GetCurrency().rate;
1548 uint score_detail_width = GetStringBoundingBox(GetString(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, max, max)).width;
1549
1550 size.width = WidgetDimensions::scaled.frametext.Horizontal() + score_info_width + WidgetDimensions::scaled.hsep_wide + this->bar_width + WidgetDimensions::scaled.hsep_wide + score_detail_width;
1551 uint left = WidgetDimensions::scaled.frametext.left;
1552 uint right = size.width - WidgetDimensions::scaled.frametext.right;
1553
1554 bool rtl = _current_text_dir == TD_RTL;
1555 this->score_info_left = rtl ? right - score_info_width : left;
1556 this->score_info_right = rtl ? right : left + score_info_width;
1557
1558 this->score_detail_left = rtl ? left : right - score_detail_width;
1559 this->score_detail_right = rtl ? left + score_detail_width : right;
1560
1561 this->bar_left = left + (rtl ? score_detail_width : score_info_width) + WidgetDimensions::scaled.hsep_wide;
1562 this->bar_right = this->bar_left + this->bar_width - 1;
1563 break;
1564 }
1565 }
1566
1567 void DrawWidget(const Rect &r, WidgetID widget) const override
1568 {
1569 /* No need to draw when there's nothing to draw */
1570 if (this->company == CompanyID::Invalid()) return;
1571
1573 if (this->IsWidgetDisabled(widget)) return;
1574 CompanyID cid = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1575 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
1576 DrawCompanyIcon(cid, CentreBounds(r.left, r.right, sprite_size.width), CentreBounds(r.top, r.bottom, sprite_size.height));
1577 return;
1578 }
1579
1580 if (!IsInsideMM(widget, WID_PRD_SCORE_FIRST, WID_PRD_SCORE_LAST + 1)) return;
1581
1582 ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
1583
1584 /* The colours used to show how the progress is going */
1585 PixelColour colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
1586 PixelColour colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
1587
1588 /* Draw all the score parts */
1589 int64_t val = _score_part[company][score_type];
1590 int64_t needed = _score_info[score_type].needed;
1591 int score = _score_info[score_type].score;
1592
1593 /* ScoreID::Total has its own rules ;) */
1594 if (score_type == ScoreID::Total) {
1595 for (ScoreID i = ScoreID::Begin; i < ScoreID::End; i++) score += _score_info[i].score;
1596 needed = SCORE_MAX;
1597 }
1598
1599 uint bar_top = CentreBounds(r.top, r.bottom, this->bar_height);
1600 uint text_top = CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
1601
1602 DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + to_underlying(score_type));
1603
1604 /* Draw the score */
1605 DrawString(this->score_info_left, this->score_info_right, text_top, GetString(STR_JUST_COMMA, score), TC_BLACK, SA_RIGHT);
1606
1607 /* Calculate the %-bar */
1608 uint x = Clamp<int64_t>(val, 0, needed) * this->bar_width / needed;
1609 bool rtl = _current_text_dir == TD_RTL;
1610 if (rtl) {
1611 x = this->bar_right - x;
1612 } else {
1613 x = this->bar_left + x;
1614 }
1615
1616 /* Draw the bar */
1617 if (x != this->bar_left) GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height - 1, rtl ? colour_notdone : colour_done);
1618 if (x != this->bar_right) GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height - 1, rtl ? colour_done : colour_notdone);
1619
1620 /* Draw it */
1621 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);
1622
1623 /* ScoreID::Loan is inverted */
1624 if (score_type == ScoreID::Loan) val = needed - val;
1625
1626 /* Draw the amount we have against what is needed
1627 * For some of them it is in currency format */
1628 switch (score_type) {
1629 case ScoreID::MinProfit:
1630 case ScoreID::MinIncome:
1631 case ScoreID::MaxIncome:
1632 case ScoreID::Money:
1633 case ScoreID::Loan:
1634 DrawString(this->score_detail_left, this->score_detail_right, text_top, GetString(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, val, needed));
1635 break;
1636 default:
1637 DrawString(this->score_detail_left, this->score_detail_right, text_top, GetString(STR_PERFORMANCE_DETAIL_AMOUNT_INT, val, needed));
1638 break;
1639 }
1640 }
1641
1642 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1643 {
1644 /* Check which button is clicked */
1646 /* Is it no on disable? */
1647 if (!this->IsWidgetDisabled(widget)) {
1648 this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1649 this->company = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1650 this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1651 this->SetDirty();
1652 }
1653 }
1654 }
1655
1656 void OnGameTick() override
1657 {
1658 /* Update the company score every 5 days */
1659 if (--this->timeout == 0) {
1660 this->UpdateCompanyStats();
1661 this->SetDirty();
1662 }
1663 }
1664
1670 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1671 {
1672 if (!gui_scope) return;
1673 /* Disable the companies who are not active */
1674 for (CompanyID i = CompanyID::Begin(); i < MAX_COMPANIES; ++i) {
1676 }
1677
1678 /* Check if the currently selected company is still active. */
1679 if (this->company != CompanyID::Invalid() && !Company::IsValidID(this->company)) {
1680 /* Raise the widget for the previous selection. */
1681 this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1682 this->company = CompanyID::Invalid();
1683 }
1684
1685 if (this->company == CompanyID::Invalid()) {
1686 for (const Company *c : Company::Iterate()) {
1687 this->company = c->index;
1688 break;
1689 }
1690 }
1691
1692 /* Make sure the widget is lowered */
1693 if (this->company != CompanyID::Invalid()) {
1694 this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1695 }
1696 }
1697};
1698
1699CompanyID PerformanceRatingDetailWindow::company = CompanyID::Invalid();
1700
1701/*******************************/
1702/* INDUSTRY PRODUCTION HISTORY */
1703/*******************************/
1704
1705struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
1706 static inline constexpr StringID RANGE_LABELS[] = {
1707 STR_GRAPH_INDUSTRY_RANGE_PRODUCED,
1708 STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED,
1709 STR_GRAPH_INDUSTRY_RANGE_DELIVERED,
1710 STR_GRAPH_INDUSTRY_RANGE_WAITING,
1711 };
1712
1713 static inline CargoTypes excluded_cargo_types{};
1714
1715 IndustryProductionGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1716 BaseCargoGraphWindow(desc, STR_JUST_COMMA)
1717 {
1718 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1719 this->num_vert_lines = GRAPH_NUM_MONTHS;
1720 this->month_increment = 1;
1721 this->x_values_increment = ECONOMY_MONTH_MINUTES;
1722 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1723 this->ranges = RANGE_LABELS;
1724
1726 if (!i->IsCargoProduced()) this->masked_range = (1U << 0) | (1U << 1);
1727 if (!i->IsCargoAccepted()) this->masked_range = (1U << 2) | (1U << 3);
1728
1729 this->InitializeWindow(window_number);
1730 }
1731
1732 void OnInit() override
1733 {
1735
1736 this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
1737 }
1738
1739 CargoTypes GetCargoTypes(WindowNumber window_number) const override
1740 {
1741 CargoTypes cargo_types{};
1743 for (const auto &a : i->accepted) {
1744 if (IsValidCargoType(a.cargo)) SetBit(cargo_types, a.cargo);
1745 }
1746 for (const auto &p : i->produced) {
1747 if (IsValidCargoType(p.cargo)) SetBit(cargo_types, p.cargo);
1748 }
1749 return cargo_types;
1750 }
1751
1752 CargoTypes &GetExcludedCargoTypes() const override
1753 {
1754 return IndustryProductionGraphWindow::excluded_cargo_types;
1755 }
1756
1757 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1758 {
1759 if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_INDUSTRY_CAPTION, this->window_number);
1760
1761 return this->Window::GetWidgetString(widget, stringid);
1762 }
1763
1764 void UpdateStatistics(bool initialize) override
1765 {
1766 int mo = (TimerGameEconomy::month / this->month_increment - this->num_vert_lines) * this->month_increment;
1767 auto yr = TimerGameEconomy::year;
1768 while (mo < 0) {
1769 yr--;
1770 mo += 12;
1771 }
1772
1773 if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1774 /* There's no reason to get new stats */
1775 return;
1776 }
1777
1778 this->excluded_data = this->GetExcludedCargoTypes();
1779 this->year = yr;
1780 this->month = mo;
1781
1782 const Industry *i = Industry::Get(this->window_number);
1783
1784 this->data.clear();
1785 this->data.reserve(
1786 2 * std::ranges::count_if(i->produced, &IsValidCargoType, &Industry::ProducedCargo::cargo) +
1787 2 * std::ranges::count_if(i->accepted, &IsValidCargoType, &Industry::AcceptedCargo::cargo));
1788
1789 for (const auto &p : i->produced) {
1790 if (!IsValidCargoType(p.cargo)) continue;
1791 const CargoSpec *cs = CargoSpec::Get(p.cargo);
1792
1793 DataSet &produced = this->data.emplace_back();
1794 produced.colour = cs->legend_colour;
1795 produced.exclude_bit = cs->Index();
1796 produced.range_bit = 0;
1797
1798 DataSet &transported = this->data.emplace_back();
1799 transported.colour = cs->legend_colour;
1800 transported.exclude_bit = cs->Index();
1801 transported.range_bit = 1;
1802 transported.dash = 2;
1803
1804 FillFromHistory<GRAPH_NUM_MONTHS>(p.history, i->valid_history, *this->scales[this->selected_scale].history_range,
1807 }
1808
1809 for (const auto &a : i->accepted) {
1810 if (!IsValidCargoType(a.cargo)) continue;
1811 const CargoSpec *cs = CargoSpec::Get(a.cargo);
1812
1813 DataSet &accepted = this->data.emplace_back();
1814 accepted.colour = cs->legend_colour;
1815 accepted.exclude_bit = cs->Index();
1816 accepted.range_bit = 2;
1817 accepted.dash = 1;
1818
1819 DataSet &waiting = this->data.emplace_back();
1820 waiting.colour = cs->legend_colour;
1821 waiting.exclude_bit = cs->Index();
1822 waiting.range_bit = 3;
1823 waiting.dash = 4;
1824
1825 FillFromHistory<GRAPH_NUM_MONTHS>(a.history.get(), i->valid_history, *this->scales[this->selected_scale].history_range,
1826 Filler{{accepted}, &Industry::AcceptedHistory::accepted},
1828 }
1829
1830 this->SetDirty();
1831 }
1832};
1833
1834static constexpr std::initializer_list<NWidgetPart> _nested_industry_production_widgets = {
1836 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1837 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION),
1838 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1839 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1840 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1841 EndContainer(),
1842 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1844 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1846 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1847 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_TOGGLE_RANGE),
1849 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1850 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1853 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),
1855 EndContainer(),
1857 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_SELECT_SCALE),
1858 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1859 EndContainer(),
1860 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1861 EndContainer(),
1863 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),
1864 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1865 EndContainer(),
1866 EndContainer(),
1867};
1868
1869static WindowDesc _industry_production_desc(
1870 WDP_AUTO, "graph_industry_production", 0, 0,
1872 {},
1873 _nested_industry_production_widgets
1874);
1875
1876void ShowIndustryProductionGraph(WindowNumber window_number)
1877{
1878 AllocateWindowDescFront<IndustryProductionGraphWindow>(_industry_production_desc, window_number);
1879}
1880
1881struct TownCargoGraphWindow : BaseCargoGraphWindow {
1882 static inline constexpr StringID RANGE_LABELS[] = {
1883 STR_GRAPH_TOWN_RANGE_PRODUCED,
1884 STR_GRAPH_TOWN_RANGE_TRANSPORTED,
1885 };
1886
1887 static inline CargoTypes excluded_cargo_types{};
1888
1889 TownCargoGraphWindow(WindowDesc &desc, WindowNumber window_number) : BaseCargoGraphWindow(desc, STR_JUST_COMMA)
1890 {
1891 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1892 this->num_vert_lines = GRAPH_NUM_MONTHS;
1893 this->month_increment = 1;
1894 this->x_values_increment = ECONOMY_MONTH_MINUTES;
1895 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1896 this->ranges = RANGE_LABELS;
1897
1898 this->InitializeWindow(window_number);
1899 }
1900
1901 void OnInit() override
1902 {
1904
1905 this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
1906 }
1907
1908 CargoTypes GetCargoTypes(WindowNumber window_number) const override
1909 {
1910 CargoTypes cargo_types{};
1911 const Town *t = Town::Get(window_number);
1912 for (const auto &s : t->supplied) {
1913 if (IsValidCargoType(s.cargo)) SetBit(cargo_types, s.cargo);
1914 }
1915 return cargo_types;
1916 }
1917
1918 CargoTypes &GetExcludedCargoTypes() const override
1919 {
1920 return TownCargoGraphWindow::excluded_cargo_types;
1921 }
1922
1923 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1924 {
1925 if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_TOWN_CARGO_CAPTION, this->window_number);
1926
1927 return this->Window::GetWidgetString(widget, stringid);
1928 }
1929
1930 void UpdateStatistics(bool initialize) override
1931 {
1932 int mo = (TimerGameEconomy::month / this->month_increment - this->num_vert_lines) * this->month_increment;
1933 auto yr = TimerGameEconomy::year;
1934 while (mo < 0) {
1935 yr--;
1936 mo += 12;
1937 }
1938
1939 if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1940 /* There's no reason to get new stats */
1941 return;
1942 }
1943
1944 this->excluded_data = this->GetExcludedCargoTypes();
1945 this->year = yr;
1946 this->month = mo;
1947
1948 const Town *t = Town::Get(this->window_number);
1949
1950 this->data.clear();
1951 this->data.reserve(
1952 2 * std::ranges::count_if(t->supplied, &IsValidCargoType, &Town::SuppliedCargo::cargo));
1953
1954 for (const auto &s : t->supplied) {
1955 if (!IsValidCargoType(s.cargo)) continue;
1956 const CargoSpec *cs = CargoSpec::Get(s.cargo);
1957
1958 DataSet &produced = this->data.emplace_back();
1959 produced.colour = cs->legend_colour;
1960 produced.exclude_bit = cs->Index();
1961 produced.range_bit = 0;
1962
1963 DataSet &transported = this->data.emplace_back();
1964 transported.colour = cs->legend_colour;
1965 transported.exclude_bit = cs->Index();
1966 transported.range_bit = 1;
1967 transported.dash = 2;
1968
1969 FillFromHistory<GRAPH_NUM_MONTHS>(s.history, t->valid_history, *this->scales[this->selected_scale].history_range,
1972 }
1973
1974 this->SetDirty();
1975 }
1976};
1977
1978static constexpr std::initializer_list<NWidgetPart> _nested_town_cargo_graph_widgets = {
1980 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1981 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION),
1982 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1983 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1984 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1985 EndContainer(),
1986 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1988 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1990 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1991 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
1993 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1994 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1997 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),
1999 EndContainer(),
2001 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
2002 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
2003 EndContainer(),
2004 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
2005 EndContainer(),
2007 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),
2008 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
2009 EndContainer(),
2010 EndContainer(),
2011};
2012
2013static WindowDesc _town_cargo_graph_desc(
2014 WDP_AUTO, "graph_town_cargo", 0, 0,
2016 {},
2017 _nested_town_cargo_graph_widgets
2018);
2019
2020void ShowTownCargoGraph(WindowNumber window_number)
2021{
2022 AllocateWindowDescFront<TownCargoGraphWindow>(_town_cargo_graph_desc, window_number);
2023}
2024
2029static std::unique_ptr<NWidgetBase> MakePerformanceDetailPanels()
2030{
2031 auto realtime = TimerGameEconomy::UsingWallclockUnits();
2032 const StringID performance_tips[] = {
2033 realtime ? STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_YEARS,
2034 STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
2035 realtime ? STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_YEARS,
2036 STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
2037 STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
2038 STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
2039 STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
2040 STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
2041 STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
2042 STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
2043 };
2044
2045 static_assert(lengthof(performance_tips) == to_underlying(ScoreID::End));
2046
2047 auto vert = std::make_unique<NWidgetVertical>(NWidContainerFlag::EqualSize);
2048 for (WidgetID widnum = WID_PRD_SCORE_FIRST; widnum <= WID_PRD_SCORE_LAST; widnum++) {
2049 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
2050 panel->SetFill(1, 1);
2051 panel->SetToolTip(performance_tips[widnum - WID_PRD_SCORE_FIRST]);
2052 vert->Add(std::move(panel));
2053 }
2054 return vert;
2055}
2056
2058std::unique_ptr<NWidgetBase> MakeCompanyButtonRowsGraphGUI()
2059{
2060 return MakeCompanyButtonRows(WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST, COLOUR_BROWN, 8, STR_PERFORMANCE_DETAIL_SELECT_COMPANY_TOOLTIP);
2061}
2062
2063static constexpr std::initializer_list<NWidgetPart> _nested_performance_rating_detail_widgets = {
2065 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
2066 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_PERFORMANCE_DETAIL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2067 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
2068 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
2069 EndContainer(),
2070 NWidget(WWT_PANEL, COLOUR_BROWN),
2072 EndContainer(),
2074};
2075
2076static WindowDesc _performance_rating_detail_desc(
2077 WDP_AUTO, "league_details", 0, 0,
2079 {},
2080 _nested_performance_rating_detail_widgets
2081);
2082
2083void ShowPerformanceRatingDetail()
2084{
2085 AllocateWindowDescFront<PerformanceRatingDetailWindow>(_performance_rating_detail_desc, 0);
2086}
2087
2088void InitializeGraphGui()
2089{
2091 PaymentRatesGraphWindow::excluded_cargo_types = {};
2092 IndustryProductionGraphWindow::excluded_cargo_types = {};
2093}
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:2430
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:2504
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.
static CompanyMask _legend_excluded_companies
Bitmasks of company and cargo indices that shouldn't be drawn.
Definition graph_gui.cpp:41
std::unique_ptr< NWidgetBase > MakeCompanyButtonRowsGraphGUI()
Make a number of rows with buttons for each company for the performance rating detail window.
static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX)
Value used for a datapoint that shouldn't be drawn.
static const uint INVALID_DATAPOINT_POS
Used to determine if the previous point was drawn.
Definition graph_gui.cpp:45
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:980
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:393
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.
TimerGameEconomy::Year year
The starting year that values are plotted against.
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.
TimerGameEconomy::Month month
The starting month that values are plotted against.
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.
bool x_values_reversed
These values are used if the graph is being plotted against values rather than the dates specified by...
int16_t x_values_increment
These values are used if the graph is being plotted against values rather than the dates specified by...
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 accepted
Total accepted.
Definition industry.h:81
uint16_t waiting
Average waiting.
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:92
uint32_t production
Total produced.
Definition town.h:91
Town data structure.
Definition town.h:63
SuppliedCargoes supplied
Cargo statistics about supplied cargo.
Definition town.h:111
ValidHistoryMask valid_history
Mask of valid history records.
Definition town.h:114
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:1822
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3261
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:518
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:1812
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:1845
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:223
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:990
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:1835
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:327
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:291
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:3440
@ 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:3321
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3199
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