OpenTTD Source 20260712-master-gcf48f866eb
picker_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "dropdown_func.h"
14#include "gui.h"
15#include "hotkeys.h"
16#include "ini_type.h"
17#include "newgrf_badge.h"
18#include "newgrf_badge_config.h"
19#include "newgrf_badge_gui.h"
20#include "picker_gui.h"
21#include "querystring_gui.h"
22#include "settings_type.h"
23#include "sortlist_type.h"
24#include "sound_func.h"
25#include "sound_type.h"
26#include "string_func.h"
27#include "stringfilter_type.h"
28#include "strings_func.h"
29#include "widget_type.h"
30#include "window_func.h"
31#include "window_gui.h"
32#include "window_type.h"
33#include "zoom_func.h"
34
36
37#include "table/sprites.h"
38#include "table/strings.h"
39
40#include <charconv>
41
42#include "safeguards.h"
43
44static std::vector<PickerCallbacks *> &GetPickerCallbacks()
45{
46 static std::vector<PickerCallbacks *> callbacks;
47 return callbacks;
48}
49
50PickerCallbacks::PickerCallbacks(const std::string &ini_group) : ini_group(ini_group)
51{
52 GetPickerCallbacks().push_back(this);
53}
54
57{
58 auto &callbacks = GetPickerCallbacks();
59 callbacks.erase(std::ranges::find(callbacks, this));
60}
61
67static void PickerLoadConfig(const IniFile &ini, PickerCallbacks &callbacks)
68{
69 callbacks.saved.clear();
70 for (const IniGroup &group : ini.groups) {
71 /* Read the collection name */
72 if (!group.name.starts_with(callbacks.ini_group)) continue;
73 auto pos = group.name.find('-');
74 if (pos == std::string_view::npos && group.name != callbacks.ini_group) continue;
75 std::string collection = (pos == std::string_view::npos) ? "" : group.name.substr(pos + 1);
76
77 if (group.items.empty() && pos != std::string_view::npos) {
78 callbacks.saved[collection];
79 continue;
80 }
81
82 for (const IniItem &item : group.items) {
83 std::array<uint8_t, 4> grfid_buf;
84
85 std::string_view str = item.name;
86
87 /* Try reading "<grfid>|<localid>" */
88 auto grfid_pos = str.find('|');
89 if (grfid_pos == std::string_view::npos) continue;
90
91 std::string_view grfid_str = str.substr(0, grfid_pos);
92 if (!ConvertHexToBytes(grfid_str, grfid_buf)) continue;
93
94 str = str.substr(grfid_pos + 1);
95 GrfID grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
96 uint16_t localid;
97 auto [ptr, err] = std::from_chars(str.data(), str.data() + str.size(), localid);
98
99 if (err == std::errc{} && ptr == str.data() + str.size()) {
100 callbacks.saved[collection].emplace(grfid, localid, 0, 0);
101 }
102 }
103 }
104}
105
111static void PickerSaveConfig(IniFile &ini, const PickerCallbacks &callbacks)
112{
113 /* Clean the ini file of any obsolete collections to prevent them coming back after a restart */
114 for (const std::string &rm_collection : callbacks.rm_collections) {
115 ini.RemoveGroup(callbacks.ini_group + "-" + rm_collection);
116 }
117
118 for (const auto &collection : callbacks.saved) {
119 IniGroup &group = ini.GetOrCreateGroup(collection.first == "" ? callbacks.ini_group : callbacks.ini_group + "-" + collection.first);
120 group.Clear();
121 for (const PickerItem &item : collection.second) {
122 std::string key = fmt::format("{:08X}|{}", std::byteswap(item.grfid), item.local_id);
123 group.CreateItem(key);
124 }
125 }
126}
127
132void PickerLoadConfig(const IniFile &ini)
133{
134 for (auto *cb : GetPickerCallbacks()) PickerLoadConfig(ini, *cb);
135}
136
142{
143 for (const auto *cb : GetPickerCallbacks()) PickerSaveConfig(ini, *cb);
144}
145
147static bool ClassIDSorter(int const &a, int const &b)
148{
149 return a < b;
150}
151
153static bool ClassTagNameFilter(int const *item, PickerFilterData &filter)
154{
155 filter.ResetState();
156 filter.AddLine(GetString(filter.callbacks->GetClassName(*item)));
157 return filter.GetState();
158}
159
161static bool TypeIDSorter(PickerItem const &a, PickerItem const &b)
162{
163 int r = a.class_index - b.class_index;
164 if (r == 0) r = a.index - b.index;
165 return r < 0;
166}
167
169static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter)
170{
171 auto badges = filter.callbacks->GetTypeBadges(item->class_index, item->index);
172 if (filter.bdf.has_value() && !filter.bdf->Filter(badges)) return false;
173 if (filter.btf.has_value() && filter.btf->Filter(badges)) return true;
174
175 filter.ResetState();
176 filter.AddLine(GetString(filter.callbacks->GetTypeName(item->class_index, item->index)));
177 return filter.GetState();
178}
179
182
184static bool CollectionIDSorter(std::string const &a, std::string const &b)
185{
186 if (a == GetString(STR_PICKER_DEFAULT_COLLECTION) || b == GetString(STR_PICKER_DEFAULT_COLLECTION)) return a == GetString(STR_PICKER_DEFAULT_COLLECTION);
187 if (picker_window->inactive.contains(a) == picker_window->inactive.contains(b)) return StrNaturalCompare(a, b) < 0;
188 return picker_window->inactive.contains(a) < picker_window->inactive.contains(b);
189}
190
191static const std::initializer_list<PickerClassList::SortFunction * const> _class_sorter_funcs = { &ClassIDSorter };
192static const std::initializer_list<PickerClassList::FilterFunction * const> _class_filter_funcs = { &ClassTagNameFilter };
193static const std::initializer_list<PickerTypeList::SortFunction * const> _type_sorter_funcs = { TypeIDSorter };
194static const std::initializer_list<PickerTypeList::FilterFunction * const> _type_filter_funcs = { TypeTagNameFilter };
195static const std::initializer_list<PickerCollectionList::SortFunction * const> _collection_sorter_funcs = { &CollectionIDSorter };
196
197
198PickerWindow::PickerWindow(WindowDesc &desc, Window *parent, WindowNumber window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks),
199 class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE),
200 type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
201{
202 this->window_number = window_number;
203
204 /* Init of nested tree is deferred.
205 * PickerWindow::ConstructWindow must be called by the inheriting window. */
206}
207
208void PickerWindow::ConstructWindow()
209{
210 this->CreateNestedTree();
211
212 /* Test if pickers should be active.*/
213 bool is_active = this->callbacks.IsActive();
214
215 this->preview_height = std::max(this->callbacks.preview_height, PREVIEW_HEIGHT);
216 picker_window = this;
217
218 /* Functionality depends on widgets being present, not window class. */
219 this->has_class_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice();
220 this->has_type_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_TYPE_MATRIX) != nullptr;
221 this->has_collection_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_COLEC_LIST) != nullptr;
222
223 if (this->has_class_picker) {
224 this->GetWidget<NWidgetCore>(WID_PW_CLASS_LIST)->SetToolTip(this->callbacks.GetClassTooltip());
225
227 } else {
228 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_CLASS_SEL); nwid != nullptr) {
229 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
230 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
231 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
232 }
233 }
234
235 this->class_editbox.cancel_button = QueryString::ACTION_CLEAR;
236 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
237 this->class_string_filter.callbacks = &this->callbacks;
238
239 this->classes.SetListing(this->callbacks.class_last_sorting);
240 this->classes.SetFiltering(this->callbacks.class_last_filtering);
241 this->classes.SetSortFuncs(_class_sorter_funcs);
242 this->classes.SetFilterFuncs(_class_filter_funcs);
243
244 /* Update saved type information. */
245 if (this->callbacks.sel_collection == "") SetWidgetsDisabledState(true, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
246 this->callbacks.saved = this->callbacks.UpdateSavedItems(this->callbacks.saved);
247 this->inactive = this->callbacks.InitializeInactiveCollections(this->callbacks.saved);
248 this->collections.ForceRebuild();
249
250 /* Clear used type information. */
251 this->callbacks.used.clear();
252
253 if (this->has_type_picker) {
254 /* Populate used type information. */
255 this->callbacks.FillUsedItems(this->callbacks.used);
256
257 SetWidgetDisabledState(WID_PW_MODE_ALL, !this->callbacks.HasClassChoice());
258
259 this->GetWidget<NWidgetCore>(WID_PW_TYPE_ITEM)->SetToolTip(this->callbacks.GetTypeTooltip());
260
261 auto *matrix = this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX);
262 matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL));
263
265 } else {
266 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_TYPE_SEL); nwid != nullptr) {
267 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
268 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
269 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
270 }
271 }
272
273 this->type_editbox.cancel_button = QueryString::ACTION_CLEAR;
274 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
275 this->type_string_filter.callbacks = &this->callbacks;
276
277 this->types.SetListing(this->callbacks.type_last_sorting);
278 this->types.SetFiltering(this->callbacks.type_last_filtering);
279 this->types.SetSortFuncs(_type_sorter_funcs);
280 this->types.SetFilterFuncs(_type_filter_funcs);
281
282 if (this->has_collection_picker) {
283 this->GetWidget<NWidgetCore>(WID_PW_COLEC_LIST)->SetToolTip(this->callbacks.GetCollectionTooltip());
284 }
285
286 this->collections.SetListing(this->callbacks.collection_last_sorting);
287 this->collections.SetSortFuncs(_collection_sorter_funcs);
288
289 this->FinishInitNested(this->window_number);
290
291 this->InvalidateData(PICKER_INVALIDATION_ALL);
292}
293
295{
296 this->badge_classes = GUIBadgeClasses(this->callbacks.GetFeature());
297 this->badge_filters = AddBadgeDropdownFilters(this, WID_PW_BADGE_FILTER, WID_PW_BADGE_FILTER, Colours::DarkGreen, this->callbacks.GetFeature());
298
299 this->widget_lookup.clear();
300 this->nested_root->FillWidgetLookup(this->widget_lookup);
301}
302
304{
305 this->callbacks.Close(data);
306 this->PickerWindowBase::Close(data);
307}
308
310{
311 switch (widget) {
312 /* Class picker */
314 fill.height = resize.height = GetCharacterHeight(FontSize::Normal) + padding.height;
315 size.height = 5 * resize.height;
316 break;
317
318 /* Type picker */
320 /* At least two items wide. */
321 size.width += resize.width;
322 fill.width = resize.width;
323 fill.height = 1;
324
325 /* Resizing in X direction only at blob size, but at pixel level in Y. */
326 resize.height = 1;
327 break;
328
329 /* Type picker */
330 case WID_PW_TYPE_ITEM:
331 size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
332 size.height = ScaleGUITrad(this->preview_height) + WidgetDimensions::scaled.fullbevel.Vertical();
333 break;
334
336 /* Hide the configuration button if no configurable badges are present. */
337 if (this->badge_classes.GetClasses().empty()) size = {0, 0};
338 break;
339 }
340}
341
342std::string PickerWindow::GetWidgetString(WidgetID widget, StringID stringid) const
343{
344 switch (widget) {
346 return this->callbacks.sel_collection == "" ? GetString(STR_PICKER_DEFAULT_COLLECTION) : this->callbacks.sel_collection;
347
348 default:
349 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
350 return this->GetWidget<NWidgetBadgeFilter>(widget)->GetStringParameter(this->badge_filter_choices);
351 }
352 break;
353 }
354 return this->Window::GetWidgetString(widget, stringid);
355}
356
357DropDownList PickerWindow::BuildCollectionDropDownList()
358{
359 DropDownList list;
360 int i = 0;
361 for (const auto &collection : collections) {
362 list.push_back(MakeDropDownListStringItem(GetString(collection == "" ? STR_PICKER_DEFAULT_COLLECTION : STR_JUST_RAW_STRING, collection), i, false, this->inactive.contains(collection)));
363 i++;
364 }
365 return list;
366}
367
368void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
369{
370 switch (widget) {
371 /* Class picker */
372 case WID_PW_CLASS_LIST: {
373 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
374 const int selected = this->callbacks.GetSelectedClass();
375 const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL);
376 const int y_step = this->GetWidget<NWidgetResizeBase>(widget)->resize_y;
377 auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes);
378 for (auto it = first; it != last; ++it) {
379 DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TextColour::White : TextColour::Black);
380 ir.top += y_step;
381 }
382 break;
383 }
384
385 /* Type picker */
386 case WID_PW_TYPE_ITEM: {
387 assert(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement() < static_cast<int>(this->types.size()));
388 const auto &item = this->types[this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement()];
389
390 DrawPixelInfo tmp_dpi;
392 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
393 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
396
397 this->callbacks.DrawType(x, y, item.class_index, item.index);
398
399 int by = ir.Height() - ScaleGUITrad(12);
400
401 GrfSpecFeature feature = this->callbacks.GetFeature();
402 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
403 PaletteID palette = _game_mode != GameMode::Normal || feature == GrfSpecFeature::Houses ? PAL_NONE : GetCompanyPalette(_local_company);
404 DrawBadgeColumn({0, by, ir.Width() - 1, ir.Height() - 1}, 0, this->badge_classes, this->callbacks.GetTypeBadges(item.class_index, item.index), feature, std::nullopt, palette);
405
406 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
407 if (this->callbacks.saved.at(this->callbacks.sel_collection).contains(item)) {
409 }
410 }
411 if (this->callbacks.used.contains(item)) {
413 }
414 }
415
416 if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
418 }
419 break;
420 }
421
422 case WID_PW_TYPE_NAME: {
423 StringID str = this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType());
425 break;
426 }
427 }
428}
429
436
437void PickerWindow::DeletePickerCollectionCallback(Window *win, bool confirmed)
438{
439 if (confirmed) {
440 PickerWindow *w = (PickerWindow*)win;
441 w->callbacks.saved.erase(w->callbacks.saved.find(w->callbacks.edit_collection));
442 w->inactive.erase(w->callbacks.edit_collection);
443 w->callbacks.rm_collections.emplace(w->callbacks.edit_collection);
444 w->callbacks.sel_collection = "";
445 w->callbacks.edit_collection.clear();
446 picker_window = w;
449 }
450}
451
453{
454 switch (widget) {
455 /* Class Picker */
456 case WID_PW_CLASS_LIST: {
457 const auto vscroll = this->GetWidget<NWidgetScrollbar>(WID_PW_CLASS_SCROLL);
458 auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST);
459 if (it == this->classes.end()) return;
460
461 if (_ctrl_pressed) {
462 /* If no collections yet exist, create the default collection. */
463 if (this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) {
464 for (int i = 0; i < this->callbacks.GetTypeCount(*it); i++) {
465 this->callbacks.saved[""].emplace(this->callbacks.GetPickerItem(*it, i));
466 }
468 this->SetDirty();
469 break;
470 }
471
472 /* If the first item is not already saved to the selected collection, add the whole class to the collection. Otherwise remove the class. */
473 auto &collection = this->callbacks.saved.at(this->callbacks.sel_collection);
474 auto first = collection.find(this->callbacks.GetPickerItem(*it, 0));
475 if (first == std::end(collection)) {
476 for (int i = 0; i < this->callbacks.GetTypeCount(*it); i++) {
477 collection.emplace(this->callbacks.GetPickerItem(*it, i));
478 }
479 } else {
480 for (int i = 0; i < this->callbacks.GetTypeCount(*it); i++) {
481 collection.erase(this->callbacks.GetPickerItem(*it, i));
482 }
483 }
485 break;
486 }
487
488 if (this->callbacks.GetSelectedClass() != *it || this->callbacks.mode.Test(PickerFilterMode::All)) {
489 this->callbacks.mode.Reset(PickerFilterMode::All); // Disable showing all.
490 this->callbacks.SetSelectedClass(*it);
492 }
493 SndClickBeep();
494 CloseWindowById(WindowClass::JoinStation, 0);
495 break;
496 }
497
498 case WID_PW_MODE_ALL:
499 case WID_PW_MODE_USED:
501 this->callbacks.mode.Flip(static_cast<PickerFilterMode>(widget - WID_PW_MODE_ALL));
502 if (!this->IsWidgetDisabled(WID_PW_MODE_ALL) && this->callbacks.mode.Test(static_cast<PickerFilterMode>(widget - WID_PW_MODE_ALL))) {
503 /* Enabling used or saved filters automatically enables all. */
504 this->callbacks.mode.Set(PickerFilterMode::All);
505 }
507 SndClickBeep();
508 break;
509
510 case WID_PW_SHRINK:
511 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? PREVIEW_HEIGHT : std::max(PREVIEW_HEIGHT, this->preview_height - STEP_PREVIEW_HEIGHT);
512 this->InvalidateData({});
513 this->ReInit();
514 break;
515
516 case WID_PW_EXPAND:
517 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? MAX_PREVIEW_HEIGHT : std::min(MAX_PREVIEW_HEIGHT, this->preview_height + STEP_PREVIEW_HEIGHT);
518 this->InvalidateData({});
519 this->ReInit();
520 break;
521
522 /* Type Picker */
523 case WID_PW_TYPE_ITEM: {
524 int sel = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
525 assert(sel < (int)this->types.size());
526 const auto &item = this->types[sel];
527
528 if (_ctrl_pressed) {
529 if (this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) {
530 this->callbacks.saved[""].emplace(item);
532 this->SetDirty();
533 break;
534 }
535
536 auto it = this->callbacks.saved.at(this->callbacks.sel_collection).find(item);
537 if (it == std::end(this->callbacks.saved.at(this->callbacks.sel_collection))) {
538 this->callbacks.saved.at(this->callbacks.sel_collection).emplace(item);
539 } else {
540 this->callbacks.saved.at(this->callbacks.sel_collection).erase(it);
541 }
543 break;
544 }
545
546 if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
547 this->callbacks.SetSelectedClass(item.class_index);
548 this->callbacks.SetSelectedType(item.index);
549 this->InvalidateData(PickerInvalidation::Position);
550 }
551 SndClickBeep();
552 CloseWindowById(WindowClass::JoinStation, 0);
553 break;
554 }
555
556 case WID_PW_COLEC_LIST: {
557 ShowDropDownList(this, this->BuildCollectionDropDownList(), -1, widget, 0, DropDownOption::Filterable);
558 CloseWindowById(WindowClass::JoinStation, 0);
559 break;
560 }
561
562 case WID_PW_COLEC_ADD:
563 this->callbacks.rename_collection = false;
565 break;
566
568 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
569 CloseChildWindows(WindowClass::ConfirmPopupQuery);
570 this->callbacks.edit_collection = this->callbacks.sel_collection;
571 this->callbacks.rename_collection = true;
572 ShowQueryString(this->callbacks.sel_collection, STR_PICKER_COLLECTION_RENAME_QUERY, MAX_LENGTH_GROUP_NAME_CHARS, this, CS_ALPHANUMERAL, QueryStringFlag::LengthIsInChars);
573 }
574 break;
575
577 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
578 CloseChildWindows(WindowClass::QueryString);
579 this->callbacks.edit_collection = this->callbacks.sel_collection;
580
581 this->inactive.contains(this->callbacks.sel_collection) ?
582 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_DISABLED_TEXT), this, DeletePickerCollectionCallback) :
583 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_TEXT), this, DeletePickerCollectionCallback);
584 }
585 break;
586
588 if (this->badge_classes.GetClasses().empty()) break;
589 ShowDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, Colours::DarkGreen), -1, widget, 0, DropDownOption::Persist);
590 break;
591
592 default:
593 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
594 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
595 PaletteID palette = _game_mode != GameMode::Normal || this->callbacks.GetFeature() == GrfSpecFeature::Houses ? PAL_NONE : GetCompanyPalette(_local_company);
596 ShowDropDownList(this, this->GetWidget<NWidgetBadgeFilter>(widget)->GetDropDownList(palette), -1, widget, 0, DropDownOption::Filterable);
597 }
598 break;
599 }
600}
601
602void PickerWindow::OnQueryTextFinished(std::optional<std::string> str)
603{
604 if (!str.has_value()) return;
605
606 if (!this->callbacks.saved.contains(*str)) {
607 if (this->callbacks.saved.contains(this->callbacks.edit_collection) && this->callbacks.rename_collection) {
608 auto rename_collection = this->callbacks.saved.extract(this->callbacks.edit_collection);
609 rename_collection.key() = *str;
610 this->callbacks.saved.insert(std::move(rename_collection));
611
612 if (this->inactive.contains(this->callbacks.edit_collection)) {
613 this->inactive.erase(this->callbacks.edit_collection);
614 this->inactive.emplace(*str);
615 }
616
617 this->callbacks.rm_collections.emplace(this->callbacks.edit_collection);
618 this->callbacks.edit_collection.clear();
619
620 } else {
621 this->callbacks.saved.insert({*str, {}});
622 }
623 }
624
625 this->callbacks.sel_collection = *str;
626 picker_window = this;
627 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
630 }
632}
633
634void PickerWindow::OnDropdownSelect(WidgetID widget, int index, int click_result)
635{
636 switch (widget) {
637 case WID_PW_COLEC_LIST: {
638 auto it = this->collections.begin() + index;
639 if (this->callbacks.sel_collection != *it) {
640 this->callbacks.sel_collection = *it;
642 this->InvalidateData(PickerInvalidation::Position);
643 }
644 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
645
646 SndClickBeep();
647 break;
648 }
649
651 bool reopen = HandleBadgeConfigurationDropDownClick(this->callbacks.GetFeature(), 1, index, click_result, this->badge_filter_choices);
652
653 this->ReInit();
654
655 if (reopen) {
656 ReplaceDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, Colours::DarkGreen), -1);
657 } else {
658 this->CloseChildWindows(WindowClass::DropdownMenu);
659 }
660
661 /* We need to refresh if a filter is removed. */
663 break;
664 }
665
666 default:
667 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
668 if (index < 0) {
669 ResetBadgeFilter(this->badge_filter_choices, this->GetWidget<NWidgetBadgeFilter>(widget)->GetBadgeClassID());
670 } else {
671 SetBadgeFilter(this->badge_filter_choices, BadgeID(index));
672 }
674 }
675 break;
676 }
677}
678
679void PickerWindow::OnInvalidateData(int data, bool gui_scope)
680{
681 if (!gui_scope) return;
682
683 PickerInvalidations pi(data);
684
686 if (this->badge_filter_choices.empty()) {
687 this->type_string_filter.bdf.reset();
688 } else {
689 this->type_string_filter.bdf.emplace(this->badge_filter_choices);
690 }
691 this->types.SetFilterState(!type_string_filter.IsEmpty() || type_string_filter.bdf.has_value());
692 }
693
694 if (pi.Test(PickerInvalidation::Class)) this->classes.ForceRebuild();
695 if (pi.Test(PickerInvalidation::Type)) this->types.ForceRebuild();
696 if (pi.Test(PickerInvalidation::Collection)) this->collections.ForceRebuild();
697
698 this->BuildPickerClassList();
699 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedClassIsValid();
700 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedClassIsVisible();
701
702 this->BuildPickerTypeList();
703 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedTypeIsValid();
704 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedTypeIsVisible();
705
707
708 if (this->has_type_picker) {
712 }
713
716}
717
719{
720 switch (hotkey) {
722 /* Cycle between the two edit boxes. */
723 if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_TYPE_FILTER)) {
725 } else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_CLASS_FILTER)) {
727 }
728 SetFocusedWindow(this);
729 return EventState::Handled;
730
731 default:
733 }
734}
735
737{
738 switch (wid) {
740 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
741 this->classes.SetFilterState(!class_string_filter.IsEmpty());
742 this->InvalidateData(PickerInvalidation::Class);
743 break;
744
746 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
747 if (!type_string_filter.IsEmpty()) {
748 this->type_string_filter.btf.emplace(this->type_string_filter, this->callbacks.GetFeature());
749 } else {
750 this->type_string_filter.btf.reset();
751 }
753 break;
754
755 default:
756 break;
757 }
758}
759
762{
763 if (!this->classes.NeedRebuild()) return;
764
765 int count = this->callbacks.GetClassCount();
766
767 this->classes.clear();
768 this->classes.reserve(count);
769
770 bool filter_used = this->callbacks.mode.Test(PickerFilterMode::Used);
771 bool filter_saved = this->callbacks.mode.Test(PickerFilterMode::Saved);
772 for (int i = 0; i < count; i++) {
773 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
774 if (filter_used && std::none_of(std::begin(this->callbacks.used), std::end(this->callbacks.used), [i](const PickerItem &item) { return item.class_index == i; })) continue;
775 if (filter_saved && this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) continue;
776 if (filter_saved && std::none_of(std::begin(this->callbacks.saved.at(this->callbacks.sel_collection)), std::end(this->callbacks.saved.at(this->callbacks.sel_collection)), [i](const PickerItem &item) { return item.class_index == i; })) continue;
777 this->classes.emplace_back(i);
778 }
779
780 this->classes.Filter(this->class_string_filter);
781 this->classes.RebuildDone();
782 this->classes.Sort();
783
784 if (!this->has_class_picker) return;
785 this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size());
786}
787
788void PickerWindow::EnsureSelectedClassIsValid()
789{
790 int class_index = this->callbacks.GetSelectedClass();
791 if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return;
792
793 if (!this->classes.empty()) {
794 class_index = this->classes.front();
795 } else {
796 /* Classes can be empty if filters are enabled, find the first usable class. */
797 int count = this->callbacks.GetClassCount();
798 for (int i = 0; i < count; i++) {
799 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
800 class_index = i;
801 break;
802 }
803 }
804
805 this->callbacks.SetSelectedClass(class_index);
806 this->types.ForceRebuild();
807}
808
809void PickerWindow::EnsureSelectedClassIsVisible()
810{
811 if (!this->has_class_picker) return;
812 if (this->classes.empty()) return;
813
814 auto it = std::ranges::find(this->classes, this->callbacks.GetSelectedClass());
815 if (it == std::end(this->classes)) return;
816
817 int pos = static_cast<int>(std::distance(std::begin(this->classes), it));
819}
820
821void PickerWindow::RefreshUsedTypeList()
822{
823 if (!this->has_type_picker) return;
824
825 this->callbacks.used.clear();
826 this->callbacks.FillUsedItems(this->callbacks.used);
827 this->InvalidateData(PickerInvalidation::Type);
828}
829
832{
833 if (!this->types.NeedRebuild()) return;
834
835 this->types.clear();
836
837 bool show_all = this->callbacks.mode.Test(PickerFilterMode::All);
838 bool filter_used = this->callbacks.mode.Test(PickerFilterMode::Used);
839 bool filter_saved = this->callbacks.mode.Test(PickerFilterMode::Saved);
840 int cls_id = this->callbacks.GetSelectedClass();
841
842 if (filter_used) {
843 /* Showing used items. May also be filtered by saved items. */
844 this->types.reserve(this->callbacks.used.size());
845 for (const PickerItem &item : this->callbacks.used) {
846 if (!show_all && item.class_index != cls_id) continue;
847 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
848 this->types.emplace_back(item);
849 }
850 } else if (filter_saved && this->callbacks.saved.contains(this->callbacks.sel_collection)) {
851 /* Showing only saved items. */
852 this->types.reserve(std::size(this->callbacks.saved.at(this->callbacks.sel_collection)));
853 for (const PickerItem &item : this->callbacks.saved.at(this->callbacks.sel_collection)) {
854 /* The used list may contain items that aren't currently loaded, skip these. */
855 if (item.class_index == -1) continue;
856 if (!show_all && item.class_index != cls_id) continue;
857 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
858 this->types.emplace_back(item);
859 }
860 } else if (show_all) {
861 /* Reserve enough space for everything. */
862 int total = 0;
863 for (int class_index : this->classes) total += this->callbacks.GetTypeCount(class_index);
864 this->types.reserve(total);
865 /* Add types in all classes. */
866 for (int class_index : this->classes) {
867 int count = this->callbacks.GetTypeCount(class_index);
868 for (int i = 0; i < count; i++) {
869 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
870 this->types.emplace_back(this->callbacks.GetPickerItem(class_index, i));
871 }
872 }
873 } else {
874 /* Add types in only the selected class. */
875 if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) {
876 int count = this->callbacks.GetTypeCount(cls_id);
877 this->types.reserve(count);
878 for (int i = 0; i < count; i++) {
879 if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue;
880 this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i));
881 }
882 }
883 }
884
885 this->types.Filter(this->type_string_filter);
886 this->types.RebuildDone();
887 this->types.Sort();
888
889 if (!this->has_type_picker) return;
890 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetCount(static_cast<int>(this->types.size()));
891}
892
893void PickerWindow::EnsureSelectedTypeIsValid()
894{
895 int class_index = this->callbacks.GetSelectedClass();
896 int index = this->callbacks.GetSelectedType();
897 if (std::any_of(std::begin(this->types), std::end(this->types), [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; })) return;
898
899 if (!this->types.empty()) {
900 class_index = this->types.front().class_index;
901 index = this->types.front().index;
902 } else {
903 /* Types can be empty if filters are enabled, find the first usable type. */
904 int count = this->callbacks.GetTypeCount(class_index);
905 for (int i = 0; i < count; i++) {
906 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
907 index = i;
908 break;
909 }
910 }
911 this->callbacks.SetSelectedClass(class_index);
912 this->callbacks.SetSelectedType(index);
913}
914
915void PickerWindow::EnsureSelectedTypeIsVisible()
916{
917 if (!this->has_type_picker) return;
918 if (this->types.empty()) {
919 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(-1);
920 return;
921 }
922
923 int class_index = this->callbacks.GetSelectedClass();
924 int index = this->callbacks.GetSelectedType();
925
926 auto it = std::ranges::find_if(this->types, [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; });
927 int pos = -1;
928 if (it != std::end(this->types)) {
929 pos = static_cast<int>(std::distance(std::begin(this->types), it));
930 }
931
932 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(pos);
933}
934
937{
938 if (!this->collections.NeedRebuild()) return;
939
940 int count = std::max(static_cast<int>(this->callbacks.saved.size()), 1);
941
942 this->collections.clear();
943 this->collections.reserve(count);
944
945 if (this->callbacks.saved.find("") == this->callbacks.saved.end()) {
946 this->collections.emplace_back("");
947 }
948
949 for (auto it = this->callbacks.saved.begin(); it != this->callbacks.saved.end(); it++) {
950 this->collections.emplace_back(it->first);
951 }
952
953 this->collections.RebuildDone();
954 this->collections.Sort();
955
956 if (!this->has_class_picker) return;
957}
958
960std::unique_ptr<NWidgetBase> MakePickerClassWidgets()
961{
962 static constexpr std::initializer_list<NWidgetPart> picker_class_widgets = {
966 NWidget(WWT_EDITBOX, Colours::DarkGreen, WID_PW_CLASS_FILTER), SetMinimalSize(144, 0), SetPadding(2), SetFill(1, 0), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
967 EndContainer(),
968 /* Collection view */
971 NWidget(WWT_PUSHTXTBTN, Colours::DarkGreen, WID_PW_COLEC_ADD), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_COLLECTION_ADD, STR_PICKER_COLLECTION_ADD_TOOLTIP),
972 NWidget(WWT_PUSHTXTBTN, Colours::DarkGreen, WID_PW_COLEC_RENAME), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_COLLECTION_RENAME, STR_PICKER_COLLECTION_RENAME_TOOLTIP),
973 NWidget(WWT_PUSHTXTBTN, Colours::DarkGreen, WID_PW_COLEC_DELETE), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_COLLECTION_DELETE, STR_PICKER_COLLECTION_DELETE_TOOLTIP),
974 EndContainer(),
975 NWidget(WWT_DROPDOWN, Colours::DarkGreen, WID_PW_COLEC_LIST), SetMinimalSize(144, 12), SetFill(0, 1), SetResize(1, 0), SetToolTip(STR_PICKER_SELECT_COLLECTION_TOOLTIP),
976 EndContainer(),
977 /* Class view */
983 EndContainer(),
985 EndContainer(),
986 EndContainer(),
987 EndContainer(),
988 EndContainer(),
989 };
990
991 return MakeNWidgets(picker_class_widgets, nullptr);
992}
993
995std::unique_ptr<NWidgetBase> MakePickerTypeWidgets()
996{
997 static constexpr std::initializer_list<NWidgetPart> picker_type_widgets = {
1002 NWidget(WWT_EDITBOX, Colours::DarkGreen, WID_PW_TYPE_FILTER), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1003 EndContainer(),
1004 NWidget(WWT_IMGBTN, Colours::DarkGreen, WID_PW_CONFIGURE_BADGES), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetResize(0, 0), SetFill(0, 1), SetSpriteTip(SPR_EXTRA_MENU, STR_BADGE_CONFIG_MENU_TOOLTIP),
1005 EndContainer(),
1007 EndContainer(),
1009 NWidget(WWT_TEXTBTN, Colours::DarkGreen, WID_PW_MODE_ALL), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_MODE_ALL, STR_PICKER_MODE_ALL_TOOLTIP),
1010 NWidget(WWT_TEXTBTN, Colours::DarkGreen, WID_PW_MODE_USED), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_MODE_USED, STR_PICKER_MODE_USED_TOOLTIP),
1011 NWidget(WWT_TEXTBTN, Colours::DarkGreen, WID_PW_MODE_SAVED), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_MODE_SAVED, STR_PICKER_MODE_SAVED_TOOLTIP),
1012 NWidget(WWT_PUSHTXTBTN, Colours::DarkGreen, WID_PW_SHRINK), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetStringTip(STR_PICKER_PREVIEW_SHRINK, STR_PICKER_PREVIEW_SHRINK_TOOLTIP),
1013 NWidget(WWT_PUSHTXTBTN, Colours::DarkGreen, WID_PW_EXPAND), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetStringTip(STR_PICKER_PREVIEW_EXPAND, STR_PICKER_PREVIEW_EXPAND_TOOLTIP),
1014 EndContainer(),
1019 EndContainer(),
1020 EndContainer(),
1021 EndContainer(),
1023 EndContainer(),
1027 EndContainer(),
1029 EndContainer(),
1030 EndContainer(),
1031 EndContainer(),
1032 };
1033
1034 return MakeNWidgets(picker_type_widgets, nullptr);
1035}
1036
1037void InvalidateAllPickerWindows()
1038{
1039 InvalidateWindowClassesData(WindowClass::BuildBusStation, PickerWindow::PICKER_INVALIDATION_ALL);
1040 InvalidateWindowClassesData(WindowClass::BuildTruckStation, PickerWindow::PICKER_INVALIDATION_ALL);
1041 InvalidateWindowClassesData(WindowClass::JoinStation, PickerWindow::PICKER_INVALIDATION_ALL);
1042 InvalidateWindowClassesData(WindowClass::BuildWaypoint, PickerWindow::PICKER_INVALIDATION_ALL);
1043 InvalidateWindowClassesData(WindowClass::BuildObject, PickerWindow::PICKER_INVALIDATION_ALL);
1044 InvalidateWindowClassesData(WindowClass::BuildHouse, PickerWindow::PICKER_INVALIDATION_ALL);
1045}
Class for backupping variables and making sure they are restored later.
constexpr enable_if_t< is_integral_v< T >, T > byteswap(T x) noexcept
Custom implementation of std::byteswap; remove once we build with C++23.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Matrix container with implicitly equal sized (virtual) sub-widgets.
Class for PickerClassWindow to collect information and retain state.
Definition picker_gui.h:49
virtual ~PickerCallbacks()
Ensure the destructor of the sub classes are called as well.
virtual int GetSelectedClass() const =0
Get the index of the selected class.
std::string edit_collection
Collection to rename or delete.
Definition picker_gui.h:218
const std::string ini_group
Ini Group for saving favourites.
Definition picker_gui.h:214
std::string sel_collection
Currently selected collection of saved items.
Definition picker_gui.h:217
virtual StringID GetTypeName(int cls_id, int id) const =0
Get the item name of a type.
virtual StringID GetClassName(int id) const =0
Get the name of a class.
virtual std::span< const BadgeID > GetTypeBadges(int cls_id, int id) const =0
Get the item's badges of a type.
std::set< std::string > rm_collections
Set of removed or renamed collections for updating ini file.
Definition picker_gui.h:219
std::map< std::string, std::set< PickerItem > > saved
Set of saved collections of items.
Definition picker_gui.h:224
virtual int GetSelectedType() const =0
Get the selected type.
Base class for windows opened from a toolbar.
Definition window_gui.h:998
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3622
static constexpr int MAX_PREVIEW_HEIGHT
Maximum height of each preview button.
Definition picker_gui.h:345
static constexpr int PREVIEW_WIDTH
Width of each preview button.
Definition picker_gui.h:339
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
int preview_height
Height of preview images.
Definition picker_gui.h:352
bool has_class_picker
Set if this window has a class picker 'component'.
Definition picker_gui.h:349
static constexpr int PREVIEW_HEIGHT
Height of each preview button.
Definition picker_gui.h:340
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
@ Position
Update scroll positions.
Definition picker_gui.h:329
@ Class
Refresh the class list.
Definition picker_gui.h:326
@ Type
Refresh the type list.
Definition picker_gui.h:327
@ Validate
Validate selected item.
Definition picker_gui.h:330
@ Collection
Refresh the collection list.
Definition picker_gui.h:328
@ Filter
Update filter state.
Definition picker_gui.h:331
bool has_collection_picker
Set if this window has a collection picker 'component'.
Definition picker_gui.h:351
static constexpr int PREVIEW_LEFT
Offset from left edge to draw preview.
Definition picker_gui.h:341
PickerTypeList types
List of types.
Definition picker_gui.h:392
std::set< std::string > inactive
Set of collections with inactive items.
Definition picker_gui.h:353
QueryString class_editbox
Filter editbox.
Definition picker_gui.h:386
bool has_type_picker
Set if this window has a type picker 'component'.
Definition picker_gui.h:350
void BuildPickerClassList()
Builds the filter list of classes.
void BuildPickerCollectionList()
Builds the filter list of collections.
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void BuildPickerTypeList()
Builds the filter list of types.
void OnDropdownSelect(WidgetID widget, int index, int click_result) override
A dropdown option associated to this window has been selected.
void OnResize() override
Called after the window got resized.
static constexpr int PREVIEW_BOTTOM
Offset from bottom edge to draw preview.
Definition picker_gui.h:342
@ PCWHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
Definition picker_gui.h:373
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
PickerCollectionList collections
List of collections.
Definition picker_gui.h:401
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
QueryString type_editbox
Filter editbox.
Definition picker_gui.h:394
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
EnumBitSet< PickerInvalidation, uint8_t > PickerInvalidations
Bitset of Pickerinvalidation elements.
Definition picker_gui.h:335
void OnInit() override
Notification that the nested widget tree gets initialized.
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.
PickerClassList classes
List of classes.
Definition picker_gui.h:384
static constexpr int STEP_PREVIEW_HEIGHT
Step for decreasing or increase preview button height.
Definition picker_gui.h:344
void SetCount(size_t num)
Sets the number of elements in the list.
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:2536
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:95
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Functions related to companies.
std::unique_ptr< DropDownListItem > MakeDropDownListStringItem(StringID str, int value, bool masked, bool shaded)
Creates new DropDownListStringItem.
Definition dropdown.cpp:49
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options, std::string *const persistent_filter_text)
Show a drop down list.
Definition dropdown.cpp:587
Functions 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.
@ Persist
Set if this dropdown should stay open after an option is selected.
@ Filterable
Set if the dropdown is filterable.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
@ Centre
Align to the centre.
@ Middle
Align to the middle.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:971
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
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:1037
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, Alignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition gfx.cpp:1572
@ Normal
Index of the normal font in the font tables.
Definition gfx_type.h:249
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ Invalid
Invalid marker.
Definition gfx_type.h:302
@ Grey
Grey.
Definition gfx_type.h:299
@ DarkGreen
Dark green.
Definition gfx_type.h:292
@ White
White colour.
Definition gfx_type.h:330
@ Gold
Gold colour.
Definition gfx_type.h:320
@ Black
Black colour.
Definition gfx_type.h:334
@ Checker
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:393
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 SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetSpriteTip(SpriteID sprite, StringID tip={})
Widget part function for setting the sprite and tooltip.
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 SetAspect(float ratio, AspectFlags flags=AspectFlag::ResizeX)
Widget part function for setting the aspect ratio.
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FontSize::Normal)
Widget part function for setting the minimal text lines.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
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:3419
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 NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
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:972
static const uint MAX_LENGTH_GROUP_NAME_CHARS
The maximum length of a group name in characters including '\0'.
Definition group_type.h:20
GUI functions that shouldn't be here.
Hotkey related functions.
Types related to reading/writing '*.ini' files.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
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.
GrfSpecFeature
Definition newgrf.h:78
@ Houses
Houses feature.
Definition newgrf.h:86
Functions related to NewGRF badges.
Functions related to NewGRF badge configuration.
void DrawBadgeColumn(Rect r, int column_group, const GUIBadgeClasses &gui_classes, std::span< const BadgeID > badges, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, PaletteID remap)
Draw a badge column group.
std::pair< WidgetID, WidgetID > AddBadgeDropdownFilters(Window *window, WidgetID container_id, WidgetID widget, Colours colour, GrfSpecFeature feature)
Add badge drop down filter widgets.
bool HandleBadgeConfigurationDropDownClick(GrfSpecFeature feature, uint columns, int result, int click_result, BadgeFilterChoices &choices)
Handle the badge configuration drop down selection.
void SetBadgeFilter(BadgeFilterChoices &choices, BadgeID badge_index)
Set badge filter choice for a class.
void ResetBadgeFilter(BadgeFilterChoices &choices, BadgeClassID badge_class_index)
Reset badge filter choice for a class.
GUI functions related to NewGRF badges.
uint32_t GrfID
The unique identifier of a NewGRF.
Definition newgrf_type.h:15
@ Normal
Playing a game.
Definition openttd.h:20
PixelColour GetColourGradient(Colours colour, Shade shade)
Get colour gradient palette index.
Definition palette.cpp:393
@ Darker
Darker colour shade.
PickerWindow * picker_window
Allow the collection sorter to test if the collection has inactive items.
static const std::initializer_list< PickerClassList::FilterFunction *const > _class_filter_funcs
Filter functions of the PickerClassList.
static bool TypeIDSorter(PickerItem const &a, PickerItem const &b)
Sort types by id.
static void PickerLoadConfig(const IniFile &ini, PickerCallbacks &callbacks)
Load favourites of a picker from config.
static const std::initializer_list< PickerTypeList::SortFunction *const > _type_sorter_funcs
Sort functions of the PickerTypeList.
static bool ClassIDSorter(int const &a, int const &b)
Sort classes by id.
static const std::initializer_list< PickerClassList::SortFunction *const > _class_sorter_funcs
Sort functions of the PickerClassList.
std::unique_ptr< NWidgetBase > MakePickerClassWidgets()
Create nested widgets for the class picker widgets.
static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter)
Filter types by class name.
static const std::initializer_list< PickerTypeList::FilterFunction *const > _type_filter_funcs
Filter functions of the PickerTypeList.
static const std::initializer_list< PickerCollectionList::SortFunction *const > _collection_sorter_funcs
Sort functions of the PickerCollectionList.
static bool CollectionIDSorter(std::string const &a, std::string const &b)
Sort collections by id.
static void PickerSaveConfig(IniFile &ini, const PickerCallbacks &callbacks)
Save favourites of a picker to config.
std::unique_ptr< NWidgetBase > MakePickerTypeWidgets()
Create nested widgets for the type picker widgets.
static bool ClassTagNameFilter(int const *item, PickerFilterData &filter)
Filter classes by class name.
Functions/types etc.
PickerFilterMode
Picker filter mode.
Definition picker_gui.h:26
@ Used
Show used types.
Definition picker_gui.h:28
@ Saved
Show saved types.
Definition picker_gui.h:29
@ All
Show all classes.
Definition picker_gui.h:27
Types related to the picker widgets.
@ WID_PW_MODE_SAVED
Toggle showing only saved types.
@ WID_PW_CLASS_FILTER
Editbox filter.
@ WID_PW_TYPE_SCROLL
Scrollbar for the matrix.
@ WID_PW_EXPAND
Button to increase preview image height.
@ WID_PW_CONFIGURE_BADGES
Button to configure badges.
@ WID_PW_TYPE_ITEM
A single item.
@ WID_PW_CLASS_LIST
List of classes.
@ WID_PW_TYPE_FILTER
Text filter.
@ WID_PW_MODE_USED
Toggle showing only used types.
@ WID_PW_BADGE_FILTER
Container for dropdown badge filters. Must be last in this list.
@ WID_PW_TYPE_MATRIX
Matrix with items.
@ WID_PW_COLEC_DELETE
Button to delete a collection.
@ WID_PW_CLASS_SEL
Stack to hide the class picker.
@ WID_PW_MODE_ALL
Toggle "Show all" filter mode.
@ WID_PW_CLASS_SCROLL
Scrollbar for list of classes.
@ WID_PW_SHRINK
Button to reduce preview image height.
@ WID_PW_COLEC_LIST
List of collections.
@ WID_PW_TYPE_SEL
Stack to hide the type picker.
@ WID_PW_TYPE_NAME
Name of selected item.
@ WID_PW_TYPE_RESIZE
Type resize handle.
@ WID_PW_COLEC_ADD
Button to create a new collections.
@ WID_PW_COLEC_RENAME
Button to rename a collections.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
Types related to global configuration settings.
Base types for having sorted lists in GUIs.
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:254
Functions related to sound.
Types related to sounds.
This file contains all sprite-related enums and defines.
static const PaletteID PALETTE_TO_GREEN
Definition sprites.h:1763
static const SpriteID SPR_BLOT
Coloured circle (used for server compatibility and installed content).
Definition sprites.h:71
static const PaletteID PALETTE_TO_YELLOW
Definition sprites.h:1760
Definition of base types and functions in a cross-platform compatible way.
bool ConvertHexToBytes(std::string_view hex, std::span< uint8_t > bytes)
Convert a hex-string to a byte-array, while validating it was actually hex.
Definition string.cpp:572
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:429
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition string_type.h:25
Searching and filtering using a stringterm.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
T y
Y coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
Ini file that supports both loading and saving.
Definition ini_type.h:87
A group within an ini file.
Definition ini_type.h:34
void Clear()
Clear all items in the group.
Definition ini_load.cpp:96
std::string name
name of group
Definition ini_type.h:37
IniItem & CreateItem(std::string_view name)
Create an item with the given name.
Definition ini_load.cpp:79
std::list< IniItem > items
all items in the group
Definition ini_type.h:35
A single "line" in an ini file.
Definition ini_type.h:23
std::string name
The name of this item.
Definition ini_type.h:24
std::list< IniGroup > groups
all groups in the ini
Definition ini_type.h:53
void RemoveGroup(std::string_view name)
Remove the group with the given name.
Definition ini_load.cpp:173
IniGroup & GetOrCreateGroup(std::string_view name)
Get the group with the given name, and if it doesn't exist create a new group.
Definition ini_load.cpp:145
const PickerCallbacks * callbacks
Callbacks for filter functions to access to callbacks.
Definition picker_gui.h:313
static const int ACTION_CLEAR
Clear editbox.
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.
int Height() const
Get height of Rect.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
High level window description.
Definition window_gui.h:172
Number to differentiate different windows of the same class.
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:984
void CloseChildWindows(WindowClass wc=WindowClass::Invalid) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1081
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1814
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:320
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:510
ResizeInfo resize
Resize information.
Definition window_gui.h:314
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:1804
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:491
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:491
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition window_gui.h:410
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:441
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:319
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:322
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:989
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:319
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:381
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:321
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
@ LengthIsInChars
the length of the string is counted in characters
Definition textbuf_gui.h:21
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
Definitions about widgets.
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:41
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ NWID_MATRIX
Matrix container.
Definition widget_type.h:69
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ 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.
NWidContainerFlag
Nested widget container flags,.
@ EqualSize
Containers should keep all their (resizing) children equally large.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1201
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:427
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3333
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
Types related to windows.
int WidgetID
Widget ID.
Definition window_type.h:21
EventState
State of handling an event.
@ Handled
The passed event is handled.
@ NotHandled
The passed event is not handled.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107