OpenTTD Source 20260311-master-g511d3794ce
picker_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "dropdown_func.h"
14#include "gui.h"
15#include "hotkeys.h"
16#include "ini_type.h"
17#include "newgrf_badge.h"
18#include "newgrf_badge_config.h"
19#include "newgrf_badge_gui.h"
20#include "picker_gui.h"
21#include "querystring_gui.h"
22#include "settings_type.h"
23#include "sortlist_type.h"
24#include "sound_func.h"
25#include "sound_type.h"
26#include "string_func.h"
27#include "stringfilter_type.h"
28#include "strings_func.h"
29#include "widget_type.h"
30#include "window_func.h"
31#include "window_gui.h"
32#include "window_type.h"
33#include "zoom_func.h"
34
36
37#include "table/sprites.h"
38#include "table/strings.h"
39
40#include <charconv>
41
42#include "safeguards.h"
43
44static std::vector<PickerCallbacks *> &GetPickerCallbacks()
45{
46 static std::vector<PickerCallbacks *> callbacks;
47 return callbacks;
48}
49
50PickerCallbacks::PickerCallbacks(const std::string &ini_group) : ini_group(ini_group)
51{
52 GetPickerCallbacks().push_back(this);
53}
54
57{
58 auto &callbacks = GetPickerCallbacks();
59 callbacks.erase(std::ranges::find(callbacks, this));
60}
61
67static void PickerLoadConfig(const IniFile &ini, PickerCallbacks &callbacks)
68{
69 callbacks.saved.clear();
70 for (const IniGroup &group : ini.groups) {
71 /* Read the collection name */
72 if (!group.name.starts_with(callbacks.ini_group)) continue;
73 auto pos = group.name.find('-');
74 if (pos == std::string_view::npos && group.name != callbacks.ini_group) continue;
75 std::string collection = (pos == std::string_view::npos) ? "" : group.name.substr(pos + 1);
76
77 if (group.items.empty() && pos != std::string_view::npos) {
78 callbacks.saved[collection];
79 continue;
80 }
81
82 for (const IniItem &item : group.items) {
83 std::array<uint8_t, 4> grfid_buf;
84
85 std::string_view str = item.name;
86
87 /* Try reading "<grfid>|<localid>" */
88 auto grfid_pos = str.find('|');
89 if (grfid_pos == std::string_view::npos) continue;
90
91 std::string_view grfid_str = str.substr(0, grfid_pos);
92 if (!ConvertHexToBytes(grfid_str, grfid_buf)) continue;
93
94 str = str.substr(grfid_pos + 1);
95 uint32_t grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
96 uint16_t localid;
97 auto [ptr, err] = std::from_chars(str.data(), str.data() + str.size(), localid);
98
99 if (err == std::errc{} && ptr == str.data() + str.size()) {
100 callbacks.saved[collection].emplace(grfid, localid, 0, 0);
101 }
102 }
103 }
104}
105
111static void PickerSaveConfig(IniFile &ini, const PickerCallbacks &callbacks)
112{
113 /* Clean the ini file of any obsolete collections to prevent them coming back after a restart */
114 for (const std::string &rm_collection : callbacks.rm_collections) {
115 ini.RemoveGroup(callbacks.ini_group + "-" + rm_collection);
116 }
117
118 for (const auto &collection : callbacks.saved) {
119 IniGroup &group = ini.GetOrCreateGroup(collection.first == "" ? callbacks.ini_group : callbacks.ini_group + "-" + collection.first);
120 group.Clear();
121 for (const PickerItem &item : collection.second) {
122 std::string key = fmt::format("{:08X}|{}", std::byteswap(item.grfid), item.local_id);
123 group.CreateItem(key);
124 }
125 }
126}
127
132void PickerLoadConfig(const IniFile &ini)
133{
134 for (auto *cb : GetPickerCallbacks()) PickerLoadConfig(ini, *cb);
135}
136
142{
143 for (const auto *cb : GetPickerCallbacks()) PickerSaveConfig(ini, *cb);
144}
145
147static bool ClassIDSorter(int const &a, int const &b)
148{
149 return a < b;
150}
151
153static bool ClassTagNameFilter(int const *item, PickerFilterData &filter)
154{
155 filter.ResetState();
156 filter.AddLine(GetString(filter.callbacks->GetClassName(*item)));
157 return filter.GetState();
158}
159
161static bool TypeIDSorter(PickerItem const &a, PickerItem const &b)
162{
163 int r = a.class_index - b.class_index;
164 if (r == 0) r = a.index - b.index;
165 return r < 0;
166}
167
169static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter)
170{
171 auto badges = filter.callbacks->GetTypeBadges(item->class_index, item->index);
172 if (filter.bdf.has_value() && !filter.bdf->Filter(badges)) return false;
173 if (filter.btf.has_value() && filter.btf->Filter(badges)) return true;
174
175 filter.ResetState();
176 filter.AddLine(GetString(filter.callbacks->GetTypeName(item->class_index, item->index)));
177 return filter.GetState();
178}
179
182
184static bool CollectionIDSorter(std::string const &a, std::string const &b)
185{
186 if (a == GetString(STR_PICKER_DEFAULT_COLLECTION) || b == GetString(STR_PICKER_DEFAULT_COLLECTION)) return a == GetString(STR_PICKER_DEFAULT_COLLECTION);
187 if (picker_window->inactive.contains(a) == picker_window->inactive.contains(b)) return StrNaturalCompare(a, b) < 0;
188 return picker_window->inactive.contains(a) < picker_window->inactive.contains(b);
189}
190
191static const std::initializer_list<PickerClassList::SortFunction * const> _class_sorter_funcs = { &ClassIDSorter };
192static const std::initializer_list<PickerClassList::FilterFunction * const> _class_filter_funcs = { &ClassTagNameFilter };
193static const std::initializer_list<PickerTypeList::SortFunction * const> _type_sorter_funcs = { TypeIDSorter };
194static const std::initializer_list<PickerTypeList::FilterFunction * const> _type_filter_funcs = { TypeTagNameFilter };
195static const std::initializer_list<PickerCollectionList::SortFunction * const> _collection_sorter_funcs = { &CollectionIDSorter };
196
197
198PickerWindow::PickerWindow(WindowDesc &desc, Window *parent, int window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks),
199 class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE),
200 type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
201{
202 this->window_number = window_number;
203
204 /* Init of nested tree is deferred.
205 * PickerWindow::ConstructWindow must be called by the inheriting window. */
206}
207
208void PickerWindow::ConstructWindow()
209{
210 this->CreateNestedTree();
211
212 /* Test if pickers should be active.*/
213 bool is_active = this->callbacks.IsActive();
214
215 this->preview_height = std::max(this->callbacks.preview_height, PREVIEW_HEIGHT);
216 picker_window = this;
217
218 /* Functionality depends on widgets being present, not window class. */
219 this->has_class_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice();
220 this->has_type_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_TYPE_MATRIX) != nullptr;
221 this->has_collection_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_COLEC_LIST) != nullptr;
222
223 if (this->has_class_picker) {
224 this->GetWidget<NWidgetCore>(WID_PW_CLASS_LIST)->SetToolTip(this->callbacks.GetClassTooltip());
225
227 } else {
228 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_CLASS_SEL); nwid != nullptr) {
229 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
230 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
231 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
232 }
233 }
234
235 this->class_editbox.cancel_button = QueryString::ACTION_CLEAR;
236 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
237 this->class_string_filter.callbacks = &this->callbacks;
238
239 this->classes.SetListing(this->callbacks.class_last_sorting);
240 this->classes.SetFiltering(this->callbacks.class_last_filtering);
241 this->classes.SetSortFuncs(_class_sorter_funcs);
242 this->classes.SetFilterFuncs(_class_filter_funcs);
243
244 /* Update saved type information. */
245 if (this->callbacks.sel_collection == "") SetWidgetsDisabledState(true, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
246 this->callbacks.saved = this->callbacks.UpdateSavedItems(this->callbacks.saved);
247 this->inactive = this->callbacks.InitializeInactiveCollections(this->callbacks.saved);
248 this->collections.ForceRebuild();
249
250 /* Clear used type information. */
251 this->callbacks.used.clear();
252
253 if (this->has_type_picker) {
254 /* Populate used type information. */
255 this->callbacks.FillUsedItems(this->callbacks.used);
256
257 SetWidgetDisabledState(WID_PW_MODE_ALL, !this->callbacks.HasClassChoice());
258
259 this->GetWidget<NWidgetCore>(WID_PW_TYPE_ITEM)->SetToolTip(this->callbacks.GetTypeTooltip());
260
261 auto *matrix = this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX);
262 matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL));
263
265 } else {
266 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_TYPE_SEL); nwid != nullptr) {
267 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
268 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
269 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
270 }
271 }
272
273 this->type_editbox.cancel_button = QueryString::ACTION_CLEAR;
274 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
275 this->type_string_filter.callbacks = &this->callbacks;
276
277 this->types.SetListing(this->callbacks.type_last_sorting);
278 this->types.SetFiltering(this->callbacks.type_last_filtering);
279 this->types.SetSortFuncs(_type_sorter_funcs);
280 this->types.SetFilterFuncs(_type_filter_funcs);
281
282 if (this->has_collection_picker) {
283 this->GetWidget<NWidgetCore>(WID_PW_COLEC_LIST)->SetToolTip(this->callbacks.GetCollectionTooltip());
284 }
285
286 this->collections.SetListing(this->callbacks.collection_last_sorting);
287 this->collections.SetSortFuncs(_collection_sorter_funcs);
288
289 this->FinishInitNested(this->window_number);
290
291 this->InvalidateData(PICKER_INVALIDATION_ALL);
292}
293
295{
296 this->badge_classes = GUIBadgeClasses(this->callbacks.GetFeature());
297 this->badge_filters = AddBadgeDropdownFilters(this, WID_PW_BADGE_FILTER, WID_PW_BADGE_FILTER, COLOUR_DARK_GREEN, this->callbacks.GetFeature());
298
299 this->widget_lookup.clear();
300 this->nested_root->FillWidgetLookup(this->widget_lookup);
301}
302
304{
305 this->callbacks.Close(data);
306 this->PickerWindowBase::Close(data);
307}
308
310{
311 switch (widget) {
312 /* Class picker */
314 fill.height = resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
315 size.height = 5 * resize.height;
316 break;
317
318 /* Type picker */
320 /* At least two items wide. */
321 size.width += resize.width;
322 fill.width = resize.width;
323 fill.height = 1;
324
325 /* Resizing in X direction only at blob size, but at pixel level in Y. */
326 resize.height = 1;
327 break;
328
329 /* Type picker */
330 case WID_PW_TYPE_ITEM:
331 size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
332 size.height = ScaleGUITrad(this->preview_height) + WidgetDimensions::scaled.fullbevel.Vertical();
333 break;
334
336 /* Hide the configuration button if no configurable badges are present. */
337 if (this->badge_classes.GetClasses().empty()) size = {0, 0};
338 break;
339 }
340}
341
342std::string PickerWindow::GetWidgetString(WidgetID widget, StringID stringid) const
343{
344 switch (widget) {
346 return this->callbacks.sel_collection == "" ? GetString(STR_PICKER_DEFAULT_COLLECTION) : this->callbacks.sel_collection;
347
348 default:
349 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
350 return this->GetWidget<NWidgetBadgeFilter>(widget)->GetStringParameter(this->badge_filter_choices);
351 }
352 break;
353 }
354 return this->Window::GetWidgetString(widget, stringid);
355}
356
357DropDownList PickerWindow::BuildCollectionDropDownList()
358{
359 DropDownList list;
360 int i = 0;
361 for (const auto &collection : collections) {
362 list.push_back(MakeDropDownListStringItem(GetString(collection == "" ? STR_PICKER_DEFAULT_COLLECTION : STR_JUST_RAW_STRING, collection), i, false, this->inactive.contains(collection)));
363 i++;
364 }
365 return list;
366}
367
368void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
369{
370 switch (widget) {
371 /* Class picker */
372 case WID_PW_CLASS_LIST: {
373 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
374 const int selected = this->callbacks.GetSelectedClass();
375 const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL);
376 const int y_step = this->GetWidget<NWidgetResizeBase>(widget)->resize_y;
377 auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes);
378 for (auto it = first; it != last; ++it) {
379 DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TC_WHITE : TC_BLACK);
380 ir.top += y_step;
381 }
382 break;
383 }
384
385 /* Type picker */
386 case WID_PW_TYPE_ITEM: {
387 assert(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement() < static_cast<int>(this->types.size()));
388 const auto &item = this->types[this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement()];
389
390 DrawPixelInfo tmp_dpi;
392 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
393 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
396
397 this->callbacks.DrawType(x, y, item.class_index, item.index);
398
399 int by = ir.Height() - ScaleGUITrad(12);
400
401 GrfSpecFeature feature = this->callbacks.GetFeature();
402 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
403 PaletteID palette = _game_mode != GM_NORMAL || feature == GSF_HOUSES ? PAL_NONE : GetCompanyPalette(_local_company);
404 DrawBadgeColumn({0, by, ir.Width() - 1, ir.Height() - 1}, 0, this->badge_classes, this->callbacks.GetTypeBadges(item.class_index, item.index), feature, std::nullopt, palette);
405
406 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
407 if (this->callbacks.saved.at(this->callbacks.sel_collection).contains(item)) {
408 DrawSprite(SPR_BLOT, PALETTE_TO_YELLOW, 0, 0);
409 }
410 }
411 if (this->callbacks.used.contains(item)) {
412 DrawSprite(SPR_BLOT, PALETTE_TO_GREEN, ir.Width() - GetSpriteSize(SPR_BLOT).width, 0);
413 }
414 }
415
416 if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
417 GfxFillRect(ir, GetColourGradient(COLOUR_GREY, SHADE_DARKER), FILLRECT_CHECKER);
418 }
419 break;
420 }
421
422 case WID_PW_TYPE_NAME: {
423 StringID str = this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType());
424 if (str != INVALID_STRING_ID) DrawString(r, str, TC_GOLD, SA_CENTER);
425 break;
426 }
427 }
428}
429
436
437void PickerWindow::DeletePickerCollectionCallback(Window *win, bool confirmed)
438{
439 if (confirmed) {
440 PickerWindow *w = (PickerWindow*)win;
441 w->callbacks.saved.erase(w->callbacks.saved.find(w->callbacks.edit_collection));
442 w->inactive.erase(w->callbacks.edit_collection);
443 w->callbacks.rm_collections.emplace(w->callbacks.edit_collection);
444 w->callbacks.sel_collection = "";
445 w->callbacks.edit_collection.clear();
446 picker_window = w;
449 }
450}
451
453{
454 switch (widget) {
455 /* Class Picker */
456 case WID_PW_CLASS_LIST: {
457 const auto vscroll = this->GetWidget<NWidgetScrollbar>(WID_PW_CLASS_SCROLL);
458 auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST);
459 if (it == this->classes.end()) return;
460
461 if (this->callbacks.GetSelectedClass() != *it || HasBit(this->callbacks.mode, PFM_ALL)) {
462 ClrBit(this->callbacks.mode, PFM_ALL); // Disable showing all.
463 this->callbacks.SetSelectedClass(*it);
465 }
466 SndClickBeep();
468 break;
469 }
470
471 case WID_PW_MODE_ALL:
472 case WID_PW_MODE_USED:
474 ToggleBit(this->callbacks.mode, widget - WID_PW_MODE_ALL);
475 if (!this->IsWidgetDisabled(WID_PW_MODE_ALL) && HasBit(this->callbacks.mode, widget - WID_PW_MODE_ALL)) {
476 /* Enabling used or saved filters automatically enables all. */
477 SetBit(this->callbacks.mode, PFM_ALL);
478 }
480 SndClickBeep();
481 break;
482
483 case WID_PW_SHRINK:
484 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? PREVIEW_HEIGHT : std::max(PREVIEW_HEIGHT, this->preview_height - STEP_PREVIEW_HEIGHT);
485 this->InvalidateData({});
486 this->ReInit();
487 break;
488
489 case WID_PW_EXPAND:
490 this->callbacks.preview_height = this->preview_height = _ctrl_pressed ? MAX_PREVIEW_HEIGHT : std::min(MAX_PREVIEW_HEIGHT, this->preview_height + STEP_PREVIEW_HEIGHT);
491 this->InvalidateData({});
492 this->ReInit();
493 break;
494
495 /* Type Picker */
496 case WID_PW_TYPE_ITEM: {
497 int sel = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
498 assert(sel < (int)this->types.size());
499 const auto &item = this->types[sel];
500
501 if (_ctrl_pressed) {
502 if (this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) {
503 this->callbacks.saved[""].emplace(item);
505 this->SetDirty();
506 break;
507 }
508
509 auto it = this->callbacks.saved.at(this->callbacks.sel_collection).find(item);
510 if (it == std::end(this->callbacks.saved.at(this->callbacks.sel_collection))) {
511 this->callbacks.saved.at(this->callbacks.sel_collection).emplace(item);
512 } else {
513 this->callbacks.saved.at(this->callbacks.sel_collection).erase(it);
514 }
516 break;
517 }
518
519 if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
520 this->callbacks.SetSelectedClass(item.class_index);
521 this->callbacks.SetSelectedType(item.index);
522 this->InvalidateData(PickerInvalidation::Position);
523 }
524 SndClickBeep();
526 break;
527 }
528
529 case WID_PW_COLEC_LIST: {
530 ShowDropDownList(this, this->BuildCollectionDropDownList(), -1, widget, 0, DropDownOption::Filterable);
532 break;
533 }
534
535 case WID_PW_COLEC_ADD:
536 this->callbacks.rename_collection = false;
538 break;
539
541 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
543 this->callbacks.edit_collection = this->callbacks.sel_collection;
544 this->callbacks.rename_collection = true;
545 ShowQueryString(this->callbacks.sel_collection, STR_PICKER_COLLECTION_RENAME_QUERY, MAX_LENGTH_GROUP_NAME_CHARS, this, CS_ALPHANUMERAL, QueryStringFlag::LengthIsInChars);
546 }
547 break;
548
550 if (this->callbacks.saved.contains(this->callbacks.sel_collection)) {
552 this->callbacks.edit_collection = this->callbacks.sel_collection;
553
554 this->inactive.contains(this->callbacks.sel_collection) ?
555 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_DISABLED_TEXT), this, DeletePickerCollectionCallback) :
556 ShowQuery(GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY), GetEncodedString(STR_PICKER_COLLECTION_DELETE_QUERY_TEXT), this, DeletePickerCollectionCallback);
557 }
558 break;
559
561 if (this->badge_classes.GetClasses().empty()) break;
562 ShowDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, COLOUR_DARK_GREEN), -1, widget, 0, DropDownOption::Persist);
563 break;
564
565 default:
566 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
567 /* Houses have recolours but not related to the company colour and other items depend on gamemode. */
568 PaletteID palette = _game_mode != GM_NORMAL || this->callbacks.GetFeature() == GSF_HOUSES ? PAL_NONE : GetCompanyPalette(_local_company);
569 ShowDropDownList(this, this->GetWidget<NWidgetBadgeFilter>(widget)->GetDropDownList(palette), -1, widget, 0, DropDownOption::Filterable);
570 }
571 break;
572 }
573}
574
575void PickerWindow::OnQueryTextFinished(std::optional<std::string> str)
576{
577 if (!str.has_value()) return;
578
579 if (!this->callbacks.saved.contains(*str)) {
580 if (this->callbacks.saved.contains(this->callbacks.edit_collection) && this->callbacks.rename_collection) {
581 auto rename_collection = this->callbacks.saved.extract(this->callbacks.edit_collection);
582 rename_collection.key() = *str;
583 this->callbacks.saved.insert(std::move(rename_collection));
584
585 if (this->inactive.contains(this->callbacks.edit_collection)) {
586 this->inactive.erase(this->callbacks.edit_collection);
587 this->inactive.emplace(*str);
588 }
589
590 this->callbacks.rm_collections.emplace(this->callbacks.edit_collection);
591 this->callbacks.edit_collection.clear();
592
593 } else {
594 this->callbacks.saved.insert({*str, {}});
595 }
596 }
597
598 this->callbacks.sel_collection = *str;
599 picker_window = this;
600 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
603 }
605}
606
607void PickerWindow::OnDropdownSelect(WidgetID widget, int index, int click_result)
608{
609 switch (widget) {
610 case WID_PW_COLEC_LIST: {
611 auto it = this->collections.begin() + index;
612 if (this->callbacks.sel_collection != *it) {
613 this->callbacks.sel_collection = *it;
615 this->InvalidateData(PickerInvalidation::Position);
616 }
617 SetWidgetsDisabledState(this->callbacks.sel_collection == "" ? true : false, WID_PW_COLEC_RENAME, WID_PW_COLEC_DELETE);
618
619 SndClickBeep();
620 break;
621 }
622
624 bool reopen = HandleBadgeConfigurationDropDownClick(this->callbacks.GetFeature(), 1, index, click_result, this->badge_filter_choices);
625
626 this->ReInit();
627
628 if (reopen) {
629 ReplaceDropDownList(this, BuildBadgeClassConfigurationList(this->badge_classes, 1, {}, COLOUR_DARK_GREEN), -1);
630 } else {
632 }
633
634 /* We need to refresh if a filter is removed. */
636 break;
637 }
638
639 default:
640 if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
641 if (index < 0) {
642 ResetBadgeFilter(this->badge_filter_choices, this->GetWidget<NWidgetBadgeFilter>(widget)->GetBadgeClassID());
643 } else {
644 SetBadgeFilter(this->badge_filter_choices, BadgeID(index));
645 }
647 }
648 break;
649 }
650}
651
652void PickerWindow::OnInvalidateData(int data, bool gui_scope)
653{
654 if (!gui_scope) return;
655
656 PickerInvalidations pi(data);
657
659 if (this->badge_filter_choices.empty()) {
660 this->type_string_filter.bdf.reset();
661 } else {
662 this->type_string_filter.bdf.emplace(this->badge_filter_choices);
663 }
664 this->types.SetFilterState(!type_string_filter.IsEmpty() || type_string_filter.bdf.has_value());
665 }
666
667 if (pi.Test(PickerInvalidation::Class)) this->classes.ForceRebuild();
668 if (pi.Test(PickerInvalidation::Type)) this->types.ForceRebuild();
669 if (pi.Test(PickerInvalidation::Collection)) this->collections.ForceRebuild();
670
671 this->BuildPickerClassList();
672 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedClassIsValid();
673 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedClassIsVisible();
674
675 this->BuildPickerTypeList();
676 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedTypeIsValid();
677 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedTypeIsVisible();
678
680
681 if (this->has_type_picker) {
682 SetWidgetLoweredState(WID_PW_MODE_ALL, HasBit(this->callbacks.mode, PFM_ALL));
683 SetWidgetLoweredState(WID_PW_MODE_USED, HasBit(this->callbacks.mode, PFM_USED));
685 }
686
689}
690
692{
693 switch (hotkey) {
695 /* Cycle between the two edit boxes. */
696 if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_TYPE_FILTER)) {
698 } else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_CLASS_FILTER)) {
700 }
701 SetFocusedWindow(this);
702 return ES_HANDLED;
703
704 default:
705 return ES_NOT_HANDLED;
706 }
707}
708
710{
711 switch (wid) {
713 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
714 this->classes.SetFilterState(!class_string_filter.IsEmpty());
715 this->InvalidateData(PickerInvalidation::Class);
716 break;
717
719 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
720 if (!type_string_filter.IsEmpty()) {
721 this->type_string_filter.btf.emplace(this->type_string_filter, this->callbacks.GetFeature());
722 } else {
723 this->type_string_filter.btf.reset();
724 }
726 break;
727
728 default:
729 break;
730 }
731}
732
735{
736 if (!this->classes.NeedRebuild()) return;
737
738 int count = this->callbacks.GetClassCount();
739
740 this->classes.clear();
741 this->classes.reserve(count);
742
743 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
744 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
745 for (int i = 0; i < count; i++) {
746 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
747 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;
748 if (filter_saved && this->callbacks.saved.find(this->callbacks.sel_collection) == this->callbacks.saved.end()) continue;
749 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;
750 this->classes.emplace_back(i);
751 }
752
753 this->classes.Filter(this->class_string_filter);
754 this->classes.RebuildDone();
755 this->classes.Sort();
756
757 if (!this->has_class_picker) return;
758 this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size());
759}
760
761void PickerWindow::EnsureSelectedClassIsValid()
762{
763 int class_index = this->callbacks.GetSelectedClass();
764 if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return;
765
766 if (!this->classes.empty()) {
767 class_index = this->classes.front();
768 } else {
769 /* Classes can be empty if filters are enabled, find the first usable class. */
770 int count = this->callbacks.GetClassCount();
771 for (int i = 0; i < count; i++) {
772 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
773 class_index = i;
774 break;
775 }
776 }
777
778 this->callbacks.SetSelectedClass(class_index);
779 this->types.ForceRebuild();
780}
781
782void PickerWindow::EnsureSelectedClassIsVisible()
783{
784 if (!this->has_class_picker) return;
785 if (this->classes.empty()) return;
786
787 auto it = std::ranges::find(this->classes, this->callbacks.GetSelectedClass());
788 if (it == std::end(this->classes)) return;
789
790 int pos = static_cast<int>(std::distance(std::begin(this->classes), it));
792}
793
794void PickerWindow::RefreshUsedTypeList()
795{
796 if (!this->has_type_picker) return;
797
798 this->callbacks.used.clear();
799 this->callbacks.FillUsedItems(this->callbacks.used);
800 this->InvalidateData(PickerInvalidation::Type);
801}
802
805{
806 if (!this->types.NeedRebuild()) return;
807
808 this->types.clear();
809
810 bool show_all = HasBit(this->callbacks.mode, PFM_ALL);
811 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
812 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
813 int cls_id = this->callbacks.GetSelectedClass();
814
815 if (filter_used) {
816 /* Showing used items. May also be filtered by saved items. */
817 this->types.reserve(this->callbacks.used.size());
818 for (const PickerItem &item : this->callbacks.used) {
819 if (!show_all && item.class_index != cls_id) continue;
820 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
821 this->types.emplace_back(item);
822 }
823 } else if (filter_saved && this->callbacks.saved.contains(this->callbacks.sel_collection)) {
824 /* Showing only saved items. */
825 this->types.reserve(std::size(this->callbacks.saved.at(this->callbacks.sel_collection)));
826 for (const PickerItem &item : this->callbacks.saved.at(this->callbacks.sel_collection)) {
827 /* The used list may contain items that aren't currently loaded, skip these. */
828 if (item.class_index == -1) continue;
829 if (!show_all && item.class_index != cls_id) continue;
830 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
831 this->types.emplace_back(item);
832 }
833 } else if (show_all) {
834 /* Reserve enough space for everything. */
835 int total = 0;
836 for (int class_index : this->classes) total += this->callbacks.GetTypeCount(class_index);
837 this->types.reserve(total);
838 /* Add types in all classes. */
839 for (int class_index : this->classes) {
840 int count = this->callbacks.GetTypeCount(class_index);
841 for (int i = 0; i < count; i++) {
842 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
843 this->types.emplace_back(this->callbacks.GetPickerItem(class_index, i));
844 }
845 }
846 } else {
847 /* Add types in only the selected class. */
848 if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) {
849 int count = this->callbacks.GetTypeCount(cls_id);
850 this->types.reserve(count);
851 for (int i = 0; i < count; i++) {
852 if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue;
853 this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i));
854 }
855 }
856 }
857
858 this->types.Filter(this->type_string_filter);
859 this->types.RebuildDone();
860 this->types.Sort();
861
862 if (!this->has_type_picker) return;
863 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetCount(static_cast<int>(this->types.size()));
864}
865
866void PickerWindow::EnsureSelectedTypeIsValid()
867{
868 int class_index = this->callbacks.GetSelectedClass();
869 int index = this->callbacks.GetSelectedType();
870 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;
871
872 if (!this->types.empty()) {
873 class_index = this->types.front().class_index;
874 index = this->types.front().index;
875 } else {
876 /* Types can be empty if filters are enabled, find the first usable type. */
877 int count = this->callbacks.GetTypeCount(class_index);
878 for (int i = 0; i < count; i++) {
879 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
880 index = i;
881 break;
882 }
883 }
884 this->callbacks.SetSelectedClass(class_index);
885 this->callbacks.SetSelectedType(index);
886}
887
888void PickerWindow::EnsureSelectedTypeIsVisible()
889{
890 if (!this->has_type_picker) return;
891 if (this->types.empty()) {
892 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(-1);
893 return;
894 }
895
896 int class_index = this->callbacks.GetSelectedClass();
897 int index = this->callbacks.GetSelectedType();
898
899 auto it = std::ranges::find_if(this->types, [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; });
900 int pos = -1;
901 if (it != std::end(this->types)) {
902 pos = static_cast<int>(std::distance(std::begin(this->types), it));
903 }
904
905 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(pos);
906}
907
910{
911 if (!this->collections.NeedRebuild()) return;
912
913 int count = std::max(static_cast<int>(this->callbacks.saved.size()), 1);
914
915 this->collections.clear();
916 this->collections.reserve(count);
917
918 if (this->callbacks.saved.find("") == this->callbacks.saved.end()) {
919 this->collections.emplace_back("");
920 }
921
922 for (auto it = this->callbacks.saved.begin(); it != this->callbacks.saved.end(); it++) {
923 this->collections.emplace_back(it->first);
924 }
925
926 this->collections.RebuildDone();
927 this->collections.Sort();
928
929 if (!this->has_class_picker) return;
930}
931
933std::unique_ptr<NWidgetBase> MakePickerClassWidgets()
934{
935 static constexpr std::initializer_list<NWidgetPart> picker_class_widgets = {
936 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_CLASS_SEL),
938 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
939 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),
940 EndContainer(),
941 /* Collection view */
944 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),
945 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),
946 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),
947 EndContainer(),
948 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),
949 EndContainer(),
950 /* Class view */
953 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
956 EndContainer(),
957 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_CLASS_SCROLL),
958 EndContainer(),
959 EndContainer(),
960 EndContainer(),
961 EndContainer(),
962 };
963
964 return MakeNWidgets(picker_class_widgets, nullptr);
965}
966
968std::unique_ptr<NWidgetBase> MakePickerTypeWidgets()
969{
970 static constexpr std::initializer_list<NWidgetPart> picker_type_widgets = {
971 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_TYPE_SEL),
974 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
975 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),
976 EndContainer(),
977 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),
978 EndContainer(),
980 EndContainer(),
982 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),
983 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),
984 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),
985 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),
986 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),
987 EndContainer(),
989 NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_PW_TYPE_SCROLL),
992 EndContainer(),
993 EndContainer(),
994 EndContainer(),
995 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_TYPE_SCROLL),
996 EndContainer(),
998 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1000 EndContainer(),
1001 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_RESIZE),
1002 EndContainer(),
1003 EndContainer(),
1004 EndContainer(),
1005 };
1006
1007 return MakeNWidgets(picker_type_widgets, nullptr);
1008}
1009
1010void InvalidateAllPickerWindows()
1011{
1012 InvalidateWindowClassesData(WC_BUS_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1013 InvalidateWindowClassesData(WC_TRUCK_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1014 InvalidateWindowClassesData(WC_SELECT_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
1015 InvalidateWindowClassesData(WC_BUILD_WAYPOINT, PickerWindow::PICKER_INVALIDATION_ALL);
1016 InvalidateWindowClassesData(WC_BUILD_OBJECT, PickerWindow::PICKER_INVALIDATION_ALL);
1017 InvalidateWindowClassesData(WC_BUILD_HOUSE, PickerWindow::PICKER_INVALIDATION_ALL);
1018}
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 ~PickerCallbacks()
Ensure the destructor of the sub classes are called as well.
virtual int GetSelectedClass() const =0
Get the index of the selected class.
std::string edit_collection
Collection to rename or delete.
Definition picker_gui.h:208
const std::string ini_group
Ini Group for saving favourites.
Definition picker_gui.h:204
std::string sel_collection
Currently selected collection of saved items.
Definition picker_gui.h:207
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:209
std::map< std::string, std::set< PickerItem > > saved
Set of saved collections of items.
Definition picker_gui.h:214
virtual int GetSelectedType() const =0
Get the selected type.
Base class for windows opened from a toolbar.
Definition window_gui.h:999
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3628
static constexpr int MAX_PREVIEW_HEIGHT
Maximum height of each preview button.
Definition picker_gui.h:304
static constexpr int PREVIEW_WIDTH
Width of each preview button.
Definition picker_gui.h:298
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:311
bool has_class_picker
Set if this window has a class picker 'component'.
Definition picker_gui.h:308
static constexpr int PREVIEW_HEIGHT
Height of each preview button.
Definition picker_gui.h:299
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
@ Position
Update scroll positions.
Definition picker_gui.h:290
@ Class
Refresh the class list.
Definition picker_gui.h:287
@ Type
Refresh the type list.
Definition picker_gui.h:288
@ Validate
Validate selected item.
Definition picker_gui.h:291
@ Collection
Refresh the collection list.
Definition picker_gui.h:289
@ Filter
Update filter state.
Definition picker_gui.h:292
bool has_collection_picker
Set if this window has a collection picker 'component'.
Definition picker_gui.h:310
static constexpr int PREVIEW_LEFT
Offset from left edge to draw preview.
Definition picker_gui.h:300
PickerTypeList types
List of types.
Definition picker_gui.h:351
std::set< std::string > inactive
Set of collections with inactive items.
Definition picker_gui.h:312
QueryString class_editbox
Filter editbox.
Definition picker_gui.h:345
bool has_type_picker
Set if this window has a type picker 'component'.
Definition picker_gui.h:309
void BuildPickerClassList()
Builds the filter list of classes.
@ PFM_USED
Show used types.
Definition picker_gui.h:281
@ PFM_ALL
Show all classes.
Definition picker_gui.h:280
@ PFM_SAVED
Show saved types.
Definition picker_gui.h:282
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:301
@ PCWHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
Definition picker_gui.h:332
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:360
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:353
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:343
static constexpr int STEP_PREVIEW_HEIGHT
Step for decreasing or increase preview button height.
Definition picker_gui.h:303
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:2504
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.
std::unique_ptr< DropDownListItem > MakeDropDownListStringItem(StringID str, int value, bool masked, bool shaded)
Creates new DropDownListStringItem.
Definition dropdown.cpp:49
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, DropDownOptions options, std::string *const persistent_filter_text)
Show a drop down list.
Definition dropdown.cpp:585
Functions related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
@ Persist
Set if this dropdown should stay open after an option is selected.
@ Filterable
Set if the dropdown is filterable.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp: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:3379
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:980
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:393
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:572
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition string.cpp:429
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition string_type.h:25
Searching and filtering using a stringterm.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
static const int MAX_CHAR_LENGTH
Max. length of UTF-8 encoded unicode character.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
T y
Y coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
Ini file that supports both loading and saving.
Definition ini_type.h:87
A group within an ini file.
Definition ini_type.h:34
void Clear()
Clear all items in the group.
Definition ini_load.cpp:96
std::string name
name of group
Definition ini_type.h:37
IniItem & CreateItem(std::string_view name)
Create an item with the given name.
Definition ini_load.cpp:79
std::list< IniItem > items
all items in the group
Definition ini_type.h:35
A single "line" in an ini file.
Definition ini_type.h:23
std::string name
The name of this item.
Definition ini_type.h:24
std::list< IniGroup > groups
all groups in the ini
Definition ini_type.h:53
void RemoveGroup(std::string_view name)
Remove the group with the given name.
Definition ini_load.cpp:173
IniGroup & GetOrCreateGroup(std::string_view name)
Get the group with the given name, and if it doesn't exist create a new group.
Definition ini_load.cpp:145
const PickerCallbacks * callbacks
Callbacks for filter functions to access to callbacks.
Definition picker_gui.h:268
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:992
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1822
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:518
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1089
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:1812
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:499
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:990
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:327
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:1209
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:435
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:3339
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