OpenTTD Source 20260206-master-g4d4e37dbf1
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
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
239 this->class_editbox.cancel_button = QueryString::ACTION_CLEAR;
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. */
249 if (this->callbacks.sel_collection == "") SetWidgetsDisabledState(true, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
250 this->callbacks.saved = this->callbacks.UpdateSavedItems(this->callbacks.saved);
251 this->inactive = this->callbacks.InitializeInactiveCollections(this->callbacks.saved);
252 this->collections.ForceRebuild();
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
261 SetWidgetDisabledState(WID_PW_MODE_ALL, !this->callbacks.HasClassChoice());
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
277 this->type_editbox.cancel_button = QueryString::ACTION_CLEAR;
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
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:
335 size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
336 size.height = ScaleGUITrad(this->preview_height) + WidgetDimensions::scaled.fullbevel.Vertical();
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:
488 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? PREVIEW_HEIGHT : std::max(PREVIEW_HEIGHT, this->preview_height - STEP_PREVIEW_HEIGHT);
489 this->InvalidateData({});
490 this->ReInit();
491 break;
492
493 case WID_PW_EXPAND:
494 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? MAX_PREVIEW_HEIGHT : std::min(MAX_PREVIEW_HEIGHT, this->preview_height + STEP_PREVIEW_HEIGHT);
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;
604 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
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 }
621 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
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
671 if (pi.Test(PickerInvalidation::Class)) this->classes.ForceRebuild();
672 if (pi.Test(PickerInvalidation::Type)) this->types.ForceRebuild();
673 if (pi.Test(PickerInvalidation::Collection)) this->collections.ForceRebuild();
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) {
686 SetWidgetLoweredState(WID_PW_MODE_ALL, HasBit(this->callbacks.mode, PFM_ALL));
687 SetWidgetLoweredState(WID_PW_MODE_USED, HasBit(this->callbacks.mode, PFM_USED));
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 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 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.
Matrix container with implicitly equal sized (virtual) sub-widgets.
Class for PickerClassWindow to collect information and retain state.
Definition picker_gui.h:39
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
const std::string ini_group
Ini Group for saving favourites.
Definition picker_gui.h:125
std::string sel_collection
Currently selected collection of saved items.
Definition picker_gui.h:128
virtual StringID GetTypeName(int cls_id, int id) const =0
Get the item 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 of a type.
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.
Base class for windows opened from a toolbar.
Definition window_gui.h:995
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3616
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.
Definition picker_gui.h:211
@ Class
Refresh the class list.
Definition picker_gui.h:208
@ Type
Refresh the type list.
Definition picker_gui.h:209
@ Validate
Validate selected item.
Definition picker_gui.h:212
@ Collection
Refresh the collection list.
Definition picker_gui.h:210
@ Filter
Update filter state.
Definition picker_gui.h:213
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:2500
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: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:419
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:972
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:669
bool _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:1038
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
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:1573
@ 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:3375
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:969
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:71
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: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:189
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: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:981
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1812
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:507
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1078
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:1802
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:488
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 NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:986
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:316
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
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:1198
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:424
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:3328
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