OpenTTD Source 20250524-master-gc366e6a48e
newgrf_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 <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "error.h"
12#include "settings_gui.h"
13#include "newgrf.h"
14#include "strings_func.h"
15#include "window_func.h"
16#include "gamelog.h"
17#include "settings_type.h"
18#include "settings_func.h"
19#include "dropdown_type.h"
20#include "dropdown_func.h"
21#include "network/network.h"
23#include "sortlist_type.h"
24#include "stringfilter_type.h"
25#include "querystring_gui.h"
27#include "newgrf_text.h"
28#include "textfile_gui.h"
29#include "tilehighlight_func.h"
30#include "fios.h"
31#include "timer/timer.h"
32#include "timer/timer_window.h"
33#include "zoom_func.h"
35
37#include "widgets/misc_widget.h"
38
39#include "table/sprites.h"
40#include "table/strings.h"
41
42#include "safeguards.h"
43
48{
49 /* Do not show errors when entering the main screen */
50 if (_game_mode == GM_MENU) return;
51
52 for (const auto &c : _grfconfig) {
53 /* Only show Fatal and Error level messages */
54 if (!c->error.has_value() || (c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL && c->error->severity != STR_NEWGRF_ERROR_MSG_ERROR)) continue;
55
56 std::vector<StringParameter> params;
57 params.emplace_back(c->GetName());
58 params.emplace_back(c->error->message != STR_NULL ? c->error->message : STR_JUST_RAW_STRING);
59 params.emplace_back(c->error->custom_message);
60 params.emplace_back(c->filename);
61 params.emplace_back(c->error->data);
62 for (const uint32_t &value : c->error->param_value) params.emplace_back(value);
63
64 if (c->error->severity == STR_NEWGRF_ERROR_MSG_FATAL) {
65 ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_FATAL_POPUP, params), {}, WL_CRITICAL);
66 } else {
67 ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_POPUP, params), {}, WL_ERROR);
68 }
69 break;
70 }
71}
72
73static StringID GetGRFPaletteString(uint8_t palette)
74{
75 if (palette & GRFP_BLT_32BPP) {
76 return (palette & GRFP_USE_WINDOWS) ? STR_NEWGRF_SETTINGS_PALETTE_LEGACY_32BPP : STR_NEWGRF_SETTINGS_PALETTE_DEFAULT_32BPP;
77 }
78 return (palette & GRFP_USE_WINDOWS) ? STR_NEWGRF_SETTINGS_PALETTE_LEGACY : STR_NEWGRF_SETTINGS_PALETTE_DEFAULT;
79}
80
81static void ShowNewGRFInfo(const GRFConfig &c, const Rect &r, bool show_params)
82{
83 Rect tr = r.Shrink(WidgetDimensions::scaled.frametext);
84 if (c.error.has_value()) {
85 std::array<StringParameter, 3 + std::tuple_size_v<decltype(c.error->param_value)>> params{};
86 auto it = params.begin();
87 *it++ = c.error->custom_message; // is skipped by built-in messages
88 *it++ = c.filename;
89 *it++ = c.error->data;
90 for (const uint32_t &value : c.error->param_value) *it++ = value;
91
92 tr.top = DrawStringMultiLine(tr, GetString(c.error->severity, GetStringWithArgs(c.error->message != STR_NULL ? c.error->message : STR_JUST_RAW_STRING, {params.begin(), it})));
93 }
94
95 /* Draw filename or not if it is not known (GRF sent over internet) */
96 if (!c.filename.empty()) {
97 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_FILENAME, c.filename));
98 }
99
100 /* Prepare and draw GRF ID */
101 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_GRF_ID, fmt::format("{:08X}", std::byteswap(c.ident.grfid))));
102
104 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_VERSION, c.version));
105 }
107 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_MIN_VERSION, c.min_loadable_version));
108 }
109
110 /* Prepare and draw MD5 sum */
111 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_MD5SUM, FormatArrayAsHex(c.ident.md5sum)));
112
113 /* Show GRF parameter list */
114 if (show_params) {
115 if (!c.param.empty()) {
116 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_PARAMETER, STR_JUST_RAW_STRING, GRFBuildParamList(c)));
117 } else {
118 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_PARAMETER, STR_NEWGRF_SETTINGS_PARAMETER_NONE, std::monostate{}));
119 }
120
121 /* Draw the palette of the NewGRF */
122 tr.top = DrawStringMultiLine(tr, GetString(STR_NEWGRF_SETTINGS_PALETTE, GetGRFPaletteString(c.palette)));
123 }
124
125 /* Show flags */
126 if (c.status == GCS_NOT_FOUND) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_NOT_FOUND);
127 if (c.status == GCS_DISABLED) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_DISABLED);
128 if (c.flags.Test(GRFConfigFlag::Invalid)) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_INCOMPATIBLE);
129 if (c.flags.Test(GRFConfigFlag::Compatible)) tr.top = DrawStringMultiLine(tr, STR_NEWGRF_COMPATIBLE_LOADED);
130
131 /* Draw GRF info if it exists */
132 if (auto desc = c.GetDescription(); desc.has_value() && !desc->empty()) {
133 tr.top = DrawStringMultiLine(tr, GetString(STR_JUST_RAW_STRING, std::move(*desc)), TC_BLACK);
134 } else {
135 tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_NO_INFO);
136 }
137}
138
145 int32_t clicked_button = INT32_MAX;
146 bool clicked_increase = false;
147 bool clicked_dropdown = false;
148 bool closing_dropdown = false;
149 int32_t clicked_row = INT32_MAX;
150 int line_height = 0;
151 Scrollbar *vscroll = nullptr;
152 bool action14present = false;
153 bool editable = false;
154
155 NewGRFParametersWindow(WindowDesc &desc, bool is_baseset, GRFConfig &c, bool editable) : Window(desc),
156 grf_config(c),
158 {
159 this->action14present = (this->grf_config.num_valid_params != this->grf_config.param.size() || !this->grf_config.param_info.empty());
160
161 this->CreateNestedTree();
162 this->GetWidget<NWidgetCore>(WID_NP_CAPTION)->SetStringTip(is_baseset ? STR_BASEGRF_PARAMETERS_CAPTION : STR_NEWGRF_PARAMETERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
163 this->vscroll = this->GetScrollbar(WID_NP_SCROLLBAR);
164 this->GetWidget<NWidgetStacked>(WID_NP_SHOW_NUMPAR)->SetDisplayedPlane(this->action14present ? SZSP_HORIZONTAL : 0);
165 this->GetWidget<NWidgetStacked>(WID_NP_SHOW_DESCRIPTION)->SetDisplayedPlane(this->action14present ? 0 : SZSP_HORIZONTAL);
166 this->FinishInitNested(); // Initializes 'this->line_height' as side effect.
167
168 this->SetWidgetDisabledState(WID_NP_RESET, !this->editable);
169
170 this->InvalidateData();
171 }
172
183
189 bool HasParameterInfo(uint nr) const
190 {
191 return nr < this->grf_config.param_info.size() && this->grf_config.param_info[nr].has_value();
192 }
193
201 {
202 return this->HasParameterInfo(nr) ? this->grf_config.param_info[nr].value() : GetDummyParameterInfo(nr);
203 }
204
205 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
206 {
207 switch (widget) {
209 case WID_NP_NUMPAR_INC: {
210 size.width = std::max(SETTING_BUTTON_WIDTH / 2, GetCharacterHeight(FS_NORMAL));
211 size.height = std::max(SETTING_BUTTON_HEIGHT, GetCharacterHeight(FS_NORMAL));
212 break;
213 }
214
215 case WID_NP_NUMPAR: {
216 Dimension d = GetStringBoundingBox(GetString(this->GetWidget<NWidgetCore>(widget)->GetString(), GetParamMaxValue(GRFConfig::MAX_NUM_PARAMS)));
217 d.width += padding.width;
218 d.height += padding.height;
219 size = maxdim(size, d);
220 break;
221 }
222
224 this->line_height = std::max(SETTING_BUTTON_HEIGHT, GetCharacterHeight(FS_NORMAL)) + padding.height;
225
226 resize.width = 1;
227 resize.height = this->line_height;
228 size.height = 5 * this->line_height;
229 break;
230
232 /* Minimum size of 4 lines. The 500 is the default size of the window. */
234 for (const auto &par_info : this->grf_config.param_info) {
235 if (!par_info.has_value()) continue;
236 auto desc = GetGRFStringFromGRFText(par_info->desc);
237 if (!desc.has_value()) continue;
238 Dimension d = GetStringMultiLineBoundingBox(*desc, suggestion);
240 suggestion = maxdim(d, suggestion);
241 }
242 size.height = suggestion.height;
243 break;
244 }
245 }
246
247 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
248 {
249 switch (widget) {
250 case WID_NP_NUMPAR:
251 return GetString(STR_NEWGRF_PARAMETERS_NUM_PARAM, this->vscroll->GetCount());
252
253 default:
254 return this->Window::GetWidgetString(widget, stringid);
255 }
256 }
257
258 std::pair<StringParameter, StringParameter> GetValueParams(const GRFParameterInfo &par_info, uint32_t value) const
259 {
260 if (par_info.type == PTYPE_BOOL) return {value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF, {}};
261
262 auto it = std::ranges::lower_bound(par_info.value_names, value, std::less{}, &GRFParameterInfo::ValueName::first);
263 if (it != std::end(par_info.value_names) && it->first == value) {
264 if (auto label = GetGRFStringFromGRFText(it->second); label.has_value()) return {STR_JUST_RAW_STRING, *label};
265 }
266
267 return {STR_JUST_INT, value};
268 }
269
270 std::string GetSettingString(const GRFParameterInfo &par_info, int i, uint32_t value) const
271 {
272 auto [param1, param2] = this->GetValueParams(par_info, value);
273 auto name = GetGRFStringFromGRFText(par_info.name);
274 return name.has_value()
275 ? GetString(STR_NEWGRF_PARAMETERS_SETTING, STR_JUST_RAW_STRING, std::string(*name), param1, param2)
276 : GetString(STR_NEWGRF_PARAMETERS_SETTING, STR_NEWGRF_PARAMETERS_DEFAULT_NAME, i + 1, param1, param2);
277 }
278
279 void DrawWidget(const Rect &r, WidgetID widget) const override
280 {
281 if (widget == WID_NP_DESCRIPTION) {
282 if (!this->HasParameterInfo(this->clicked_row)) return;
283 const GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row);
284 auto desc = GetGRFStringFromGRFText(par_info.desc);
285 if (!desc.has_value()) return;
287 return;
288 } else if (widget != WID_NP_BACKGROUND) {
289 return;
290 }
291
292 Rect ir = r.Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
293 bool rtl = _current_text_dir == TD_RTL;
294 uint buttons_left = rtl ? ir.right - SETTING_BUTTON_WIDTH : ir.left;
296
297 int button_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
298 int text_y_offset = (this->line_height - GetCharacterHeight(FS_NORMAL)) / 2;
299 for (int32_t i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) {
300 GRFParameterInfo &par_info = this->GetParameterInfo(i);
301 uint32_t current_value = this->grf_config.GetValue(par_info);
302 bool selected = (i == this->clicked_row);
303
304 if (par_info.type == PTYPE_BOOL) {
305 DrawBoolButton(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, COLOUR_MAUVE, current_value != 0, this->editable);
306 } else if (par_info.type == PTYPE_UINT_ENUM) {
307 if (par_info.complete_labels) {
308 DrawDropDownButton(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && this->clicked_dropdown, this->editable);
309 } else {
310 DrawArrowButtons(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, this->editable && current_value > par_info.min_value, this->editable && current_value < par_info.max_value);
311 }
312 }
313
314 DrawString(tr.left, tr.right, ir.top + text_y_offset, this->GetSettingString(par_info, i, current_value), selected ? TC_WHITE : TC_LIGHT_BLUE);
315 ir.top += this->line_height;
316 }
317 }
318
319 void OnPaint() override
320 {
321 if (this->closing_dropdown) {
322 this->closing_dropdown = false;
323 this->clicked_dropdown = false;
324 }
325 this->DrawWidgets();
326 }
327
328 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
329 {
330 switch (widget) {
332 if (this->editable && !this->action14present && !this->grf_config.param.empty()) {
333 this->grf_config.param.pop_back();
334 this->InvalidateData();
336 }
337 break;
338
340 if (this->editable && !this->action14present && this->grf_config.param.size() < this->grf_config.num_valid_params) {
341 this->grf_config.param.emplace_back(0);
342 this->InvalidateData();
344 }
345 break;
346
347 case WID_NP_BACKGROUND: {
348 if (!this->editable) break;
349 int32_t num = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NP_BACKGROUND);
350 if (num >= this->vscroll->GetCount()) break;
351
352 if (this->clicked_row != num) {
355 this->clicked_row = num;
356 this->clicked_dropdown = false;
357 }
358
359 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
360 int x = pt.x - r.left;
361 if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x;
362
363 GRFParameterInfo &par_info = this->GetParameterInfo(num);
364
365 /* One of the arrows is clicked */
366 uint32_t old_val = this->grf_config.GetValue(par_info);
367 if (par_info.type != PTYPE_BOOL && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && par_info.complete_labels) {
368 if (this->clicked_dropdown) {
369 /* unclick the dropdown */
371 this->clicked_dropdown = false;
372 this->closing_dropdown = false;
373 } else {
374 int rel_y = (pt.y - r.top) % this->line_height;
375
376 Rect wi_rect;
377 wi_rect.left = pt.x - (_current_text_dir == TD_RTL ? SETTING_BUTTON_WIDTH - 1 - x : x);;
378 wi_rect.right = wi_rect.left + SETTING_BUTTON_WIDTH - 1;
379 wi_rect.top = pt.y - rel_y + (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
380 wi_rect.bottom = wi_rect.top + SETTING_BUTTON_HEIGHT - 1;
381
382 /* For dropdowns we also have to check the y position thoroughly, the mouse may not above the just opening dropdown */
383 if (pt.y >= wi_rect.top && pt.y <= wi_rect.bottom) {
384 this->clicked_dropdown = true;
385 this->closing_dropdown = false;
386
387 DropDownList list;
388 for (const auto &[value, name] : par_info.value_names) {
389 auto text = GetGRFStringFromGRFText(name);
390 assert(text.has_value()); // ensured by "complete_labels"
391 list.push_back(MakeDropDownListStringItem(GetString(STR_JUST_RAW_STRING, std::string(*text)), value));
392 }
393
394 ShowDropDownListAt(this, std::move(list), old_val, WID_NP_SETTING_DROPDOWN, wi_rect, COLOUR_ORANGE);
395 }
396 }
397 } else if (IsInsideMM(x, 0, SETTING_BUTTON_WIDTH)) {
398 uint32_t val = old_val;
399 if (par_info.type == PTYPE_BOOL) {
400 val = !val;
401 } else {
402 if (x >= SETTING_BUTTON_WIDTH / 2) {
403 /* Increase button clicked */
404 if (val < par_info.max_value) val++;
405 this->clicked_increase = true;
406 } else {
407 /* Decrease button clicked */
408 if (val > par_info.min_value) val--;
409 this->clicked_increase = false;
410 }
411 }
412 if (val != old_val) {
413 this->grf_config.SetValue(par_info, val);
414
415 this->clicked_button = num;
416 this->unclick_timeout.Reset();
417 }
418 } else if (par_info.type == PTYPE_UINT_ENUM && !par_info.complete_labels && click_count >= 2) {
419 /* Display a query box so users can enter a custom value. */
420 ShowQueryString(GetString(STR_JUST_INT, old_val), STR_CONFIG_SETTING_QUERY_CAPTION, 10, this, CS_NUMERAL, {});
421 }
422 this->SetDirty();
423 break;
424 }
425
426 case WID_NP_RESET:
427 if (!this->editable) break;
428 this->grf_config.SetParameterDefaults();
429 this->InvalidateData();
431 break;
432 }
433 }
434
435 void OnQueryTextFinished(std::optional<std::string> str) override
436 {
437 if (!str.has_value() || str->empty()) return;
438 auto value = ParseInteger<int32_t>(*str, 10, true);
439 if (!value.has_value()) return;
440 GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row);
441 this->grf_config.SetValue(par_info, *value);
442 this->SetDirty();
443 }
444
445 void OnDropdownSelect(WidgetID widget, int index) override
446 {
447 if (widget != WID_NP_SETTING_DROPDOWN) return;
448 assert(this->clicked_dropdown);
449 GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row);
450 this->grf_config.SetValue(par_info, index);
451 this->SetDirty();
452 }
453
454 void OnDropdownClose(Point, WidgetID widget, int, bool) override
455 {
456 if (widget != WID_NP_SETTING_DROPDOWN) return;
457 /* We cannot raise the dropdown button just yet. OnClick needs some hint, whether
458 * the same dropdown button was clicked again, and then not open the dropdown again.
459 * So, we only remember that it was closed, and process it on the next OnPaint, which is
460 * after OnClick. */
461 assert(this->clicked_dropdown);
462 this->closing_dropdown = true;
463 this->SetDirty();
464 }
465
466 void OnResize() override
467 {
468 this->vscroll->SetCapacityFromWidget(this, WID_NP_BACKGROUND);
469 }
470
476 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
477 {
478 if (!gui_scope) return;
479 if (!this->action14present) {
480 this->SetWidgetDisabledState(WID_NP_NUMPAR_DEC, !this->editable || this->grf_config.param.empty());
481 this->SetWidgetDisabledState(WID_NP_NUMPAR_INC, !this->editable || std::size(this->grf_config.param) >= this->grf_config.num_valid_params);
482 }
483
484 this->vscroll->SetCount(this->action14present ? this->grf_config.num_valid_params : GRFConfig::MAX_NUM_PARAMS);
485 if (this->clicked_row != INT32_MAX && this->clicked_row >= this->vscroll->GetCount()) {
486 this->clicked_row = INT32_MAX;
488 }
489 }
490
492 TimeoutTimer<TimerWindow> unclick_timeout = {std::chrono::milliseconds(150), [this]() {
493 this->clicked_button = INT32_MAX;
494 this->SetDirty();
495 }};
496};
498
499
500static constexpr NWidgetPart _nested_newgrf_parameter_widgets[] = {
502 NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
503 NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_NP_CAPTION),
504 NWidget(WWT_DEFSIZEBOX, COLOUR_MAUVE),
505 EndContainer(),
506 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NP_SHOW_NUMPAR),
507 NWidget(WWT_PANEL, COLOUR_MAUVE), SetResize(1, 0), SetFill(1, 0), SetPIP(4, 0, 4),
508 NWidget(NWID_HORIZONTAL), SetPIP(4, 0, 4),
511 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NP_NUMPAR), SetResize(1, 0), SetFill(1, 0), SetPadding(0, 0, 0, 4),
512 EndContainer(),
513 EndContainer(),
514 EndContainer(),
518 EndContainer(),
520 NWidget(WWT_PANEL, COLOUR_MAUVE, WID_NP_DESCRIPTION), SetResize(1, 0), SetFill(1, 0),
521 EndContainer(),
522 EndContainer(),
524 NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_NP_RESET), SetStringTip(STR_NEWGRF_PARAMETERS_RESET, STR_NEWGRF_PARAMETERS_RESET_TOOLTIP),
525 NWidget(WWT_PANEL, COLOUR_MAUVE), SetResize(1, 0), SetFill(1, 0),
526 EndContainer(),
527 NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
528 EndContainer(),
529};
530
533 WDP_CENTER, "settings_newgrf_config", 500, 208,
535 {},
536 _nested_newgrf_parameter_widgets
537);
538
539void OpenGRFParameterWindow(bool is_baseset, GRFConfig &c, bool editable)
540{
542 new NewGRFParametersWindow(_newgrf_parameters_desc, is_baseset, c, editable);
543}
544
547 const GRFConfig *grf_config = nullptr;
548
550 {
551 this->ConstructWindow();
552
553 auto textfile = this->grf_config->GetTextfile(file_type);
554 this->LoadTextfile(textfile.value(), NEWGRF_DIR);
555 }
556
557 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
558 {
559 if (widget == WID_TF_CAPTION) {
560 return GetString(stringid, STR_CONTENT_TYPE_NEWGRF, this->grf_config->GetName());
561 }
562
563 return this->Window::GetWidgetString(widget, stringid);
564 }
565};
566
567void ShowNewGRFTextfileWindow(Window *parent, TextfileType file_type, const GRFConfig *c)
568{
569 parent->CloseChildWindowById(WC_TEXTFILE, file_type);
570 new NewGRFTextfileWindow(parent, file_type, c);
571}
572
573typedef std::map<uint32_t, const GRFConfig *> GrfIdMap;
574
580static void FillGrfidMap(const GRFConfigList &lst, GrfIdMap &grfid_map)
581{
582 for (const auto &c : lst) {
583 grfid_map.emplace(c->ident.grfid, c.get());
584 }
585}
586
587static void NewGRFConfirmationCallback(Window *w, bool confirmed);
588static void ShowSavePresetWindow(std::string_view initial_text);
589
595
596 static const uint EDITBOX_MAX_SIZE = 50;
597
598 static Listing last_sorting;
600 static const std::initializer_list<GUIGRFConfigList::SortFunction * const> sorter_funcs;
601 static const std::initializer_list<GUIGRFConfigList::FilterFunction * const> filter_funcs;
602
604 const GRFConfig *avail_sel = nullptr;
605 int avail_pos = -1;
608
610
611 GRFConfigList actives{};
613
614 GRFConfigList &orig_list;
615 bool editable = false;
616 bool show_params = false;
617 bool execute = false;
618 int preset = -1;
619 int active_over = -1;
620 bool modified = false;
621
622 Scrollbar *vscroll = nullptr;
623 Scrollbar *vscroll2 = nullptr;
624
625 NewGRFWindow(WindowDesc &desc, bool editable, bool show_params, bool execute, GRFConfigList &orig_list) : Window(desc), filter_editbox(EDITBOX_MAX_SIZE), orig_list(orig_list)
626 {
627 this->editable = editable;
628 this->execute = execute;
629 this->show_params = show_params;
630
631 CopyGRFConfigList(this->actives, orig_list, false);
633
634 this->CreateNestedTree();
635 this->vscroll = this->GetScrollbar(WID_NS_SCROLLBAR);
636 this->vscroll2 = this->GetScrollbar(WID_NS_SCROLL2BAR);
637
638 this->GetWidget<NWidgetStacked>(WID_NS_SHOW_REMOVE)->SetDisplayedPlane(this->editable ? 0 : 1);
639 this->GetWidget<NWidgetStacked>(WID_NS_SHOW_EDIT)->SetDisplayedPlane(this->editable ? 0 : (this->show_params ? 1 : SZSP_HORIZONTAL));
640 this->GetWidget<NWidgetStacked>(WID_NS_SHOW_APPLY)->SetDisplayedPlane(this->editable && this->execute ? 0 : SZSP_VERTICAL);
642
644 this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
645 if (editable) {
647 } else {
649 }
650
651 this->avails.SetListing(this->last_sorting);
652 this->avails.SetFiltering(this->last_filtering);
653 this->avails.SetSortFuncs(this->sorter_funcs);
654 this->avails.SetFilterFuncs(this->filter_funcs);
655 this->avails.ForceRebuild();
656
658 }
659
660 void Close([[maybe_unused]] int data = 0) override
661 {
664
665 if (this->editable && this->modified && !this->execute && !_exit_game) {
666 CopyGRFConfigList(this->orig_list, this->actives, true);
667 ResetGRFConfig(false);
669 }
670
671 this->Window::Close();
672 }
673
674 int GetCurrentActivePosition() const
675 {
676 if (this->active_sel != nullptr) {
677 auto it = std::ranges::find_if(this->actives, [this](const auto &c) { return c.get() == this->active_sel; });
678 if (it != std::end(this->actives)) return static_cast<int>(std::distance(std::begin(this->actives), it));
679 }
680 return -1;
681 }
682
688 {
689 GrfIdMap grfid_map;
690 FillGrfidMap(this->actives, grfid_map);
691
692 for (const auto &a : _all_grfs) {
693 GrfIdMap::const_iterator iter = grfid_map.find(a->ident.grfid);
694 if (iter != grfid_map.end() && a->version > iter->second->version) return true;
695 }
696 return false;
697 }
698
701 {
702 GrfIdMap grfid_map;
703 FillGrfidMap(this->actives, grfid_map);
704
705 for (const auto &a : _all_grfs) {
706 GrfIdMap::iterator iter = grfid_map.find(a->ident.grfid);
707 if (iter == grfid_map.end() || iter->second->version >= a->version) continue;
708
709 auto c = std::ranges::find_if(this->actives, [&iter](const auto &grfconfig) { return grfconfig.get() == iter->second; });
710 assert(c != std::end(this->actives));
711 auto d = std::make_unique<GRFConfig>(*iter->second);
712 if (d->IsCompatible((*c)->version)) {
713 d->CopyParams(**c);
714 } else {
715 d->SetParameterDefaults();
716 }
717 if (this->active_sel == c->get()) {
720 this->active_sel = nullptr;
721 }
722 *c = std::move(d);
723 iter->second = c->get();
724 }
725 }
726
727 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
728 {
729 switch (widget) {
730 case WID_NS_FILE_LIST:
731 {
732 Dimension d = maxdim(GetScaledSpriteSize(SPR_SQUARE), GetScaledSpriteSize(SPR_WARNING_SIGN));
733 resize.height = std::max<uint>(d.height + 2U, GetCharacterHeight(FS_NORMAL));
734 size.height = std::max(size.height, padding.height + 6 * resize.height);
735 break;
736 }
737
739 {
740 Dimension d = maxdim(GetScaledSpriteSize(SPR_SQUARE), GetScaledSpriteSize(SPR_WARNING_SIGN));
741 resize.height = std::max<uint>(d.height + 2U, GetCharacterHeight(FS_NORMAL));
742 size.height = std::max(size.height, padding.height + 8 * resize.height);
743 break;
744 }
745
747 Dimension dim = GetStringBoundingBox(STR_NEWGRF_SETTINGS_INFO_TITLE);
748 size.height = std::max(size.height, dim.height + WidgetDimensions::scaled.frametext.Vertical());
749 size.width = std::max(size.width, dim.width + WidgetDimensions::scaled.frametext.Horizontal());
750 break;
751 }
752
754 size.height = std::max<uint>(size.height, WidgetDimensions::scaled.framerect.Vertical() + 10 * GetCharacterHeight(FS_NORMAL));
755 break;
756
757 case WID_NS_PRESET_LIST: {
758 Dimension d = GetStringBoundingBox(STR_NUM_CUSTOM);
759 for (const auto &i : this->grf_presets) {
760 d = maxdim(d, GetStringBoundingBox(GetString(STR_JUST_RAW_STRING, i)));
761 }
762 d.width += padding.width;
763 size = maxdim(d, size);
764 break;
765 }
766
769 Dimension d = GetStringBoundingBox(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON);
770 size = maxdim(d, GetStringBoundingBox(STR_INTRO_ONLINE_CONTENT));
771 size.width += padding.width;
772 size.height += padding.height;
773 break;
774 }
775 }
776 }
777
778 void OnResize() override
779 {
780 this->vscroll->SetCapacityFromWidget(this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.Vertical());
781 this->vscroll2->SetCapacityFromWidget(this, WID_NS_AVAIL_LIST, WidgetDimensions::scaled.framerect.Vertical());
782 }
783
784 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
785 {
786 switch (widget) {
788 if (this->preset == -1) return GetString(STR_NUM_CUSTOM);
789
790 return this->grf_presets[this->preset];
791
792 default:
793 return this->Window::GetWidgetString(widget, stringid);
794 }
795 }
796
802 inline PaletteID GetPalette(const GRFConfig &c) const
803 {
804 PaletteID pal;
805
806 /* Pick a colour */
807 switch (c.status) {
808 case GCS_NOT_FOUND:
809 case GCS_DISABLED:
810 pal = PALETTE_TO_RED;
811 break;
812 case GCS_ACTIVATED:
813 pal = PALETTE_TO_GREEN;
814 break;
815 default:
816 pal = PALETTE_TO_BLUE;
817 break;
818 }
819
820 /* Do not show a "not-failure" colour when it actually failed to load */
821 if (pal != PALETTE_TO_RED) {
823 pal = PALETTE_TO_GREY;
824 } else if (c.flags.Test(GRFConfigFlag::Compatible)) {
825 pal = PALETTE_TO_ORANGE;
826 }
827 }
828
829 return pal;
830 }
831
832 void DrawWidget(const Rect &r, WidgetID widget) const override
833 {
834 switch (widget) {
835 case WID_NS_FILE_LIST: {
836 const Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
838
839 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
840 uint step_height = this->GetWidget<NWidgetBase>(WID_NS_FILE_LIST)->resize_y;
841 Dimension square = GetSpriteSize(SPR_SQUARE);
842 Dimension warning = GetSpriteSize(SPR_WARNING_SIGN);
843 int square_offset_y = (step_height - square.height) / 2;
844 int warning_offset_y = (step_height - warning.height) / 2;
845 int offset_y = (step_height - GetCharacterHeight(FS_NORMAL)) / 2;
846
847 bool rtl = _current_text_dir == TD_RTL;
848 uint text_left = rtl ? tr.left : tr.left + square.width + 13;
849 uint text_right = rtl ? tr.right - square.width - 13 : tr.right;
850 uint square_left = rtl ? tr.right - square.width - 3 : tr.left + 3;
851 uint warning_left = rtl ? tr.right - square.width - warning.width - 8 : tr.left + square.width + 8;
852
853 int i = 0;
854 for (const auto &c : this->actives) {
855 if (this->vscroll->IsVisible(i)) {
856 std::string text = c->GetName();
857 bool h = (this->active_sel == c.get());
858 PaletteID pal = this->GetPalette(*c);
859
860 if (h) {
861 GfxFillRect(br.left, tr.top, br.right, tr.top + step_height - 1, PC_DARK_BLUE);
862 } else if (i == this->active_over) {
863 /* Get index of current selection. */
864 int active_sel_pos = this->GetCurrentActivePosition();
865 if (active_sel_pos != this->active_over) {
866 uint top = this->active_over < active_sel_pos ? tr.top + 1 : tr.top + step_height - 2;
867 GfxFillRect(tr.left, top - 1, tr.right, top + 1, PC_GREY);
868 }
869 }
870 DrawSprite(SPR_SQUARE, pal, square_left, tr.top + square_offset_y);
871 if (c->error.has_value()) DrawSprite(SPR_WARNING_SIGN, 0, warning_left, tr.top + warning_offset_y);
872 uint txtoffset = !c->error.has_value() ? 0 : warning.width;
873 DrawString(text_left + (rtl ? 0 : txtoffset), text_right - (rtl ? txtoffset : 0), tr.top + offset_y, std::move(text), h ? TC_WHITE : TC_ORANGE);
874 tr.top += step_height;
875 }
876 i++;
877 }
878 if (i == this->active_over && this->vscroll->IsVisible(i)) { // Highlight is after the last GRF entry.
879 GfxFillRect(tr.left, tr.top, tr.right, tr.top + 2, PC_GREY);
880 }
881 break;
882 }
883
884 case WID_NS_AVAIL_LIST: {
885 const Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
886 GfxFillRect(br, this->active_over == -2 ? PC_DARK_GREY : PC_BLACK);
887
888 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
889 uint step_height = this->GetWidget<NWidgetBase>(WID_NS_AVAIL_LIST)->resize_y;
890 int offset_y = (step_height - GetCharacterHeight(FS_NORMAL)) / 2;
891
892 auto [first, last] = this->vscroll2->GetVisibleRangeIterators(this->avails);
893 for (auto it = first; it != last; ++it) {
894 const GRFConfig *c = *it;
895 bool h = (c == this->avail_sel);
896 std::string text = c->GetName();
897
898 if (h) GfxFillRect(br.left, tr.top, br.right, tr.top + step_height - 1, PC_DARK_BLUE);
899 DrawString(tr.left, tr.right, tr.top + offset_y, std::move(text), h ? TC_WHITE : TC_SILVER);
900 tr.top += step_height;
901 }
902 break;
903 }
904
906 /* Create the nice darker rectangle at the details top. */
907 GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_MAUVE, SHADE_NORMAL));
908 DrawString(r.left, r.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), STR_NEWGRF_SETTINGS_INFO_TITLE, TC_FROMSTRING, SA_HOR_CENTER);
909 break;
910 }
911
912 case WID_NS_NEWGRF_INFO: {
913 const GRFConfig *selected = this->active_sel;
914 if (selected == nullptr) selected = this->avail_sel;
915 if (selected != nullptr) {
916 ShowNewGRFInfo(*selected, r, this->show_params);
917 }
918 break;
919 }
920 }
921 }
922
923 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
924 {
925 if (widget >= WID_NS_NEWGRF_TEXTFILE && widget < WID_NS_NEWGRF_TEXTFILE + TFT_CONTENT_END) {
926 if (this->active_sel == nullptr && this->avail_sel == nullptr) return;
927
928 ShowNewGRFTextfileWindow(this, (TextfileType)(widget - WID_NS_NEWGRF_TEXTFILE), this->active_sel != nullptr ? this->active_sel : this->avail_sel);
929 return;
930 }
931
932 switch (widget) {
933 case WID_NS_PRESET_LIST: {
934 DropDownList list;
935
936 /* Add 'None' option for clearing list */
937 list.push_back(MakeDropDownListStringItem(STR_NONE, -1));
938
939 for (uint i = 0; i < this->grf_presets.size(); i++) {
940 list.push_back(MakeDropDownListStringItem(std::string{this->grf_presets[i]}, i));
941 }
942
943 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
944 ShowDropDownList(this, std::move(list), this->preset, WID_NS_PRESET_LIST);
945 break;
946 }
947
948 case WID_NS_OPEN_URL: {
949 const GRFConfig *c = (this->avail_sel == nullptr) ? this->active_sel : this->avail_sel;
950 auto url = c->GetURL();
951 if (url.has_value()) OpenBrowser(std::move(*url));
952 break;
953 }
954
956 ShowSavePresetWindow((this->preset == -1) ? std::string_view{} : this->grf_presets[this->preset]);
957 break;
958
960 if (this->preset == -1) return;
961
962 DeleteGRFPresetFromConfig(this->grf_presets[this->preset]);
964 this->preset = -1;
965 this->InvalidateData();
966 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
967 break;
968
969 case WID_NS_MOVE_UP: { // Move GRF up
970 if (this->active_sel == nullptr || !this->editable) break;
971
972 int pos = this->GetCurrentActivePosition();
973 if (pos <= 0) break;
974
975 std::swap(this->actives[pos - 1], this->actives[pos]);
976
977 this->vscroll->ScrollTowards(pos - 1);
978 this->preset = -1;
980 break;
981 }
982
983 case WID_NS_MOVE_DOWN: { // Move GRF down
984 if (this->active_sel == nullptr || !this->editable) break;
985
986 int pos = this->GetCurrentActivePosition();
987 if (pos == -1 || static_cast<size_t>(pos) >= this->actives.size() - 1) break;
988
989 std::swap(this->actives[pos], this->actives[pos + 1]);
990
991 this->vscroll->ScrollTowards(pos + 1);
992 this->preset = -1;
994 break;
995 }
996
997 case WID_NS_FILE_LIST: { // Select an active GRF.
999
1000 const GRFConfig *old_sel = this->active_sel;
1001 uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top);
1002 if (i < this->actives.size()) {
1003 this->active_sel = this->actives[i].get();
1004 } else {
1005 this->active_sel = nullptr;
1006 }
1007 if (this->active_sel != old_sel) {
1010 }
1011 this->avail_sel = nullptr;
1012 this->avail_pos = -1;
1013
1014 this->InvalidateData();
1015 if (click_count == 1) {
1016 if (this->editable && this->active_sel != nullptr) SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
1017 break;
1018 }
1019 /* With double click, continue */
1020 [[fallthrough]];
1021 }
1022
1023 case WID_NS_REMOVE: { // Remove GRF
1024 if (this->active_sel == nullptr || !this->editable) break;
1027
1028 /* Choose the next GRF file to be the selected file. */
1029 int pos = this->GetCurrentActivePosition();
1030 if (pos < 0) break;
1031
1032 auto it = std::next(std::begin(this->actives), pos);
1033 it = this->actives.erase(it);
1034 if (this->actives.empty()) {
1035 this->active_sel = nullptr;
1036 } else if (it == std::end(this->actives)) {
1037 this->active_sel = this->actives.back().get();
1038 } else {
1039 this->active_sel = it->get();
1040 }
1041 this->preset = -1;
1042 this->avail_pos = -1;
1043 this->avail_sel = nullptr;
1044 this->avails.ForceRebuild();
1046 break;
1047 }
1048
1049 case WID_NS_UPGRADE: { // Upgrade GRF.
1050 if (!this->editable || this->actives.empty()) break;
1053 break;
1054 }
1055
1056 case WID_NS_AVAIL_LIST: { // Select a non-active GRF.
1058
1059 auto it = this->vscroll2->GetScrolledItemFromWidget(this->avails, pt.y, this, WID_NS_AVAIL_LIST, WidgetDimensions::scaled.framerect.top);
1060 this->active_sel = nullptr;
1062 if (it != std::end(this->avails)) {
1063 if (this->avail_sel != *it) this->CloseChildWindows(WC_TEXTFILE);
1064 this->avail_sel = *it;
1065 this->avail_pos = static_cast<int>(std::distance(std::begin(this->avails), it));
1066 }
1067 this->InvalidateData();
1068 if (click_count == 1) {
1069 if (this->editable && this->avail_sel != nullptr && !this->avail_sel->flags.Test(GRFConfigFlag::Invalid)) SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
1070 break;
1071 }
1072 /* With double click, continue */
1073 [[fallthrough]];
1074 }
1075
1076 case WID_NS_ADD:
1077 if (this->avail_sel == nullptr || !this->editable || this->avail_sel->flags.Test(GRFConfigFlag::Invalid)) break;
1078
1079 this->AddGRFToActive();
1080 break;
1081
1082 case WID_NS_APPLY_CHANGES: // Apply changes made to GRF list
1083 if (!this->editable) break;
1084
1085 ShowQuery(
1086 GetEncodedString(STR_NEWGRF_POPUP_CAUTION_CAPTION),
1087 GetEncodedString(STR_NEWGRF_CONFIRMATION_TEXT),
1088 this,
1090 );
1091
1092 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
1093 break;
1094
1096 case WID_NS_SET_PARAMETERS: { // Edit parameters
1097 if (this->active_sel == nullptr || !this->show_params || this->active_sel->num_valid_params == 0) break;
1098
1099 OpenGRFParameterWindow(false, *this->active_sel, this->editable);
1101 break;
1102 }
1103
1105 if (this->active_sel != nullptr && this->editable) {
1106 this->active_sel->palette ^= GRFP_USE_MASK;
1107 this->SetDirty();
1109 }
1110 break;
1111
1114 if (!_network_available) {
1115 ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR);
1116 } else {
1117 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
1118
1120 }
1121 break;
1122
1125 RequestNewGRFScan(this);
1126 break;
1127 }
1128 }
1129
1130 void OnNewGRFsScanned() override
1131 {
1132 if (this->active_sel == nullptr) this->CloseChildWindows(WC_TEXTFILE);
1133 this->avail_sel = nullptr;
1134 this->avail_pos = -1;
1135 this->avails.ForceRebuild();
1136 this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window
1137 }
1138
1139 void OnDropdownSelect(WidgetID widget, int index) override
1140 {
1141 if (widget != WID_NS_PRESET_LIST) return;
1142 if (!this->editable) return;
1143
1145 this->preset = index;
1146
1147 if (index != -1) {
1148 this->actives = LoadGRFPresetFromConfig(this->grf_presets[index]);
1149 }
1150 this->avails.ForceRebuild();
1151
1155 this->active_sel = nullptr;
1157 }
1158
1159 void OnQueryTextFinished(std::optional<std::string> str) override
1160 {
1161 if (!str.has_value()) return;
1162
1163 SaveGRFPresetToConfig(*str, this->actives);
1164 this->grf_presets = GetGRFPresetList();
1165
1166 /* Switch to this preset */
1167 for (uint i = 0; i < this->grf_presets.size(); i++) {
1168 if (this->grf_presets[i] == str) {
1169 this->preset = i;
1170 break;
1171 }
1172 }
1173
1174 this->InvalidateData();
1175 }
1176
1181 {
1182 /* Update scrollbars */
1183 this->vscroll->SetCount(this->actives.size() + 1); // Reserve empty space for drag and drop handling.
1184
1185 if (this->avail_pos >= 0) this->vscroll2->ScrollTowards(this->avail_pos);
1186 }
1187
1193 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1194 {
1195 if (!gui_scope) return;
1196 switch (data) {
1197 default:
1198 /* Nothing important to do */
1199 break;
1200
1202 /* Search the list for items that are now found and mark them as such. */
1203 for (auto &c : this->actives) {
1204 bool compatible = c->flags.Test(GRFConfigFlag::Compatible);
1205 if (c->status != GCS_NOT_FOUND && !compatible) continue;
1206
1207 const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, compatible ? &c->original_md5sum : &c->ident.md5sum);
1208 if (f == nullptr || f->flags.Test(GRFConfigFlag::Invalid)) continue;
1209
1210 c = std::make_unique<GRFConfig>(*f);
1211 }
1212
1213 this->avails.ForceRebuild();
1214 [[fallthrough]];
1215
1217 this->modified = false;
1219 break;
1220
1222 this->preset = -1;
1223 [[fallthrough]];
1224
1227
1228 /* Changes have been made to the list of active NewGRFs */
1229 this->modified = true;
1230
1231 break;
1232 }
1233
1234 this->BuildAvailables();
1235
1236 this->SetWidgetDisabledState(WID_NS_APPLY_CHANGES, !((this->editable && this->modified) || _settings_client.gui.newgrf_developer_tools));
1237 this->SetWidgetsDisabledState(!this->editable,
1240 );
1241 this->SetWidgetDisabledState(WID_NS_ADD, !this->editable || this->avail_sel == nullptr || this->avail_sel->flags.Test(GRFConfigFlag::Invalid));
1242 this->SetWidgetDisabledState(WID_NS_UPGRADE, !this->editable || this->actives.empty() || !this->CanUpgradeCurrent());
1243
1244 bool disable_all = this->active_sel == nullptr || !this->editable;
1245 this->SetWidgetsDisabledState(disable_all,
1249 );
1250
1251 const GRFConfig *selected_config = (this->avail_sel == nullptr) ? this->active_sel : this->avail_sel;
1252 for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
1253 this->SetWidgetDisabledState(WID_NS_NEWGRF_TEXTFILE + tft, selected_config == nullptr || !selected_config->GetTextfile(tft).has_value());
1254 }
1255 this->SetWidgetDisabledState(WID_NS_OPEN_URL, selected_config == nullptr || !selected_config->GetURL().has_value());
1256
1257 this->SetWidgetDisabledState(WID_NS_SET_PARAMETERS, !this->show_params || this->active_sel == nullptr || this->active_sel->num_valid_params == 0);
1258 this->SetWidgetDisabledState(WID_NS_VIEW_PARAMETERS, !this->show_params || this->active_sel == nullptr || this->active_sel->num_valid_params == 0);
1259 this->SetWidgetDisabledState(WID_NS_TOGGLE_PALETTE, disable_all ||
1261
1262 if (!disable_all) {
1263 /* All widgets are now enabled, so disable widgets we can't use */
1264 if (this->active_sel == this->actives.front().get()) this->DisableWidget(WID_NS_MOVE_UP);
1265 if (this->active_sel == this->actives.back().get()) this->DisableWidget(WID_NS_MOVE_DOWN);
1266 }
1267
1268 this->SetWidgetDisabledState(WID_NS_PRESET_DELETE, this->preset == -1);
1269
1270 bool has_missing = false;
1271 bool has_compatible = false;
1272 for (const auto &c : this->actives) {
1273 has_missing |= c->status == GCS_NOT_FOUND;
1274 has_compatible |= c->flags.Test(GRFConfigFlag::Compatible);
1275 }
1276 StringID text;
1277 StringID tool_tip;
1278 if (has_missing || has_compatible) {
1279 text = STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON;
1280 tool_tip = STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP;
1281 } else {
1282 text = STR_INTRO_ONLINE_CONTENT;
1283 tool_tip = STR_INTRO_TOOLTIP_ONLINE_CONTENT;
1284 }
1285 this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD)->SetStringTip(text, tool_tip);
1286 this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD2)->SetStringTip(text, tool_tip);
1287
1288 this->SetWidgetDisabledState(WID_NS_PRESET_SAVE, has_missing);
1289 }
1290
1291 EventState OnKeyPress([[maybe_unused]] char32_t key, uint16_t keycode) override
1292 {
1293 if (!this->editable) return ES_NOT_HANDLED;
1294
1295 if (this->vscroll2->UpdateListPositionOnKeyPress(this->avail_pos, keycode) == ES_NOT_HANDLED) return ES_NOT_HANDLED;
1296
1297 if (this->avail_pos >= 0) {
1298 this->active_sel = nullptr;
1300 if (this->avail_sel != this->avails[this->avail_pos]) this->CloseChildWindows(WC_TEXTFILE);
1301 this->avail_sel = this->avails[this->avail_pos];
1302 this->vscroll2->ScrollTowards(this->avail_pos);
1303 this->InvalidateData(0);
1304 }
1305
1306 return ES_HANDLED;
1307 }
1308
1309 void OnEditboxChanged(WidgetID widget) override
1310 {
1311 if (!this->editable) return;
1312
1313 if (widget == WID_NS_FILTER) {
1314 string_filter.SetFilterTerm(this->filter_editbox.text.GetText());
1316 this->avails.ForceRebuild();
1317 this->InvalidateData(0);
1318 }
1319 }
1320
1321 void OnDragDrop(Point pt, WidgetID widget) override
1322 {
1323 if (!this->editable) return;
1324
1325 if (widget == WID_NS_FILE_LIST) {
1326 if (this->active_sel != nullptr) {
1327 int from_pos = this->GetCurrentActivePosition();
1328
1329 /* Gets the drag-and-drop destination offset. Ignore the last dummy line. */
1330 int to_pos = std::min(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top), this->vscroll->GetCount() - 2);
1331 if (to_pos != from_pos) { // Don't move NewGRF file over itself.
1332 if (to_pos > from_pos) ++to_pos;
1333
1334 auto from = std::next(std::begin(this->actives), from_pos);
1335 auto to = std::next(std::begin(this->actives), to_pos);
1336 Slide(from, std::next(from), to);
1337
1338 this->vscroll->ScrollTowards(to_pos);
1339 this->preset = -1;
1340 this->InvalidateData();
1341 }
1342 } else if (this->avail_sel != nullptr) {
1343 int to_pos = std::min(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top), this->vscroll->GetCount() - 1);
1344 this->AddGRFToActive(to_pos);
1345 }
1346 } else if (widget == WID_NS_AVAIL_LIST && this->active_sel != nullptr) {
1347 /* Remove active NewGRF file by dragging it over available list. */
1348 Point dummy = {-1, -1};
1349 this->OnClick(dummy, WID_NS_REMOVE, 1);
1350 }
1351
1353
1354 if (this->active_over != -1) {
1355 /* End of drag-and-drop, hide dragged destination highlight. */
1356 this->SetWidgetDirty(this->active_over == -2 ? WID_NS_AVAIL_LIST : WID_NS_FILE_LIST);
1357 this->active_over = -1;
1358 }
1359 }
1360
1361 void OnMouseDrag(Point pt, WidgetID widget) override
1362 {
1363 if (!this->editable) return;
1364
1365 if (widget == WID_NS_FILE_LIST && (this->active_sel != nullptr || this->avail_sel != nullptr)) {
1366 /* An NewGRF file is dragged over the active list. */
1367 int to_pos = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST, WidgetDimensions::scaled.framerect.top);
1368 /* Skip the last dummy line if the source is from the active list. */
1369 to_pos = std::min(to_pos, this->vscroll->GetCount() - (this->active_sel != nullptr ? 2 : 1));
1370
1371 if (to_pos != this->active_over) {
1372 this->active_over = to_pos;
1374 }
1375 } else if (widget == WID_NS_AVAIL_LIST && this->active_sel != nullptr) {
1376 this->active_over = -2;
1378 } else if (this->active_over != -1) {
1379 this->SetWidgetDirty(this->active_over == -2 ? WID_NS_AVAIL_LIST : WID_NS_FILE_LIST);
1380 this->active_over = -1;
1381 }
1382 }
1383
1384private:
1386 static bool NameSorter(const GRFConfig * const &a, const GRFConfig * const &b)
1387 {
1388 std::string name_a = StrMakeValid(a->GetName(), {}); // Make a copy without control codes.
1389 std::string name_b = StrMakeValid(b->GetName(), {}); // Make a copy without control codes.
1390 int i = StrNaturalCompare(name_a, name_b, true); // Sort by name (natural sorting).
1391 if (i != 0) return i < 0;
1392
1393 i = a->version - b->version;
1394 if (i != 0) return i < 0;
1395
1396 return a->ident.md5sum < b->ident.md5sum;
1397 }
1398
1400 static bool TagNameFilter(const GRFConfig * const *a, StringFilter &filter)
1401 {
1402 filter.ResetState();
1403 filter.AddLine((*a)->GetName());
1404 filter.AddLine((*a)->filename);
1405 if (auto desc = (*a)->GetDescription(); desc.has_value()) filter.AddLine(*desc);
1406 return filter.GetState();;
1407 }
1408
1409 void BuildAvailables()
1410 {
1411 if (!this->avails.NeedRebuild()) return;
1412
1413 this->avails.clear();
1414
1415 for (const auto &c : _all_grfs) {
1416 if (std::ranges::any_of(this->actives, [&c](const auto &gc) { return gc->ident.HasGrfIdentifier(c->ident.grfid, &c->ident.md5sum); })) continue;
1417
1419 this->avails.push_back(c.get());
1420 } else {
1422 /* Never triggers; FindGRFConfig returns either c, or a newer version of c. */
1423 assert(best != nullptr);
1424
1425 /*
1426 * If the best version is 0, then all NewGRF with this GRF ID
1427 * have version 0, so for backward compatibility reasons we
1428 * want to show them all.
1429 * If we are the best version, then we definitely want to
1430 * show that NewGRF!.
1431 */
1432 if (best->version == 0 || best->ident.HasGrfIdentifier(c->ident.grfid, &c->ident.md5sum)) {
1433 this->avails.push_back(c.get());
1434 }
1435 }
1436 }
1437
1438 this->avails.Filter(this->string_filter);
1439 this->avails.RebuildDone();
1440 this->avails.Sort();
1441
1442 if (this->avail_sel != nullptr) {
1443 this->avail_pos = find_index(this->avails, this->avail_sel);
1444 if (this->avail_pos == -1) {
1445 this->avail_sel = nullptr;
1446 }
1447 }
1448
1449 this->vscroll2->SetCount(this->avails.size()); // Update the scrollbar
1450 }
1451
1457 bool AddGRFToActive(int ins_pos = -1)
1458 {
1459 if (this->avail_sel == nullptr || !this->editable || this->avail_sel->flags.Test(GRFConfigFlag::Invalid)) return false;
1460
1462
1463 /* Get number of non-static NewGRFs. */
1464 size_t count = std::ranges::count_if(this->actives, [](const auto &gc) { return !gc->flags.Test(GRFConfigFlag::Static); });
1465 if (count >= NETWORK_MAX_GRF_COUNT) {
1466 ShowErrorMessage(GetEncodedString(STR_NEWGRF_TOO_MANY_NEWGRFS), {}, WL_INFO);
1467 return false;
1468 }
1469
1470 /* Check for duplicate GRF ID. */
1471 if (std::ranges::any_of(this->actives, [&grfid = this->avail_sel->ident.grfid](const auto &gc) { return gc->ident.grfid == grfid; })) {
1472 ShowErrorMessage(GetEncodedString(STR_NEWGRF_DUPLICATE_GRFID), {}, WL_INFO);
1473 return false;
1474 }
1475
1476 auto entry = (ins_pos >= 0 && static_cast<size_t>(ins_pos) < std::size(this->actives))
1477 ? std::next(std::begin(this->actives), ins_pos)
1478 : std::end(this->actives);
1479
1480 /* Copy GRF details from scanned list. */
1481 entry = this->actives.insert(entry, std::make_unique<GRFConfig>(*this->avail_sel));
1482 (*entry)->SetParameterDefaults();
1483
1484 /* Select next (or previous, if last one) item in the list. */
1485 int new_pos = this->avail_pos + 1;
1486 if (new_pos >= (int)this->avails.size()) new_pos = this->avail_pos - 1;
1487 this->avail_pos = new_pos;
1488 if (new_pos >= 0) this->avail_sel = this->avails[new_pos];
1489
1490 this->avails.ForceRebuild();
1492 return true;
1493 }
1494};
1495
1500void ShowMissingContentWindow(const GRFConfigList &list)
1501{
1502 /* Only show the things in the current list, or everything when nothing's selected */
1503 ContentVector cv;
1504 for (const auto &c : list) {
1505 if (c->status != GCS_NOT_FOUND && !c->flags.Test(GRFConfigFlag::Compatible)) continue;
1506
1507 auto ci = std::make_unique<ContentInfo>();
1508 ci->type = CONTENT_TYPE_NEWGRF;
1510 ci->name = c->GetName();
1511 ci->unique_id = std::byteswap(c->ident.grfid);
1513 cv.push_back(std::move(ci));
1514 }
1515 ShowNetworkContentListWindow(cv.empty() ? nullptr : &cv, CONTENT_TYPE_NEWGRF);
1516}
1517
1520
1521const std::initializer_list<NewGRFWindow::GUIGRFConfigList::SortFunction * const> NewGRFWindow::sorter_funcs = {
1522 &NameSorter,
1523};
1524
1525const std::initializer_list<NewGRFWindow::GUIGRFConfigList::FilterFunction * const> NewGRFWindow::filter_funcs = {
1526 &TagNameFilter,
1527};
1528
1536public:
1537 static const uint MAX_EXTRA_INFO_WIDTH;
1538 static const uint MIN_EXTRA_FOR_3_COLUMNS;
1539
1540 std::unique_ptr<NWidgetBase> avs{};
1541 std::unique_ptr<NWidgetBase> acs{};
1542 std::unique_ptr<NWidgetBase> inf{};
1543 bool editable = true;
1544
1545 NWidgetNewGRFDisplay(std::unique_ptr<NWidgetBase> &&avs, std::unique_ptr<NWidgetBase> &&acs, std::unique_ptr<NWidgetBase> &&inf) : NWidgetBase(NWID_CUSTOM)
1546 , avs(std::move(avs))
1547 , acs(std::move(acs))
1548 , inf(std::move(inf))
1549 {
1550 }
1551
1552 void SetupSmallestSize(Window *w) override
1553 {
1554 /* Copy state flag from the window. */
1555 assert(dynamic_cast<NewGRFWindow *>(w) != nullptr);
1556 NewGRFWindow *ngw = (NewGRFWindow *)w;
1557 this->editable = ngw->editable;
1558
1559 this->avs->SetupSmallestSize(w);
1560 this->acs->SetupSmallestSize(w);
1561 this->inf->SetupSmallestSize(w);
1562
1563 uint min_avs_width = this->avs->smallest_x + this->avs->padding.Horizontal();
1564 uint min_acs_width = this->acs->smallest_x + this->acs->padding.Horizontal();
1565 uint min_inf_width = this->inf->smallest_x + this->inf->padding.Horizontal();
1566
1567 uint min_avs_height = this->avs->smallest_y + this->avs->padding.Vertical();
1568 uint min_acs_height = this->acs->smallest_y + this->acs->padding.Vertical();
1569 uint min_inf_height = this->inf->smallest_y + this->inf->padding.Vertical();
1570
1571 /* Smallest window is in two column mode. */
1572 this->smallest_x = std::max(min_avs_width, min_acs_width) + WidgetDimensions::scaled.hsep_wide + min_inf_width;
1573 this->smallest_y = std::max(min_inf_height, min_acs_height + WidgetDimensions::scaled.vsep_wide + min_avs_height);
1574
1575 /* Filling. */
1576 this->fill_x = std::lcm(this->avs->fill_x, this->acs->fill_x);
1577 if (this->inf->fill_x > 0 && (this->fill_x == 0 || this->fill_x > this->inf->fill_x)) this->fill_x = this->inf->fill_x;
1578
1579 this->fill_y = this->avs->fill_y;
1580 if (this->acs->fill_y > 0 && (this->fill_y == 0 || this->fill_y > this->acs->fill_y)) this->fill_y = this->acs->fill_y;
1581 this->fill_y = std::lcm(this->fill_y, this->inf->fill_y);
1582
1583 /* Resizing. */
1584 this->resize_x = std::lcm(this->avs->resize_x, this->acs->resize_x);
1585 if (this->inf->resize_x > 0 && (this->resize_x == 0 || this->resize_x > this->inf->resize_x)) this->resize_x = this->inf->resize_x;
1586
1587 this->resize_y = this->avs->resize_y;
1588 if (this->acs->resize_y > 0 && (this->resize_y == 0 || this->resize_y > this->acs->resize_y)) this->resize_y = this->acs->resize_y;
1589 this->resize_y = std::lcm(this->resize_y, this->inf->resize_y);
1590
1591 /* Make sure the height suits the 3 column (resp. not-editable) format; the 2 column format can easily fill space between the lists */
1592 this->smallest_y = ComputeMaxSize(min_acs_height, this->smallest_y + this->resize_y - 1, this->resize_y);
1593 }
1594
1595 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
1596 {
1597 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1598
1599 uint min_avs_width = this->avs->smallest_x + this->avs->padding.Horizontal();
1600 uint min_acs_width = this->acs->smallest_x + this->acs->padding.Horizontal();
1601 uint min_inf_width = this->inf->smallest_x + this->inf->padding.Horizontal();
1602
1603 uint min_list_width = std::max(min_avs_width, min_acs_width); // Smallest width of the lists such that they have equal width (incl padding).
1604 uint avs_extra_width = min_list_width - min_avs_width; // Additional width needed for avs to reach min_list_width.
1605 uint acs_extra_width = min_list_width - min_acs_width; // Additional width needed for acs to reach min_list_width.
1606
1607 /* Use 2 or 3 columns? */
1608 uint min_three_columns = min_avs_width + min_acs_width + min_inf_width + 2 * WidgetDimensions::scaled.hsep_wide;
1609 uint min_two_columns = min_list_width + min_inf_width + WidgetDimensions::scaled.hsep_wide;
1610 bool use_three_columns = this->editable && (min_three_columns + ScaleGUITrad(MIN_EXTRA_FOR_3_COLUMNS) <= given_width);
1611
1612 /* Info panel is a separate column in both modes. Compute its width first. */
1613 uint extra_width, inf_width;
1614 if (use_three_columns) {
1615 extra_width = given_width - min_three_columns;
1616 inf_width = std::min<uint>(ScaleGUITrad(MAX_EXTRA_INFO_WIDTH), extra_width / 2);
1617 } else {
1618 extra_width = given_width - min_two_columns;
1619 inf_width = std::min<uint>(ScaleGUITrad(MAX_EXTRA_INFO_WIDTH), extra_width / 2);
1620 }
1621 inf_width = ComputeMaxSize(this->inf->smallest_x, this->inf->smallest_x + inf_width, this->inf->GetHorizontalStepSize(sizing));
1622 extra_width -= inf_width - this->inf->smallest_x;
1623
1624 uint inf_height = ComputeMaxSize(this->inf->smallest_y, given_height, this->inf->GetVerticalStepSize(sizing));
1625
1626 if (use_three_columns) {
1627 /* Three column display, first make both lists equally wide, then divide whatever is left between both lists.
1628 * Only keep track of what avs gets, all other space goes to acs. */
1629 uint avs_width = std::min(avs_extra_width, extra_width);
1630 extra_width -= avs_width;
1631 extra_width -= std::min(acs_extra_width, extra_width);
1632 avs_width += extra_width / 2;
1633
1634 avs_width = ComputeMaxSize(this->avs->smallest_x, this->avs->smallest_x + avs_width, this->avs->GetHorizontalStepSize(sizing));
1635
1636 uint acs_width = given_width - // Remaining space, including horizontal padding.
1637 inf_width - this->inf->padding.Horizontal() -
1638 avs_width - this->avs->padding.Horizontal() - 2 * WidgetDimensions::scaled.hsep_wide;
1639 acs_width = ComputeMaxSize(min_acs_width, acs_width, this->acs->GetHorizontalStepSize(sizing)) -
1640 this->acs->padding.Horizontal();
1641
1642 /* Never use fill_y on these; the minimal size is chosen, so that the 3 column view looks nice */
1643 uint avs_height = ComputeMaxSize(this->avs->smallest_y, given_height, this->avs->resize_y);
1644 uint acs_height = ComputeMaxSize(this->acs->smallest_y, given_height, this->acs->resize_y);
1645
1646 /* Assign size and position to the children. */
1647 if (rtl) {
1648 x += this->inf->padding.left;
1649 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1650 x += inf_width + this->inf->padding.right + WidgetDimensions::scaled.hsep_wide;
1651 } else {
1652 x += this->avs->padding.left;
1653 this->avs->AssignSizePosition(sizing, x, y + this->avs->padding.top, avs_width, avs_height, rtl);
1654 x += avs_width + this->avs->padding.right + WidgetDimensions::scaled.hsep_wide;
1655 }
1656
1657 x += this->acs->padding.left;
1658 this->acs->AssignSizePosition(sizing, x, y + this->acs->padding.top, acs_width, acs_height, rtl);
1659 x += acs_width + this->acs->padding.right + WidgetDimensions::scaled.hsep_wide;
1660
1661 if (rtl) {
1662 x += this->avs->padding.left;
1663 this->avs->AssignSizePosition(sizing, x, y + this->avs->padding.top, avs_width, avs_height, rtl);
1664 } else {
1665 x += this->inf->padding.left;
1666 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1667 }
1668 } else {
1669 /* Two columns, all space in extra_width goes to both lists. Since the lists are underneath each other,
1670 * the column is min_list_width wide at least. */
1671 uint avs_width = ComputeMaxSize(this->avs->smallest_x, this->avs->smallest_x + avs_extra_width + extra_width,
1672 this->avs->GetHorizontalStepSize(sizing));
1673 uint acs_width = ComputeMaxSize(this->acs->smallest_x, this->acs->smallest_x + acs_extra_width + extra_width,
1674 this->acs->GetHorizontalStepSize(sizing));
1675
1676 uint min_avs_height = (!this->editable) ? 0 : this->avs->smallest_y + this->avs->padding.Vertical() + WidgetDimensions::scaled.vsep_wide;
1677 uint min_acs_height = this->acs->smallest_y + this->acs->padding.Vertical();
1678 uint extra_height = given_height - min_acs_height - min_avs_height;
1679
1680 /* Never use fill_y on these; instead use WidgetDimensions::scaled.vsep_wide as filler */
1681 uint avs_height = ComputeMaxSize(this->avs->smallest_y, this->avs->smallest_y + extra_height / 2, this->avs->resize_y);
1682 if (this->editable) extra_height -= avs_height - this->avs->smallest_y;
1683 uint acs_height = ComputeMaxSize(this->acs->smallest_y, this->acs->smallest_y + extra_height, this->acs->resize_y);
1684
1685 /* Assign size and position to the children. */
1686 if (rtl) {
1687 x += this->inf->padding.left;
1688 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1689 x += inf_width + this->inf->padding.right + WidgetDimensions::scaled.hsep_wide;
1690
1691 this->acs->AssignSizePosition(sizing, x + this->acs->padding.left, y + this->acs->padding.top, acs_width, acs_height, rtl);
1692 if (this->editable) {
1693 this->avs->AssignSizePosition(sizing, x + this->avs->padding.left, y + given_height - avs_height - this->avs->padding.bottom, avs_width, avs_height, rtl);
1694 } else {
1695 this->avs->AssignSizePosition(sizing, 0, 0, this->avs->smallest_x, this->avs->smallest_y, rtl);
1696 }
1697 } else {
1698 this->acs->AssignSizePosition(sizing, x + this->acs->padding.left, y + this->acs->padding.top, acs_width, acs_height, rtl);
1699 if (this->editable) {
1700 this->avs->AssignSizePosition(sizing, x + this->avs->padding.left, y + given_height - avs_height - this->avs->padding.bottom, avs_width, avs_height, rtl);
1701 } else {
1702 this->avs->AssignSizePosition(sizing, 0, 0, this->avs->smallest_x, this->avs->smallest_y, rtl);
1703 }
1704 uint dx = this->acs->current_x + this->acs->padding.Horizontal();
1705 if (this->editable) {
1706 dx = std::max(dx, this->avs->current_x + this->avs->padding.Horizontal());
1707 }
1708 x += dx + WidgetDimensions::scaled.hsep_wide + this->inf->padding.left;
1709 this->inf->AssignSizePosition(sizing, x, y + this->inf->padding.top, inf_width, inf_height, rtl);
1710 }
1711 }
1712 }
1713
1714 void FillWidgetLookup(WidgetLookup &widget_lookup) override
1715 {
1716 this->avs->FillWidgetLookup(widget_lookup);
1717 this->acs->FillWidgetLookup(widget_lookup);
1718 this->inf->FillWidgetLookup(widget_lookup);
1719 }
1720
1721 NWidgetCore *GetWidgetFromPos(int x, int y) override
1722 {
1723 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1724
1725 NWidgetCore *nw = (this->editable) ? this->avs->GetWidgetFromPos(x, y) : nullptr;
1726 if (nw == nullptr) nw = this->acs->GetWidgetFromPos(x, y);
1727 if (nw == nullptr) nw = this->inf->GetWidgetFromPos(x, y);
1728 return nw;
1729 }
1730
1731 void Draw(const Window *w) override
1732 {
1733 if (this->editable) this->avs->Draw(w);
1734 this->acs->Draw(w);
1735 this->inf->Draw(w);
1736 }
1737};
1738
1741
1742static constexpr NWidgetPart _nested_newgrf_actives_widgets[] = {
1744 /* Left side, presets. */
1747 NWidget(WWT_TEXT, INVALID_COLOUR), SetStringTip(STR_NEWGRF_SETTINGS_SELECT_PRESET),
1748 SetPadding(0, WidgetDimensions::unscaled.hsep_wide, 0, 0),
1749 NWidget(WWT_DROPDOWN, COLOUR_YELLOW, WID_NS_PRESET_LIST), SetFill(1, 0), SetResize(1, 0),
1750 SetToolTip(STR_NEWGRF_SETTINGS_PRESET_LIST_TOOLTIP),
1751 EndContainer(),
1753 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_PRESET_SAVE), SetFill(1, 0), SetResize(1, 0),
1754 SetStringTip(STR_NEWGRF_SETTINGS_PRESET_SAVE, STR_NEWGRF_SETTINGS_PRESET_SAVE_TOOLTIP),
1755 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_PRESET_DELETE), SetFill(1, 0), SetResize(1, 0),
1756 SetStringTip(STR_NEWGRF_SETTINGS_PRESET_DELETE, STR_NEWGRF_SETTINGS_PRESET_DELETE_TOOLTIP),
1757 EndContainer(),
1758 EndContainer(),
1759
1760 NWidget(WWT_FRAME, COLOUR_MAUVE), SetStringTip(STR_NEWGRF_SETTINGS_ACTIVE_LIST), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
1761 /* Left side, active grfs. */
1763 NWidget(WWT_PANEL, COLOUR_MAUVE),
1764 NWidget(WWT_INSET, COLOUR_MAUVE, WID_NS_FILE_LIST), SetMinimalSize(100, 1), SetPadding(2),
1765 SetFill(1, 1), SetResize(1, 1), SetScrollbar(WID_NS_SCROLLBAR), SetToolTip(STR_NEWGRF_SETTINGS_FILE_TOOLTIP),
1766 EndContainer(),
1767 EndContainer(),
1769 EndContainer(),
1770
1771 /* Buttons. */
1772 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_REMOVE),
1774 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_REMOVE), SetFill(1, 0), SetResize(1, 0),
1775 SetStringTip(STR_NEWGRF_SETTINGS_REMOVE, STR_NEWGRF_SETTINGS_REMOVE_TOOLTIP),
1777 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_MOVE_UP), SetFill(1, 0), SetResize(1, 0),
1778 SetStringTip(STR_NEWGRF_SETTINGS_MOVEUP, STR_NEWGRF_SETTINGS_MOVEUP_TOOLTIP),
1779 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_MOVE_DOWN), SetFill(1, 0), SetResize(1, 0),
1780 SetStringTip(STR_NEWGRF_SETTINGS_MOVEDOWN, STR_NEWGRF_SETTINGS_MOVEDOWN_TOOLTIP),
1781 EndContainer(),
1782 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_UPGRADE), SetFill(1, 0), SetResize(1, 0),
1783 SetStringTip(STR_NEWGRF_SETTINGS_UPGRADE, STR_NEWGRF_SETTINGS_UPGRADE_TOOLTIP),
1784 EndContainer(),
1785
1787 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_RESCAN_FILES2), SetFill(1, 0), SetResize(1, 0),
1788 SetStringTip(STR_NEWGRF_SETTINGS_RESCAN_FILES, STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP),
1789 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_CONTENT_DOWNLOAD2), SetFill(1, 0), SetResize(1, 0),
1790 SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
1791 EndContainer(),
1792 EndContainer(),
1793 EndContainer(),
1794 EndContainer(),
1795};
1796
1797static constexpr NWidgetPart _nested_newgrf_availables_widgets[] = {
1798 NWidget(WWT_FRAME, COLOUR_MAUVE), SetStringTip(STR_NEWGRF_SETTINGS_INACTIVE_LIST), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
1799 /* Left side, available grfs, filter edit box. */
1801 NWidget(WWT_TEXT, INVALID_COLOUR), SetFill(0, 1), SetStringTip(STR_NEWGRF_FILTER_TITLE),
1802 NWidget(WWT_EDITBOX, COLOUR_MAUVE, WID_NS_FILTER), SetFill(1, 0), SetResize(1, 0),
1803 SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1804 EndContainer(),
1805
1806 /* Left side, available grfs. */
1808 NWidget(WWT_PANEL, COLOUR_MAUVE),
1809 NWidget(WWT_INSET, COLOUR_MAUVE, WID_NS_AVAIL_LIST), SetMinimalSize(100, 1), SetPadding(2),
1811 EndContainer(),
1812 EndContainer(),
1814 EndContainer(),
1815
1816 /* Left side, available grfs, buttons. */
1818 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_ADD), SetFill(1, 0), SetResize(1, 0),
1819 SetStringTip(STR_NEWGRF_SETTINGS_ADD, STR_NEWGRF_SETTINGS_ADD_FILE_TOOLTIP),
1821 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_RESCAN_FILES), SetFill(1, 0), SetResize(1, 0),
1822 SetStringTip(STR_NEWGRF_SETTINGS_RESCAN_FILES, STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP),
1823 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_CONTENT_DOWNLOAD), SetFill(1, 0), SetResize(1, 0),
1824 SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
1825 EndContainer(),
1826 EndContainer(),
1827 EndContainer(),
1828};
1829
1830static constexpr NWidgetPart _nested_newgrf_infopanel_widgets[] = {
1832 /* Right side, info panel. */
1833 NWidget(WWT_PANEL, COLOUR_MAUVE),
1834 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NS_NEWGRF_INFO_TITLE), SetFill(1, 0), SetResize(1, 0),
1835 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NS_NEWGRF_INFO), SetFill(1, 1), SetResize(1, 1), SetMinimalSize(150, 100),
1836 EndContainer(),
1837
1838 /* Right side, info buttons. */
1841 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_OPEN_URL), SetFill(1, 0), SetResize(1, 0),
1842 SetStringTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
1844 SetStringTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP),
1845 EndContainer(),
1848 SetStringTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP),
1850 SetStringTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP),
1851 EndContainer(),
1852 EndContainer(),
1853
1854 /* Right side, config buttons. */
1855 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_EDIT),
1858 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_SET_PARAMETERS), SetFill(1, 0), SetResize(1, 0),
1859 SetStringTip(STR_NEWGRF_SETTINGS_SET_PARAMETERS),
1860 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_TOGGLE_PALETTE), SetFill(1, 0), SetResize(1, 0),
1861 SetStringTip(STR_NEWGRF_SETTINGS_TOGGLE_PALETTE, STR_NEWGRF_SETTINGS_TOGGLE_PALETTE_TOOLTIP),
1862 EndContainer(),
1863 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_APPLY),
1864 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_APPLY_CHANGES), SetFill(1, 0), SetResize(1, 0),
1865 SetStringTip(STR_NEWGRF_SETTINGS_APPLY_CHANGES),
1866 EndContainer(),
1867 EndContainer(),
1868 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_VIEW_PARAMETERS), SetFill(1, 0), SetResize(1, 0),
1869 SetStringTip(STR_NEWGRF_SETTINGS_SHOW_PARAMETERS),
1870 EndContainer(),
1871 EndContainer(),
1872};
1873
1875std::unique_ptr<NWidgetBase> NewGRFDisplay()
1876{
1877 std::unique_ptr<NWidgetBase> avs = MakeNWidgets(_nested_newgrf_availables_widgets, nullptr);
1878 std::unique_ptr<NWidgetBase> acs = MakeNWidgets(_nested_newgrf_actives_widgets, nullptr);
1879 std::unique_ptr<NWidgetBase> inf = MakeNWidgets(_nested_newgrf_infopanel_widgets, nullptr);
1880
1881 return std::make_unique<NWidgetNewGRFDisplay>(std::move(avs), std::move(acs), std::move(inf));
1882}
1883
1884/* Widget definition of the manage newgrfs window */
1885static constexpr NWidgetPart _nested_newgrf_widgets[] = {
1887 NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
1888 NWidget(WWT_CAPTION, COLOUR_MAUVE), SetStringTip(STR_NEWGRF_SETTINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1889 NWidget(WWT_DEFSIZEBOX, COLOUR_MAUVE),
1890 EndContainer(),
1891 NWidget(WWT_PANEL, COLOUR_MAUVE),
1893 /* Resize button. */
1895 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1896 NWidget(WWT_RESIZEBOX, COLOUR_MAUVE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1897 EndContainer(),
1898 EndContainer(),
1899};
1900
1901/* Window definition of the manage newgrfs window */
1902static WindowDesc _newgrf_desc(
1903 WDP_CENTER, "settings_newgrf", 300, 263,
1905 {},
1906 _nested_newgrf_widgets
1907);
1908
1914static void NewGRFConfirmationCallback(Window *w, bool confirmed)
1915{
1916 if (confirmed) {
1919 NewGRFWindow *nw = dynamic_cast<NewGRFWindow*>(w);
1920 assert(nw != nullptr);
1921
1923 _gamelog.GRFUpdate(_grfconfig, nw->actives); // log GRF changes
1924 CopyGRFConfigList(nw->orig_list, nw->actives, false);
1927
1928 /* Show new, updated list */
1929 int pos = nw->GetCurrentActivePosition();
1930
1931 CopyGRFConfigList(nw->actives, nw->orig_list, false);
1932
1933 if (nw->active_sel != nullptr) {
1934 /* Set current selection from position */
1935 if (static_cast<size_t>(pos) >= nw->actives.size()) {
1936 nw->active_sel = nw->actives.back().get();
1937 } else {
1938 auto it = std::next(std::begin(nw->actives), pos);
1939 nw->active_sel = it->get();
1940 }
1941 }
1942 nw->avails.ForceRebuild();
1943 nw->modified = false;
1944
1945 w->InvalidateData();
1946
1947 ReInitAllWindows(false);
1949 }
1950}
1951
1952
1953
1962void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfigList &config)
1963{
1965 new NewGRFWindow(_newgrf_desc, editable, show_params, exec_changes, config);
1966}
1967
1971 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
1972 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_SAVE_PRESET_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1973 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
1974 EndContainer(),
1975 NWidget(WWT_PANEL, COLOUR_GREY),
1977 NWidget(WWT_INSET, COLOUR_GREY, WID_SVP_PRESET_LIST), SetPadding(2, 1, 2, 2),
1978 SetToolTip(STR_SAVE_PRESET_LIST_TOOLTIP), SetResize(1, 10), SetScrollbar(WID_SVP_SCROLLBAR), EndContainer(),
1980 EndContainer(),
1981 EndContainer(),
1982 NWidget(WWT_PANEL, COLOUR_GREY),
1983 NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SVP_EDITBOX), SetPadding(2, 2, 2, 2), SetFill(1, 0), SetResize(1, 0),
1984 SetStringTip(STR_SAVE_PRESET_TITLE, STR_SAVE_PRESET_EDITBOX_TOOLTIP),
1985 EndContainer(),
1987 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SVP_SAVE), SetStringTip(STR_SAVE_PRESET_SAVE, STR_SAVE_PRESET_SAVE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
1988 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
1989 EndContainer(),
1990};
1991
1994 WDP_CENTER, "save_preset", 140, 110,
1998);
1999
2001struct SavePresetWindow : public Window {
2004 Scrollbar *vscroll = nullptr;
2005 int selected = -1;
2006
2011 SavePresetWindow(std::string_view initial_text) : Window(_save_preset_desc), presetname_editbox(32)
2012 {
2013 this->presets = GetGRFPresetList();
2014 if (!initial_text.empty()) {
2015 for (uint i = 0; i < this->presets.size(); i++) {
2016 if (this->presets[i] == initial_text) {
2017 this->selected = i;
2018 break;
2019 }
2020 }
2021 }
2022
2024 this->presetname_editbox.ok_button = WID_SVP_SAVE;
2025
2026 this->CreateNestedTree();
2027 this->vscroll = this->GetScrollbar(WID_SVP_SCROLLBAR);
2028 this->FinishInitNested(0);
2029
2030 this->vscroll->SetCount(this->presets.size());
2032 this->presetname_editbox.text.Assign(initial_text);
2033 }
2034
2036 {
2037 }
2038
2039 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2040 {
2041 switch (widget) {
2042 case WID_SVP_PRESET_LIST: {
2044 size.height = 0;
2045 for (uint i = 0; i < this->presets.size(); i++) {
2047 size.width = std::max(size.width, d.width + padding.width);
2048 resize.height = std::max(resize.height, d.height);
2049 }
2050 size.height = ClampU((uint)this->presets.size(), 5, 20) * resize.height + padding.height;
2051 break;
2052 }
2053 }
2054 }
2055
2056 void DrawWidget(const Rect &r, WidgetID widget) const override
2057 {
2058 switch (widget) {
2059 case WID_SVP_PRESET_LIST: {
2060 const Rect br = r.Shrink(WidgetDimensions::scaled.bevel);
2061 GfxFillRect(br, PC_BLACK);
2062
2063 uint step_height = this->GetWidget<NWidgetBase>(WID_SVP_PRESET_LIST)->resize_y;
2064 int offset_y = (step_height - GetCharacterHeight(FS_NORMAL)) / 2;
2065 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2066
2067 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->presets);
2068 for (auto it = first; it != last; ++it) {
2069 int row = static_cast<int>(std::distance(std::begin(this->presets), it));
2070 if (row == this->selected) GfxFillRect(br.left, tr.top, br.right, tr.top + step_height - 1, PC_DARK_BLUE);
2071
2072 DrawString(tr.left, tr.right, tr.top + offset_y, *it, (row == this->selected) ? TC_WHITE : TC_SILVER);
2073 tr.top += step_height;
2074 }
2075 break;
2076 }
2077 }
2078 }
2079
2080 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2081 {
2082 switch (widget) {
2083 case WID_SVP_PRESET_LIST: {
2084 auto it = this->vscroll->GetScrolledItemFromWidget(this->presets, pt.y, this, WID_SVP_PRESET_LIST);
2085 if (it != this->presets.end()) {
2086 this->selected = it - this->presets.begin();
2087 this->presetname_editbox.text.Assign(*it);
2090 }
2091 break;
2092 }
2093
2094 case WID_SVP_SAVE: {
2096 if (w != nullptr) {
2097 auto text = this->presetname_editbox.text.GetText();
2098 if (!text.empty()) w->OnQueryTextFinished(std::string{text});
2099 }
2100 this->Close();
2101 break;
2102 }
2103 }
2104 }
2105
2106 void OnResize() override
2107 {
2108 this->vscroll->SetCapacityFromWidget(this, WID_SVP_PRESET_LIST, WidgetDimensions::scaled.framerect.Vertical());
2109 }
2110};
2111
2116static void ShowSavePresetWindow(std::string_view initial_text)
2117{
2119 new SavePresetWindow(initial_text);
2120}
2121
2124 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_NEWGRF_SCAN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2125 NWidget(WWT_PANEL, COLOUR_GREY),
2127 NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_NEWGRF_SCAN_MESSAGE), SetFill(1, 0),
2128 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SP_PROGRESS_BAR), SetFill(1, 0),
2129 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SP_PROGRESS_TEXT), SetFill(1, 0), SetMinimalSize(400, 0),
2130 EndContainer(),
2131 EndContainer(),
2132};
2133
2136 WDP_CENTER, {}, 0, 0,
2138 {},
2140);
2141
2144 std::string last_name{};
2145 int scanned = 0;
2146
2149 {
2150 this->InitNested(1);
2151 }
2152
2153 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2154 {
2155 switch (widget) {
2157 size = GetStringBoundingBox(GetString(STR_GENERATION_PROGRESS, GetParamMaxValue(100)));
2158 /* We need some spacing for the 'border' */
2161 break;
2162
2163 case WID_SP_PROGRESS_TEXT: {
2164 uint64_t max_digits = GetParamMaxDigits(4);
2165 /* We really don't know the width. We could determine it by scanning the NewGRFs,
2166 * but this is the status window for scanning them... */
2167 size.width = std::max<uint>(size.width, GetStringBoundingBox(GetString(STR_NEWGRF_SCAN_STATUS, max_digits, max_digits)).width + padding.width);
2169 break;
2170 }
2171 }
2172 }
2173
2174 void DrawWidget(const Rect &r, WidgetID widget) const override
2175 {
2176 switch (widget) {
2177 case WID_SP_PROGRESS_BAR: {
2178 /* Draw the % complete with a bar and a text */
2180 Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
2181 uint percent = scanned * 100 / std::max(1U, _settings_client.gui.last_newgrf_count);
2182 DrawFrameRect(ir.WithWidth(ir.Width() * percent / 100, _current_text_dir == TD_RTL), COLOUR_MAUVE, {});
2183 DrawString(ir.left, ir.right, CentreBounds(ir.top, ir.bottom, GetCharacterHeight(FS_NORMAL)), GetString(STR_GENERATION_PROGRESS, percent), TC_FROMSTRING, SA_HOR_CENTER);
2184 break;
2185 }
2186
2188 DrawString(r.left, r.right, r.top, GetString(STR_NEWGRF_SCAN_STATUS, this->scanned, _settings_client.gui.last_newgrf_count), TC_FROMSTRING, SA_HOR_CENTER);
2189
2190 DrawString(r.left, r.right, r.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal, this->last_name, TC_BLACK, SA_HOR_CENTER);
2191 break;
2192 }
2193 }
2194
2200 void UpdateNewGRFScanStatus(uint num, std::string &&name)
2201 {
2202 this->last_name = std::move(name);
2203 this->scanned = num;
2205
2206 this->SetDirty();
2207 }
2208};
2209
2215void UpdateNewGRFScanStatus(uint num, std::string &&name)
2216{
2218 if (w == nullptr) w = new ScanProgressWindow();
2219 w->UpdateNewGRFScanStatus(num, std::move(name));
2220}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
List template of 'things' T to sort in a GUI.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetFiltering(Filtering f)
Import filter conditions.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
void StartAction(GamelogActionType at)
Stores information about new action, but doesn't allocate it Action is allocated only when there is a...
Definition gamelog.cpp:65
void StopAction()
Stops logging of any changes.
Definition gamelog.cpp:74
void GRFUpdate(const GRFConfigList &oldg, const GRFConfigList &newg)
Compares two NewGRF lists and logs any change.
Definition gamelog.cpp:606
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
uint smallest_x
Smallest horizontal size of the widget in a filled window.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
uint smallest_y
Smallest vertical size of the widget in a filled window.
uint fill_y
Vertical fill stepsize (from initial size, 0 means not resizable).
uint resize_y
Vertical resize step (0 means not resizable).
uint current_y
Current vertical size (after resizing).
Base class for a 'real' widget.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1286
Custom nested widget container for the NewGRF gui.
bool editable
Editable status of the parent NewGRF window (if false, drop all widgets that make the window editable...
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
std::unique_ptr< NWidgetBase > acs
Widget with the active grfs list and buttons.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
static const uint MAX_EXTRA_INFO_WIDTH
Maximal additional width given to the panel.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
std::unique_ptr< NWidgetBase > inf
Info panel.
static const uint MIN_EXTRA_FOR_3_COLUMNS
Minimal additional width needed before switching to 3 columns.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
std::unique_ptr< NWidgetBase > avs
Widget with the available grfs list and buttons.
Scrollbar data structure.
bool IsVisible(size_type item) const
Checks whether given current item is visible in the list.
void SetCount(size_t num)
Sets the number of elements in the list.
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
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:2482
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:2556
size_type GetCount() const
Gets the number of elements in the list.
EventState UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
Update the given list position as if it were on this scroll bar when the given keycode was pressed.
Definition widget.cpp:2503
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
size_type GetPosition() const
Gets the position of the first visible element in the list.
A timeout timer will fire once after the interval.
Definition timer.h:116
void Reset()
Reset the timer, so it will fire again after the timeout.
Definition timer.h:140
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:41
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:58
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:60
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:62
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
static const uint NETWORK_MAX_GRF_COUNT
Maximum number of GRFs that can be sent.
Definition config.h:90
int find_index(Container const &container, typename Container::const_reference item)
Helper function to get the index of an item Consider using std::set, std::unordered_set or std::flat_...
auto Slide(TIter first, TIter last, TIter position) -> std::pair< TIter, TIter >
Move elements between first and last to a new position, rotating elements in between as necessary.
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:408
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:390
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
Functions related to errors.
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
@ WL_INFO
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition error.h:24
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition fileio_type.h:96
Declarations for savegames operations.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
Functions to be called to log fundamental changes to the game.
@ GLAT_GRF
GRF changed.
Definition gamelog.h:19
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:958
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:887
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:658
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1024
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion)
Calculate string bounding box for multi-line strings.
Definition gfx.cpp:741
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:775
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:251
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:384
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
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 SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
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 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.
std::unique_ptr< NWidgetBase > MakeNWidgets(std::span< const NWidgetPart > nwid_parts, std::unique_ptr< NWidgetBase > &&container)
Construct a nested widget tree from an array of parts.
Definition widget.cpp:3431
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetArrowWidgetTypeTip(ArrowWidgetValues widget_type, StringID tip={})
Widget part function for setting the arrow widget type and tooltip.
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:955
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
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 uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
void ShowQuery(EncodedString &&caption, EncodedString &&message, Window *parent, QueryCallbackProc *callback, bool focus)
Show a confirmation window with standard 'yes' and 'no' buttons The window is aligned to the centre o...
void ShowQueryString(std::string_view str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
Types related to the misc widgets.
@ WID_TF_CAPTION
The caption of the window.
Definition misc_widget.h:50
bool _network_available
is network mode available?
Definition network.cpp:69
Basic functions/variables used all over the place.
Part of the network protocol handling content distribution.
void ShowNetworkContentListWindow(ContentVector *cv=nullptr, ContentType type1=CONTENT_TYPE_END, ContentType type2=CONTENT_TYPE_END)
Show the content list window with a given set of content.
std::vector< std::unique_ptr< ContentInfo > > ContentVector
Vector with content info.
Base for the NewGRF implementation.
void ReloadNewGRFData()
Reload all NewGRF files during a running game.
GRFConfigList _grfconfig
First item in list of current GRF set up.
void CopyGRFConfigList(GRFConfigList &dst, const GRFConfigList &src, bool init_only)
Copy a GRF Config list.
std::string GRFBuildParamList(const GRFConfig &c)
Build a string containing space separated parameter values, and terminate.
const GRFConfig * FindGRFConfig(uint32_t grfid, FindGRFConfigMode mode, const MD5Hash *md5sum, uint32_t desired_version)
Find a NewGRF in the scanned list.
void ResetGRFConfig(bool defaults)
Reset the current GRF Config to either blank or newgame settings.
GRFConfigList _all_grfs
First item in list of all scanned NewGRFs.
void ClearGRFConfigList(GRFConfigList &config)
Clear a GRF Config list, freeing all nodes.
@ GCS_DISABLED
GRF file is disabled.
@ GCS_NOT_FOUND
GRF file was not found in the local cache.
@ GCS_ACTIVATED
GRF file has been activated.
@ Compatible
GRF file does not exactly match the requested GRF (different MD5SUM), but grfid matches)
@ Static
GRF file is used statically (can be used in any MP game)
@ Invalid
GRF is unusable with this version of OpenTTD.
@ PTYPE_UINT_ENUM
The parameter allows a range of numbers, each of which can have a special name.
@ PTYPE_BOOL
The parameter is either 0 or 1.
@ FGCM_NEWEST
Find newest Grf.
@ FGCM_NEWEST_VALID
Find newest Grf, ignoring Grfs with GRFConfigFlag::Invalid set.
@ FGCM_EXACT
Only find Grfs matching md5sum.
@ GRFP_GRF_UNSET
The NewGRF provided no information.
@ GRFP_USE_WINDOWS
The palette state is set to use the Windows palette.
@ GRFP_BLT_32BPP
The NewGRF prefers a 32 bpp blitter.
@ GRFP_USE_MASK
Bitmask to get only the use palette use states.
@ GRFP_GRF_MASK
Bitmask to get only the NewGRF supplied information.
static void ShowSavePresetWindow(std::string_view initial_text)
Open the window for saving a preset.
std::map< uint32_t, const GRFConfig * > GrfIdMap
Map of grfid to the grf config.
static WindowDesc _save_preset_desc(WDP_CENTER, "save_preset", 140, 110, WC_SAVE_PRESET, WC_GAME_OPTIONS, WindowDefaultFlag::Modal, _nested_save_preset_widgets)
Window description of the preset save window.
static WindowDesc _scan_progress_desc(WDP_CENTER, {}, 0, 0, WC_MODAL_PROGRESS, WC_NONE, {}, _nested_scan_progress_widgets)
Description of the widgets and other settings of the window.
static void NewGRFConfirmationCallback(Window *w, bool confirmed)
Callback function for the newgrf 'apply changes' confirmation window.
static void FillGrfidMap(const GRFConfigList &lst, GrfIdMap &grfid_map)
Add all grf configs from c into the map.
static constexpr NWidgetPart _nested_scan_progress_widgets[]
Widgets for the progress window.
void UpdateNewGRFScanStatus(uint num, std::string &&name)
Update the NewGRF scan status.
void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfigList &config)
Setup the NewGRF gui.
static constexpr NWidgetPart _nested_save_preset_widgets[]
Widget parts of the save preset window.
std::unique_ptr< NWidgetBase > NewGRFDisplay()
Construct nested container widget for managing the lists and the info panel of the NewGRF GUI.
static WindowDesc _newgrf_parameters_desc(WDP_CENTER, "settings_newgrf_config", 500, 208, WC_GRF_PARAMETERS, WC_NONE, {}, _nested_newgrf_parameter_widgets)
Window definition for the change grf parameters window.
void ShowNewGRFError()
Show the first NewGRF error we can find.
void ShowMissingContentWindow(const GRFConfigList &list)
Show the content list window with all missing grfs from the given list.
std::optional< std::string_view > GetGRFStringFromGRFText(const GRFTextList &text_list)
Get a C-string from a GRFText-list.
Header of Action 04 "universal holder" structure and functions.
Types related to the newgrf widgets.
@ WID_NS_VIEW_PARAMETERS
Open Parameters Window for selected NewGRF for viewing parameters.
@ WID_NS_APPLY_CHANGES
Apply changes to NewGRF config.
@ WID_NS_OPEN_URL
Open URL of NewGRF.
@ WID_NS_CONTENT_DOWNLOAD2
Open content download (active NewGRFs).
@ WID_NS_RESCAN_FILES
Rescan files (available NewGRFs).
@ WID_NS_CONTENT_DOWNLOAD
Open content download (available NewGRFs).
@ WID_NS_SHOW_EDIT
Select display of the buttons below the 'details'.
@ WID_NS_MOVE_UP
Move NewGRF up in active list.
@ WID_NS_SHOW_REMOVE
Select active list buttons (0 = normal, 1 = simple layout).
@ WID_NS_NEWGRF_INFO
Panel for Info on selected NewGRF.
@ WID_NS_NEWGRF_TEXTFILE
Open NewGRF readme, changelog (+1) or license (+2).
@ WID_NS_SET_PARAMETERS
Open Parameters Window for selected NewGRF for editing parameters.
@ WID_NS_ADD
Add NewGRF to active list.
@ WID_NS_MOVE_DOWN
Move NewGRF down in active list.
@ WID_NS_SCROLL2BAR
Scrollbar for available NewGRF list.
@ WID_NS_UPGRADE
Upgrade NewGRFs that have a newer version available.
@ WID_NS_PRESET_SAVE
Save list of active NewGRFs as presets.
@ WID_NS_FILTER
Filter list of available NewGRFs.
@ WID_NS_FILE_LIST
List window of active NewGRFs.
@ WID_NS_SHOW_APPLY
Select display of the apply button.
@ WID_NS_SCROLLBAR
Scrollbar for active NewGRF list.
@ WID_NS_PRESET_DELETE
Delete active preset.
@ WID_NS_TOGGLE_PALETTE
Toggle Palette of selected, active NewGRF.
@ WID_NS_RESCAN_FILES2
Rescan files (active NewGRFs).
@ WID_NS_NEWGRF_INFO_TITLE
Title for Info on selected NewGRF.
@ WID_NS_REMOVE
Remove NewGRF from active list.
@ WID_NS_PRESET_LIST
Active NewGRF preset.
@ WID_NS_AVAIL_LIST
List window of available NewGRFs.
@ WID_SP_PROGRESS_BAR
Simple progress bar.
@ WID_SP_PROGRESS_TEXT
Text explaining what is happening.
@ WID_NP_NUMPAR_INC
Button to increase number of parameters.
@ WID_NP_RESET
Reset button.
@ WID_NP_NUMPAR_DEC
Button to decrease number of parameters.
@ WID_NP_SCROLLBAR
Scrollbar to scroll through all settings.
@ WID_NP_CAPTION
Caption of the window.
@ WID_NP_SETTING_DROPDOWN
Dynamically created dropdown for changing setting value.
@ WID_NP_BACKGROUND
Panel to draw the settings on.
@ WID_NP_NUMPAR
Optional number of parameters.
@ WID_NP_SHOW_NUMPAR
NWID_SELECTION to optionally display WID_NP_NUMPAR.
@ WID_NP_SHOW_DESCRIPTION
NWID_SELECTION to optionally display parameter descriptions.
@ WID_NP_DESCRIPTION
Multi-line description of a parameter.
@ WID_SVP_SAVE
Button to save the preset.
@ WID_SVP_PRESET_LIST
List with available preset names.
@ WID_SVP_SCROLLBAR
Scrollbar for the list available preset names.
@ WID_SVP_EDITBOX
Edit box for changing the preset name.
bool RequestNewGRFScan(NewGRFScanCallback *callback)
Request a new NewGRF scan.
Definition openttd.cpp:1328
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
static const uint8_t PC_GREY
Grey palette colour.
static const uint8_t PC_DARK_GREY
Dark grey palette colour.
static const uint8_t PC_DARK_BLUE
Dark blue palette colour.
static const uint8_t PC_BLACK
Black palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
void SaveGRFPresetToConfig(std::string_view config_name, GRFConfigList &config)
Save a NewGRF configuration with a preset name.
StringList GetGRFPresetList()
Get the list of known NewGrf presets.
void DeleteGRFPresetFromConfig(std::string_view config_name)
Delete a NewGRF configuration by preset name.
GRFConfigList LoadGRFPresetFromConfig(std::string_view config_name)
Load a NewGRF configuration by preset-name.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:59
Functions related to setting/changing the settings.
void DrawArrowButtons(int x, int y, Colours button_colour, uint8_t state, bool clickable_left, bool clickable_right)
Draw [<][>] boxes.
void DrawBoolButton(int x, int y, Colours button_colour, Colours background, bool state, bool clickable)
Draw a toggle button.
void DrawDropDownButton(int x, int y, Colours button_colour, bool state, bool clickable)
Draw a dropdown button.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
Types related to global configuration settings.
Base types for having sorted lists in GUIs.
This file contains all sprite-related enums and defines.
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1394
Definition of base types and functions in a cross-platform compatible way.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:75
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition string.cpp:425
static void StrMakeValid(Builder &builder, StringConsumer &consumer, StringValidationSettings settings)
Copies the valid (UTF-8) characters from consumer to the builder.
Definition string.cpp:117
Parse strings.
@ CS_NUMERAL
Only numeric ones.
Definition string_type.h:26
std::vector< std::string > StringList
Type for a list of strings.
Definition string_type.h:60
Searching and filtering using a stringterm.
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:237
EncodedString GetEncodedStringWithArgs(StringID str, std::span< const StringParameter > params)
Encode a string with its parameters into an encoded string.
Definition strings.cpp:103
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition strings.cpp:327
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:91
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:57
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:219
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.
GUISettings gui
settings related to the GUI
@ DoesNotExist
The content does not exist in the content system.
Dimensions (a width and height) of a rectangle in 2D.
Data structure describing what to show in the list (filter criteria).
Information about GRF, used in the game and (part of it) in savegames.
MD5Hash original_md5sum
MD5 checksum of original file if only a 'compatible' file was loaded.
void SetParameterDefaults()
Set the default value for all parameters as specified by action14.
uint8_t palette
GRFPalette, bitset.
std::vector< std::optional< GRFParameterInfo > > param_info
NOSAVE: extra information about the parameters.
uint32_t version
NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown.
std::vector< uint32_t > param
GRF parameters.
std::optional< std::string > GetURL() const
Get the grf url.
GRFStatus status
NOSAVE: GRFStatus, enum.
void SetValue(const GRFParameterInfo &info, uint32_t value)
Set the value of the given user-changeable parameter.
std::optional< GRFError > error
NOSAVE: Error/Warning during GRF loading (Action 0x0B)
std::optional< std::string > GetTextfile(TextfileType type) const
Search a textfile file next to this NewGRF.
GRFConfigFlags flags
NOSAVE: GCF_Flags, bitset.
uint8_t num_valid_params
NOSAVE: Number of valid parameters (action 0x14)
std::string filename
Filename - either with or without full path.
GRFIdentifier ident
grfid and md5sum to uniquely identify newgrfs
uint32_t min_loadable_version
NOSAVE: Minimum compatible version a NewGRF can define.
std::string GetName() const
Get the name of this grf.
std::optional< std::string > GetDescription() const
Get the grf info.
uint32_t GetValue(const GRFParameterInfo &info) const
Get the value of the given user-changeable parameter.
bool HasGrfIdentifier(uint32_t grfid, const MD5Hash *md5sum) const
Does the identification match the provided values?
uint32_t grfid
GRF ID (defined by Action 0x08)
MD5Hash md5sum
MD5 checksum of file to distinguish files with the same GRF ID (eg. newer version of GRF)
Information about one grf parameter.
bool complete_labels
True if all values have a label.
uint8_t param_nr
GRF parameter to store content in.
uint32_t min_value
The minimal value this parameter can have.
uint32_t max_value
The maximal value of this parameter.
GRFParameterType type
The type of this parameter.
std::vector< ValueName > value_names
Names for each value.
GRFTextList name
The name of this parameter.
GRFTextList desc
The description of this parameter.
uint32_t last_newgrf_count
the numbers of NewGRFs we found during the last scan
bool newgrf_show_old_versions
whether to show old versions in the NewGRF list
bool scenario_developer
activate scenario developer: allow modifying NewGRFs in an existing game
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
Data structure describing how to show the list (what sort direction and criteria).
Partial widget specification to allow NWidgets to be written nested.
Window for setting the parameters of a NewGRF.
bool clicked_dropdown
Whether the dropdown is open.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void OnPaint() override
The window must be repainted.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
bool editable
Allow editing parameters.
bool HasParameterInfo(uint nr) const
Test if GRF Parameter Info exists for a given parameter index.
static GRFParameterInfo dummy_parameter_info
Dummy info in case a newgrf didn't provide info about some parameter.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
TimeoutTimer< TimerWindow > unclick_timeout
When reset, unclick the button after a small timeout.
bool action14present
True if action14 information is present.
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.
int line_height
Height of a row in the matrix widget.
bool clicked_increase
True if the increase button was clicked, false for the decrease button.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
int32_t clicked_row
The selected parameter, or INT32_MAX when none is selected.
bool closing_dropdown
True, if the dropdown list is currently closing.
void OnDropdownClose(Point, WidgetID widget, int, bool) override
A dropdown window associated to this window has been closed.
GRFConfig & grf_config
Set the parameters of this GRFConfig.
int32_t clicked_button
The row in which a button was clicked or INT32_MAX when none is selected.
static GRFParameterInfo & GetDummyParameterInfo(uint nr)
Get a dummy parameter-info object with default information.
GRFParameterInfo & GetParameterInfo(uint nr) const
Get GRF Parameter Info exists for a given parameter index.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
Callback for NewGRF scanning.
Window for displaying the textfile of a NewGRF.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
const GRFConfig * grf_config
View the textfile of this GRFConfig.
Window for showing NewGRF files.
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.
bool editable
Is the window editable?
void OnEditboxChanged(WidgetID widget) override
The text in an editbox has been edited.
bool CanUpgradeCurrent()
Test whether the currently active set of NewGRFs can be upgraded with the available NewGRFs.
GRFConfig * active_sel
Selected active grf item.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
bool show_params
Are the grf-parameters shown in the info-panel?
void UpgradeCurrent()
Upgrade the currently active set of NewGRFs.
void OnResize() override
Called after the window got resized.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
bool AddGRFToActive(int ins_pos=-1)
Insert a GRF into the active list.
GRFConfigList actives
Temporary active grf list to which changes are made.
int avail_pos
Index of avail_sel if existing, else -1.
int active_over
Active GRF item over which another one is dragged, -1 if none.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnMouseDrag(Point pt, WidgetID widget) override
An 'object' is being dragged at the provided position, highlight the target if possible.
bool modified
The list of active NewGRFs has been modified since the last time they got saved.
bool execute
On pressing 'apply changes' are grf changes applied immediately, or only list is updated.
GRFConfigList & orig_list
List active grfs in the game. Used as initial value, may be updated by the window.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
EventState OnKeyPress(char32_t key, uint16_t keycode) override
A key has been pressed.
int preset
Selected preset or -1 if none selected.
PaletteID GetPalette(const GRFConfig &c) const
Pick the palette for the sprite of the grf to display.
StringList grf_presets
List of known NewGRF presets.
const GRFConfig * avail_sel
Currently selected available grf. nullptr is none is selected.
void UpdateScrollBars()
Updates the scroll bars for the active and inactive NewGRF lists.
QueryString filter_editbox
Filter editbox;.
static bool TagNameFilter(const GRFConfig *const *a, StringFilter &filter)
Filter grfs by tags/name.
void OnDragDrop(Point pt, WidgetID widget) override
A dragged 'object' has been released.
static Filtering last_filtering
Default filtering of GUIGRFConfigList.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
static const std::initializer_list< GUIGRFConfigList::SortFunction *const > sorter_funcs
Sort functions of the GUIGRFConfigList.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
static const std::initializer_list< GUIGRFConfigList::FilterFunction *const > filter_funcs
Filter functions of the GUIGRFConfigList.
void OnNewGRFsScanned() override
Called whenever the NewGRF scan completed.
GUIGRFConfigList avails
Available (non-active) grfs.
StringFilter string_filter
Filter for available grf.
static Listing last_sorting
Default sorting of GUIGRFConfigList.
static bool NameSorter(const GRFConfig *const &a, const GRFConfig *const &b)
Sort grfs by name.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Coordinates of a point in 2D.
Data stored about a string that can be modified in the GUI.
int ok_button
Widget button of parent window to simulate when pressing OK in OSK.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
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.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Class for the save preset window.
QueryString presetname_editbox
Edit box of the save preset.
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 DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnResize() override
Called after the window got resized.
int selected
Selected entry in the preset list, or -1 if none selected.
Scrollbar * vscroll
Pointer to the scrollbar widget.
StringList presets
Available presets.
SavePresetWindow(std::string_view initial_text)
Constructor of the save preset window.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Window for showing the progress of NewGRF scanning.
std::string last_name
The name of the last 'seen' NewGRF.
int scanned
The number of NewGRFs that we have seen.
void UpdateNewGRFScanStatus(uint num, std::string &&name)
Update the NewGRF scan status.
ScanProgressWindow()
Create the window.
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.
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(std::string_view str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
The data required to format and validate a single parameter of a string.
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
void Assign(std::string_view text)
Copy a string into the textbuffer.
Definition textbuf.cpp:420
Window for displaying a textfile.
TextfileType file_type
Type of textfile to view.
virtual void LoadTextfile(const std::string &textfile, Subdirectory dir)
Loads the textfile text from file and setup lines.
High level window description.
Definition window_gui.h:167
Data structure for an opened window.
Definition window_gui.h:273
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1091
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1778
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:777
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3205
Window * parent
Parent window.
Definition window_gui.h:328
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:555
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:503
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1064
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void DisableWidget(WidgetID widget_index)
Sets a widget to disabled.
Definition window_gui.h:391
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:515
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1768
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1079
virtual void OnQueryTextFinished(std::optional< std::string > str)
The query window opened from this window has closed.
Definition window_gui.h:785
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:484
int top
y position of top edge of the window
Definition window_gui.h:310
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1791
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:312
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
@ CONTENT_TYPE_NEWGRF
The content consists of a NewGRF.
GUI functions related to textfiles.
TextfileType
Additional text files accompanying Tar archives.
@ TFT_LICENSE
Content license.
@ TFT_README
Content readme.
@ TFT_CHANGELOG
Content changelog.
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
@ HT_DRAG
dragging items in the depot windows
Definition of Interval and OneShot timers.
Definition of the Window system.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:298
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:41
@ NWID_CUSTOM
General Custom widget.
Definition widget_type.h:78
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:49
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:71
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:63
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:67
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:51
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:53
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:77
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:69
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:61
@ WWT_FRAME
Frame.
Definition widget_type.h:52
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:38
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:60
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:57
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:62
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:50
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:72
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_VERTICAL
Display plane with zero size horizontally, and filling and resizing vertically.
@ EqualSize
Containers should keep all their (resizing) children equally large.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition()
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:21
@ AWV_INCREASE
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:22
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition widget_type.h:30
Window * FindWindowByClass(WindowClass cls)
Find any window by its class.
Definition window.cpp:1155
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3381
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1194
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1140
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3147
Window functions not directly related to making/drawing windows.
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
@ BorderOnly
Draw border only, no background.
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ WDP_CENTER
Center the window.
Definition window_gui.h:145
int WidgetID
Widget ID.
Definition window_type.h:20
@ WN_GAME_OPTIONS_NEWGRF_STATE
NewGRF settings.
Definition window_type.h:27
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ GOID_NEWGRF_CURRENT_LOADED
The current list of active NewGRF has been loaded.
@ GOID_NEWGRF_RESCANNED
NewGRFs were just rescanned.
@ GOID_NEWGRF_CHANGES_MADE
Changes have been made to a given NewGRF either through the palette or its parameters.
@ GOID_NEWGRF_LIST_EDITED
List of active NewGRFs is being edited.
@ WC_BUILD_OBJECT
Build object; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:47
@ WC_SAVE_PRESET
Save preset; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers:
@ WC_GRF_PARAMETERS
NewGRF parameters; Window numbers:
@ WC_MODAL_PROGRESS
Progress report of landscape generation; Window numbers:
Functions related to zooming.