OpenTTD Source 20260218-master-g2123fca5ea
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
183static bool CollectionIDSorter(std::string const &a, std::string const &b)
184{
185 if (a == GetString(STR_PICKER_DEFAULT_COLLECTION) || b == GetString(STR_PICKER_DEFAULT_COLLECTION)) return a == GetString(STR_PICKER_DEFAULT_COLLECTION);
186 if (picker_window->inactive.contains(a) == picker_window->inactive.contains(b)) return StrNaturalCompare(a, b) < 0;
187 return picker_window->inactive.contains(a) < picker_window->inactive.contains(b);
188}
189
190static const std::initializer_list<PickerClassList::SortFunction * const> _class_sorter_funcs = { &ClassIDSorter };
191static const std::initializer_list<PickerClassList::FilterFunction * const> _class_filter_funcs = { &ClassTagNameFilter };
192static const std::initializer_list<PickerTypeList::SortFunction * const> _type_sorter_funcs = { TypeIDSorter };
193static const std::initializer_list<PickerTypeList::FilterFunction * const> _type_filter_funcs = { TypeTagNameFilter };
194static const std::initializer_list<PickerCollectionList::SortFunction * const> _collection_sorter_funcs = { &CollectionIDSorter };
195
196
197PickerWindow::PickerWindow(WindowDesc &desc, Window *parent, int window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks),
198 class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE),
199 type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
200{
201 this->window_number = window_number;
202
203 /* Init of nested tree is deferred.
204 * PickerWindow::ConstructWindow must be called by the inheriting window. */
205}
206
207void PickerWindow::ConstructWindow()
208{
209 this->CreateNestedTree();
210
211 /* Test if pickers should be active.*/
212 bool is_active = this->callbacks.IsActive();
213
214 this->preview_height = std::max(this->callbacks.preview_height, PREVIEW_HEIGHT);
215 picker_window = this;
216
217 /* Functionality depends on widgets being present, not window class. */
218 this->has_class_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice();
219 this->has_type_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_TYPE_MATRIX) != nullptr;
220 this->has_collection_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_COLEC_LIST) != nullptr;
221
222 if (this->has_class_picker) {
223 this->GetWidget<NWidgetCore>(WID_PW_CLASS_LIST)->SetToolTip(this->callbacks.GetClassTooltip());
224
226 } else {
227 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_CLASS_SEL); nwid != nullptr) {
228 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
229 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
230 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
231 }
232 }
233
234 this->class_editbox.cancel_button = QueryString::ACTION_CLEAR;
235 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
236 this->class_string_filter.callbacks = &this->callbacks;
237
238 this->classes.SetListing(this->callbacks.class_last_sorting);
239 this->classes.SetFiltering(this->callbacks.class_last_filtering);
240 this->classes.SetSortFuncs(_class_sorter_funcs);
241 this->classes.SetFilterFuncs(_class_filter_funcs);
242
243 /* Update saved type information. */
244 if (this->callbacks.sel_collection == "") SetWidgetsDisabledState(true, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
245 this->callbacks.saved = this->callbacks.UpdateSavedItems(this->callbacks.saved);
246 this->inactive = this->callbacks.InitializeInactiveCollections(this->callbacks.saved);
247 this->collections.ForceRebuild();
248
249 /* Clear used type information. */
250 this->callbacks.used.clear();
251
252 if (this->has_type_picker) {
253 /* Populate used type information. */
254 this->callbacks.FillUsedItems(this->callbacks.used);
255
256 SetWidgetDisabledState(WID_PW_MODE_ALL, !this->callbacks.HasClassChoice());
257
258 this->GetWidget<NWidgetCore>(WID_PW_TYPE_ITEM)->SetToolTip(this->callbacks.GetTypeTooltip());
259
260 auto *matrix = this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX);
261 matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL));
262
264 } else {
265 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_TYPE_SEL); nwid != nullptr) {
266 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
267 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
268 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
269 }
270 }
271
272 this->type_editbox.cancel_button = QueryString::ACTION_CLEAR;
273 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
274 this->type_string_filter.callbacks = &this->callbacks;
275
276 this->types.SetListing(this->callbacks.type_last_sorting);
277 this->types.SetFiltering(this->callbacks.type_last_filtering);
278 this->types.SetSortFuncs(_type_sorter_funcs);
279 this->types.SetFilterFuncs(_type_filter_funcs);
280
281 if (this->has_collection_picker) {
282 this->GetWidget<NWidgetCore>(WID_PW_COLEC_LIST)->SetToolTip(this->callbacks.GetCollectionTooltip());
283 }
284
285 this->collections.SetListing(this->callbacks.collection_last_sorting);
286 this->collections.SetSortFuncs(_collection_sorter_funcs);
287
288 this->FinishInitNested(this->window_number);
289
290 this->InvalidateData(PICKER_INVALIDATION_ALL);
291}
292
294{
295 this->badge_classes = GUIBadgeClasses(this->callbacks.GetFeature());
296 this->badge_filters = AddBadgeDropdownFilters(this, WID_PW_BADGE_FILTER, WID_PW_BADGE_FILTER, COLOUR_DARK_GREEN, this->callbacks.GetFeature());
297
298 this->widget_lookup.clear();
299 this->nested_root->FillWidgetLookup(this->widget_lookup);
300}
301
303{
304 this->callbacks.Close(data);
305 this->PickerWindowBase::Close(data);
306}
307
309{
310 switch (widget) {
311 /* Class picker */
313 fill.height = resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
314 size.height = 5 * resize.height;
315 break;
316
317 /* Type picker */
319 /* At least two items wide. */
320 size.width += resize.width;
321 fill.width = resize.width;
322 fill.height = 1;
323
324 /* Resizing in X direction only at blob size, but at pixel level in Y. */
325 resize.height = 1;
326 break;
327
328 /* Type picker */
329 case WID_PW_TYPE_ITEM:
330 size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
331 size.height = ScaleGUITrad(this->preview_height) + WidgetDimensions::scaled.fullbevel.Vertical();
332 break;
333
335 /* Hide the configuration button if no configurable badges are present. */
336 if (this->badge_classes.GetClasses().empty()) size = {0, 0};
337 break;
338 }
339}
340
341std::string PickerWindow::GetWidgetString(WidgetID widget, StringID stringid) const
342{
343 switch (widget) {
345 return this->callbacks.sel_collection == "" ? GetString(STR_PICKER_DEFAULT_COLLECTION) : this->callbacks.sel_collection;
346
347 default:
348 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
349 return this->GetWidget<NWidgetBadgeFilter>(widget)->GetStringParameter(this->badge_filter_choices);
350 }
351 break;
352 }
353 return this->Window::GetWidgetString(widget, stringid);
354}
355
356DropDownList PickerWindow::BuildCollectionDropDownList()
357{
358 DropDownList list;
359 int i = 0;
360 for (const auto &collection : collections) {
361 list.push_back(MakeDropDownListStringItem(GetString(collection == "" ? STR_PICKER_DEFAULT_COLLECTION : STR_JUST_RAW_STRING, collection), i, false, this->inactive.contains(collection)));
362 i++;
363 }
364 return list;
365}
366
367void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
368{
369 switch (widget) {
370 /* Class picker */
371 case WID_PW_CLASS_LIST: {
372 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
373 const int selected = this->callbacks.GetSelectedClass();
374 const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL);
375 const int y_step = this->GetWidget<NWidgetResizeBase>(widget)->resize_y;
376 auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes);
377 for (auto it = first; it != last; ++it) {
378 DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TC_WHITE : TC_BLACK);
379 ir.top += y_step;
380 }
381 break;
382 }
383
384 /* Type picker */
385 case WID_PW_TYPE_ITEM: {
386 assert(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement() < static_cast<int>(this->types.size()));
387 const auto &item = this->types[this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement()];
388
389 DrawPixelInfo tmp_dpi;
391 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
392 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
395
396 this->callbacks.DrawType(x, y, item.class_index, item.index);
397
398 int by = ir.Height() - ScaleGUITrad(12);
399
400 GrfSpecFeature feature = this->callbacks.GetFeature();
401 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
402 PaletteID palette = _game_mode != GM_NORMAL || feature == GSF_HOUSES ? PAL_NONE : GetCompanyPalette(_local_company);
403 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);
404
405 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
406 if (this->callbacks.saved.at(this->callbacks.sel_collection).contains(item)) {
407 DrawSprite(SPR_BLOT, PALETTE_TO_YELLOW, 0, 0);
408 }
409 }
410 if (this->callbacks.used.contains(item)) {
411 DrawSprite(SPR_BLOT, PALETTE_TO_GREEN, ir.Width() - GetSpriteSize(SPR_BLOT).width, 0);
412 }
413 }
414
415 if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
416 GfxFillRect(ir, GetColourGradient(COLOUR_GREY, SHADE_DARKER), FILLRECT_CHECKER);
417 }
418 break;
419 }
420
421 case WID_PW_TYPE_NAME: {
422 StringID str = this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType());
423 if (str != INVALID_STRING_ID) DrawString(r, str, TC_GOLD, SA_CENTER);
424 break;
425 }
426 }
427}
428
435
436void PickerWindow::DeletePickerCollectionCallback(Window *win, bool confirmed)
437{
438 if (confirmed) {
439 PickerWindow *w = (PickerWindow*)win;
440 w->callbacks.saved.erase(w->callbacks.saved.find(w->callbacks.edit_collection));
441 w->inactive.erase(w->callbacks.edit_collection);
442 w->callbacks.rm_collections.emplace(w->callbacks.edit_collection);
443 w->callbacks.sel_collection = "";
444 w->callbacks.edit_collection.clear();
445 picker_window = w;
448 }
449}
450
452{
453 switch (widget) {
454 /* Class Picker */
455 case WID_PW_CLASS_LIST: {
456 const auto vscroll = this->GetWidget<NWidgetScrollbar>(WID_PW_CLASS_SCROLL);
457 auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST);
458 if (it == this->classes.end()) return;
459
460 if (this->callbacks.GetSelectedClass() != *it || HasBit(this->callbacks.mode, PFM_ALL)) {
461 ClrBit(this->callbacks.mode, PFM_ALL); // Disable showing all.
462 this->callbacks.SetSelectedClass(*it);
464 }
465 SndClickBeep();
467 break;
468 }
469
470 case WID_PW_MODE_ALL:
471 case WID_PW_MODE_USED:
473 ToggleBit(this->callbacks.mode, widget - WID_PW_MODE_ALL);
474 if (!this->IsWidgetDisabled(WID_PW_MODE_ALL) && HasBit(this->callbacks.mode, widget - WID_PW_MODE_ALL)) {
475 /* Enabling used or saved filters automatically enables all. */
476 SetBit(this->callbacks.mode, PFM_ALL);
477 }
479 SndClickBeep();
480 break;
481
482 case WID_PW_SHRINK:
483 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? PREVIEW_HEIGHT : std::max(PREVIEW_HEIGHT, this->preview_height - STEP_PREVIEW_HEIGHT);
484 this->InvalidateData({});
485 this->ReInit();
486 break;
487
488 case WID_PW_EXPAND:
489 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? MAX_PREVIEW_HEIGHT : std::min(MAX_PREVIEW_HEIGHT, this->preview_height + STEP_PREVIEW_HEIGHT);
490 this->InvalidateData({});
491 this->ReInit();
492 break;
493
494 /* Type Picker */
495 case WID_PW_TYPE_ITEM: {
496 int sel = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
497 assert(sel < (int)this->types.size());
498 const auto &item = this->types[sel];
499
500 if (_ctrl_pressed) {
501 if (this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) {
502 this->callbacks.saved[""].emplace(item);
504 this->SetDirty();
505 break;
506 }
507
508 auto it = this->callbacks.saved.at(this->callbacks.sel_collection).find(item);
509 if (it == std::end(this->callbacks.saved.at(this->callbacks.sel_collection))) {
510 this->callbacks.saved.at(this->callbacks.sel_collection).emplace(item);
511 } else {
512 this->callbacks.saved.at(this->callbacks.sel_collection).erase(it);
513 }
515 break;
516 }
517
518 if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
519 this->callbacks.SetSelectedClass(item.class_index);
520 this->callbacks.SetSelectedType(item.index);
521 this->InvalidateData(PickerInvalidation::Position);
522 }
523 SndClickBeep();
525 break;
526 }
527
528 case WID_PW_COLEC_LIST: {
529 ShowDropDownList(this, this->BuildCollectionDropDownList(), -1, widget, 0);
531 break;
532 }
533
534 case WID_PW_COLEC_ADD:
535 this->callbacks.rename_collection = false;
537 break;
538
540 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
542 this->callbacks.edit_collection = this->callbacks.sel_collection;
543 this->callbacks.rename_collection = true;
544 ShowQueryString(this->callbacks.sel_collection, STR_PICKER_COLLECTION_RENAME_QUERY, MAX_LENGTH_GROUP_NAME_CHARS, this, CS_ALPHANUMERAL, QueryStringFlag::LengthIsInChars);
545 }
546 break;
547
549 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
551 this->callbacks.edit_collection = this->callbacks.sel_collection;
552
553 this->inactive.contains(this->callbacks.sel_collection) ?
554 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_DISABLED_TEXT), this, DeletePickerCollectionCallback) :
555 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_TEXT), this, DeletePickerCollectionCallback);
556 }
557 break;
558
560 if (this->badge_classes.GetClasses().empty()) break;
561 ShowDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, COLOUR_DARK_GREEN), -1, widget, 0, DropDownOption::Persist);
562 break;
563
564 default:
565 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
566 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
567 PaletteID palette = _game_mode != GM_NORMAL || this->callbacks.GetFeature() == GSF_HOUSES ? PAL_NONE : GetCompanyPalette(_local_company);
568 ShowDropDownList(this, this->GetWidget<NWidgetBadgeFilter>(widget)->GetDropDownList(palette), -1, widget, 0);
569 }
570 break;
571 }
572}
573
574void PickerWindow::OnQueryTextFinished(std::optional<std::string> str)
575{
576 if (!str.has_value()) return;
577
578 if (!this->callbacks.saved.contains(*str)) {
579 if (this->callbacks.saved.contains(this->callbacks.edit_collection) && this->callbacks.rename_collection) {
580 auto rename_collection = this->callbacks.saved.extract(this->callbacks.edit_collection);
581 rename_collection.key() = *str;
582 this->callbacks.saved.insert(std::move(rename_collection));
583
584 if (this->inactive.contains(this->callbacks.edit_collection)) {
585 this->inactive.erase(this->callbacks.edit_collection);
586 this->inactive.emplace(*str);
587 }
588
589 this->callbacks.rm_collections.emplace(this->callbacks.edit_collection);
590 this->callbacks.edit_collection.clear();
591
592 } else {
593 this->callbacks.saved.insert({*str, {}});
594 }
595 }
596
597 this->callbacks.sel_collection = *str;
598 picker_window = this;
599 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
602 }
604}
605
606void PickerWindow::OnDropdownSelect(WidgetID widget, int index, int click_result)
607{
608 switch (widget) {
609 case WID_PW_COLEC_LIST: {
610 auto it = this->collections.begin() + index;
611 if (this->callbacks.sel_collection != *it) {
612 this->callbacks.sel_collection = *it;
614 this->InvalidateData(PickerInvalidation::Position);
615 }
616 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
617
618 SndClickBeep();
619 break;
620 }
621
623 bool reopen = HandleBadgeConfigurationDropDownClick(this->callbacks.GetFeature(), 1, index, click_result, this->badge_filter_choices);
624
625 this->ReInit();
626
627 if (reopen) {
628 ReplaceDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, COLOUR_DARK_GREEN), -1);
629 } else {
631 }
632
633 /* We need to refresh if a filter is removed. */
635 break;
636 }
637
638 default:
639 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
640 if (index < 0) {
641 ResetBadgeFilter(this->badge_filter_choices, this->GetWidget<NWidgetBadgeFilter>(widget)->GetBadgeClassID());
642 } else {
643 SetBadgeFilter(this->badge_filter_choices, BadgeID(index));
644 }
646 }
647 break;
648 }
649}
650
651void PickerWindow::OnInvalidateData(int data, bool gui_scope)
652{
653 if (!gui_scope) return;
654
655 PickerInvalidations pi(data);
656
658 if (this->badge_filter_choices.empty()) {
659 this->type_string_filter.bdf.reset();
660 } else {
661 this->type_string_filter.bdf.emplace(this->badge_filter_choices);
662 }
663 this->types.SetFilterState(!type_string_filter.IsEmpty() || type_string_filter.bdf.has_value());
664 }
665
666 if (pi.Test(PickerInvalidation::Class)) this->classes.ForceRebuild();
667 if (pi.Test(PickerInvalidation::Type)) this->types.ForceRebuild();
668 if (pi.Test(PickerInvalidation::Collection)) this->collections.ForceRebuild();
669
670 this->BuildPickerClassList();
671 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedClassIsValid();
672 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedClassIsVisible();
673
674 this->BuildPickerTypeList();
675 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedTypeIsValid();
676 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedTypeIsVisible();
677
679
680 if (this->has_type_picker) {
681 SetWidgetLoweredState(WID_PW_MODE_ALL, HasBit(this->callbacks.mode, PFM_ALL));
682 SetWidgetLoweredState(WID_PW_MODE_USED, HasBit(this->callbacks.mode, PFM_USED));
684 }
685
688}
689
691{
692 switch (hotkey) {
694 /* Cycle between the two edit boxes. */
695 if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_TYPE_FILTER)) {
697 } else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_CLASS_FILTER)) {
699 }
700 SetFocusedWindow(this);
701 return ES_HANDLED;
702
703 default:
704 return ES_NOT_HANDLED;
705 }
706}
707
709{
710 switch (wid) {
712 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
713 this->classes.SetFilterState(!class_string_filter.IsEmpty());
714 this->InvalidateData(PickerInvalidation::Class);
715 break;
716
718 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
719 if (!type_string_filter.IsEmpty()) {
720 this->type_string_filter.btf.emplace(this->type_string_filter, this->callbacks.GetFeature());
721 } else {
722 this->type_string_filter.btf.reset();
723 }
725 break;
726
727 default:
728 break;
729 }
730}
731
734{
735 if (!this->classes.NeedRebuild()) return;
736
737 int count = this->callbacks.GetClassCount();
738
739 this->classes.clear();
740 this->classes.reserve(count);
741
742 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
743 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
744 for (int i = 0; i < count; i++) {
745 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
746 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;
747 if (filter_saved && this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) continue;
748 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;
749 this->classes.emplace_back(i);
750 }
751
752 this->classes.Filter(this->class_string_filter);
753 this->classes.RebuildDone();
754 this->classes.Sort();
755
756 if (!this->has_class_picker) return;
757 this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size());
758}
759
760void PickerWindow::EnsureSelectedClassIsValid()
761{
762 int class_index = this->callbacks.GetSelectedClass();
763 if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return;
764
765 if (!this->classes.empty()) {
766 class_index = this->classes.front();
767 } else {
768 /* Classes can be empty if filters are enabled, find the first usable class. */
769 int count = this->callbacks.GetClassCount();
770 for (int i = 0; i < count; i++) {
771 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
772 class_index = i;
773 break;
774 }
775 }
776
777 this->callbacks.SetSelectedClass(class_index);
778 this->types.ForceRebuild();
779}
780
781void PickerWindow::EnsureSelectedClassIsVisible()
782{
783 if (!this->has_class_picker) return;
784 if (this->classes.empty()) return;
785
786 auto it = std::ranges::find(this->classes, this->callbacks.GetSelectedClass());
787 if (it == std::end(this->classes)) return;
788
789 int pos = static_cast<int>(std::distance(std::begin(this->classes), it));
791}
792
793void PickerWindow::RefreshUsedTypeList()
794{
795 if (!this->has_type_picker) return;
796
797 this->callbacks.used.clear();
798 this->callbacks.FillUsedItems(this->callbacks.used);
799 this->InvalidateData(PickerInvalidation::Type);
800}
801
804{
805 if (!this->types.NeedRebuild()) return;
806
807 this->types.clear();
808
809 bool show_all = HasBit(this->callbacks.mode, PFM_ALL);
810 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
811 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
812 int cls_id = this->callbacks.GetSelectedClass();
813
814 if (filter_used) {
815 /* Showing used items. May also be filtered by saved items. */
816 this->types.reserve(this->callbacks.used.size());
817 for (const PickerItem &item : this->callbacks.used) {
818 if (!show_all && item.class_index != cls_id) continue;
819 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
820 this->types.emplace_back(item);
821 }
822 } else if (filter_saved && this->callbacks.saved.contains(this->callbacks.sel_collection)) {
823 /* Showing only saved items. */
824 this->types.reserve(std::size(this->callbacks.saved.at(this->callbacks.sel_collection)));
825 for (const PickerItem &item : this->callbacks.saved.at(this->callbacks.sel_collection)) {
826 /* The used list may contain items that aren't currently loaded, skip these. */
827 if (item.class_index == -1) continue;
828 if (!show_all && item.class_index != cls_id) continue;
829 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
830 this->types.emplace_back(item);
831 }
832 } else if (show_all) {
833 /* Reserve enough space for everything. */
834 int total = 0;
835 for (int class_index : this->classes) total += this->callbacks.GetTypeCount(class_index);
836 this->types.reserve(total);
837 /* Add types in all classes. */
838 for (int class_index : this->classes) {
839 int count = this->callbacks.GetTypeCount(class_index);
840 for (int i = 0; i < count; i++) {
841 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
842 this->types.emplace_back(this->callbacks.GetPickerItem(class_index, i));
843 }
844 }
845 } else {
846 /* Add types in only the selected class. */
847 if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) {
848 int count = this->callbacks.GetTypeCount(cls_id);
849 this->types.reserve(count);
850 for (int i = 0; i < count; i++) {
851 if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue;
852 this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i));
853 }
854 }
855 }
856
857 this->types.Filter(this->type_string_filter);
858 this->types.RebuildDone();
859 this->types.Sort();
860
861 if (!this->has_type_picker) return;
862 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetCount(static_cast<int>(this->types.size()));
863}
864
865void PickerWindow::EnsureSelectedTypeIsValid()
866{
867 int class_index = this->callbacks.GetSelectedClass();
868 int index = this->callbacks.GetSelectedType();
869 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;
870
871 if (!this->types.empty()) {
872 class_index = this->types.front().class_index;
873 index = this->types.front().index;
874 } else {
875 /* Types can be empty if filters are enabled, find the first usable type. */
876 int count = this->callbacks.GetTypeCount(class_index);
877 for (int i = 0; i < count; i++) {
878 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
879 index = i;
880 break;
881 }
882 }
883 this->callbacks.SetSelectedClass(class_index);
884 this->callbacks.SetSelectedType(index);
885}
886
887void PickerWindow::EnsureSelectedTypeIsVisible()
888{
889 if (!this->has_type_picker) return;
890 if (this->types.empty()) {
891 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(-1);
892 return;
893 }
894
895 int class_index = this->callbacks.GetSelectedClass();
896 int index = this->callbacks.GetSelectedType();
897
898 auto it = std::ranges::find_if(this->types, [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; });
899 int pos = -1;
900 if (it != std::end(this->types)) {
901 pos = static_cast<int>(std::distance(std::begin(this->types), it));
902 }
903
904 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(pos);
905}
906
909{
910 if (!this->collections.NeedRebuild()) return;
911
912 int count = std::max(static_cast<int>(this->callbacks.saved.size()), 1);
913
914 this->collections.clear();
915 this->collections.reserve(count);
916
917 if (this->callbacks.saved.find("") == this->callbacks.saved.end()) {
918 this->collections.emplace_back("");
919 }
920
921 for (auto it = this->callbacks.saved.begin(); it != this->callbacks.saved.end(); it++) {
922 this->collections.emplace_back(it->first);
923 }
924
925 this->collections.RebuildDone();
926 this->collections.Sort();
927
928 if (!this->has_class_picker) return;
929}
930
932std::unique_ptr<NWidgetBase> MakePickerClassWidgets()
933{
934 static constexpr std::initializer_list<NWidgetPart> picker_class_widgets = {
935 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_CLASS_SEL),
937 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
938 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),
939 EndContainer(),
940 /* Collection view */
943 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),
944 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),
945 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),
946 EndContainer(),
947 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),
948 EndContainer(),
949 /* Class view */
952 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
955 EndContainer(),
956 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_CLASS_SCROLL),
957 EndContainer(),
958 EndContainer(),
959 EndContainer(),
960 EndContainer(),
961 };
962
963 return MakeNWidgets(picker_class_widgets, nullptr);
964}
965
967std::unique_ptr<NWidgetBase> MakePickerTypeWidgets()
968{
969 static constexpr std::initializer_list<NWidgetPart> picker_type_widgets = {
970 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_TYPE_SEL),
973 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
974 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),
975 EndContainer(),
976 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),
977 EndContainer(),
979 EndContainer(),
981 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),
982 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),
983 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),
984 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),
985 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),
986 EndContainer(),
988 NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_PW_TYPE_SCROLL),
991 EndContainer(),
992 EndContainer(),
993 EndContainer(),
994 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_TYPE_SCROLL),
995 EndContainer(),
997 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
999 EndContainer(),
1000 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_RESIZE),
1001 EndContainer(),
1002 EndContainer(),
1003 EndContainer(),
1004 };
1005
1006 return MakeNWidgets(picker_type_widgets, nullptr);
1007}
1008
1009void InvalidateAllPickerWindows()
1010{
1011 InvalidateWindowClassesData(WC_BUS_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1012 InvalidateWindowClassesData(WC_TRUCK_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1013 InvalidateWindowClassesData(WC_SELECT_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1014 InvalidateWindowClassesData(WC_BUILD_WAYPOINT, PickerWindow::PICKER_INVALIDATION_ALL);
1015 InvalidateWindowClassesData(WC_BUILD_OBJECT, PickerWindow::PICKER_INVALIDATION_ALL);
1016 InvalidateWindowClassesData(WC_BUILD_HOUSE, PickerWindow::PICKER_INVALIDATION_ALL);
1017}
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:207
const std::string ini_group
Ini Group for saving favourites.
Definition picker_gui.h:203
std::string sel_collection
Currently selected collection of saved items.
Definition picker_gui.h:206
virtual StringID GetTypeName(int cls_id, int id) const =0
Get the item name of a type.
virtual StringID GetClassName(int id) const =0
Get the name of a class.
virtual std::span< const BadgeID > GetTypeBadges(int cls_id, int id) const =0
Get the item's badges of a type.
std::set< std::string > rm_collections
Set of removed or renamed collections for updating ini file.
Definition picker_gui.h:208
std::map< std::string, std::set< PickerItem > > saved
Set of saved collections of items.
Definition picker_gui.h:213
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:3613
static constexpr int MAX_PREVIEW_HEIGHT
Maximum height of each preview button.
Definition picker_gui.h:303
static constexpr int PREVIEW_WIDTH
Width of each preview button.
Definition picker_gui.h:297
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:310
bool has_class_picker
Set if this window has a class picker 'component'.
Definition picker_gui.h:307
static constexpr int PREVIEW_HEIGHT
Height of each preview button.
Definition picker_gui.h:298
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
@ Position
Update scroll positions.
Definition picker_gui.h:289
@ Class
Refresh the class list.
Definition picker_gui.h:286
@ Type
Refresh the type list.
Definition picker_gui.h:287
@ Validate
Validate selected item.
Definition picker_gui.h:290
@ Collection
Refresh the collection list.
Definition picker_gui.h:288
@ Filter
Update filter state.
Definition picker_gui.h:291
bool has_collection_picker
Set if this window has a collection picker 'component'.
Definition picker_gui.h:309
static constexpr int PREVIEW_LEFT
Offset from left edge to draw preview.
Definition picker_gui.h:299
PickerTypeList types
List of types.
Definition picker_gui.h:350
std::set< std::string > inactive
Set of collections with inactive items.
Definition picker_gui.h:311
QueryString class_editbox
Filter editbox.
Definition picker_gui.h:344
bool has_type_picker
Set if this window has a type picker 'component'.
Definition picker_gui.h:308
void BuildPickerClassList()
Builds the filter list of classes.
@ PFM_USED
Show used types.
Definition picker_gui.h:280
@ PFM_ALL
Show all classes.
Definition picker_gui.h:279
@ PFM_SAVED
Show saved types.
Definition picker_gui.h:281
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:300
@ PCWHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
Definition picker_gui.h:331
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:359
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:352
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:342
static constexpr int STEP_PREVIEW_HEIGHT
Step for decreasing or increase preview button height.
Definition picker_gui.h:302
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:967
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:267
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:979
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:505
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1076
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:486
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:314
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:1196
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:422
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:3325
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