OpenTTD Source 15.0-beta2
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 <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "gui.h"
14#include "hotkeys.h"
15#include "ini_type.h"
16#include "newgrf_badge.h"
17#include "picker_gui.h"
18#include "querystring_gui.h"
19#include "settings_type.h"
20#include "sortlist_type.h"
21#include "sound_func.h"
22#include "sound_type.h"
23#include "string_func.h"
24#include "stringfilter_type.h"
25#include "strings_func.h"
26#include "widget_type.h"
27#include "window_func.h"
28#include "window_gui.h"
29#include "window_type.h"
30#include "zoom_func.h"
31
33
34#include "table/sprites.h"
35#include "table/strings.h"
36
37#include <charconv>
38
39#include "safeguards.h"
40
41static std::vector<PickerCallbacks *> &GetPickerCallbacks()
42{
43 static std::vector<PickerCallbacks *> callbacks;
44 return callbacks;
45}
46
47PickerCallbacks::PickerCallbacks(const std::string &ini_group) : ini_group(ini_group)
48{
49 GetPickerCallbacks().push_back(this);
50}
51
52PickerCallbacks::~PickerCallbacks()
53{
54 auto &callbacks = GetPickerCallbacks();
55 callbacks.erase(std::ranges::find(callbacks, this));
56}
57
63static void PickerLoadConfig(const IniFile &ini, PickerCallbacks &callbacks)
64{
65 const IniGroup *group = ini.GetGroup(callbacks.ini_group);
66 if (group == nullptr) return;
67
68 callbacks.saved.clear();
69 for (const IniItem &item : group->items) {
70 std::array<uint8_t, 4> grfid_buf;
71
72 std::string_view str = item.name;
73
74 /* Try reading "<grfid>|<localid>" */
75 auto grfid_pos = str.find('|');
76 if (grfid_pos == std::string_view::npos) continue;
77
78 std::string_view grfid_str = str.substr(0, grfid_pos);
79 if (!ConvertHexToBytes(grfid_str, grfid_buf)) continue;
80
81 str = str.substr(grfid_pos + 1);
82 uint32_t grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
83 uint16_t localid;
84 auto [ptr, err] = std::from_chars(str.data(), str.data() + str.size(), localid);
85
86 if (err == std::errc{} && ptr == str.data() + str.size()) {
87 callbacks.saved.insert({grfid, localid, 0, 0});
88 }
89 }
90}
91
97static void PickerSaveConfig(IniFile &ini, const PickerCallbacks &callbacks)
98{
99 IniGroup &group = ini.GetOrCreateGroup(callbacks.ini_group);
100 group.Clear();
101
102 for (const PickerItem &item : callbacks.saved) {
103 std::string key = fmt::format("{:08X}|{}", std::byteswap(item.grfid), item.local_id);
104 group.CreateItem(key);
105 }
106}
107
112void PickerLoadConfig(const IniFile &ini)
113{
114 for (auto *cb : GetPickerCallbacks()) PickerLoadConfig(ini, *cb);
115}
116
122{
123 for (const auto *cb : GetPickerCallbacks()) PickerSaveConfig(ini, *cb);
124}
125
127static bool ClassIDSorter(int const &a, int const &b)
128{
129 return a < b;
130}
131
133static bool ClassTagNameFilter(int const *item, PickerFilterData &filter)
134{
135 filter.ResetState();
136 filter.AddLine(GetString(filter.callbacks->GetClassName(*item)));
137 return filter.GetState();
138}
139
141static bool TypeIDSorter(PickerItem const &a, PickerItem const &b)
142{
143 int r = a.class_index - b.class_index;
144 if (r == 0) r = a.index - b.index;
145 return r < 0;
146}
147
149static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter)
150{
151 if (filter.btf.has_value() && filter.btf->Filter(filter.callbacks->GetTypeBadges(item->class_index, item->index))) return true;
152
153 filter.ResetState();
154 filter.AddLine(GetString(filter.callbacks->GetTypeName(item->class_index, item->index)));
155 return filter.GetState();
156}
157
158static const std::initializer_list<PickerClassList::SortFunction * const> _class_sorter_funcs = { &ClassIDSorter };
159static const std::initializer_list<PickerClassList::FilterFunction * const> _class_filter_funcs = { &ClassTagNameFilter };
160static const std::initializer_list<PickerTypeList::SortFunction * const> _type_sorter_funcs = { TypeIDSorter };
161static const std::initializer_list<PickerTypeList::FilterFunction * const> _type_filter_funcs = { TypeTagNameFilter };
162
163PickerWindow::PickerWindow(WindowDesc &desc, Window *parent, int window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks),
164 class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE),
165 type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
166{
167 this->window_number = window_number;
168
169 /* Init of nested tree is deferred.
170 * PickerWindow::ConstructWindow must be called by the inheriting window. */
171}
172
173void PickerWindow::ConstructWindow()
174{
175 this->CreateNestedTree();
176
177 /* Test if pickers should be active.*/
178 bool is_active = this->callbacks.IsActive();
179
180 /* Functionality depends on widgets being present, not window class. */
181 this->has_class_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice();
182 this->has_type_picker = is_active && this->GetWidget<NWidgetBase>(WID_PW_TYPE_MATRIX) != nullptr;
183
184 if (this->has_class_picker) {
185 this->GetWidget<NWidgetCore>(WID_PW_CLASS_LIST)->SetToolTip(this->callbacks.GetClassTooltip());
186
188 } else {
189 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_CLASS_SEL); nwid != nullptr) {
190 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
191 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
192 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
193 }
194 }
195
197 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
198 this->class_string_filter.callbacks = &this->callbacks;
199
200 this->classes.SetListing(this->callbacks.class_last_sorting);
201 this->classes.SetFiltering(this->callbacks.class_last_filtering);
202 this->classes.SetSortFuncs(_class_sorter_funcs);
203 this->classes.SetFilterFuncs(_class_filter_funcs);
204
205 /* Update saved type information. */
206 this->callbacks.saved = this->callbacks.UpdateSavedItems(this->callbacks.saved);
207
208 /* Clear used type information. */
209 this->callbacks.used.clear();
210
211 if (this->has_type_picker) {
212 /* Populate used type information. */
213 this->callbacks.FillUsedItems(this->callbacks.used);
214
216
217 this->GetWidget<NWidgetCore>(WID_PW_TYPE_ITEM)->SetToolTip(this->callbacks.GetTypeTooltip());
218
219 auto *matrix = this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX);
220 matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL));
221
223 } else {
224 if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_TYPE_SEL); nwid != nullptr) {
225 /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
226 bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
227 nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
228 }
229 }
230
232 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
233 this->type_string_filter.callbacks = &this->callbacks;
234
235 this->types.SetListing(this->callbacks.type_last_sorting);
236 this->types.SetFiltering(this->callbacks.type_last_filtering);
237 this->types.SetSortFuncs(_type_sorter_funcs);
238 this->types.SetFilterFuncs(_type_filter_funcs);
239
240 this->FinishInitNested(this->window_number);
241
242 this->InvalidateData(PICKER_INVALIDATION_ALL);
243}
244
246{
247 this->badge_classes = GUIBadgeClasses(this->callbacks.GetFeature());
248}
249
251{
252 this->callbacks.Close(data);
253 this->PickerWindowBase::Close(data);
254}
255
256void PickerWindow::UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
257{
258 switch (widget) {
259 /* Class picker */
261 resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
262 size.height = 5 * resize.height;
263 break;
264
265 /* Type picker */
267 /* At least two items wide. */
268 size.width += resize.width;
269 fill.width = resize.width;
270 fill.height = 1;
271
272 /* Resizing in X direction only at blob size, but at pixel level in Y. */
273 resize.height = 1;
274 break;
275
276 /* Type picker */
277 case WID_PW_TYPE_ITEM:
280 break;
281 }
282}
283
284void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
285{
286 switch (widget) {
287 /* Class picker */
288 case WID_PW_CLASS_LIST: {
289 Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
290 const int selected = this->callbacks.GetSelectedClass();
291 const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL);
292 const int y_step = this->GetWidget<NWidgetResizeBase>(widget)->resize_y;
293 auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes);
294 for (auto it = first; it != last; ++it) {
295 DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TC_WHITE : TC_BLACK);
296 ir.top += y_step;
297 }
298 break;
299 }
300
301 /* Type picker */
302 case WID_PW_TYPE_ITEM: {
303 assert(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement() < static_cast<int>(this->types.size()));
304 const auto &item = this->types[this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement()];
305
306 DrawPixelInfo tmp_dpi;
308 if (FillDrawPixelInfo(&tmp_dpi, ir)) {
309 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
312
313 this->callbacks.DrawType(x, y, item.class_index, item.index);
314
315 int by = ir.Height() - ScaleGUITrad(12);
316
317 GrfSpecFeature feature = this->callbacks.GetFeature();
318 DrawBadgeColumn({0, by, ir.Width() - 1, ir.Height() - 1}, 0, this->badge_classes, this->callbacks.GetTypeBadges(item.class_index, item.index), feature, std::nullopt, PAL_NONE);
319
320 if (this->callbacks.saved.contains(item)) {
321 DrawSprite(SPR_BLOT, PALETTE_TO_YELLOW, 0, 0);
322 }
323 if (this->callbacks.used.contains(item)) {
324 DrawSprite(SPR_BLOT, PALETTE_TO_GREEN, ir.Width() - GetSpriteSize(SPR_BLOT).width, 0);
325 }
326 }
327
328 if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
329 GfxFillRect(ir, GetColourGradient(COLOUR_GREY, SHADE_DARKER), FILLRECT_CHECKER);
330 }
331 break;
332 }
333
334 case WID_PW_TYPE_NAME:
335 DrawString(r, this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType()), TC_ORANGE, SA_CENTER);
336 break;
337 }
338}
339
346
348{
349 switch (widget) {
350 /* Class Picker */
351 case WID_PW_CLASS_LIST: {
352 const auto vscroll = this->GetWidget<NWidgetScrollbar>(WID_PW_CLASS_SCROLL);
353 auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST);
354 if (it == this->classes.end()) return;
355
356 if (this->callbacks.GetSelectedClass() != *it || HasBit(this->callbacks.mode, PFM_ALL)) {
357 ClrBit(this->callbacks.mode, PFM_ALL); // Disable showing all.
358 this->callbacks.SetSelectedClass(*it);
360 }
363 break;
364 }
365
366 case WID_PW_MODE_ALL:
367 case WID_PW_MODE_USED:
369 ToggleBit(this->callbacks.mode, widget - WID_PW_MODE_ALL);
370 if (!this->IsWidgetDisabled(WID_PW_MODE_ALL) && HasBit(this->callbacks.mode, widget - WID_PW_MODE_ALL)) {
371 /* Enabling used or saved filters automatically enables all. */
372 SetBit(this->callbacks.mode, PFM_ALL);
373 }
375 break;
376
377 /* Type Picker */
378 case WID_PW_TYPE_ITEM: {
379 int sel = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
380 assert(sel < (int)this->types.size());
381 const auto &item = this->types[sel];
382
383 if (_ctrl_pressed) {
384 auto it = this->callbacks.saved.find(item);
385 if (it == std::end(this->callbacks.saved)) {
386 this->callbacks.saved.insert(item);
387 } else {
388 this->callbacks.saved.erase(it);
389 }
390 this->InvalidateData(PickerInvalidation::Type);
391 break;
392 }
393
394 if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
395 this->callbacks.SetSelectedClass(item.class_index);
396 this->callbacks.SetSelectedType(item.index);
397 this->InvalidateData(PickerInvalidation::Position);
398 }
401 break;
402 }
403 }
404}
405
406void PickerWindow::OnInvalidateData(int data, bool gui_scope)
407{
408 if (!gui_scope) return;
409
410 PickerInvalidations pi(data);
413
414 this->BuildPickerClassList();
415 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedClassIsValid();
416 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedClassIsVisible();
417
418 this->BuildPickerTypeList();
419 if (pi.Test(PickerInvalidation::Validate)) this->EnsureSelectedTypeIsValid();
420 if (pi.Test(PickerInvalidation::Position)) this->EnsureSelectedTypeIsVisible();
421
422 if (this->has_type_picker) {
426 }
427}
428
430{
431 switch (hotkey) {
433 /* Cycle between the two edit boxes. */
434 if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_TYPE_FILTER)) {
436 } else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->GetIndex() != WID_PW_CLASS_FILTER)) {
438 }
439 SetFocusedWindow(this);
440 return ES_HANDLED;
441
442 default:
443 return ES_NOT_HANDLED;
444 }
445}
446
448{
449 switch (wid) {
451 this->class_string_filter.SetFilterTerm(this->class_editbox.text.GetText());
452 this->classes.SetFilterState(!class_string_filter.IsEmpty());
453 this->InvalidateData(PickerInvalidation::Class);
454 break;
455
457 this->type_string_filter.SetFilterTerm(this->type_editbox.text.GetText());
458 if (!type_string_filter.IsEmpty()) {
459 this->type_string_filter.btf.emplace(this->type_string_filter, this->callbacks.GetFeature());
460 } else {
461 this->type_string_filter.btf.reset();
462 }
463 this->types.SetFilterState(!type_string_filter.IsEmpty());
464 this->InvalidateData(PickerInvalidation::Type);
465 break;
466
467 default:
468 break;
469 }
470}
471
474{
475 if (!this->classes.NeedRebuild()) return;
476
477 int count = this->callbacks.GetClassCount();
478
479 this->classes.clear();
480 this->classes.reserve(count);
481
482 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
483 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
484 for (int i = 0; i < count; i++) {
485 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
486 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;
487 if (filter_saved && std::none_of(std::begin(this->callbacks.saved), std::end(this->callbacks.saved), [i](const PickerItem &item) { return item.class_index == i; })) continue;
488 this->classes.emplace_back(i);
489 }
490
491 this->classes.Filter(this->class_string_filter);
492 this->classes.RebuildDone();
493 this->classes.Sort();
494
495 if (!this->has_class_picker) return;
496 this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size());
497}
498
499void PickerWindow::EnsureSelectedClassIsValid()
500{
501 int class_index = this->callbacks.GetSelectedClass();
502 if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return;
503
504 if (!this->classes.empty()) {
505 class_index = this->classes.front();
506 } else {
507 /* Classes can be empty if filters are enabled, find the first usable class. */
508 int count = this->callbacks.GetClassCount();
509 for (int i = 0; i < count; i++) {
510 if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
511 class_index = i;
512 break;
513 }
514 }
515
516 this->callbacks.SetSelectedClass(class_index);
517 this->types.ForceRebuild();
518}
519
520void PickerWindow::EnsureSelectedClassIsVisible()
521{
522 if (!this->has_class_picker) return;
523 if (this->classes.empty()) return;
524
525 auto it = std::ranges::find(this->classes, this->callbacks.GetSelectedClass());
526 if (it == std::end(this->classes)) return;
527
528 int pos = static_cast<int>(std::distance(std::begin(this->classes), it));
530}
531
532void PickerWindow::RefreshUsedTypeList()
533{
534 if (!this->has_type_picker) return;
535
536 this->callbacks.used.clear();
537 this->callbacks.FillUsedItems(this->callbacks.used);
538 this->InvalidateData(PickerInvalidation::Type);
539}
540
543{
544 if (!this->types.NeedRebuild()) return;
545
546 this->types.clear();
547
548 bool show_all = HasBit(this->callbacks.mode, PFM_ALL);
549 bool filter_used = HasBit(this->callbacks.mode, PFM_USED);
550 bool filter_saved = HasBit(this->callbacks.mode, PFM_SAVED);
551 int cls_id = this->callbacks.GetSelectedClass();
552
553 if (filter_used) {
554 /* Showing used items. May also be filtered by saved items. */
555 this->types.reserve(this->callbacks.used.size());
556 for (const PickerItem &item : this->callbacks.used) {
557 if (!show_all && item.class_index != cls_id) continue;
558 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
559 this->types.emplace_back(item);
560 }
561 } else if (filter_saved) {
562 /* Showing only saved items. */
563 this->types.reserve(this->callbacks.saved.size());
564 for (const PickerItem &item : this->callbacks.saved) {
565 /* The used list may contain items that aren't currently loaded, skip these. */
566 if (item.class_index == -1) continue;
567 if (!show_all && item.class_index != cls_id) continue;
568 if (this->callbacks.GetTypeName(item.class_index, item.index) == INVALID_STRING_ID) continue;
569 this->types.emplace_back(item);
570 }
571 } else if (show_all) {
572 /* Reserve enough space for everything. */
573 int total = 0;
574 for (int class_index : this->classes) total += this->callbacks.GetTypeCount(class_index);
575 this->types.reserve(total);
576 /* Add types in all classes. */
577 for (int class_index : this->classes) {
578 int count = this->callbacks.GetTypeCount(class_index);
579 for (int i = 0; i < count; i++) {
580 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
581 this->types.emplace_back(this->callbacks.GetPickerItem(class_index, i));
582 }
583 }
584 } else {
585 /* Add types in only the selected class. */
586 if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) {
587 int count = this->callbacks.GetTypeCount(cls_id);
588 this->types.reserve(count);
589 for (int i = 0; i < count; i++) {
590 if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue;
591 this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i));
592 }
593 }
594 }
595
596 this->types.Filter(this->type_string_filter);
597 this->types.RebuildDone();
598 this->types.Sort();
599
600 if (!this->has_type_picker) return;
601 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetCount(static_cast<int>(this->types.size()));
602}
603
604void PickerWindow::EnsureSelectedTypeIsValid()
605{
606 int class_index = this->callbacks.GetSelectedClass();
607 int index = this->callbacks.GetSelectedType();
608 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;
609
610 if (!this->types.empty()) {
611 class_index = this->types.front().class_index;
612 index = this->types.front().index;
613 } else {
614 /* Types can be empty if filters are enabled, find the first usable type. */
615 int count = this->callbacks.GetTypeCount(class_index);
616 for (int i = 0; i < count; i++) {
617 if (this->callbacks.GetTypeName(class_index, i) == INVALID_STRING_ID) continue;
618 index = i;
619 break;
620 }
621 }
622 this->callbacks.SetSelectedClass(class_index);
623 this->callbacks.SetSelectedType(index);
624}
625
626void PickerWindow::EnsureSelectedTypeIsVisible()
627{
628 if (!this->has_type_picker) return;
629 if (this->types.empty()) {
630 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(-1);
631 return;
632 }
633
634 int class_index = this->callbacks.GetSelectedClass();
635 int index = this->callbacks.GetSelectedType();
636
637 auto it = std::ranges::find_if(this->types, [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; });
638 if (it == std::end(this->types)) return;
639
640 int pos = static_cast<int>(std::distance(std::begin(this->types), it));
641 this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(pos);
642}
643
645std::unique_ptr<NWidgetBase> MakePickerClassWidgets()
646{
647 static constexpr NWidgetPart picker_class_widgets[] = {
648 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_CLASS_SEL),
650 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
651 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),
652 EndContainer(),
654 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
657 EndContainer(),
658 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_CLASS_SCROLL),
659 EndContainer(),
660 EndContainer(),
661 EndContainer(),
662 };
663
664 return MakeNWidgets(picker_class_widgets, nullptr);
665}
666
668std::unique_ptr<NWidgetBase> MakePickerTypeWidgets()
669{
670 static constexpr NWidgetPart picker_type_widgets[] = {
671 NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_TYPE_SEL),
673 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
674 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),
675 EndContainer(),
677 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),
678 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),
679 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),
680 EndContainer(),
682 NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_PW_TYPE_SCROLL),
685 EndContainer(),
686 EndContainer(),
687 EndContainer(),
688 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_TYPE_SCROLL),
689 EndContainer(),
691 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
693 EndContainer(),
694 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_RESIZE),
695 EndContainer(),
696 EndContainer(),
697 EndContainer(),
698 };
699
700 return MakeNWidgets(picker_type_widgets, nullptr);
701}
702
703void InvalidateAllPickerWindows()
704{
705 InvalidateWindowClassesData(WC_BUS_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
706 InvalidateWindowClassesData(WC_TRUCK_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
707 InvalidateWindowClassesData(WC_SELECT_STATION, PickerWindow::PICKER_INVALIDATION_ALL);
708 InvalidateWindowClassesData(WC_BUILD_WAYPOINT, PickerWindow::PICKER_INVALIDATION_ALL);
709 InvalidateWindowClassesData(WC_BUILD_OBJECT, PickerWindow::PICKER_INVALIDATION_ALL);
710 InvalidateWindowClassesData(WC_BUILD_HOUSE, PickerWindow::PICKER_INVALIDATION_ALL);
711}
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Enum-as-bit-set wrapper.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetFiltering(Filtering f)
Import filter conditions.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
WidgetID GetIndex() const
Get the WidgetID of this nested widget.
Definition widget.cpp:1234
Matrix container with implicitly equal sized (virtual) sub-widgets.
Class for PickerClassWindow to collect information and retain state.
Definition picker_gui.h:38
virtual int GetSelectedClass() const =0
Get the index of the selected class.
Filtering type_last_filtering
Default filtering of PickerTypeList.
Definition picker_gui.h:93
virtual PickerItem GetPickerItem(int cls_id, int id) const =0
Get data about an item.
const std::string ini_group
Ini Group for saving favourites.
Definition picker_gui.h:95
virtual void SetSelectedClass(int id) const =0
Set the selected class.
virtual bool IsActive() const =0
Should picker class/type selection be enabled?
virtual StringID GetTypeName(int cls_id, int id) const =0
Get the item of a type.
virtual bool IsTypeAvailable(int cls_id, int id) const =0
Test if an item is currently buildable.
virtual void FillUsedItems(std::set< PickerItem > &items)=0
Fill a set with all items that are used by the current player.
virtual StringID GetTypeTooltip() const =0
Get the tooltip string for the type grid.
virtual bool HasClassChoice() const =0
Are there multiple classes to chose from?
Listing type_last_sorting
Default sorting of PickerTypeList.
Definition picker_gui.h:92
std::set< PickerItem > used
Set of items used in the current game by the current company.
Definition picker_gui.h:98
virtual StringID GetClassTooltip() const =0
Get the tooltip string for the class list.
virtual int GetTypeCount(int cls_id) const =0
Get the number of types in a class.
virtual int GetClassCount() const =0
Get the number of classes.
virtual void SetSelectedType(int id) const =0
Set the selected type.
virtual StringID GetClassName(int id) const =0
Get the name of a class.
virtual std::set< PickerItem > UpdateSavedItems(const std::set< PickerItem > &src)=0
Update link between grfid/localidx and class_index/index in saved items.
virtual void DrawType(int x, int y, int cls_id, int id) const =0
Draw preview image of an item.
virtual std::span< const BadgeID > GetTypeBadges(int cls_id, int id) const =0
Get the item of a type.
Filtering class_last_filtering
Default filtering of PickerClassList.
Definition picker_gui.h:90
virtual int GetSelectedType() const =0
Get the selected type.
uint8_t mode
Bitmask of PickerFilterModes.
Definition picker_gui.h:96
std::set< PickerItem > saved
Set of saved favourite items.
Definition picker_gui.h:99
Listing class_last_sorting
Default sorting of PickerClassList.
Definition picker_gui.h:89
Base class for windows opened from a toolbar.
Definition window_gui.h:982
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:3529
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
bool has_class_picker
Set if this window has a class picker 'component'.
Definition picker_gui.h:178
static const int PREVIEW_LEFT
Offset from left edge to draw preview.
Definition picker_gui.h:173
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
@ Position
Update scroll positions.
@ Class
Refresh the class list.
@ Type
Refresh the type list.
@ Validate
Validate selected item.
PickerTypeList types
List of types.
Definition picker_gui.h:213
QueryString class_editbox
Filter editbox.
Definition picker_gui.h:207
bool has_type_picker
Set if this window has a type picker 'component'.
Definition picker_gui.h:179
void BuildPickerClassList()
Builds the filter list of classes.
@ PFM_USED
Show used types.
Definition picker_gui.h:157
@ PFM_ALL
Show all classes.
Definition picker_gui.h:156
@ PFM_SAVED
Show saved types.
Definition picker_gui.h:158
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
void BuildPickerTypeList()
Builds the filter list of types.
void OnResize() override
Called after the window got resized.
@ PCWHK_FOCUS_FILTER_BOX
Focus the edit box for editing the filter string.
Definition picker_gui.h:194
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
static const int PREVIEW_WIDTH
Width of each preview button.
Definition picker_gui.h:171
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:215
static const int PREVIEW_BOTTOM
Offset from bottom edge to draw preview.
Definition picker_gui.h:174
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:205
static const int PREVIEW_HEIGHT
Height of each preview button.
Definition picker_gui.h:172
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:2523
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:29
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:94
Functions related to companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:923
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:658
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
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:989
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:1519
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:245
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:387
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:335
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 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 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:3361
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
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.
GUI functions that shouldn't be here.
Hotkey related functions.
Types related to reading/writing '*.ini' files.
GrfSpecFeature
Definition newgrf.h:70
void DrawBadgeColumn(Rect r, int column_group, const GUIBadgeClasses &badge_classes, std::span< const BadgeID > badges, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, PaletteID remap)
Draw a badge column group.
Functions related to NewGRF badges.
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:387
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 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_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_TYPE_MATRIX
Matrix with items.
@ 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_TYPE_SEL
Stack to hide the type picker.
@ WID_PW_TYPE_NAME
Name of selected item.
@ WID_PW_TYPE_RESIZE
Type resize handle.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:58
Types related to global configuration settings.
Base types for having sorted lists in GUIs.
Functions related to sound.
Types related to sounds.
@ SND_15_BEEP
19 == 0x13 GUI button click
Definition sound_type.h:66
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:659
Functions related to low-level strings.
Searching and filtering using a stringterm.
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.
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...
SoundSettings sound
sound effect settings
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:158
Ini file that supports both loading and saving.
Definition ini_type.h:88
A group within an ini file.
Definition ini_type.h:34
void Clear()
Clear all items in the group.
Definition ini_load.cpp:97
IniItem & CreateItem(std::string_view name)
Create an item with the given name.
Definition ini_load.cpp:80
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
const IniGroup * GetGroup(std::string_view name) const
Get the group with the given name.
Definition ini_load.cpp:118
IniGroup & GetOrCreateGroup(std::string_view name)
Get the group with the given name, and if it doesn't exist create a new group.
Definition ini_load.cpp:146
Partial widget specification to allow NWidgets to be written nested.
const PickerCallbacks * callbacks
Callbacks for filter functions to access to callbacks.
Definition picker_gui.h:146
Coordinates of a point in 2D.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
int Height() const
Get height of Rect.
bool click_beep
Beep on a random selection of buttons.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(std::string_view str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
const char * GetText() const
Get the current text.
Definition textbuf.cpp:284
High level window description.
Definition window_gui.h:168
Data structure for an opened window.
Definition window_gui.h:274
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1736
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
ResizeInfo resize
Resize information.
Definition window_gui.h:315
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1726
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:485
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
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:313
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
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
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:48
Definitions about widgets.
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:61
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:65
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:49
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:75
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:67
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:38
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:58
@ NWID_MATRIX
Matrix container.
Definition widget_type.h:68
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:70
@ 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.
@ 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:1143
void SetFocusedWindow(Window *w)
Set the window that has the focus.
Definition window.cpp:421
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:3241
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_TRUCK_STATION
Build truck station; Window numbers:
@ WC_BUS_STATION
Build bus station; 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