OpenTTD Source 20260129-master-g2bb01bd0e4
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
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
55PickerCallbacks::~PickerCallbacks()
56{
57 auto &callbacks = GetPickerCallbacks();
58 callbacks.erase(std::ranges::find(callbacks, this));
59}
60
66static void PickerLoadConfig(const IniFile &ini, PickerCallbacks &callbacks)
67{
68 callbacks.saved.clear();
69 for (const IniGroup &group : ini.groups) {
70 /* Read the collection name */
71 if (!group.name.starts_with(callbacks.ini_group)) continue;
72 auto pos = group.name.find('-');
73 if (pos == std::string_view::npos && group.name != callbacks.ini_group) continue;
74 std::string collection = (pos == std::string_view::npos) ? "" : group.name.substr(pos + 1);
75
76 if (group.items.empty() && pos != std::string_view::npos) {
77 callbacks.saved[collection];
78 continue;
79 }
80
81 for (const IniItem &item : group.items) {
82 std::array<uint8_t, 4> grfid_buf;
83
84 std::string_view str = item.name;
85
86 /* Try reading "<grfid>|<localid>" */
87 auto grfid_pos = str.find('|');
88 if (grfid_pos == std::string_view::npos) continue;
89
90 std::string_view grfid_str = str.substr(0, grfid_pos);
91 if (!ConvertHexToBytes(grfid_str, grfid_buf)) continue;
92
93 str = str.substr(grfid_pos + 1);
94 uint32_t grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
95 uint16_t localid;
96 auto [ptr, err] = std::from_chars(str.data(), str.data() + str.size(), localid);
97
98 if (err == std::errc{} && ptr == str.data() + str.size()) {
99 callbacks.saved[collection].emplace(grfid, localid, 0, 0);
100 }
101 }
102 }
103}
104
110static void PickerSaveConfig(IniFile &ini, const PickerCallbacks &callbacks)
111{
112 /* Clean the ini file of any obsolete collections to prevent them coming back after a restart */
113 for (const std::string &rm_collection : callbacks.rm_collections) {
114 ini.RemoveGroup(callbacks.ini_group + "-" + rm_collection);
115 }
116
117 for (const auto &collection : callbacks.saved) {
118 IniGroup &group = ini.GetOrCreateGroup(collection.first == "" ? callbacks.ini_group : callbacks.ini_group + "-" + collection.first);
119 group.Clear();
120 for (const PickerItem &item : collection.second) {
121 std::string key = fmt::format("{:08X}|{}", std::byteswap(item.grfid), item.local_id);
122 group.CreateItem(key);
123 }
124 }
125}
126
131void PickerLoadConfig(const IniFile &ini)
132{
133 for (auto *cb : GetPickerCallbacks()) PickerLoadConfig(ini, *cb);
134}
135
141{
142 for (const auto *cb : GetPickerCallbacks()) PickerSaveConfig(ini, *cb);
143}
144
146static bool ClassIDSorter(int const &a, int const &b)
147{
148 return a < b;
149}
150
152static bool ClassTagNameFilter(int const *item, PickerFilterData &filter)
153{
154 filter.ResetState();
155 filter.AddLine(GetString(filter.callbacks->GetClassName(*item)));
156 return filter.GetState();
157}
158
160static bool TypeIDSorter(PickerItem const &a, PickerItem const &b)
161{
162 int r = a.class_index - b.class_index;
163 if (r == 0) r = a.index - b.index;
164 return r < 0;
165}
166
168static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter)
169{
170 auto badges = filter.callbacks->GetTypeBadges(item->class_index, item->index);
171 if (filter.bdf.has_value() && !filter.bdf->Filter(badges)) return false;
172 if (filter.btf.has_value() && filter.btf->Filter(badges)) return true;
173
174 filter.ResetState();
175 filter.AddLine(GetString(filter.callbacks->GetTypeName(item->class_index, item->index)));
176 return filter.GetState();
177}
178
181
188static bool CollectionIDSorter(std::string const &a, std::string const &b)
189{
190 if (a == GetString(STR_PICKER_DEFAULT_COLLECTION) || b == GetString(STR_PICKER_DEFAULT_COLLECTION)) return a == GetString(STR_PICKER_DEFAULT_COLLECTION);
191 if (picker_window->inactive.contains(a) == picker_window->inactive.contains(b)) return StrNaturalCompare(a, b) < 0;
192 return picker_window->inactive.contains(a) < picker_window->inactive.contains(b);
193}
194
195static const std::initializer_list<PickerClassList::SortFunction * const> _class_sorter_funcs = { &ClassIDSorter };
196static const std::initializer_list<PickerClassList::FilterFunction * const> _class_filter_funcs = { &ClassTagNameFilter };
197static const std::initializer_list<PickerTypeList::SortFunction * const> _type_sorter_funcs = { TypeIDSorter };
198static const std::initializer_list<PickerTypeList::FilterFunction * const> _type_filter_funcs = { TypeTagNameFilter };
199static const std::initializer_list<PickerCollectionList::SortFunction * const> _collection_sorter_funcs = { &CollectionIDSorter };
200
201
202PickerWindow::PickerWindow(WindowDesc &desc, Window *parent, int window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks),
203 class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE),
204 type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
205{
206 this->window_number = window_number;
207
208 /* Init of nested tree is deferred.
209 * PickerWindow::ConstructWindow must be called by the inheriting window. */
210}
211
212void PickerWindow::ConstructWindow()
213{
214 this->CreateNestedTree();
215
216 /* Test if pickers should be active.*/
217 bool is_active = this->callbacks.IsActive();
218
219 this->preview_height = std::max(this->callbacks.preview_height, PREVIEW_HEIGHT);
220 picker_window = this;
221
222 /* Functionality depends on widgets being present, not window class. */
223 this->has_class_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice();
224 this->has_type_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_TYPE_MATRIX) != nullptr;
225 this->has_collection_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_COLEC_LIST) != nullptr;
226
227 if (this->has_class_picker) {
228 this->GetWidget<NWidgetCore>(WID_PW_CLASS_LIST)->SetToolTip(this->callbacks.GetClassTooltip());
229
231 } else {
232 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_CLASS_SEL); nwid != nullptr) {
233 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
234 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
235 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
236 }
237 }
238
240 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
241 this->class_string_filter.callbacks = &this->callbacks;
242
243 this->classes.SetListing(this->callbacks.class_last_sorting);
244 this->classes.SetFiltering(this->callbacks.class_last_filtering);
245 this->classes.SetSortFuncs(_class_sorter_funcs);
246 this->classes.SetFilterFuncs(_class_filter_funcs);
247
248 /* Update saved type information. */
250 this->callbacks.saved = this->callbacks.UpdateSavedItems(this->callbacks.saved);
251 this->inactive = this->callbacks.InitializeInactiveCollections(this->callbacks.saved);
253
254 /* Clear used type information. */
255 this->callbacks.used.clear();
256
257 if (this->has_type_picker) {
258 /* Populate used type information. */
259 this->callbacks.FillUsedItems(this->callbacks.used);
260
262
263 this->GetWidget<NWidgetCore>(WID_PW_TYPE_ITEM)->SetToolTip(this->callbacks.GetTypeTooltip());
264
265 auto *matrix = this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX);
266 matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL));
267
269 } else {
270 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_TYPE_SEL); nwid != nullptr) {
271 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
272 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
273 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
274 }
275 }
276
278 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
279 this->type_string_filter.callbacks = &this->callbacks;
280
281 this->types.SetListing(this->callbacks.type_last_sorting);
282 this->types.SetFiltering(this->callbacks.type_last_filtering);
283 this->types.SetSortFuncs(_type_sorter_funcs);
284 this->types.SetFilterFuncs(_type_filter_funcs);
285
286 if (this->has_collection_picker) {
287 this->GetWidget<NWidgetCore>(WID_PW_COLEC_LIST)->SetToolTip(this->callbacks.GetCollectionTooltip());
288 }
289
290 this->collections.SetListing(this->callbacks.collection_last_sorting);
291 this->collections.SetSortFuncs(_collection_sorter_funcs);
292
293 this->FinishInitNested(this->window_number);
294
295 this->InvalidateData(PICKER_INVALIDATION_ALL);
296}
297
299{
300 this->badge_classes = GUIBadgeClasses(this->callbacks.GetFeature());
301 this->badge_filters = AddBadgeDropdownFilters(this, WID_PW_BADGE_FILTER, WID_PW_BADGE_FILTER, COLOUR_DARK_GREEN, this->callbacks.GetFeature());
302
303 this->widget_lookup.clear();
304 this->nested_root->FillWidgetLookup(this->widget_lookup);
305}
306
308{
309 this->callbacks.Close(data);
310 this->PickerWindowBase::Close(data);
311}
312
313void PickerWindow::UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
314{
315 switch (widget) {
316 /* Class picker */
318 fill.height = resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
319 size.height = 5 * resize.height;
320 break;
321
322 /* Type picker */
324 /* At least two items wide. */
325 size.width += resize.width;
326 fill.width = resize.width;
327 fill.height = 1;
328
329 /* Resizing in X direction only at blob size, but at pixel level in Y. */
330 resize.height = 1;
331 break;
332
333 /* Type picker */
334 case WID_PW_TYPE_ITEM:
337 break;
338
340 /* Hide the configuration button if no configurable badges are present. */
341 if (this->badge_classes.GetClasses().empty()) size = {0, 0};
342 break;
343 }
344}
345
346std::string PickerWindow::GetWidgetString(WidgetID widget, StringID stringid) const
347{
348 switch (widget) {
350 return this->callbacks.sel_collection == "" ? GetString(STR_PICKER_DEFAULT_COLLECTION) : this->callbacks.sel_collection;
351
352 default:
353 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
354 return this->GetWidget<NWidgetBadgeFilter>(widget)->GetStringParameter(this->badge_filter_choices);
355 }
356 break;
357 }
358 return this->Window::GetWidgetString(widget, stringid);
359}
360
361DropDownList PickerWindow::BuildCollectionDropDownList()
362{
363 DropDownList list;
364 int i = 0;
365 for (const auto &collection : collections) {
366 list.push_back(MakeDropDownListStringItem(GetString(collection == "" ? STR_PICKER_DEFAULT_COLLECTION : STR_JUST_RAW_STRING, collection), i, false, this->inactive.contains(collection)));
367 i++;
368 }
369 return list;
370}
371
372void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
373{
374 switch (widget) {
375 /* Class picker */
376 case WID_PW_CLASS_LIST: {
377 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
378 const int selected = this->callbacks.GetSelectedClass();
379 const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL);
380 const int y_step = this->GetWidget<NWidgetResizeBase>(widget)->resize_y;
381 auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes);
382 for (auto it = first; it != last; ++it) {
383 DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TC_WHITE : TC_BLACK);
384 ir.top += y_step;
385 }
386 break;
387 }
388
389 /* Type picker */
390 case WID_PW_TYPE_ITEM: {
391 assert(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement() < static_cast<int>(this->types.size()));
392 const auto &item = this->types[this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement()];
393
394 DrawPixelInfo tmp_dpi;
396 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
397 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
400
401 this->callbacks.DrawType(x, y, item.class_index, item.index);
402
403 int by = ir.Height() - ScaleGUITrad(12);
404
405 GrfSpecFeature feature = this->callbacks.GetFeature();
406 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
407 PaletteID palette = _game_mode != GM_NORMAL || feature == GSF_HOUSES ? PAL_NONE : GetCompanyPalette(_local_company);
408 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);
409
410 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
411 if (this->callbacks.saved.at(this->callbacks.sel_collection).contains(item)) {
412 DrawSprite(SPR_BLOT, PALETTE_TO_YELLOW, 0, 0);
413 }
414 }
415 if (this->callbacks.used.contains(item)) {
416 DrawSprite(SPR_BLOT, PALETTE_TO_GREEN, ir.Width() - GetSpriteSize(SPR_BLOT).width, 0);
417 }
418 }
419
420 if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
421 GfxFillRect(ir, GetColourGradient(COLOUR_GREY, SHADE_DARKER), FILLRECT_CHECKER);
422 }
423 break;
424 }
425
426 case WID_PW_TYPE_NAME: {
427 StringID str = this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType());
428 if (str != INVALID_STRING_ID) DrawString(r, str, TC_GOLD, SA_CENTER);
429 break;
430 }
431 }
432}
433
440
441void PickerWindow::DeletePickerCollectionCallback(Window *win, bool confirmed)
442{
443 if (confirmed) {
444 PickerWindow *w = (PickerWindow*)win;
445 w->callbacks.saved.erase(w->callbacks.saved.find(w->callbacks.edit_collection));
446 w->inactive.erase(w->callbacks.edit_collection);
447 w->callbacks.rm_collections.emplace(w->callbacks.edit_collection);
448 w->callbacks.sel_collection = "";
449 w->callbacks.edit_collection.clear();
450 picker_window = w;
453 }
454}
455
457{
458 switch (widget) {
459 /* Class Picker */
460 case WID_PW_CLASS_LIST: {
461 const auto vscroll = this->GetWidget<NWidgetScrollbar>(WID_PW_CLASS_SCROLL);
462 auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST);
463 if (it == this->classes.end()) return;
464
465 if (this->callbacks.GetSelectedClass() != *it || HasBit(this->callbacks.mode, PFM_ALL)) {
466 ClrBit(this->callbacks.mode, PFM_ALL); // Disable showing all.
467 this->callbacks.SetSelectedClass(*it);
469 }
470 SndClickBeep();
472 break;
473 }
474
475 case WID_PW_MODE_ALL:
476 case WID_PW_MODE_USED:
478 ToggleBit(this->callbacks.mode, widget - WID_PW_MODE_ALL);
479 if (!this->IsWidgetDisabled(WID_PW_MODE_ALL) && HasBit(this->callbacks.mode, widget - WID_PW_MODE_ALL)) {
480 /* Enabling used or saved filters automatically enables all. */
481 SetBit(this->callbacks.mode, PFM_ALL);
482 }
484 SndClickBeep();
485 break;
486
487 case WID_PW_SHRINK:
489 this->InvalidateData({});
490 this->ReInit();
491 break;
492
493 case WID_PW_EXPAND:
495 this->InvalidateData({});
496 this->ReInit();
497 break;
498
499 /* Type Picker */
500 case WID_PW_TYPE_ITEM: {
501 int sel = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
502 assert(sel < (int)this->types.size());
503 const auto &item = this->types[sel];
504
505 if (_ctrl_pressed) {
506 if (this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) {
507 this->callbacks.saved[""].emplace(item);
509 this->SetDirty();
510 break;
511 }
512
513 auto it = this->callbacks.saved.at(this->callbacks.sel_collection).find(item);
514 if (it == std::end(this->callbacks.saved.at(this->callbacks.sel_collection))) {
515 this->callbacks.saved.at(this->callbacks.sel_collection).emplace(item);
516 } else {
517 this->callbacks.saved.at(this->callbacks.sel_collection).erase(it);
518 }
520 break;
521 }
522
523 if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
524 this->callbacks.SetSelectedClass(item.class_index);
525 this->callbacks.SetSelectedType(item.index);
526 this->InvalidateData(PickerInvalidation::Position);
527 }
528 SndClickBeep();
530 break;
531 }
532
533 case WID_PW_COLEC_LIST: {
534 ShowDropDownList(this, this->BuildCollectionDropDownList(), -1, widget, 0);
536 break;
537 }
538
539 case WID_PW_COLEC_ADD:
540 this->callbacks.rename_collection = false;
542 break;
543
545 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
547 this->callbacks.edit_collection = this->callbacks.sel_collection;
548 this->callbacks.rename_collection = true;
549 ShowQueryString(this->callbacks.sel_collection, STR_PICKER_COLLECTION_RENAME_QUERY, MAX_LENGTH_GROUP_NAME_CHARS, this, CS_ALPHANUMERAL, QueryStringFlag::LengthIsInChars);
550 }
551 break;
552
554 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
556 this->callbacks.edit_collection = this->callbacks.sel_collection;
557
558 this->inactive.contains(this->callbacks.sel_collection) ?
559 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_DISABLED_TEXT), this, DeletePickerCollectionCallback) :
560 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_TEXT), this, DeletePickerCollectionCallback);
561 }
562 break;
563
565 if (this->badge_classes.GetClasses().empty()) break;
566 ShowDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, COLOUR_DARK_GREEN), -1, widget, 0, DropDownOption::Persist);
567 break;
568
569 default:
570 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
571 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
572 PaletteID palette = _game_mode != GM_NORMAL || this->callbacks.GetFeature() == GSF_HOUSES ? PAL_NONE : GetCompanyPalette(_local_company);
573 ShowDropDownList(this, this->GetWidget<NWidgetBadgeFilter>(widget)->GetDropDownList(palette), -1, widget, 0);
574 }
575 break;
576 }
577}
578
579void PickerWindow::OnQueryTextFinished(std::optional<std::string> str)
580{
581 if (!str.has_value()) return;
582
583 if (!this->callbacks.saved.contains(*str)) {
584 if (this->callbacks.saved.contains(this->callbacks.edit_collection) && this->callbacks.rename_collection) {
585 auto rename_collection = this->callbacks.saved.extract(this->callbacks.edit_collection);
586 rename_collection.key() = *str;
587 this->callbacks.saved.insert(std::move(rename_collection));
588
589 if (this->inactive.contains(this->callbacks.edit_collection)) {
590 this->inactive.erase(this->callbacks.edit_collection);
591 this->inactive.emplace(*str);
592 }
593
594 this->callbacks.rm_collections.emplace(this->callbacks.edit_collection);
595 this->callbacks.edit_collection.clear();
596
597 } else {
598 this->callbacks.saved.insert({*str, {}});
599 }
600 }
601
602 this->callbacks.sel_collection = *str;
603 picker_window = this;
607 }
609}
610
611void PickerWindow::OnDropdownSelect(WidgetID widget, int index, int click_result)
612{
613 switch (widget) {
614 case WID_PW_COLEC_LIST: {
615 auto it = this->collections.begin() + index;
616 if (this->callbacks.sel_collection != *it) {
617 this->callbacks.sel_collection = *it;
619 this->InvalidateData(PickerInvalidation::Position);
620 }
622
623 SndClickBeep();
624 break;
625 }
626
628 bool reopen = HandleBadgeConfigurationDropDownClick(this->callbacks.GetFeature(), 1, index, click_result, this->badge_filter_choices);
629
630 this->ReInit();
631
632 if (reopen) {
633 ReplaceDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, COLOUR_DARK_GREEN), -1);
634 } else {
636 }
637
638 /* We need to refresh if a filter is removed. */
640 break;
641 }
642
643 default:
644 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
645 if (index < 0) {
646 ResetBadgeFilter(this->badge_filter_choices, this->GetWidget<NWidgetBadgeFilter>(widget)->GetBadgeClassID());
647 } else {
648 SetBadgeFilter(this->badge_filter_choices, BadgeID(index));
649 }
651 }
652 break;
653 }
654}
655
656void PickerWindow::OnInvalidateData(int data, bool gui_scope)
657{
658 if (!gui_scope) return;
659
660 PickerInvalidations pi(data);
661
663 if (this->badge_filter_choices.empty()) {
664 this->type_string_filter.bdf.reset();
665 } else {
666 this->type_string_filter.bdf.emplace(this->badge_filter_choices);
667 }
668 this->types.SetFilterState(!type_string_filter.IsEmpty() || type_string_filter.bdf.has_value());
669 }
670
674
675 this->BuildPickerClassList();
676 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedClassIsValid();
677 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedClassIsVisible();
678
679 this->BuildPickerTypeList();
680 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedTypeIsValid();
681 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedTypeIsVisible();
682
684
685 if (this->has_type_picker) {
689 }
690
693}
694
696{
697 switch (hotkey) {
699 /* Cycle between the two edit boxes. */
700 if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_TYPE_FILTER)) {
702 } else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_CLASS_FILTER)) {
704 }
705 SetFocusedWindow(this);
706 return ES_HANDLED;
707
708 default:
709 return ES_NOT_HANDLED;
710 }
711}
712
714{
715 switch (wid) {
717 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
718 this->classes.SetFilterState(!class_string_filter.IsEmpty());
719 this->InvalidateData(PickerInvalidation::Class);
720 break;
721
723 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
724 if (!type_string_filter.IsEmpty()) {
725 this->type_string_filter.btf.emplace(this->type_string_filter, this->callbacks.GetFeature());
726 } else {
727 this->type_string_filter.btf.reset();
728 }
730 break;
731
732 default:
733 break;
734 }
735}
736
739{
740 if (!this->classes.NeedRebuild()) return;
741
742 int count = this->callbacks.GetClassCount();
743
744 this->classes.clear();
745 this->classes.reserve(count);
746
747 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
748 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
749 for (int i = 0; i < count; i++) {
750 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
751 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;
752 if (filter_saved && this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) continue;
753 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;
754 this->classes.emplace_back(i);
755 }
756
757 this->classes.Filter(this->class_string_filter);
758 this->classes.RebuildDone();
759 this->classes.Sort();
760
761 if (!this->has_class_picker) return;
762 this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size());
763}
764
765void PickerWindow::EnsureSelectedClassIsValid()
766{
767 int class_index = this->callbacks.GetSelectedClass();
768 if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return;
769
770 if (!this->classes.empty()) {
771 class_index = this->classes.front();
772 } else {
773 /* Classes can be empty if filters are enabled, find the first usable class. */
774 int count = this->callbacks.GetClassCount();
775 for (int i = 0; i < count; i++) {
776 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
777 class_index = i;
778 break;
779 }
780 }
781
782 this->callbacks.SetSelectedClass(class_index);
783 this->types.ForceRebuild();
784}
785
786void PickerWindow::EnsureSelectedClassIsVisible()
787{
788 if (!this->has_class_picker) return;
789 if (this->classes.empty()) return;
790
791 auto it = std::ranges::find(this->classes, this->callbacks.GetSelectedClass());
792 if (it == std::end(this->classes)) return;
793
794 int pos = static_cast<int>(std::distance(std::begin(this->classes), it));
796}
797
798void PickerWindow::RefreshUsedTypeList()
799{
800 if (!this->has_type_picker) return;
801
802 this->callbacks.used.clear();
803 this->callbacks.FillUsedItems(this->callbacks.used);
804 this->InvalidateData(PickerInvalidation::Type);
805}
806
809{
810 if (!this->types.NeedRebuild()) return;
811
812 this->types.clear();
813
814 bool show_all = HasBit(this->callbacks.mode, PFM_ALL);
815 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
816 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
817 int cls_id = this->callbacks.GetSelectedClass();
818
819 if (filter_used) {
820 /* Showing used items. May also be filtered by saved items. */
821 this->types.reserve(this->callbacks.used.size());
822 for (const PickerItem &item : this->callbacks.used) {
823 if (!show_all && item.class_index != cls_id) continue;
824 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
825 this->types.emplace_back(item);
826 }
827 } else if (filter_saved && this->callbacks.saved.contains(this->callbacks.sel_collection)) {
828 /* Showing only saved items. */
829 this->types.reserve(std::size(this->callbacks.saved.at(this->callbacks.sel_collection)));
830 for (const PickerItem &item : this->callbacks.saved.at(this->callbacks.sel_collection)) {
831 /* The used list may contain items that aren't currently loaded, skip these. */
832 if (item.class_index == -1) continue;
833 if (!show_all && item.class_index != cls_id) continue;
834 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
835 this->types.emplace_back(item);
836 }
837 } else if (show_all) {
838 /* Reserve enough space for everything. */
839 int total = 0;
840 for (int class_index : this->classes) total += this->callbacks.GetTypeCount(class_index);
841 this->types.reserve(total);
842 /* Add types in all classes. */
843 for (int class_index : this->classes) {
844 int count = this->callbacks.GetTypeCount(class_index);
845 for (int i = 0; i < count; i++) {
846 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
847 this->types.emplace_back(this->callbacks.GetPickerItem(class_index, i));
848 }
849 }
850 } else {
851 /* Add types in only the selected class. */
852 if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) {
853 int count = this->callbacks.GetTypeCount(cls_id);
854 this->types.reserve(count);
855 for (int i = 0; i < count; i++) {
856 if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue;
857 this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i));
858 }
859 }
860 }
861
862 this->types.Filter(this->type_string_filter);
863 this->types.RebuildDone();
864 this->types.Sort();
865
866 if (!this->has_type_picker) return;
867 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetCount(static_cast<int>(this->types.size()));
868}
869
870void PickerWindow::EnsureSelectedTypeIsValid()
871{
872 int class_index = this->callbacks.GetSelectedClass();
873 int index = this->callbacks.GetSelectedType();
874 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;
875
876 if (!this->types.empty()) {
877 class_index = this->types.front().class_index;
878 index = this->types.front().index;
879 } else {
880 /* Types can be empty if filters are enabled, find the first usable type. */
881 int count = this->callbacks.GetTypeCount(class_index);
882 for (int i = 0; i < count; i++) {
883 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
884 index = i;
885 break;
886 }
887 }
888 this->callbacks.SetSelectedClass(class_index);
889 this->callbacks.SetSelectedType(index);
890}
891
892void PickerWindow::EnsureSelectedTypeIsVisible()
893{
894 if (!this->has_type_picker) return;
895 if (this->types.empty()) {
896 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(-1);
897 return;
898 }
899
900 int class_index = this->callbacks.GetSelectedClass();
901 int index = this->callbacks.GetSelectedType();
902
903 auto it = std::ranges::find_if(this->types, [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; });
904 int pos = -1;
905 if (it != std::end(this->types)) {
906 pos = static_cast<int>(std::distance(std::begin(this->types), it));
907 }
908
909 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(pos);
910}
911
914{
915 if (!this->collections.NeedRebuild()) return;
916
917 int count = std::max(static_cast<int>(this->callbacks.saved.size()), 1);
918
919 this->collections.clear();
920 this->collections.reserve(count);
921
922 if (this->callbacks.saved.find("") == this->callbacks.saved.end()) {
923 this->collections.emplace_back("");
924 }
925
926 for (auto it = this->callbacks.saved.begin(); it != this->callbacks.saved.end(); it++) {
927 this->collections.emplace_back(it->first);
928 }
929
930 this->collections.RebuildDone();
931 this->collections.Sort();
932
933 if (!this->has_class_picker) return;
934}
935
937std::unique_ptr<NWidgetBase> MakePickerClassWidgets()
938{
939 static constexpr std::initializer_list<NWidgetPart> picker_class_widgets = {
940 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_CLASS_SEL),
942 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
943 NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_PW_CLASS_FILTER), SetMinimalSize(144, 0), SetPadding(2), SetFill(1, 0), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
944 EndContainer(),
945 /* Collection view */
948 NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_PW_COLEC_ADD), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_COLLECTION_ADD, STR_PICKER_COLLECTION_ADD_TOOLTIP),
949 NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_PW_COLEC_RENAME), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_COLLECTION_RENAME, STR_PICKER_COLLECTION_RENAME_TOOLTIP),
950 NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_PW_COLEC_DELETE), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_COLLECTION_DELETE, STR_PICKER_COLLECTION_DELETE_TOOLTIP),
951 EndContainer(),
952 NWidget(WWT_DROPDOWN, COLOUR_DARK_GREEN, WID_PW_COLEC_LIST), SetMinimalSize(144, 12), SetFill(0, 1), SetResize(1, 0), SetToolTip(STR_PICKER_SELECT_COLLECTION_TOOLTIP),
953 EndContainer(),
954 /* Class view */
957 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
960 EndContainer(),
961 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_CLASS_SCROLL),
962 EndContainer(),
963 EndContainer(),
964 EndContainer(),
965 EndContainer(),
966 };
967
968 return MakeNWidgets(picker_class_widgets, nullptr);
969}
970
972std::unique_ptr<NWidgetBase> MakePickerTypeWidgets()
973{
974 static constexpr std::initializer_list<NWidgetPart> picker_type_widgets = {
975 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_TYPE_SEL),
978 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
979 NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_FILTER), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
980 EndContainer(),
981 NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, 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),
982 EndContainer(),
984 EndContainer(),
986 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_PW_MODE_ALL), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_MODE_ALL, STR_PICKER_MODE_ALL_TOOLTIP),
987 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_PW_MODE_USED), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_MODE_USED, STR_PICKER_MODE_USED_TOOLTIP),
988 NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_PW_MODE_SAVED), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_PICKER_MODE_SAVED, STR_PICKER_MODE_SAVED_TOOLTIP),
989 NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_PW_SHRINK), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetStringTip(STR_PICKER_PREVIEW_SHRINK, STR_PICKER_PREVIEW_SHRINK_TOOLTIP),
990 NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_PW_EXPAND), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetStringTip(STR_PICKER_PREVIEW_EXPAND, STR_PICKER_PREVIEW_EXPAND_TOOLTIP),
991 EndContainer(),
993 NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_PW_TYPE_SCROLL),
996 EndContainer(),
997 EndContainer(),
998 EndContainer(),
999 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_TYPE_SCROLL),
1000 EndContainer(),
1002 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1004 EndContainer(),
1005 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_RESIZE),
1006 EndContainer(),
1007 EndContainer(),
1008 EndContainer(),
1009 };
1010
1011 return MakeNWidgets(picker_type_widgets, nullptr);
1012}
1013
1014void InvalidateAllPickerWindows()
1015{
1016 InvalidateWindowClassesData(WC_BUS_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1017 InvalidateWindowClassesData(WC_TRUCK_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1018 InvalidateWindowClassesData(WC_SELECT_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1019 InvalidateWindowClassesData(WC_BUILD_WAYPOINT, PickerWindow::PICKER_INVALIDATION_ALL);
1020 InvalidateWindowClassesData(WC_BUILD_OBJECT, PickerWindow::PICKER_INVALIDATION_ALL);
1021 InvalidateWindowClassesData(WC_BUILD_HOUSE, PickerWindow::PICKER_INVALIDATION_ALL);
1022}
Class for backupping variables and making sure they are restored later.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Enum-as-bit-set wrapper.
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.
Matrix container with implicitly equal sized (virtual) sub-widgets.
Class for PickerClassWindow to collect information and retain state.
Definition picker_gui.h:39
std::set< std::string > InitializeInactiveCollections(const std::map< std::string, std::set< PickerItem > > collections)
Initialize the list of active collections for sorting purposes.
Definition picker_gui.h:97
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:129
Filtering type_last_filtering
Default filtering of PickerTypeList.
Definition picker_gui.h:121
virtual PickerItem GetPickerItem(int cls_id, int id) const =0
Get data about an item.
const std::string ini_group
Ini Group for saving favourites.
Definition picker_gui.h:125
bool rename_collection
Are we renaming a collection?
Definition picker_gui.h:127
Listing collection_last_sorting
Default sorting of PickerCollectionList.
Definition picker_gui.h:123
virtual void SetSelectedClass(int id) const =0
Set the selected class.
std::string sel_collection
Currently selected collection of saved items.
Definition picker_gui.h:128
virtual bool IsActive() const =0
Should picker class/type selection be enabled?
virtual StringID GetTypeName(int cls_id, int id) const =0
Get the item of a type.
virtual bool IsTypeAvailable(int cls_id, int id) const =0
Test if an item is currently buildable.
virtual void FillUsedItems(std::set< PickerItem > &items)=0
Fill a set with all items that are used by the current player.
virtual StringID GetTypeTooltip() const =0
Get the tooltip string for the type grid.
virtual bool HasClassChoice() const =0
Are there multiple classes to chose from?
Listing type_last_sorting
Default sorting of PickerTypeList.
Definition picker_gui.h:120
virtual StringID GetCollectionTooltip() const =0
Get the tooltip string for the collection list.
std::set< PickerItem > used
Set of items used in the current game by the current company.
Definition picker_gui.h:134
virtual StringID GetClassTooltip() const =0
Get the tooltip string for the class list.
virtual int GetTypeCount(int cls_id) const =0
Get the number of types in a class.
virtual int GetClassCount() const =0
Get the number of classes.
virtual void SetSelectedType(int id) const =0
Set the selected type.
virtual StringID GetClassName(int id) const =0
Get the name of a class.
int preview_height
Previously adjusted height.
Definition picker_gui.h:132
virtual void DrawType(int x, int y, int cls_id, int id) const =0
Draw preview image of an item.
virtual std::span< const BadgeID > GetTypeBadges(int cls_id, int id) const =0
Get the item of a type.
virtual std::map< std::string, std::set< PickerItem > > UpdateSavedItems(const std::map< std::string, std::set< PickerItem > > &src)=0
Update link between grfid/localidx and class_index/index in saved items.
Filtering class_last_filtering
Default filtering of PickerClassList.
Definition picker_gui.h:118
std::set< std::string > rm_collections
Set of removed or renamed collections for updating ini file.
Definition picker_gui.h:130
std::map< std::string, std::set< PickerItem > > saved
Set of saved collections of items.
Definition picker_gui.h:135
virtual int GetSelectedType() const =0
Get the selected type.
uint8_t mode
Bitmask of PickerFilterModes.
Definition picker_gui.h:126
Listing class_last_sorting
Default sorting of PickerClassList.
Definition picker_gui.h:117
Base class for windows opened from a toolbar.
Definition window_gui.h:993
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3606
static constexpr int MAX_PREVIEW_HEIGHT
Maximum height of each preview button.
Definition picker_gui.h:225
static constexpr int PREVIEW_WIDTH
Width of each preview button.
Definition picker_gui.h:219
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:232
bool has_class_picker
Set if this window has a class picker 'component'.
Definition picker_gui.h:229
static constexpr int PREVIEW_HEIGHT
Height of each preview button.
Definition picker_gui.h:220
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
@ Position
Update scroll positions.
@ Class
Refresh the class list.
@ Type
Refresh the type list.
@ Validate
Validate selected item.
@ Collection
Refresh the collection list.
@ Filter
Update filter state.
bool has_collection_picker
Set if this window has a collection picker 'component'.
Definition picker_gui.h:231
static constexpr int PREVIEW_LEFT
Offset from left edge to draw preview.
Definition picker_gui.h:221
PickerTypeList types
List of types.
Definition picker_gui.h:272
std::set< std::string > inactive
Set of collections with inactive items.
Definition picker_gui.h:233
QueryString class_editbox
Filter editbox.
Definition picker_gui.h:266
bool has_type_picker
Set if this window has a type picker 'component'.
Definition picker_gui.h:230
void BuildPickerClassList()
Builds the filter list of classes.
@ PFM_USED
Show used types.
Definition picker_gui.h:202
@ PFM_ALL
Show all classes.
Definition picker_gui.h:201
@ PFM_SAVED
Show saved types.
Definition picker_gui.h:203
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:222
@ PCWHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
Definition picker_gui.h:253
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:281
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:274
EventState OnHotkey(int hotkey) override
A hotkey has been pressed.
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:264
static constexpr int STEP_PREVIEW_HEIGHT
Step for decreasing or increase preview button height.
Definition picker_gui.h:224
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:2499
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
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
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.
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options)
Show a drop down list.
Definition dropdown.cpp:418
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.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:968
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:668
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
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:1034
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:115
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:1568
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:346
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 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:3374
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 SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
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:968
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.
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:69
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.
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
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.
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:253
Functions related to sound.
Types related to sounds.
This file contains all sprite-related enums and defines.
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:570
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:427
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:86
A group within an ini file.
Definition ini_type.h:34
void Clear()
Clear all items in the group.
Definition ini_load.cpp:97
IniItem & CreateItem(std::string_view name)
Create an item with the given name.
Definition ini_load.cpp:80
A single "line" in an ini file.
Definition ini_type.h:23
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:174
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:146
const PickerCallbacks * callbacks
Callbacks for filter functions to access to callbacks.
Definition picker_gui.h:189
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.
int Height() const
Get height of Rect.
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.
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
High level window description.
Definition window_gui.h:168
Data structure for an opened window.
Definition window_gui.h:274
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:980
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1809
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:506
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1077
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:516
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1799
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:487
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:492
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition window_gui.h:411
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:442
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:320
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:323
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:315
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:322
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
@ LengthIsInChars
the length of the string is counted in characters
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:1195
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:423
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:3318
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:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ WC_BUILD_OBJECT
Build object; Window numbers:
@ WC_BUILD_HOUSE
Build house; Window numbers:
@ WC_SELECT_STATION
Select station (when joining stations); Window numbers:
@ WC_CONFIRM_POPUP_QUERY
Popup with confirm question; Window numbers:
@ WC_TRUCK_STATION
Build truck station; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_BUS_STATION
Build bus station; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers:
@ WC_BUILD_WAYPOINT
Build waypoint; Window numbers:
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107