diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86abd9f9c1..c913e1f190 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -338,6 +338,8 @@ add_files( palette_func.h pbs.cpp pbs.h + picker_gui.cpp + picker_gui.h progress.cpp progress.h querystring_gui.h diff --git a/src/lang/english.txt b/src/lang/english.txt index 4e804cd9c2..66858310f6 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2785,7 +2785,6 @@ STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP :{BLACK}Select r # Rail waypoint construction window STR_WAYPOINT_CAPTION :{WHITE}Waypoint -STR_WAYPOINT_GRAPHICS_TOOLTIP :{BLACK}Select waypoint type # Rail station construction window STR_STATION_BUILD_RAIL_CAPTION :{WHITE}Rail Station Selection @@ -2798,8 +2797,16 @@ STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP :{BLACK}Select l STR_STATION_BUILD_DRAG_DROP :{BLACK}Drag & Drop STR_STATION_BUILD_DRAG_DROP_TOOLTIP :{BLACK}Build a station using drag & drop -STR_STATION_BUILD_STATION_CLASS_TOOLTIP :{BLACK}Select a station class to display -STR_STATION_BUILD_STATION_TYPE_TOOLTIP :{BLACK}Select the station type to build +STR_PICKER_STATION_CLASS_TOOLTIP :Select a station class to display +STR_PICKER_STATION_TYPE_TOOLTIP :Select a station type to build. Ctrl+Click to add or remove in saved items +STR_PICKER_WAYPOINT_CLASS_TOOLTIP :Select a waypoint class to display +STR_PICKER_WAYPOINT_TYPE_TOOLTIP :Select a waypoint to build. Ctrl+Click to add or remove in saved items +STR_PICKER_ROADSTOP_BUS_CLASS_TOOLTIP :Select a bus station class to display +STR_PICKER_ROADSTOP_BUS_TYPE_TOOLTIP :Select a bus station type to build. Ctrl+Click to add or remove in saved items +STR_PICKER_ROADSTOP_TRUCK_CLASS_TOOLTIP :Select a lorry station class to display +STR_PICKER_ROADSTOP_TRUCK_TYPE_TOOLTIP :Select a lorry station type to build. Ctrl+Click to add or remove in saved items +STR_PICKER_OBJECT_CLASS_TOOLTIP :Select an object class to display +STR_PICKER_OBJECT_TYPE_TOOLTIP :Select an object type to build. Ctrl+Click to add or remove in saved items. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only STR_STATION_CLASS_DFLT :Default STR_STATION_CLASS_DFLT_STATION :Default station @@ -2942,8 +2949,6 @@ STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Purchase # Object construction window STR_OBJECT_BUILD_CAPTION :{WHITE}Object Selection -STR_OBJECT_BUILD_TOOLTIP :{BLACK}Select object to build. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only -STR_OBJECT_BUILD_CLASS_TOOLTIP :{BLACK}Select class of the object to build STR_OBJECT_BUILD_PREVIEW_TOOLTIP :{BLACK}Preview of the object STR_OBJECT_BUILD_SIZE :{BLACK}Size: {GOLD}{NUM} x {NUM} tiles diff --git a/src/newgrf_class.h b/src/newgrf_class.h index a4c12f1cfc..e04c86adac 100644 --- a/src/newgrf_class.h +++ b/src/newgrf_class.h @@ -42,6 +42,9 @@ private: static void InsertDefaults(); public: + using spec_type = Tspec; + using index_type = Tindex; + uint32_t global_id; ///< Global ID for class, e.g. 'DFLT', 'WAYP', etc. StringID name; ///< Name of this class. @@ -67,8 +70,6 @@ public: uint GetSpecCount() const { return static_cast(this->spec.size()); } /** Get the number of potentially user-available specs within the class. */ uint GetUISpecCount() const { return this->ui_count; } - int GetUIFromIndex(int index) const; - int GetIndexFromUI(int ui_index) const; const Tspec *GetSpec(uint index) const; @@ -80,7 +81,6 @@ public: static void Assign(Tspec *spec); static uint GetClassCount(); static uint GetUIClassCount(); - static Tindex GetUIClass(uint index); static NewGRFClass *Get(Tindex class_index); static const Tspec *GetByGrf(uint32_t grfid, uint16_t local_id); diff --git a/src/newgrf_class_func.h b/src/newgrf_class_func.h index dce269649b..47656f1e23 100644 --- a/src/newgrf_class_func.h +++ b/src/newgrf_class_func.h @@ -105,21 +105,6 @@ uint NewGRFClass::GetUIClassCount() return std::count_if(std::begin(NewGRFClass::classes), std::end(NewGRFClass::classes), [](const auto &cls) { return cls.GetUISpecCount() > 0; }); } -/** - * Get the nth-class with user available specs. - * @param index UI index of a class. - * @return The class ID of the class. - */ -template -Tindex NewGRFClass::GetUIClass(uint index) -{ - for (const auto &cls : NewGRFClass::classes) { - if (cls.GetUISpecCount() == 0) continue; - if (index-- == 0) return cls.Index(); - } - NOT_REACHED(); -} - /** * Get a spec from the class at a given index. * @param index The index where to find the spec. @@ -132,38 +117,6 @@ const Tspec *NewGRFClass::GetSpec(uint index) const return index < this->GetSpecCount() ? this->spec[index] : nullptr; } -/** - * Translate a UI spec index into a spec index. - * @param ui_index UI index of the spec. - * @return index of the spec, or -1 if out of range. - */ -template -int NewGRFClass::GetIndexFromUI(int ui_index) const -{ - if (ui_index < 0) return -1; - for (uint i = 0; i < this->GetSpecCount(); i++) { - if (!this->IsUIAvailable(i)) continue; - if (ui_index-- == 0) return i; - } - return -1; -} - -/** - * Translate a spec index into a UI spec index. - * @param index index of the spec. - * @return UI index of the spec, or -1 if out of range. - */ -template -int NewGRFClass::GetUIFromIndex(int index) const -{ - if ((uint)index >= this->GetSpecCount()) return -1; - uint ui_index = 0; - for (int i = 0; i < index; i++) { - if (this->IsUIAvailable(i)) ui_index++; - } - return ui_index; -} - /** * Retrieve a spec by GRF location. * @param grfid GRF ID of spec. @@ -177,7 +130,8 @@ const Tspec *NewGRFClass::GetByGrf(uint32_t grfid, uint16_t for (const auto &cls : NewGRFClass::classes) { for (const auto &spec : cls.spec) { if (spec == nullptr) continue; - if (spec->grf_prop.grffile->grfid == grfid && spec->grf_prop.local_id == local_id) return spec; + if (spec->grf_prop.local_id != local_id) continue; + if ((spec->grf_prop.grffile == nullptr ? 0 : spec->grf_prop.grffile->grfid) == grfid) return spec; } } diff --git a/src/object_gui.cpp b/src/object_gui.cpp index 421b7f8598..42f4c743b9 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -14,10 +14,8 @@ #include "newgrf_object.h" #include "newgrf_text.h" #include "object.h" -#include "querystring_gui.h" -#include "sortlist_type.h" -#include "stringfilter_type.h" -#include "string_func.h" +#include "picker_gui.h" +#include "sound_func.h" #include "strings_func.h" #include "viewport_func.h" #include "tilehighlight_func.h" @@ -34,199 +32,108 @@ #include "safeguards.h" -static ObjectClassID _selected_object_class; ///< Currently selected available object class. -static int _selected_object_index; ///< Index of the currently selected object if existing, else \c -1. -static uint8_t _selected_object_view; ///< the view of the selected object - -/** Enum referring to the Hotkeys in the build object window */ -enum BuildObjectHotkeys { - BOHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string +struct ObjectPickerSelection { + ObjectClassID sel_class; ///< Selected object class. + uint16_t sel_type; ///< Selected object type within the class. + uint8_t sel_view; ///< Selected view of the object. }; +static ObjectPickerSelection _object_gui; ///< Settings of the object picker. + +class ObjectPickerCallbacks : public PickerCallbacksNewGRFClass { +public: + StringID GetClassTooltip() const override { return STR_PICKER_OBJECT_CLASS_TOOLTIP; } + StringID GetTypeTooltip() const override { return STR_PICKER_OBJECT_TYPE_TOOLTIP; } + + bool IsActive() const override + { + for (const auto &cls : ObjectClass::Classes()) { + for (const auto *spec : cls.Specs()) { + if (spec != nullptr && spec->IsEverAvailable()) return true; + } + } + return false; + } + + int GetSelectedClass() const override { return _object_gui.sel_class; } + void SetSelectedClass(int id) const override { _object_gui.sel_class = this->GetClassIndex(id); } + + StringID GetClassName(int id) const override + { + const auto *objclass = this->GetClass(id); + if (objclass->GetUISpecCount() == 0) return INVALID_STRING_ID; + return objclass->name; + } + + int GetSelectedType() const override { return _object_gui.sel_type; } + void SetSelectedType(int id) const override { _object_gui.sel_type = id; } + + StringID GetTypeName(int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + return (spec == nullptr || !spec->IsEverAvailable()) ? INVALID_STRING_ID : spec->name; + } + + bool IsTypeAvailable(int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + return spec->IsAvailable(); + } + + void DrawType(int x, int y, int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + if (spec->grf_prop.grffile == nullptr) { + extern const DrawTileSprites _objects[]; + const DrawTileSprites *dts = &_objects[spec->grf_prop.local_id]; + DrawOrigTileSeqInGUI(x, y, dts, PAL_NONE); + } else { + DrawNewObjectTileInGUI(x, y, spec, std::min(_object_gui.sel_view, spec->views - 1)); + } + } + + static ObjectPickerCallbacks instance; +}; +/* static */ ObjectPickerCallbacks ObjectPickerCallbacks::instance; /** The window used for building objects. */ -class BuildObjectWindow : public Window { - typedef GUIList GUIObjectClassList; ///< Type definition for the list to hold available object classes. - - static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box. - - int object_margin; ///< The margin (in pixels) around an object. - int line_height; ///< The height of a single line. - int info_height; ///< The height of the info box. - Scrollbar *vscroll; ///< The scrollbar. - - static Listing last_sorting; ///< Default sorting of #GUIObjectClassList. - static Filtering last_filtering; ///< Default filtering of #GUIObjectClassList. - static const std::initializer_list sorter_funcs; ///< Sort functions of the #GUIObjectClassList. - static const std::initializer_list filter_funcs; ///< Filter functions of the #GUIObjectClassList. - GUIObjectClassList object_classes; ///< Available object classes. - StringFilter string_filter; ///< Filter for available objects. - QueryString filter_editbox; ///< Filter editbox. - - /** Scroll #WID_BO_CLASS_LIST so that the selected object class is visible. */ - void EnsureSelectedObjectClassIsVisible() - { - uint pos = 0; - for (auto object_class_id : this->object_classes) { - if (object_class_id == _selected_object_class) break; - pos++; - } - this->vscroll->ScrollTowards(pos); - } - - /** - * Tests whether the previously selected object can be selected. - * @return \c true if the selected object is available, \c false otherwise. - */ - bool CanRestoreSelectedObject() - { - if (_selected_object_index == -1) return false; - - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - if ((int)objclass->GetSpecCount() <= _selected_object_index) return false; - - return objclass->GetSpec(_selected_object_index)->IsAvailable(); - } +class BuildObjectWindow : public PickerWindow { + int info_height; ///< The height of the info box. public: - BuildObjectWindow(WindowDesc *desc, WindowNumber number) : Window(desc), info_height(1), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE) + BuildObjectWindow(WindowDesc *desc, WindowNumber) : PickerWindow(desc, nullptr, 0, ObjectPickerCallbacks::instance), info_height(1) { - this->CreateNestedTree(); - - this->vscroll = this->GetScrollbar(WID_BO_SCROLLBAR); - - this->querystrings[WID_BO_FILTER] = &this->filter_editbox; - - this->object_classes.SetListing(this->last_sorting); - this->object_classes.SetFiltering(this->last_filtering); - this->object_classes.SetSortFuncs(this->sorter_funcs); - this->object_classes.SetFilterFuncs(this->filter_funcs); - this->object_classes.ForceRebuild(); - - BuildObjectClassesAvailable(); - SelectClassAndObject(); - - this->FinishInitNested(number); - - NWidgetMatrix *matrix = this->GetWidget(WID_BO_SELECT_MATRIX); - matrix->SetScrollbar(this->GetScrollbar(WID_BO_SELECT_SCROLL)); - matrix->SetCount(ObjectClass::Get(_selected_object_class)->GetUISpecCount()); - - this->GetWidget(WID_BO_OBJECT_MATRIX)->SetCount(4); - ResetObjectToPlace(); - - this->vscroll->SetCount(this->object_classes.size()); - - EnsureSelectedObjectClassIsVisible(); - + this->ConstructWindow(); this->InvalidateData(); } - /** Sort object classes by ObjectClassID. */ - static bool ObjectClassIDSorter(ObjectClassID const &a, ObjectClassID const &b) - { - return a < b; - } - - /** Filter object classes by class name. */ - static bool TagNameFilter(ObjectClassID const *oc, StringFilter &filter) - { - ObjectClass *objclass = ObjectClass::Get(*oc); - - filter.ResetState(); - filter.AddLine(GetString(objclass->name)); - return filter.GetState(); - } - - /** Builds the filter list of available object classes. */ - void BuildObjectClassesAvailable() - { - if (!this->object_classes.NeedRebuild()) return; - - this->object_classes.clear(); - this->object_classes.reserve(ObjectClass::GetClassCount()); - - for (const auto &cls : ObjectClass::Classes()) { - if (cls.GetUISpecCount() == 0) continue; // Is this needed here? - object_classes.push_back(cls.Index()); - } - - this->object_classes.Filter(this->string_filter); - this->object_classes.RebuildDone(); - this->object_classes.Sort(); - - this->vscroll->SetCount(this->object_classes.size()); - } - - /** - * Checks if the previously selected current object class and object - * can be shown as selected to the user when the dialog is opened. - */ - void SelectClassAndObject() - { - assert(!this->object_classes.empty()); // object GUI should be disabled elsewise - if (_selected_object_class == ObjectClassID::OBJECT_CLASS_BEGIN) { - /* This happens during the first time the window is open during the game life cycle. */ - this->SelectOtherClass(this->object_classes[0]); - } else { - /* Check if the previously selected object class is not available anymore as a - * result of starting a new game without the corresponding NewGRF. */ - bool available = _selected_object_class < ObjectClass::GetClassCount(); - this->SelectOtherClass(available ? _selected_object_class : this->object_classes[0]); - } - - if (this->CanRestoreSelectedObject()) { - this->SelectOtherObject(_selected_object_index); - } else { - this->SelectFirstAvailableObject(true); - } - assert(ObjectClass::Get(_selected_object_class)->GetUISpecCount() > 0); // object GUI should be disabled elsewise - } - void SetStringParameters(WidgetID widget) const override { switch (widget) { - case WID_BO_OBJECT_NAME: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); - SetDParam(0, spec != nullptr ? spec->name : STR_EMPTY); - break; - } - case WID_BO_OBJECT_SIZE: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); + ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class); + const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type); int size = spec == nullptr ? 0 : spec->size; - SetDParam(0, GB(size, HasBit(_selected_object_view, 0) ? 4 : 0, 4)); - SetDParam(1, GB(size, HasBit(_selected_object_view, 0) ? 0 : 4, 4)); + SetDParam(0, GB(size, HasBit(_object_gui.sel_view, 0) ? 4 : 0, 4)); + SetDParam(1, GB(size, HasBit(_object_gui.sel_view, 0) ? 0 : 4, 4)); break; } - default: break; + default: + this->PickerWindow::SetStringParameters(widget); + break; } } void OnInit() override { - this->object_margin = ScaleGUITrad(4); + this->GetWidget(WID_BO_OBJECT_MATRIX)->SetCount(4); + this->PickerWindow::OnInit(); } void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { - case WID_BO_CLASS_LIST: { - for (auto object_class_id : this->object_classes) { - ObjectClass *objclass = ObjectClass::Get(object_class_id); - if (objclass->GetUISpecCount() == 0) continue; - size.width = std::max(size.width, GetStringBoundingBox(objclass->name).width + padding.width); - } - this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height; - resize.height = this->line_height; - size.height = 5 * this->line_height; - break; - } - - case WID_BO_OBJECT_NAME: case WID_BO_OBJECT_SIZE: /* We do not want the window to resize when selecting objects; better clip texts */ size.width = 0; @@ -234,8 +141,8 @@ public: case WID_BO_OBJECT_MATRIX: { /* Get the right amount of buttons based on the current spec. */ - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); + const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class); + const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type); if (spec != nullptr) { if (spec->views >= 2) size.width += resize.width; if (spec->views >= 4) size.height += resize.height; @@ -246,41 +153,14 @@ public: } case WID_BO_OBJECT_SPRITE: { - bool two_wide = false; // Whether there will be two widgets next to each other in the matrix or not. - uint height[2] = {0, 0}; // The height for the different views; in this case views 1/2 and 4. - - /* Get the height and view information. */ - for (const auto &spec : ObjectSpec::Specs()) { - if (!spec.IsEverAvailable()) continue; - two_wide |= spec.views >= 2; - height[spec.views / 4] = std::max(spec.height, height[spec.views / 4]); - } - - /* Determine the pixel heights. */ - for (auto &h : height) { - h *= ScaleGUITrad(TILE_HEIGHT); - h += ScaleGUITrad(TILE_PIXELS) + 2 * this->object_margin; - } - - /* Now determine the size of the minimum widgets. When there are two columns, then - * we want these columns to be slightly less wide. When there are two rows, then - * determine the size of the widgets based on the maximum size for a single row - * of widgets, or just the twice the widget height of the two row ones. */ - size.height = std::max(height[0], height[1] * 2); - if (two_wide) { - size.width = (3 * ScaleGUITrad(TILE_PIXELS) + 2 * this->object_margin) * 2; - } else { - size.width = 4 * ScaleGUITrad(TILE_PIXELS) + 2 * this->object_margin; - } - - /* Get the right size for the single widget based on the current spec. */ - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); + /* Get the right amount of buttons based on the current spec. */ + const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class); + const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type); + size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal(); + size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical(); if (spec != nullptr) { - if (spec->views <= 1) size.width += WidgetDimensions::scaled.hsep_normal; - if (spec->views <= 2) size.height += WidgetDimensions::scaled.vsep_normal; - if (spec->views >= 2) size.width /= 2; - if (spec->views >= 4) size.height /= 2; + if (spec->views <= 1) size.width = size.width * 2 + WidgetDimensions::scaled.hsep_normal; + if (spec->views <= 2) size.height = size.height * 2 + WidgetDimensions::scaled.vsep_normal; } break; } @@ -289,103 +169,49 @@ public: size.height = this->info_height; break; - case WID_BO_SELECT_MATRIX: - fill.height = 1; - resize.height = 1; + default: + this->PickerWindow::UpdateWidgetSize(widget, size, padding, fill, resize); break; - - case WID_BO_SELECT_IMAGE: - size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal(); - size.height = ScaleGUITrad(58) + WidgetDimensions::scaled.fullbevel.Vertical(); - break; - - default: break; } } void DrawWidget(const Rect &r, WidgetID widget) const override { switch (widget) { - case WID_BO_CLASS_LIST: { - Rect mr = r.Shrink(WidgetDimensions::scaled.matrix); - uint pos = 0; - for (auto object_class_id : this->object_classes) { - ObjectClass *objclass = ObjectClass::Get(object_class_id); - if (objclass->GetUISpecCount() == 0) continue; - if (!this->vscroll->IsVisible(pos++)) continue; - DrawString(mr, objclass->name, - (object_class_id == _selected_object_class) ? TC_WHITE : TC_BLACK); - mr.top += this->line_height; - } - break; - } - case WID_BO_OBJECT_SPRITE: { - if (_selected_object_index == -1) break; - - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); + const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class); + const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type); if (spec == nullptr) break; - /* Height of the selection matrix. - * Depending on the number of views, the matrix has a 1x1, 1x2, 2x1 or 2x2 layout. To make the previews - * look nice in all layouts, we use the 4x4 layout (smallest previews) as starting point. For the bigger - * previews in the layouts with less views we add space homogeneously on all sides, so the 4x4 preview-rectangle - * is centered in the 2x1, 1x2 resp. 1x1 buttons. */ const NWidgetMatrix *matrix = this->GetWidget(widget)->GetParentWidget(); - uint matrix_height = matrix->current_y; DrawPixelInfo tmp_dpi; /* Set up a clipping area for the preview. */ Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); if (FillDrawPixelInfo(&tmp_dpi, ir)) { AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); + int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT); + int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM); + if (spec->grf_prop.grffile == nullptr) { extern const DrawTileSprites _objects[]; const DrawTileSprites *dts = &_objects[spec->grf_prop.local_id]; - DrawOrigTileSeqInGUI(ir.Width() / 2 - 1, (ir.Height() + matrix_height / 2) / 2 - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), dts, PAL_NONE); + DrawOrigTileSeqInGUI(x, y, dts, PAL_NONE); } else { - DrawNewObjectTileInGUI(ir.Width() / 2 - 1, (ir.Height() + matrix_height / 2) / 2 - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), spec, matrix->GetCurrentElement()); + DrawNewObjectTileInGUI(x, y, spec, matrix->GetCurrentElement()); } } break; } - case WID_BO_SELECT_IMAGE: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - int obj_index = objclass->GetIndexFromUI(this->GetWidget(widget)->GetParentWidget()->GetCurrentElement()); - if (obj_index < 0) break; - const ObjectSpec *spec = objclass->GetSpec(obj_index); - if (spec == nullptr) break; - - DrawPixelInfo tmp_dpi; - /* Set up a clipping area for the preview. */ - Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); - if (FillDrawPixelInfo(&tmp_dpi, ir)) { - AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - if (spec->grf_prop.grffile == nullptr) { - extern const DrawTileSprites _objects[]; - const DrawTileSprites *dts = &_objects[spec->grf_prop.local_id]; - DrawOrigTileSeqInGUI(ir.Width() / 2 - 1, ir.Height() - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), dts, PAL_NONE); - } else { - DrawNewObjectTileInGUI(ir.Width() / 2 - 1, ir.Height() - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), spec, - std::min(_selected_object_view, spec->views - 1)); - } - } - if (!spec->IsAvailable()) { - GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER); - } - break; - } - case WID_BO_INFO: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); + const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class); + const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type); if (spec == nullptr) break; /* Get the extra message for the GUI */ if (HasBit(spec->callback_mask, CBM_OBJ_FUND_MORE_TEXT)) { - uint16_t callback_res = GetObjectCallback(CBID_OBJECT_FUND_MORE_TEXT, 0, 0, spec, nullptr, INVALID_TILE, _selected_object_view); + uint16_t callback_res = GetObjectCallback(CBID_OBJECT_FUND_MORE_TEXT, 0, 0, spec, nullptr, INVALID_TILE, _object_gui.sel_view); if (callback_res != CALLBACK_FAILED && callback_res != 0x400) { if (callback_res > 0x400) { ErrorUnknownCallbackResult(spec->grf_prop.grffile->grfid, CBID_OBJECT_FUND_MORE_TEXT, callback_res); @@ -407,131 +233,80 @@ public: } } } + break; } + + default: + this->PickerWindow::DrawWidget(r, widget); + break; } } - /** - * Select the specified object class. - * @param object_class Object class select. - */ - void SelectOtherClass(ObjectClassID object_class) + void UpdateSelectSize(const ObjectSpec *spec) { - _selected_object_class = object_class; - ObjectClass *objclass = ObjectClass::Get(object_class); - this->GetWidget(WID_BO_SELECT_MATRIX)->SetCount(objclass->GetUISpecCount()); - } - - /** - * Select the specified object in #_selected_object_class class. - * @param object_index Object index to select, \c -1 means select nothing. - */ - void SelectOtherObject(int object_index) - { - _selected_object_index = object_index; - if (_selected_object_index != -1) { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); - _selected_object_view = std::min(_selected_object_view, spec->views - 1); - this->ReInit(); - } else { - _selected_object_view = 0; - } - - if (_selected_object_index != -1) { - SetObjectToPlaceWnd(SPR_CURSOR_TRANSMITTER, PAL_NONE, HT_RECT | HT_DIAGONAL, this); - } else { - ResetObjectToPlace(); - } - - this->UpdateButtons(_selected_object_class, _selected_object_index, _selected_object_view); - } - - void UpdateSelectSize() - { - if (_selected_object_index == -1) { + if (spec == nullptr) { SetTileSelectSize(1, 1); + ResetObjectToPlace(); } else { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - const ObjectSpec *spec = objclass->GetSpec(_selected_object_index); - int w = GB(spec->size, HasBit(_selected_object_view, 0) ? 4 : 0, 4); - int h = GB(spec->size, HasBit(_selected_object_view, 0) ? 0 : 4, 4); + _object_gui.sel_view = std::min(_object_gui.sel_view, spec->views - 1); + SetObjectToPlaceWnd(SPR_CURSOR_TRANSMITTER, PAL_NONE, HT_RECT | HT_DIAGONAL, this); + int w = GB(spec->size, HasBit(_object_gui.sel_view, 0) ? 4 : 0, 4); + int h = GB(spec->size, HasBit(_object_gui.sel_view, 0) ? 0 : 4, 4); SetTileSelectSize(w, h); + this->ReInit(); } } /** * Update buttons to show the selection to the user. - * @param object_class The class of the selected object. - * @param sel_index Index of the object to select, or \c -1 . - * @param sel_view View of the object to select. + * @param spec The object spec of the selected object. */ - void UpdateButtons(ObjectClassID object_class, int sel_index, uint sel_view) + void UpdateButtons(const ObjectSpec *spec) { - int view_number, object_number; - if (sel_index == -1) { - view_number = -1; // If no object selected, also hide the selected view. - object_number = -1; - } else { - view_number = sel_view; - ObjectClass *objclass = ObjectClass::Get(object_class); - object_number = objclass->GetUIFromIndex(sel_index); - } - - this->GetWidget(WID_BO_OBJECT_MATRIX)->SetClicked(view_number); - this->GetWidget(WID_BO_SELECT_MATRIX)->SetClicked(object_number); - this->UpdateSelectSize(); + this->GetWidget(WID_BO_OBJECT_MATRIX)->SetClicked(_object_gui.sel_view); + this->UpdateSelectSize(spec); this->SetDirty(); } void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { + this->PickerWindow::OnInvalidateData(data, gui_scope); + if (!gui_scope) return; - this->BuildObjectClassesAvailable(); - } - - void OnResize() override - { - this->vscroll->SetCapacityFromWidget(this, WID_BO_CLASS_LIST); + if ((data & PickerWindow::PFI_POSITION) != 0) { + const auto objclass = ObjectClass::Get(_object_gui.sel_class); + const auto spec = objclass->GetSpec(_object_gui.sel_type); + _object_gui.sel_view = std::min(_object_gui.sel_view, spec->views - 1); + this->UpdateButtons(spec); + } } void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { switch (widget) { - case WID_BO_CLASS_LIST: { - auto it = this->vscroll->GetScrolledItemFromWidget(this->object_classes, pt.y, this, widget); - if (it == this->object_classes.end()) break; - - this->SelectOtherClass(*it); - this->SelectFirstAvailableObject(false); - break; - } - - case WID_BO_SELECT_IMAGE: { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - int num_clicked = objclass->GetIndexFromUI(this->GetWidget(widget)->GetParentWidget()->GetCurrentElement()); - if (num_clicked >= 0 && objclass->GetSpec(num_clicked)->IsAvailable()) this->SelectOtherObject(num_clicked); - break; - } - case WID_BO_OBJECT_SPRITE: - if (_selected_object_index != -1) { - _selected_object_view = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); - this->SelectOtherObject(_selected_object_index); // Re-select the object for a different view. + if (_object_gui.sel_type != MAX_UVALUE(uint16_t)) { + _object_gui.sel_view = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); + this->InvalidateData(PickerWindow::PFI_POSITION); + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); } break; + + default: + this->PickerWindow::OnClick(pt, widget, click_count); + break; } } void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override { - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = ObjectClass::Get(_object_gui.sel_class)->GetSpec(_object_gui.sel_type); if (spec->size == OBJECT_SIZE_1X1) { VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT); } else { - Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _selected_object_view); + Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _object_gui.sel_view); } } @@ -552,85 +327,14 @@ public: if (TileX(end_tile) == Map::MaxX()) end_tile += TileDiffXY(-1, 0); if (TileY(end_tile) == Map::MaxY()) end_tile += TileDiffXY(0, -1); } - const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index); + const ObjectSpec *spec = ObjectClass::Get(_object_gui.sel_class)->GetSpec(_object_gui.sel_type); Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, - end_tile, start_tile, spec->Index(), _selected_object_view, (_ctrl_pressed ? true : false)); + end_tile, start_tile, spec->Index(), _object_gui.sel_view, (_ctrl_pressed ? true : false)); } void OnPlaceObjectAbort() override { - this->UpdateButtons(_selected_object_class, -1, _selected_object_view); - } - - EventState OnHotkey(int hotkey) override - { - switch (hotkey) { - case BOHK_FOCUS_FILTER_BOX: - this->SetFocusedWidget(WID_BO_FILTER); - SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused. - break; - - default: - return ES_NOT_HANDLED; - } - - return ES_HANDLED; - } - - void OnEditboxChanged(WidgetID widget) override - { - if (widget == WID_BO_FILTER) { - string_filter.SetFilterTerm(this->filter_editbox.text.buf); - this->object_classes.SetFilterState(!string_filter.IsEmpty()); - this->object_classes.ForceRebuild(); - this->InvalidateData(); - } - } - - /** - * Select the first available object. - * @param change_class If true, change the class if no object in the current - * class is available. - */ - void SelectFirstAvailableObject(bool change_class) - { - ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - - /* First try to select an object in the selected class. */ - for (uint i = 0; i < objclass->GetSpecCount(); i++) { - const ObjectSpec *spec = objclass->GetSpec(i); - if (spec->IsAvailable()) { - this->SelectOtherObject(i); - return; - } - } - if (change_class) { - /* If that fails, select the first available object - * from a random class. */ - for (auto object_class_id : this->object_classes) { - ObjectClass *objclass = ObjectClass::Get(object_class_id); - for (uint i = 0; i < objclass->GetSpecCount(); i++) { - const ObjectSpec *spec = objclass->GetSpec(i); - if (spec->IsAvailable()) { - this->SelectOtherClass(object_class_id); - this->SelectOtherObject(i); - return; - } - } - } - } - /* If all objects are unavailable, select nothing... */ - if (objclass->GetUISpecCount() == 0) { - /* ... but make sure that the class is not empty. */ - for (auto object_class_id : this->object_classes) { - ObjectClass *objclass = ObjectClass::Get(object_class_id); - if (objclass->GetUISpecCount() > 0) { - this->SelectOtherClass(object_class_id); - break; - } - } - } - this->SelectOtherObject(-1); + this->UpdateButtons(nullptr); } /** @@ -647,22 +351,10 @@ public: } static inline HotkeyList hotkeys{"buildobject", { - Hotkey('F', "focus_filter_box", BOHK_FOCUS_FILTER_BOX), + Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX), }, BuildObjectGlobalHotkeys}; }; - -Listing BuildObjectWindow::last_sorting = { false, 0 }; -Filtering BuildObjectWindow::last_filtering = { false, 0 }; - -const std::initializer_list BuildObjectWindow::sorter_funcs = { - &ObjectClassIDSorter, -}; - -const std::initializer_list BuildObjectWindow::filter_funcs = { - &TagNameFilter, -}; - static constexpr NWidgetPart _nested_build_object_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), @@ -672,41 +364,22 @@ static constexpr NWidgetPart _nested_build_object_widgets[] = { NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), - NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BO_FILTER), SetFill(1, 0), SetResize(1, 0), - SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), - EndContainer(), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_BO_CLASS_LIST), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_OBJECT_BUILD_CLASS_TOOLTIP), SetScrollbar(WID_BO_SCROLLBAR), - NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BO_SCROLLBAR), - EndContainer(), + NWidget(NWID_VERTICAL), + NWidgetFunction(MakePickerClassWidgets), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker), NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0), NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BO_OBJECT_MATRIX), SetPIP(0, 2, 0), NWidget(WWT_PANEL, COLOUR_GREY, WID_BO_OBJECT_SPRITE), SetDataTip(0x0, STR_OBJECT_BUILD_PREVIEW_TOOLTIP), EndContainer(), + EndContainer(), EndContainer(), - EndContainer(), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_NAME), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetAlignment(SA_CENTER), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_SIZE), SetDataTip(STR_OBJECT_BUILD_SIZE, STR_NULL), SetAlignment(SA_CENTER), - EndContainer(), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BO_SELECT_SCROLL), - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BO_SELECT_MATRIX), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BO_SELECT_IMAGE), SetDataTip(0x0, STR_OBJECT_BUILD_TOOLTIP), - SetFill(0, 0), SetResize(0, 0), SetScrollbar(WID_BO_SELECT_SCROLL), - EndContainer(), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_SIZE), SetDataTip(STR_OBJECT_BUILD_SIZE, STR_NULL), SetAlignment(SA_CENTER), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BO_INFO), SetFill(1, 0), SetResize(1, 0), EndContainer(), EndContainer(), EndContainer(), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BO_INFO), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 0), SetResize(1, 0), - EndContainer(), - NWidget(NWID_VERTICAL), - NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BO_SELECT_SCROLL), - NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), - EndContainer(), + NWidgetFunction(MakePickerTypeWidgets), EndContainer(), }; @@ -722,7 +395,7 @@ static WindowDesc _build_object_desc( Window *ShowBuildObjectPicker() { /* Don't show the place object button when there are no objects to place. */ - if (ObjectClass::GetUIClassCount() > 0) { + if (ObjectPickerCallbacks::instance.IsActive()) { return AllocateWindowDescFront(&_build_object_desc, 0); } return nullptr; @@ -731,5 +404,5 @@ Window *ShowBuildObjectPicker() /** Reset all data of the object GUI. */ void InitializeObjectGui() { - _selected_object_class = ObjectClassID::OBJECT_CLASS_BEGIN; + _object_gui.sel_class = ObjectClassID::OBJECT_CLASS_BEGIN; } diff --git a/src/picker_gui.cpp b/src/picker_gui.cpp new file mode 100644 index 0000000000..a9eda4298f --- /dev/null +++ b/src/picker_gui.cpp @@ -0,0 +1,465 @@ +/* + * This file is part of OpenTTD. + * 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. + * 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. + * 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 . + */ + +/** @file picker_gui.cpp %File for dealing with picker windows */ + +#include "stdafx.h" +#include "core/backup_type.hpp" +#include "gui.h" +#include "hotkeys.h" +#include "picker_gui.h" +#include "querystring_gui.h" +#include "settings_type.h" +#include "sortlist_type.h" +#include "sound_func.h" +#include "sound_type.h" +#include "stringfilter_type.h" +#include "strings_func.h" +#include "widget_type.h" +#include "window_func.h" +#include "window_gui.h" +#include "window_type.h" +#include "zoom_func.h" + +#include "widgets/picker_widget.h" + +#include "safeguards.h" + +/** Sort classes by id. */ +static bool ClassIDSorter(int const &a, int const &b) +{ + return a < b; +} + +/** Filter classes by class name. */ +static bool ClassTagNameFilter(int const *item, PickerFilterData &filter) +{ + filter.ResetState(); + filter.AddLine(GetString(filter.callbacks->GetClassName(*item))); + return filter.GetState(); +} + +/** Sort types by id. */ +static bool TypeIDSorter(PickerItem const &a, PickerItem const &b) +{ + int r = a.class_index - b.class_index; + if (r == 0) r = a.index - b.index; + return r < 0; +} + +/** Filter types by class name. */ +static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter) +{ + filter.ResetState(); + filter.AddLine(GetString(filter.callbacks->GetTypeName(item->class_index, item->index))); + return filter.GetState(); +} + +static const std::initializer_list _class_sorter_funcs = { &ClassIDSorter }; ///< Sort functions of the #PickerClassList +static const std::initializer_list _class_filter_funcs = { &ClassTagNameFilter }; ///< Filter functions of the #PickerClassList. +static const std::initializer_list _type_sorter_funcs = { TypeIDSorter }; ///< Sort functions of the #PickerTypeList. +static const std::initializer_list _type_filter_funcs = { TypeTagNameFilter }; ///< Filter functions of the #PickerTypeList. + +PickerWindow::PickerWindow(WindowDesc *desc, Window *parent, int window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks), + class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE), + type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE) +{ + this->window_number = window_number; + + /* Init of nested tree is deferred. + * PickerWindow::ConstructWindow must be called by the inheriting window. */ +} + +void PickerWindow::ConstructWindow() +{ + this->CreateNestedTree(); + + /* Test if pickers should be active.*/ + bool isActive = this->callbacks.IsActive(); + + /* Functionality depends on widgets being present, not window class. */ + this->has_class_picker = isActive && this->GetWidget(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice(); + this->has_type_picker = isActive && this->GetWidget(WID_PW_TYPE_MATRIX) != nullptr; + + if (this->has_class_picker) { + this->GetWidget(WID_PW_CLASS_LIST)->tool_tip = this->callbacks.GetClassTooltip(); + + this->querystrings[WID_PW_CLASS_FILTER] = &this->class_editbox; + } else { + if (auto *nwid = this->GetWidget(WID_PW_CLASS_SEL); nwid != nullptr) { + /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */ + bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL); + nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL); + } + } + + this->class_editbox.cancel_button = QueryString::ACTION_CLEAR; + this->class_string_filter.SetFilterTerm(this->class_editbox.text.buf); + this->class_string_filter.callbacks = &this->callbacks; + + this->classes.SetListing(this->callbacks.class_last_sorting); + this->classes.SetFiltering(this->callbacks.class_last_filtering); + this->classes.SetSortFuncs(_class_sorter_funcs); + this->classes.SetFilterFuncs(_class_filter_funcs); + + if (this->has_type_picker) { + this->GetWidget(WID_PW_TYPE_ITEM)->tool_tip = this->callbacks.GetTypeTooltip(); + + auto *matrix = this->GetWidget(WID_PW_TYPE_MATRIX); + matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL)); + + this->querystrings[WID_PW_TYPE_FILTER] = &this->type_editbox; + } else { + if (auto *nwid = this->GetWidget(WID_PW_TYPE_SEL); nwid != nullptr) { + /* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */ + bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL); + nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL); + } + } + + this->type_editbox.cancel_button = QueryString::ACTION_CLEAR; + this->type_string_filter.SetFilterTerm(this->type_editbox.text.buf); + this->type_string_filter.callbacks = &this->callbacks; + + this->types.SetListing(this->callbacks.type_last_sorting); + this->types.SetFiltering(this->callbacks.type_last_filtering); + this->types.SetSortFuncs(_type_sorter_funcs); + this->types.SetFilterFuncs(_type_filter_funcs); + + this->FinishInitNested(this->window_number); + + this->InvalidateData(PFI_CLASS | PFI_TYPE | PFI_POSITION | PFI_VALIDATE); +} + +void PickerWindow::Close(int data) +{ + this->callbacks.Close(data); + this->PickerWindowBase::Close(data); +} + +void PickerWindow::UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) +{ + switch (widget) { + /* Class picker */ + case WID_PW_CLASS_LIST: + resize.height = GetCharacterHeight(FS_NORMAL) + padding.height; + size.height = 5 * resize.height; + break; + + /* Type picker */ + case WID_PW_TYPE_MATRIX: + /* At least two items wide. */ + size.width += resize.width; + fill.width = resize.width; + fill.height = 1; + + /* Resizing in X direction only at blob size, but at pixel level in Y. */ + resize.height = 1; + break; + + /* Type picker */ + case WID_PW_TYPE_ITEM: + size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal(); + size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical(); + break; + } +} + +void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const +{ + switch (widget) { + /* Class picker */ + case WID_PW_CLASS_LIST: { + Rect ir = r.Shrink(WidgetDimensions::scaled.matrix); + const int selected = this->callbacks.GetSelectedClass(); + const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL); + auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes); + for (auto it = first; it != last; ++it) { + DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TC_WHITE : TC_BLACK); + ir.top += this->resize.step_height; + } + break; + } + + /* Type picker */ + case WID_PW_TYPE_ITEM: { + assert(this->GetWidget(widget)->GetParentWidget()->GetCurrentElement() < static_cast(this->types.size())); + const auto &item = this->types[this->GetWidget(widget)->GetParentWidget()->GetCurrentElement()]; + + DrawPixelInfo tmp_dpi; + Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); + if (FillDrawPixelInfo(&tmp_dpi, ir)) { + AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); + int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT); + int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM); + + this->callbacks.DrawType(x, y, item.class_index, item.index); + } + + if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) { + GfxFillRect(ir, GetColourGradient(COLOUR_GREY, SHADE_DARKER), FILLRECT_CHECKER); + } + break; + } + + case WID_PW_TYPE_NAME: + DrawString(r, this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType()), TC_ORANGE, SA_CENTER); + break; + } +} + +void PickerWindow::OnResize() +{ + if (this->has_class_picker) { + this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCapacityFromWidget(this, WID_PW_CLASS_LIST); + } +} + +void PickerWindow::OnClick(Point pt, WidgetID widget, int) +{ + switch (widget) { + /* Class Picker */ + case WID_PW_CLASS_LIST: { + const auto vscroll = this->GetWidget(WID_PW_CLASS_SCROLL); + auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST); + if (it == this->classes.end()) return; + + if (this->callbacks.GetSelectedClass() != *it) { + this->callbacks.SetSelectedClass(*it); + this->InvalidateData(PFI_TYPE | PFI_POSITION | PFI_VALIDATE); + } + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + CloseWindowById(WC_SELECT_STATION, 0); + break; + } + + /* Type Picker */ + case WID_PW_TYPE_ITEM: { + int sel = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); + assert(sel < (int)this->types.size()); + const auto &item = this->types[sel]; + if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) { + this->callbacks.SetSelectedClass(item.class_index); + this->callbacks.SetSelectedType(item.index); + this->InvalidateData(PFI_POSITION); + } + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + CloseWindowById(WC_SELECT_STATION, 0); + break; + } + } +} + +void PickerWindow::OnInvalidateData(int data, bool gui_scope) +{ + if (!gui_scope) return; + + if ((data & PFI_CLASS) != 0) this->classes.ForceRebuild(); + if ((data & PFI_TYPE) != 0) this->types.ForceRebuild(); + + this->BuildPickerClassList(); + if ((data & PFI_VALIDATE) != 0) this->EnsureSelectedClassIsValid(); + if ((data & PFI_POSITION) != 0) this->EnsureSelectedClassIsVisible(); + + this->BuildPickerTypeList(); + if ((data & PFI_VALIDATE) != 0) this->EnsureSelectedTypeIsValid(); + if ((data & PFI_POSITION) != 0) this->EnsureSelectedTypeIsVisible(); +} + +EventState PickerWindow::OnHotkey(int hotkey) +{ + switch (hotkey) { + case PCWHK_FOCUS_FILTER_BOX: + /* Cycle between the two edit boxes. */ + if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->index != WID_PW_TYPE_FILTER)) { + this->SetFocusedWidget(WID_PW_TYPE_FILTER); + } else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->index != WID_PW_CLASS_FILTER)) { + this->SetFocusedWidget(WID_PW_CLASS_FILTER); + } + SetFocusedWindow(this); + return ES_HANDLED; + + default: + return ES_NOT_HANDLED; + } +} + +void PickerWindow::OnEditboxChanged(WidgetID wid) +{ + switch (wid) { + case WID_PW_CLASS_FILTER: + this->class_string_filter.SetFilterTerm(this->class_editbox.text.buf); + this->classes.SetFilterState(!class_string_filter.IsEmpty()); + this->InvalidateData(PFI_CLASS); + break; + + case WID_PW_TYPE_FILTER: + this->type_string_filter.SetFilterTerm(this->type_editbox.text.buf); + this->types.SetFilterState(!type_string_filter.IsEmpty()); + this->InvalidateData(PFI_TYPE); + break; + + default: + break; + } +} + +/** Builds the filter list of classes. */ +void PickerWindow::BuildPickerClassList() +{ + if (!this->classes.NeedRebuild()) return; + + int count = this->callbacks.GetClassCount(); + + this->classes.clear(); + this->classes.reserve(count); + + for (int i = 0; i < count; i++) { + if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue; + this->classes.emplace_back(i); + } + + this->classes.Filter(this->class_string_filter); + this->classes.RebuildDone(); + this->classes.Sort(); + + if (!this->has_class_picker) return; + this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size()); +} + +void PickerWindow::EnsureSelectedClassIsValid() +{ + int class_index = this->callbacks.GetSelectedClass(); + if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return; + + class_index = this->classes.front(); + this->callbacks.SetSelectedClass(class_index); +} + +void PickerWindow::EnsureSelectedClassIsVisible() +{ + if (!this->has_class_picker) return; + if (this->classes.empty()) return; + + auto it = std::find(std::begin(this->classes), std::end(this->classes), this->callbacks.GetSelectedClass()); + if (it == std::end(this->classes)) return; + + int pos = static_cast(std::distance(std::begin(this->classes), it)); + this->GetScrollbar(WID_PW_CLASS_SCROLL)->ScrollTowards(pos); +} + +/** Builds the filter list of types. */ +void PickerWindow::BuildPickerTypeList() +{ + if (!this->types.NeedRebuild()) return; + + this->types.clear(); + int cls_id = this->callbacks.GetSelectedClass(); + + { + /* Add types in only the selected class. */ + if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) { + int count = this->callbacks.GetTypeCount(cls_id); + this->types.reserve(count); + for (int i = 0; i < count; i++) { + if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue; + this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i)); + } + } + } + + this->types.Filter(this->type_string_filter); + this->types.RebuildDone(); + this->types.Sort(); + + if (!this->has_type_picker) return; + this->GetWidget(WID_PW_TYPE_MATRIX)->SetCount(static_cast(this->types.size())); +} + +void PickerWindow::EnsureSelectedTypeIsValid() +{ + int class_index = this->callbacks.GetSelectedClass(); + int index = this->callbacks.GetSelectedType(); + 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; + + class_index = this->types.front().class_index; + index = this->types.front().index; + this->callbacks.SetSelectedClass(class_index); + this->callbacks.SetSelectedType(index); +} + +void PickerWindow::EnsureSelectedTypeIsVisible() +{ + if (!this->has_type_picker) return; + if (this->types.empty()) { + this->GetWidget(WID_PW_TYPE_MATRIX)->SetClicked(-1); + return; + } + + int class_index = this->callbacks.GetSelectedClass(); + int index = this->callbacks.GetSelectedType(); + + auto it = std::find_if(std::begin(this->types), std::end(this->types), [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; }); + if (it == std::end(this->types)) return; + + int pos = static_cast(std::distance(std::begin(this->types), it)); + this->GetWidget(WID_PW_TYPE_MATRIX)->SetClicked(pos); +} + +/** Create nested widgets for the class picker widgets. */ +std::unique_ptr MakePickerClassWidgets() +{ + static constexpr NWidgetPart picker_class_widgets[] = { + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_CLASS_SEL), + NWidget(NWID_VERTICAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_PW_CLASS_FILTER), SetMinimalSize(144, 0), SetPadding(2), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_PW_CLASS_LIST), SetFill(1, 1), SetResize(1, 1), SetPadding(WidgetDimensions::unscaled.picker), + SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_PW_CLASS_SCROLL), + EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_CLASS_SCROLL), + EndContainer(), + EndContainer(), + EndContainer(), + }; + + return MakeNWidgets(std::begin(picker_class_widgets), std::end(picker_class_widgets), nullptr); +} + +/** Create nested widgets for the type picker widgets. */ +std::unique_ptr MakePickerTypeWidgets() +{ + static constexpr NWidgetPart picker_type_widgets[] = { + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_TYPE_SEL), + NWidget(NWID_VERTICAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_FILTER), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_PW_TYPE_SCROLL), + NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_PW_TYPE_MATRIX), SetPIP(0, 2, 0), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(WWT_PANEL, COLOUR_GREY, WID_PW_TYPE_ITEM), SetScrollbar(WID_PW_TYPE_SCROLL), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_TYPE_SCROLL), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_PW_TYPE_NAME), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(1, 0), SetFill(1, 0), SetMinimalTextLines(1, 0), + EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_RESIZE), + EndContainer(), + EndContainer(), + EndContainer(), + }; + + return MakeNWidgets(std::begin(picker_type_widgets), std::end(picker_type_widgets), nullptr); +} diff --git a/src/picker_gui.h b/src/picker_gui.h new file mode 100644 index 0000000000..861abe0811 --- /dev/null +++ b/src/picker_gui.h @@ -0,0 +1,181 @@ +/* + * This file is part of OpenTTD. + * 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. + * 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. + * 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 . + */ + +/** @file picker_gui.h Functions/types etc. related to the picker GUI. */ + +#ifndef PICKER_GUI_H +#define PICKER_GUI_H + +#include "querystring_gui.h" +#include "sortlist_type.h" +#include "stringfilter_type.h" +#include "strings_type.h" +#include "timer/timer.h" +#include "timer/timer_game_calendar.h" +#include "window_gui.h" +#include "window_type.h" + +struct PickerItem { + uint32_t grfid; + uint16_t local_id; + int class_index; + int index; + + inline auto operator<=>(const PickerItem &other) const + { + if (auto cmp = this->grfid <=> other.grfid; cmp != 0) return cmp; + return this->local_id <=> other.local_id; + } +}; + +/** Class for PickerClassWindow to collect information and retain state. */ +class PickerCallbacks { +public: + virtual ~PickerCallbacks() {} + virtual void Close(int) { } + + /** Should picker class/type selection be enabled? */ + virtual bool IsActive() const = 0; + /** Are there multiple classes to chose from? */ + virtual bool HasClassChoice() const = 0; + + /* Class callbacks */ + /** Get the tooltip string for the class list. */ + virtual StringID GetClassTooltip() const = 0; + /** Get the number of classes. @note Used only to estimate space requirements. */ + virtual int GetClassCount() const = 0; + /** Get the index of the selected class. */ + virtual int GetSelectedClass() const = 0; + /** Set the selected class. */ + virtual void SetSelectedClass(int id) const = 0; + /** Get the name of a class. */ + virtual StringID GetClassName(int id) const = 0; + + /* Type callbacks */ + /** Get the tooltip string for the type grid. */ + virtual StringID GetTypeTooltip() const = 0; + /** Get the number of types in a class. @note Used only to estimate space requirements. */ + virtual int GetTypeCount(int cls_id) const = 0; + + /** Get the selected type. */ + virtual int GetSelectedType() const = 0; + /** Set the selected type. */ + virtual void SetSelectedType(int id) const = 0; + /** Get data about an item. */ + virtual PickerItem GetPickerItem(int cls_id, int id) const = 0; + /** Get the item of a type. */ + virtual StringID GetTypeName(int cls_id, int id) const = 0; + /** Test if an item is currently buildable. */ + virtual bool IsTypeAvailable(int cls_id, int id) const = 0; + /** Draw preview image of an item. */ + virtual void DrawType(int x, int y, int cls_id, int id) const = 0; + + Listing class_last_sorting = { false, 0 }; ///< Default sorting of #PickerClassList. + Filtering class_last_filtering = { false, 0 }; ///< Default filtering of #PickerClassList. + + Listing type_last_sorting = { false, 0 }; ///< Default sorting of #PickerTypeList. + Filtering type_last_filtering = { false, 0 }; ///< Default filtering of #PickerTypeList. +}; + +/** Helper for PickerCallbacks when the class system is based on NewGRFClass. */ +template +class PickerCallbacksNewGRFClass : public PickerCallbacks { +public: + inline typename T::index_type GetClassIndex(int cls_id) const { return static_cast(cls_id); } + inline const T *GetClass(int cls_id) const { return T::Get(this->GetClassIndex(cls_id)); } + inline const typename T::spec_type *GetSpec(int cls_id, int id) const { return this->GetClass(cls_id)->GetSpec(id); } + + bool HasClassChoice() const override { return T::GetUIClassCount() > 1; } + + int GetClassCount() const override { return T::GetClassCount(); } + int GetTypeCount(int cls_id) const override { return this->GetClass(cls_id)->GetSpecCount(); } + + PickerItem GetPickerItem(const typename T::spec_type *spec, int cls_id = -1, int id = -1) const + { + if (spec == nullptr) return {0, 0, cls_id, id}; + return {spec->grf_prop.grffile == nullptr ? 0 : spec->grf_prop.grffile->grfid, spec->grf_prop.local_id, spec->class_index, spec->index}; + } + + PickerItem GetPickerItem(int cls_id, int id) const override + { + return GetPickerItem(GetClass(cls_id)->GetSpec(id), cls_id, id); + } +}; + +struct PickerFilterData : StringFilter { + const PickerCallbacks *callbacks; ///< Callbacks for filter functions to access to callbacks. +}; + +using PickerClassList = GUIList; ///< GUIList holding classes to display. +using PickerTypeList = GUIList; ///< GUIList holding classes/types to display. + +class PickerWindow : public PickerWindowBase { +public: + enum PickerFilterInvalidation { + PFI_CLASS = 1U << 0, ///< Refresh the class list. + PFI_TYPE = 1U << 1, ///< Refresh the type list. + PFI_POSITION = 1U << 2, ///< Update scroll positions. + PFI_VALIDATE = 1U << 3, ///< Validate selected item. + }; + + static const int PREVIEW_WIDTH = 64; ///< Width of each preview button. + static const int PREVIEW_HEIGHT = 48; ///< Height of each preview button. + static const int PREVIEW_LEFT = 31; ///< Offset from left edge to draw preview. + static const int PREVIEW_BOTTOM = 31; ///< Offset from bottom edge to draw preview. + + static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box. + + bool has_class_picker = false; ///< Set if this window has a class picker 'component'. + bool has_type_picker = false; ///< Set if this window has a type picker 'component'. + + PickerWindow(WindowDesc *desc, Window *parent, int window_number, PickerCallbacks &callbacks); + void Close(int data = 0) override; + void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override; + void DrawWidget(const Rect &r, WidgetID widget) const override; + void OnResize() override; + void OnClick(Point pt, WidgetID widget, int click_count) override; + void OnInvalidateData(int data = 0, bool gui_scope = true) override; + EventState OnHotkey(int hotkey) override; + void OnEditboxChanged(WidgetID wid) override; + + /** Enum referring to the Hotkeys in the picker window */ + enum PickerClassWindowHotkeys { + PCWHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string + }; + +protected: + void ConstructWindow(); + + PickerCallbacks &callbacks; + +private: + PickerClassList classes; ///< List of classes. + PickerFilterData class_string_filter; + QueryString class_editbox; ///< Filter editbox. + + void BuildPickerClassList(); + void EnsureSelectedClassIsValid(); + void EnsureSelectedClassIsVisible(); + + PickerTypeList types; ///< List of types. + PickerFilterData type_string_filter; + QueryString type_editbox; ///< Filter editbox + + void BuildPickerTypeList(); + void EnsureSelectedTypeIsValid(); + void EnsureSelectedTypeIsVisible(); + + IntervalTimer yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) { + this->SetDirty(); + }}; +}; + +class NWidgetBase; +std::unique_ptr MakePickerClassWidgets(); +std::unique_ptr MakePickerTypeWidgets(); + +#endif /* PICKER_GUI_H */ diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 9d6728804b..eea214f670 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -32,16 +32,13 @@ #include "vehicle_func.h" #include "zoom_func.h" #include "rail_gui.h" -#include "querystring_gui.h" -#include "sortlist_type.h" -#include "stringfilter_type.h" -#include "string_func.h" #include "station_cmd.h" #include "tunnelbridge_cmd.h" #include "waypoint_cmd.h" #include "rail_cmd.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" +#include "picker_gui.h" #include "station_map.h" #include "tunnelbridge_map.h" @@ -54,20 +51,22 @@ static RailType _cur_railtype; ///< Rail type of the current build-rail toolbar. static bool _remove_button_clicked; ///< Flag whether 'remove' toggle-button is currently enabled static DiagDirection _build_depot_direction; ///< Currently selected depot direction -static uint16_t _cur_waypoint_type; ///< Currently selected waypoint type static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI) static SignalType _cur_signal_type; ///< set the signal type (for signal GUI) -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation - - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - uint16_t station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - uint16_t station_count; ///< Number of custom stations (if newstations is \c true ) +struct WaypointPickerSelection { + StationClassID sel_class; ///< Selected station class. + uint16_t sel_type; ///< Selected station type within the class. }; -static RailStationGUISettings _railstation; ///< Settings of the station builder GUI +static WaypointPickerSelection _waypoint_gui; ///< Settings of the waypoint picker. + +struct StationPickerSelection { + StationClassID sel_class; ///< Selected station class. + uint16_t sel_type; ///< Selected station type within the class. + Axis axis; ///< Selected orientation of the station. +}; +static StationPickerSelection _station_gui; ///< Settings of the station picker. static void HandleStationPlacement(TileIndex start, TileIndex end); @@ -181,7 +180,7 @@ void CcStation(Commands, const CommandCost &result, TileIndex tile) if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile); /* Only close the station builder window if the default station and non persistent building is chosen. */ - if (_railstation.station_class == STAT_CLASS_DFLT && _railstation.station_type == 0 && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); + if (_station_gui.sel_class == STAT_CLASS_DFLT && _station_gui.sel_type == 0 && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); } /** @@ -199,9 +198,9 @@ static void PlaceRail_Station(TileIndex tile) } else { int w = _settings_client.gui.station_numtracks; int h = _settings_client.gui.station_platlength; - if (!_railstation.orientation) Swap(w, h); + if (!_station_gui.axis) Swap(w, h); - RailStationGUISettings params = _railstation; + StationPickerSelection params = _station_gui; RailType rt = _cur_railtype; uint8_t numtracks = _settings_client.gui.station_numtracks; uint8_t platlength = _settings_client.gui.station_platlength; @@ -209,9 +208,9 @@ static void PlaceRail_Station(TileIndex tile) auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, rt, params.orientation, numtracks, platlength, params.station_class, params.station_type, INVALID_STATION, adjacent).Succeeded(); + return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, INVALID_STATION, adjacent).Succeeded(); } else { - return Command::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, tile, rt, params.orientation, numtracks, platlength, params.station_class, params.station_type, to_join, adjacent); + return Command::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, to_join, adjacent); } }; @@ -363,7 +362,7 @@ static void BuildRailClick_Remove(Window *w) if (!_settings_client.gui.station_dragdrop) { int x = _settings_client.gui.station_numtracks; int y = _settings_client.gui.station_platlength; - if (_railstation.orientation == 0) Swap(x, y); + if (_station_gui.axis == 0) Swap(x, y); SetTileSelectSize(x, y); } else { VpSetPlaceSizingLimit(_settings_game.station.station_spread); @@ -602,7 +601,7 @@ struct BuildRailToolbarWindow : Window { case WID_RAT_BUILD_WAYPOINT: this->last_user_action = widget; - if (HandlePlacePushButton(this, WID_RAT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT) && StationClass::Get(STAT_CLASS_WAYP)->GetSpecCount() > 1) { + if (HandlePlacePushButton(this, WID_RAT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT)) { ShowBuildWaypointPicker(this); } break; @@ -766,13 +765,12 @@ struct BuildRailToolbarWindow : Window { TileArea ta(start_tile, end_tile); Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y; bool adjacent = _ctrl_pressed; - uint16_t waypoint_type = _cur_waypoint_type; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, STAT_CLASS_WAYP, waypoint_type, INVALID_STATION, adjacent).Succeeded(); + return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, INVALID_STATION, adjacent).Succeeded(); } else { - return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, ta.tile, axis, ta.w, ta.h, STAT_CLASS_WAYP, waypoint_type, to_join, adjacent); + return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, to_join, adjacent); } }; @@ -929,65 +927,83 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) uint numtracks = ta.w; uint platlength = ta.h; - if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); + if (_station_gui.axis == AXIS_X) Swap(numtracks, platlength); - RailStationGUISettings params = _railstation; + StationPickerSelection params = _station_gui; RailType rt = _cur_railtype; bool adjacent = _ctrl_pressed; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, rt, params.orientation, numtracks, platlength, params.station_class, params.station_type, INVALID_STATION, adjacent).Succeeded(); + return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, INVALID_STATION, adjacent).Succeeded(); } else { - return Command::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, ta.tile, rt, params.orientation, numtracks, platlength, params.station_class, params.station_type, to_join, adjacent); + return Command::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, to_join, adjacent); } }; ShowSelectStationIfNeeded(ta, proc); } -/** Enum referring to the Hotkeys in the build rail station window */ -enum BuildRalStationHotkeys { - BRASHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string -}; +class StationPickerCallbacks : public PickerCallbacksNewGRFClass { +public: + StringID GetClassTooltip() const override { return STR_PICKER_STATION_CLASS_TOOLTIP; } + StringID GetTypeTooltip() const override { return STR_PICKER_STATION_TYPE_TOOLTIP; } -struct BuildRailStationWindow : public PickerWindowBase { -private: - uint line_height; ///< Height of a single line in the newstation selection matrix (#WID_BRAS_NEWST_LIST widget). - uint coverage_height; ///< Height of the coverage texts. - Scrollbar *vscroll; ///< Vertical scrollbar of the new station list. - Scrollbar *vscroll2; ///< Vertical scrollbar of the matrix with new stations. - - typedef GUIList GUIStationClassList; ///< Type definition for the list to hold available station classes. - - static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box. - - static Listing last_sorting; ///< Default sorting of #GUIStationClassList. - static Filtering last_filtering; ///< Default filtering of #GUIStationClassList. - static const std::initializer_list sorter_funcs; ///< Sort functions of the #GUIStationClassList. - static const std::initializer_list filter_funcs; ///< Filter functions of the #GUIStationClassList. - GUIStationClassList station_classes; ///< Available station classes. - StringFilter string_filter; ///< Filter for available station classes. - QueryString filter_editbox; ///< Filter editbox. - - /** - * Scrolls #WID_BRAS_NEWST_SCROLL so that the selected station class is visible. - * - * Note that this method should be called shortly after SelectClassAndStation() which will ensure - * an actual existing station class is selected, or the one at position 0 which will always be - * the default TTD rail station. - */ - void EnsureSelectedStationClassIsVisible() + bool IsActive() const override { - uint pos = 0; - for (auto station_class : this->station_classes) { - if (station_class == _railstation.station_class) break; - pos++; + for (const auto &cls : StationClass::Classes()) { + if (IsWaypointClass(cls)) continue; + for (const auto *spec : cls.Specs()) { + if (spec != nullptr) return true; + } } - this->vscroll->SetCount(this->station_classes.size()); - this->vscroll->ScrollTowards(pos); + return false; } + bool HasClassChoice() const override + { + return std::count_if(std::begin(StationClass::Classes()), std::end(StationClass::Classes()), std::not_fn(IsWaypointClass)) > 1; + } + + int GetSelectedClass() const override { return _station_gui.sel_class; } + void SetSelectedClass(int id) const override { _station_gui.sel_class = this->GetClassIndex(id); } + + StringID GetClassName(int id) const override + { + const auto *sc = GetClass(id); + if (IsWaypointClass(*sc)) return INVALID_STRING_ID; + return sc->name; + } + + int GetSelectedType() const override { return _station_gui.sel_type; } + void SetSelectedType(int id) const override { _station_gui.sel_type = id; } + + StringID GetTypeName(int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + return (spec == nullptr) ? STR_STATION_CLASS_DFLT_STATION : spec->name; + } + + bool IsTypeAvailable(int cls_id, int id) const override + { + return IsStationAvailable(this->GetSpec(cls_id, id)); + } + + void DrawType(int x, int y, int cls_id, int id) const override + { + if (!DrawStationTile(x, y, _cur_railtype, _station_gui.axis, this->GetClassIndex(cls_id), id)) { + StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 2 + _station_gui.axis); + } + } + + static StationPickerCallbacks instance; +}; +/* static */ StationPickerCallbacks StationPickerCallbacks::instance; + +struct BuildRailStationWindow : public PickerWindow { +private: + uint coverage_height; ///< Height of the coverage texts. + /** * Verify whether the currently selected station size is allowed after selecting a new station class/type. * If not, change the station size variables ( _settings_client.gui.station_numtracks and _settings_client.gui.station_platlength ). @@ -1022,38 +1038,16 @@ private: } public: - BuildRailStationWindow(WindowDesc *desc, Window *parent, bool newstation) : PickerWindowBase(desc, parent), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE) + BuildRailStationWindow(WindowDesc *desc, Window *parent) : PickerWindow(desc, parent, TRANSPORT_RAIL, StationPickerCallbacks::instance) { this->coverage_height = 2 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; - this->vscroll = nullptr; - _railstation.newstations = newstation; + this->ConstructWindow(); + this->InvalidateData(); + } - this->CreateNestedTree(); - this->GetWidget(WID_BRAS_SHOW_NEWST_ADDITIONS)->SetDisplayedPlane(newstation ? 0 : SZSP_NONE); - this->GetWidget(WID_BRAS_SHOW_NEWST_MATRIX)->SetDisplayedPlane(newstation ? 0 : SZSP_NONE); - this->GetWidget(WID_BRAS_SHOW_NEWST_DEFSIZE)->SetDisplayedPlane(newstation ? 0 : SZSP_NONE); - this->GetWidget(WID_BRAS_SHOW_NEWST_RESIZE)->SetDisplayedPlane(newstation ? 0 : SZSP_NONE); - /* Hide the station class filter if no stations other than the default one are available. */ - this->GetWidget(WID_BRAS_FILTER_CONTAINER)->SetDisplayedPlane(newstation ? 0 : SZSP_NONE); - if (newstation) { - this->vscroll = this->GetScrollbar(WID_BRAS_NEWST_SCROLL); - this->vscroll2 = this->GetScrollbar(WID_BRAS_MATRIX_SCROLL); - - this->querystrings[WID_BRAS_FILTER_EDITBOX] = &this->filter_editbox; - this->station_classes.SetListing(this->last_sorting); - this->station_classes.SetFiltering(this->last_filtering); - this->station_classes.SetSortFuncs(this->sorter_funcs); - this->station_classes.SetFilterFuncs(this->filter_funcs); - } - - this->station_classes.ForceRebuild(); - - BuildStationClassesAvailable(); - SelectClassAndStation(); - - this->FinishInitNested(TRANSPORT_RAIL); - - this->LowerWidget(WID_BRAS_PLATFORM_DIR_X + _railstation.orientation); + void OnInit() override + { + this->LowerWidget(WID_BRAS_PLATFORM_DIR_X + _station_gui.axis); if (_settings_client.gui.station_dragdrop) { this->LowerWidget(WID_BRAS_PLATFORM_DRAG_N_DROP); } else { @@ -1063,138 +1057,35 @@ public: this->SetWidgetLoweredState(WID_BRAS_HIGHLIGHT_OFF, !_settings_client.gui.station_show_coverage); this->SetWidgetLoweredState(WID_BRAS_HIGHLIGHT_ON, _settings_client.gui.station_show_coverage); - if (!newstation) { - _railstation.station_class = StationClassID::STAT_CLASS_DFLT; - _railstation.station_type = 0; - this->vscroll2 = nullptr; - } else { - _railstation.station_count = StationClass::Get(_railstation.station_class)->GetSpecCount(); - _railstation.station_type = std::min(_railstation.station_type, _railstation.station_count - 1); - - NWidgetMatrix *matrix = this->GetWidget(WID_BRAS_MATRIX); - matrix->SetScrollbar(this->vscroll2); - matrix->SetCount(_railstation.station_count); - matrix->SetClicked(_railstation.station_type); - - EnsureSelectedStationClassIsVisible(); - } - - this->InvalidateData(); + this->PickerWindow::OnInit(); } void Close([[maybe_unused]] int data = 0) override { CloseWindowById(WC_SELECT_STATION, 0); - this->PickerWindowBase::Close(); - } - - /** Sort station classes by StationClassID. */ - static bool StationClassIDSorter(StationClassID const &a, StationClassID const &b) - { - return a < b; - } - - /** Filter station classes by class name. */ - static bool TagNameFilter(StationClassID const * sc, StringFilter &filter) - { - filter.ResetState(); - filter.AddLine(GetString(StationClass::Get(*sc)->name)); - return filter.GetState(); - } - - /** Builds the filter list of available station classes. */ - void BuildStationClassesAvailable() - { - if (!this->station_classes.NeedRebuild()) return; - - this->station_classes.clear(); - this->station_classes.reserve(StationClass::GetClassCount()); - - for (const auto &cls : StationClass::Classes()) { - /* Skip waypoints. */ - if (IsWaypointClass(cls)) continue; - if (cls.GetUISpecCount() == 0) continue; - station_classes.push_back(cls.Index()); - } - - if (_railstation.newstations) { - this->station_classes.Filter(this->string_filter); - this->station_classes.RebuildDone(); - this->station_classes.Sort(); - - this->vscroll->SetCount(this->station_classes.size()); - } - } - - /** - * Checks if the previously selected current station class and station - * can be shown as selected to the user when the dialog is opened. - */ - void SelectClassAndStation() - { - if (_railstation.station_class == StationClassID::STAT_CLASS_DFLT) { - /* This happens during the first time the window is open during the game life cycle. */ - this->SelectOtherClass(StationClassID::STAT_CLASS_DFLT); - } else { - /* Check if the previously selected station class is not available anymore as a - * result of starting a new game without the corresponding NewGRF. */ - bool available = _railstation.station_class < StationClass::GetClassCount(); - this->SelectOtherClass(available ? _railstation.station_class : StationClassID::STAT_CLASS_DFLT); - } - } - - /** - * Select the specified station class. - * @param station_class Station class select. - */ - void SelectOtherClass(StationClassID station_class) - { - _railstation.station_class = station_class; + this->PickerWindow::Close(); } void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { - if (!gui_scope) return; - - this->BuildStationClassesAvailable(); - } - - EventState OnHotkey(int hotkey) override - { - switch (hotkey) { - case BRASHK_FOCUS_FILTER_BOX: - this->SetFocusedWidget(WID_BRAS_FILTER_EDITBOX); - SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused. - break; - - default: - return ES_NOT_HANDLED; + if (gui_scope) { + const StationSpec *statspec = StationClass::Get(_station_gui.sel_class)->GetSpec(_station_gui.sel_type); + this->CheckSelectedSize(statspec); } - return ES_HANDLED; - } - - void OnEditboxChanged(WidgetID widget) override - { - if (widget == WID_BRAS_FILTER_EDITBOX) { - string_filter.SetFilterTerm(this->filter_editbox.text.buf); - this->station_classes.SetFilterState(!string_filter.IsEmpty()); - this->station_classes.ForceRebuild(); - this->InvalidateData(); - } + this->PickerWindow::OnInvalidateData(data, gui_scope); } void OnPaint() override { - bool newstations = _railstation.newstations; - const StationSpec *statspec = newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; + const StationSpec *statspec = StationClass::Get(_station_gui.sel_class)->GetSpec(_station_gui.sel_type); if (_settings_client.gui.station_dragdrop) { SetTileSelectSize(1, 1); } else { int x = _settings_client.gui.station_numtracks; int y = _settings_client.gui.station_platlength; - if (_railstation.orientation == AXIS_X) Swap(x, y); + if (_station_gui.axis == AXIS_X) Swap(x, y); if (!_remove_button_clicked) { SetTileSelectSize(x, y); } @@ -1235,54 +1126,18 @@ public: void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { - case WID_BRAS_NEWST_LIST: { - Dimension d = {0, 0}; - for (auto station_class : this->station_classes) { - d = maxdim(d, GetStringBoundingBox(StationClass::Get(station_class)->name)); - } - size.width = std::max(size.width, d.width + padding.width); - this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height; - size.height = 5 * this->line_height; - resize.height = this->line_height; - break; - } - - case WID_BRAS_SHOW_NEWST_TYPE: { - if (!_railstation.newstations) { - size.width = 0; - size.height = 0; - break; - } - - /* If newstations exist, compute the non-zero minimal size. */ - Dimension d = {0, 0}; - StringID str = this->GetWidget(widget)->widget_data; - for (auto station_class : this->station_classes) { - StationClass *stclass = StationClass::Get(station_class); - for (uint j = 0; j < stclass->GetSpecCount(); j++) { - const StationSpec *statspec = stclass->GetSpec(j); - SetDParam(0, (statspec != nullptr && statspec->name != 0) ? statspec->name : STR_STATION_CLASS_DFLT_STATION); - d = maxdim(d, GetStringBoundingBox(str)); - } - } - size.width = std::max(size.width, d.width + padding.width); - break; - } - case WID_BRAS_PLATFORM_DIR_X: case WID_BRAS_PLATFORM_DIR_Y: - case WID_BRAS_IMAGE: - size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal(); - size.height = ScaleGUITrad(58) + WidgetDimensions::scaled.fullbevel.Vertical(); + size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal(); + size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical(); break; case WID_BRAS_COVERAGE_TEXTS: size.height = this->coverage_height; break; - case WID_BRAS_MATRIX: - fill.height = 1; - resize.height = 1; + default: + this->PickerWindow::UpdateWidgetSize(widget, size, padding, fill, resize); break; } } @@ -1297,9 +1152,9 @@ public: Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); if (FillDrawPixelInfo(&tmp_dpi, ir)) { AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (ir.Height() + ScaleSpriteTrad(58)) / 2 - ScaleSpriteTrad(31); - if (!DrawStationTile(x, y, _cur_railtype, AXIS_X, _railstation.station_class, _railstation.station_type)) { + int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT); + int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM); + if (!DrawStationTile(x, y, _cur_railtype, AXIS_X, _station_gui.sel_class, _station_gui.sel_type)) { StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 2); } } @@ -1311,66 +1166,18 @@ public: Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); if (FillDrawPixelInfo(&tmp_dpi, ir)) { AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (ir.Height() + ScaleSpriteTrad(58)) / 2 - ScaleSpriteTrad(31); - if (!DrawStationTile(x, y, _cur_railtype, AXIS_Y, _railstation.station_class, _railstation.station_type)) { + int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT); + int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM); + if (!DrawStationTile(x, y, _cur_railtype, AXIS_Y, _station_gui.sel_class, _station_gui.sel_type)) { StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 3); } } break; } - case WID_BRAS_NEWST_LIST: { - Rect ir = r.Shrink(WidgetDimensions::scaled.matrix); - uint statclass = 0; - for (auto station_class : this->station_classes) { - if (this->vscroll->IsVisible(statclass)) { - DrawString(ir, - StationClass::Get(station_class)->name, - station_class == _railstation.station_class ? TC_WHITE : TC_BLACK); - ir.top += this->line_height; - } - statclass++; - } + default: + this->PickerWindow::DrawWidget(r, widget); break; - } - - case WID_BRAS_IMAGE: { - uint16_t type = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); - assert(type < _railstation.station_count); - /* Check station availability callback */ - const StationSpec *statspec = StationClass::Get(_railstation.station_class)->GetSpec(type); - - /* Set up a clipping area for the station preview. */ - Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); - if (FillDrawPixelInfo(&tmp_dpi, ir)) { - AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (ir.Height() + ScaleSpriteTrad(58)) / 2 - ScaleSpriteTrad(31); - if (!DrawStationTile(x, y, _cur_railtype, _railstation.orientation, _railstation.station_class, type)) { - StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 2 + _railstation.orientation); - } - } - if (!IsStationAvailable(statspec)) { - GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER); - } - break; - } - } - } - - void OnResize() override - { - if (this->vscroll != nullptr) { // New stations available. - this->vscroll->SetCapacityFromWidget(this, WID_BRAS_NEWST_LIST); - } - } - - void SetStringParameters(WidgetID widget) const override - { - if (widget == WID_BRAS_SHOW_NEWST_TYPE) { - const StationSpec *statspec = StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type); - SetDParam(0, (statspec != nullptr && statspec->name != 0) ? statspec->name : STR_STATION_CLASS_DFLT_STATION); } } @@ -1379,9 +1186,9 @@ public: switch (widget) { case WID_BRAS_PLATFORM_DIR_X: case WID_BRAS_PLATFORM_DIR_Y: - this->RaiseWidget(WID_BRAS_PLATFORM_DIR_X + _railstation.orientation); - _railstation.orientation = (Axis)(widget - WID_BRAS_PLATFORM_DIR_X); - this->LowerWidget(WID_BRAS_PLATFORM_DIR_X + _railstation.orientation); + this->RaiseWidget(WID_BRAS_PLATFORM_DIR_X + _station_gui.axis); + _station_gui.axis = (Axis)(widget - WID_BRAS_PLATFORM_DIR_X); + this->LowerWidget(WID_BRAS_PLATFORM_DIR_X + _station_gui.axis); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); this->SetDirty(); CloseWindowById(WC_SELECT_STATION, 0); @@ -1400,7 +1207,7 @@ public: _settings_client.gui.station_numtracks = widget - WID_BRAS_PLATFORM_NUM_BEGIN; _settings_client.gui.station_dragdrop = false; - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; + const StationSpec *statspec = StationClass::Get(_station_gui.sel_class)->GetSpec(_station_gui.sel_type); if (statspec != nullptr && HasBit(statspec->disallowed_lengths, _settings_client.gui.station_platlength - 1)) { /* The previously selected number of platforms in invalid */ for (uint i = 0; i < 7; i++) { @@ -1433,7 +1240,7 @@ public: _settings_client.gui.station_platlength = widget - WID_BRAS_PLATFORM_LEN_BEGIN; _settings_client.gui.station_dragdrop = false; - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; + const StationSpec *statspec = StationClass::Get(_station_gui.sel_class)->GetSpec(_station_gui.sel_type); if (statspec != nullptr && HasBit(statspec->disallowed_platforms, _settings_client.gui.station_numtracks - 1)) { /* The previously selected number of tracks in invalid */ for (uint i = 0; i < 7; i++) { @@ -1459,7 +1266,7 @@ public: this->ToggleWidgetLoweredState(WID_BRAS_PLATFORM_DRAG_N_DROP); /* get the first allowed length/number of platforms */ - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; + const StationSpec *statspec = StationClass::Get(_station_gui.sel_class)->GetSpec(_station_gui.sel_type); if (statspec != nullptr && HasBit(statspec->disallowed_lengths, _settings_client.gui.station_platlength - 1)) { for (uint i = 0; i < 7; i++) { if (!HasBit(statspec->disallowed_lengths, i)) { @@ -1498,46 +1305,9 @@ public: SetViewportCatchmentStation(nullptr, true); break; - case WID_BRAS_NEWST_LIST: { - auto it = this->vscroll->GetScrolledItemFromWidget(this->station_classes, pt.y, this, WID_BRAS_NEWST_LIST); - if (it == this->station_classes.end()) return; - StationClassID station_class_id = *it; - if (_railstation.station_class != station_class_id) { - StationClass *station_class = StationClass::Get(station_class_id); - _railstation.station_class = station_class_id; - _railstation.station_count = station_class->GetSpecCount(); - _railstation.station_type = 0; - - this->CheckSelectedSize(station_class->GetSpec(_railstation.station_type)); - - NWidgetMatrix *matrix = this->GetWidget(WID_BRAS_MATRIX); - matrix->SetCount(_railstation.station_count); - matrix->SetClicked(_railstation.station_type); - } - if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); - this->SetDirty(); - CloseWindowById(WC_SELECT_STATION, 0); + default: + this->PickerWindow::OnClick(pt, widget, click_count); break; - } - - case WID_BRAS_IMAGE: { - uint16_t y = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); - if (y >= _railstation.station_count) return; - - /* Check station availability callback */ - const StationSpec *statspec = StationClass::Get(_railstation.station_class)->GetSpec(y); - if (!IsStationAvailable(statspec)) return; - - _railstation.station_type = y; - - this->CheckSelectedSize(statspec); - this->GetWidget(widget)->GetParentWidget()->SetClicked(_railstation.station_type); - - if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); - this->SetDirty(); - CloseWindowById(WC_SELECT_STATION, 0); - break; - } } } @@ -1546,10 +1316,6 @@ public: CheckRedrawStationCoverage(this); } - IntervalTimer yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) { - this->SetDirty(); - }}; - /** * Handler for global hotkeys of the BuildRailStationWindow. * @param hotkey Hotkey @@ -1564,112 +1330,66 @@ public: } static inline HotkeyList hotkeys{"buildrailstation", { - Hotkey('F', "focus_filter_box", BRASHK_FOCUS_FILTER_BOX), + Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX), }, BuildRailStationGlobalHotkeys}; }; -Listing BuildRailStationWindow::last_sorting = { false, 0 }; -Filtering BuildRailStationWindow::last_filtering = { false, 0 }; - -const std::initializer_list BuildRailStationWindow::sorter_funcs = { - &StationClassIDSorter, -}; - -const std::initializer_list BuildRailStationWindow::filter_funcs = { - &TagNameFilter, -}; - static constexpr NWidgetPart _nested_station_builder_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_RAIL_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_SHOW_NEWST_DEFSIZE), - NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), - EndContainer(), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_FILTER_CONTAINER), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), - NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BRAS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0), - SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_SHOW_NEWST_ADDITIONS), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_BRAS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0), - SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BRAS_NEWST_SCROLL), - NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BRAS_NEWST_SCROLL), - EndContainer(), - EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_X), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_Y), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(), - EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BRAS_SHOW_NEWST_TYPE), SetMinimalSize(144, 11), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_NUMBER_OF_TRACKS, STR_NULL), - NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_1), SetAspect(1.25f), SetDataTip(STR_BLACK_1, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_2), SetAspect(1.25f), SetDataTip(STR_BLACK_2, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_3), SetAspect(1.25f), SetDataTip(STR_BLACK_3, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_4), SetAspect(1.25f), SetDataTip(STR_BLACK_4, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_5), SetAspect(1.25f), SetDataTip(STR_BLACK_5, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_6), SetAspect(1.25f), SetDataTip(STR_BLACK_6, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_7), SetAspect(1.25f), SetDataTip(STR_BLACK_7, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), - EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_PLATFORM_LENGTH, STR_NULL), - NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_1), SetAspect(1.25f), SetDataTip(STR_BLACK_1, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_2), SetAspect(1.25f), SetDataTip(STR_BLACK_2, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_3), SetAspect(1.25f), SetDataTip(STR_BLACK_3, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_4), SetAspect(1.25f), SetDataTip(STR_BLACK_4, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_5), SetAspect(1.25f), SetDataTip(STR_BLACK_5, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_6), SetAspect(1.25f), SetDataTip(STR_BLACK_6, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_7), SetAspect(1.25f), SetDataTip(STR_BLACK_7, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), - EndContainer(), - NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_DRAG_N_DROP), SetMinimalSize(75, 12), SetDataTip(STR_STATION_BUILD_DRAG_DROP, STR_STATION_BUILD_DRAG_DROP_TOOLTIP), - EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), - NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_OFF), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_ON), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), - EndContainer(), + NWidget(NWID_VERTICAL), + NWidgetFunction(MakePickerClassWidgets), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), + NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_X), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_Y), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(), EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_SHOW_NEWST_MATRIX), - /* We need an additional background for the matrix, as the matrix cannot handle the scrollbar due to not being an NWidgetCore. */ - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BRAS_MATRIX_SCROLL), - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BRAS_MATRIX), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BRAS_IMAGE), - SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BRAS_MATRIX_SCROLL), - EndContainer(), - EndContainer(), - EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_NUMBER_OF_TRACKS, STR_NULL), + NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_1), SetAspect(1.25f), SetDataTip(STR_BLACK_1, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_2), SetAspect(1.25f), SetDataTip(STR_BLACK_2, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_3), SetAspect(1.25f), SetDataTip(STR_BLACK_3, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_4), SetAspect(1.25f), SetDataTip(STR_BLACK_4, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_5), SetAspect(1.25f), SetDataTip(STR_BLACK_5, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_6), SetAspect(1.25f), SetDataTip(STR_BLACK_6, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_7), SetAspect(1.25f), SetDataTip(STR_BLACK_7, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP), EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_PLATFORM_LENGTH, STR_NULL), + NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_1), SetAspect(1.25f), SetDataTip(STR_BLACK_1, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_2), SetAspect(1.25f), SetDataTip(STR_BLACK_2, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_3), SetAspect(1.25f), SetDataTip(STR_BLACK_3, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_4), SetAspect(1.25f), SetDataTip(STR_BLACK_4, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_5), SetAspect(1.25f), SetDataTip(STR_BLACK_5, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_6), SetAspect(1.25f), SetDataTip(STR_BLACK_6, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_7), SetAspect(1.25f), SetDataTip(STR_BLACK_7, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_DRAG_N_DROP), SetMinimalSize(75, 12), SetDataTip(STR_STATION_BUILD_DRAG_DROP, STR_STATION_BUILD_DRAG_DROP_TOOLTIP), + EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_OFF), SetMinimalSize(60, 12), SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_ON), SetMinimalSize(60, 12), SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), + EndContainer(), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BRAS_COVERAGE_TEXTS), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0), EndContainer(), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BRAS_COVERAGE_TEXTS), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_SHOW_NEWST_RESIZE), - NWidget(NWID_VERTICAL), - NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BRAS_MATRIX_SCROLL), - NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), EndContainer(), EndContainer(), + NWidgetFunction(MakePickerTypeWidgets), EndContainer(), }; /** High level window description of the station-build window (default & newGRF) */ static WindowDesc _station_builder_desc( - WDP_AUTO, "build_station_rail", 350, 0, + WDP_AUTO, "build_station_rail", 0, 0, WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_CONSTRUCTION, std::begin(_nested_station_builder_widgets), std::end(_nested_station_builder_widgets), @@ -1679,8 +1399,7 @@ static WindowDesc _station_builder_desc( /** Open station build window */ static Window *ShowStationBuilder(Window *parent) { - bool newstations = StationClass::GetClassCount() > 2 || StationClass::Get(STAT_CLASS_DFLT)->GetSpecCount() != 1; - return new BuildRailStationWindow(&_station_builder_desc, parent, newstations); + return new BuildRailStationWindow(&_station_builder_desc, parent); } struct BuildSignalWindow : public PickerWindowBase { @@ -2018,196 +1737,72 @@ static void ShowBuildTrainDepotPicker(Window *parent) new BuildRailDepotWindow(&_build_depot_desc, parent); } -struct BuildRailWaypointWindow : PickerWindowBase { - using WaypointList = GUIList; - static const uint FILTER_LENGTH = 20; +class WaypointPickerCallbacks : public PickerCallbacksNewGRFClass { +public: + StringID GetClassTooltip() const override { return STR_PICKER_WAYPOINT_CLASS_TOOLTIP; } + StringID GetTypeTooltip() const override { return STR_PICKER_WAYPOINT_TYPE_TOOLTIP; } - const StationClass *waypoints; - WaypointList list; - StringFilter string_filter; ///< Filter for waypoint name - static QueryString editbox; ///< Filter editbox - - BuildRailWaypointWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent) + bool IsActive() const override { - this->waypoints = StationClass::Get(STAT_CLASS_WAYP); - - this->CreateNestedTree(); - - NWidgetMatrix *matrix = this->GetWidget(WID_BRW_WAYPOINT_MATRIX); - matrix->SetScrollbar(this->GetScrollbar(WID_BRW_SCROLL)); - - this->FinishInitNested(TRANSPORT_RAIL); - - this->querystrings[WID_BRW_FILTER] = &this->editbox; - this->editbox.cancel_button = QueryString::ACTION_CLEAR; - this->string_filter.SetFilterTerm(this->editbox.text.buf); - - this->list.ForceRebuild(); - this->BuildPickerList(); - } - - void Close([[maybe_unused]] int data = 0) override - { - CloseWindowById(WC_SELECT_STATION, 0); - this->PickerWindowBase::Close(); - } - - bool FilterByText(const StationSpec *statspec) - { - if (this->string_filter.IsEmpty()) return true; - this->string_filter.ResetState(); - if (statspec == nullptr) { - this->string_filter.AddLine(GetString(STR_STATION_CLASS_WAYP_WAYPOINT)); - } else { - this->string_filter.AddLine(GetString(statspec->name)); - if (statspec->grf_prop.grffile != nullptr) { - const GRFConfig *gc = GetGRFConfig(statspec->grf_prop.grffile->grfid); - this->string_filter.AddLine(gc->GetName()); + for (const auto &cls : StationClass::Classes()) { + if (!IsWaypointClass(cls)) continue; + for (const auto *spec : cls.Specs()) { + if (spec != nullptr) return true; } } - return this->string_filter.GetState(); + return false; } - void BuildPickerList() + bool HasClassChoice() const override { - if (!this->list.NeedRebuild()) return; - - this->list.clear(); - this->list.reserve(this->waypoints->GetSpecCount()); - for (uint i = 0; i < this->waypoints->GetSpecCount(); i++) { - const StationSpec *statspec = this->waypoints->GetSpec(i); - if (!FilterByText(statspec)) continue; - - this->list.push_back(i); - } - this->list.RebuildDone(); - - NWidgetMatrix *matrix = this->GetWidget(WID_BRW_WAYPOINT_MATRIX); - matrix->SetCount((int)this->list.size()); - matrix->SetClicked(this->UpdateSelection(_cur_waypoint_type)); + return std::count_if(std::begin(StationClass::Classes()), std::end(StationClass::Classes()), IsWaypointClass) > 1; } - uint UpdateSelection(uint type) + void Close(int) override { ResetObjectToPlace(); } + int GetSelectedClass() const override { return _waypoint_gui.sel_class; } + void SetSelectedClass(int id) const override { _waypoint_gui.sel_class = this->GetClassIndex(id); } + + StringID GetClassName(int id) const override { - auto found = std::find(std::begin(this->list), std::end(this->list), type); - if (found != std::end(this->list)) return found - std::begin(this->list); - - /* Selection isn't in the list, default to first */ - if (this->list.empty()) { - _cur_waypoint_type = 0; - return -1; - } else { - _cur_waypoint_type = this->list.front(); - return 0; - } + const auto *sc = GetClass(id); + if (!IsWaypointClass(*sc)) return INVALID_STRING_ID; + return sc->name; } - void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override + int GetSelectedType() const override { return _waypoint_gui.sel_type; } + void SetSelectedType(int id) const override { _waypoint_gui.sel_type = id; } + + StringID GetTypeName(int cls_id, int id) const override { - switch (widget) { - case WID_BRW_WAYPOINT_MATRIX: - /* Two blobs high and three wide. */ - size.width += resize.width * 2; - size.height += resize.height * 1; - - /* Resizing in X direction only at blob size, but at pixel level in Y. */ - resize.height = 1; - break; - - case WID_BRW_WAYPOINT: - size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal(); - size.height = ScaleGUITrad(58) + WidgetDimensions::scaled.fullbevel.Vertical(); - break; - } + const auto *spec = this->GetSpec(cls_id, id); + return (spec == nullptr) ? STR_STATION_CLASS_WAYP_WAYPOINT : spec->name; } - void SetStringParameters(WidgetID widget) const override + bool IsTypeAvailable(int cls_id, int id) const override { - if (widget == WID_BRW_NAME) { - if (!this->list.empty() && IsInsideBS(_cur_waypoint_type, 0, this->waypoints->GetSpecCount())) { - const StationSpec *statspec = this->waypoints->GetSpec(_cur_waypoint_type); - if (statspec == nullptr) { - SetDParam(0, STR_STATION_CLASS_WAYP_WAYPOINT); - } else { - SetDParam(0, statspec->name); - } - } else { - SetDParam(0, STR_EMPTY); - } - } + return IsStationAvailable(this->GetSpec(cls_id, id)); } - void OnPaint() override + void DrawType(int x, int y, int cls_id, int id) const override { - this->BuildPickerList(); - this->DrawWidgets(); + DrawWaypointSprite(x, y, this->GetClassIndex(cls_id), id, _cur_railtype); } - void DrawWidget(const Rect &r, WidgetID widget) const override - { - switch (widget) { - case WID_BRW_WAYPOINT: { - uint16_t type = this->list.at(this->GetWidget(widget)->GetParentWidget()->GetCurrentElement()); - const StationSpec *statspec = this->waypoints->GetSpec(type); - - DrawPixelInfo tmp_dpi; - Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); - if (FillDrawPixelInfo(&tmp_dpi, ir)) { - AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (ir.Height() + ScaleSpriteTrad(58)) / 2 - ScaleSpriteTrad(31); - DrawWaypointSprite(x, y, type, _cur_railtype); - } - - if (!IsStationAvailable(statspec)) { - GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER); - } - } - } - } - - void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override - { - switch (widget) { - case WID_BRW_WAYPOINT: { - uint16_t sel = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); - assert(sel < this->list.size()); - uint16_t type = this->list.at(sel); - - /* Check station availability callback */ - const StationSpec *statspec = this->waypoints->GetSpec(type); - if (!IsStationAvailable(statspec)) return; - - _cur_waypoint_type = type; - this->GetWidget(widget)->GetParentWidget()->SetClicked(sel); - if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); - this->SetDirty(); - break; - } - } - } - - void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override - { - if (!gui_scope) return; - this->list.ForceRebuild(); - } - - void OnEditboxChanged(WidgetID wid) override - { - if (wid == WID_BRW_FILTER) { - this->string_filter.SetFilterTerm(this->editbox.text.buf); - this->InvalidateData(); - } - } - - void OnRealtimeTick([[maybe_unused]] uint delta_ms) override - { - CheckRedrawWaypointCoverage(this); - } + static WaypointPickerCallbacks instance; }; +/* static */ WaypointPickerCallbacks WaypointPickerCallbacks::instance; -/* static */ QueryString BuildRailWaypointWindow::editbox(BuildRailWaypointWindow::FILTER_LENGTH * MAX_CHAR_LENGTH, BuildRailWaypointWindow::FILTER_LENGTH); +struct BuildRailWaypointWindow : public PickerWindow { + BuildRailWaypointWindow(WindowDesc *desc, Window *parent) : PickerWindow(desc, parent, TRANSPORT_RAIL, WaypointPickerCallbacks::instance) + { + this->ConstructWindow(); + this->InvalidateData(); + } + + static inline HotkeyList hotkeys{"buildrailwaypoint", { + Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX), + }}; +}; /** Nested widget definition for the build NewGRF rail waypoint window */ static constexpr NWidgetPart _nested_build_waypoint_widgets[] = { @@ -2216,22 +1811,9 @@ static constexpr NWidgetPart _nested_build_waypoint_widgets[] = { NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_WAYPOINT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), EndContainer(), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_BRW_FILTER), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), - EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BRW_SCROLL), - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BRW_WAYPOINT_MATRIX), SetPIP(0, 2, 0), SetPadding(WidgetDimensions::unscaled.picker), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BRW_WAYPOINT), SetDataTip(0x0, STR_WAYPOINT_GRAPHICS_TOOLTIP), SetScrollbar(WID_BRW_SCROLL), EndContainer(), - EndContainer(), - EndContainer(), - NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BRW_SCROLL), - EndContainer(), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BRW_NAME), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetAlignment(SA_CENTER), - EndContainer(), - NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), + NWidgetFunction(MakePickerClassWidgets), + NWidgetFunction(MakePickerTypeWidgets), EndContainer(), }; @@ -2239,11 +1821,13 @@ static WindowDesc _build_waypoint_desc( WDP_AUTO, "build_waypoint", 0, 0, WC_BUILD_WAYPOINT, WC_BUILD_TOOLBAR, WDF_CONSTRUCTION, - std::begin(_nested_build_waypoint_widgets), std::end(_nested_build_waypoint_widgets) + std::begin(_nested_build_waypoint_widgets), std::end(_nested_build_waypoint_widgets), + &BuildRailWaypointWindow::hotkeys ); static void ShowBuildWaypointPicker(Window *parent) { + if (!WaypointPickerCallbacks::instance.IsActive()) return; new BuildRailWaypointWindow(&_build_waypoint_desc, parent); } @@ -2253,7 +1837,10 @@ static void ShowBuildWaypointPicker(Window *parent) void InitializeRailGui() { _build_depot_direction = DIAGDIR_NW; - _railstation.station_class = StationClassID::STAT_CLASS_DFLT; + _station_gui.sel_class = StationClassID::STAT_CLASS_DFLT; + _station_gui.sel_type = 0; + _waypoint_gui.sel_class = StationClassID::STAT_CLASS_WAYP; + _waypoint_gui.sel_type = 0; } /** diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 90b58e47d9..18ed083687 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -36,10 +36,7 @@ #include "road_cmd.h" #include "tunnelbridge_cmd.h" #include "newgrf_roadstop.h" -#include "querystring_gui.h" -#include "sortlist_type.h" -#include "stringfilter_type.h" -#include "string_func.h" +#include "picker_gui.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" @@ -64,40 +61,43 @@ static RoadType _cur_roadtype; static DiagDirection _road_depot_orientation; -struct RoadStopGUISettings { - DiagDirection orientation; - - RoadStopClassID roadstop_class; - uint16_t roadstop_type; - uint16_t roadstop_count; +struct RoadStopPickerSelection { + RoadStopClassID sel_class; ///< Selected road stop class. + uint16_t sel_type; ///< Selected road stop type within the class. + DiagDirection orientation; ///< Selected orientation of the road stop. }; -static RoadStopGUISettings _roadstop_gui_settings; +static RoadStopPickerSelection _roadstop_gui; + +static bool IsRoadStopEverAvailable(const RoadStopSpec *spec, StationType type) +{ + if (spec == nullptr) return true; + + if (HasBit(spec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false; + if (HasBit(spec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false; + + switch (spec->stop_type) { + case ROADSTOPTYPE_ALL: return true; + case ROADSTOPTYPE_PASSENGER: return type == STATION_BUS; + case ROADSTOPTYPE_FREIGHT: return type == STATION_TRUCK; + default: NOT_REACHED(); + } +} /** * Check whether a road stop type can be built. * @return true if building is allowed. */ -static bool IsRoadStopAvailable(const RoadStopSpec *roadstopspec, StationType type) +static bool IsRoadStopAvailable(const RoadStopSpec *spec, StationType type) { - if (roadstopspec == nullptr) return true; + if (spec == nullptr) return true; + if (IsRoadStopEverAvailable(spec, type)) return true; - if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false; - if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false; + if (!HasBit(spec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true; - if (roadstopspec->stop_type != ROADSTOPTYPE_ALL) { - switch (type) { - case STATION_BUS: if (roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER) return false; break; - case STATION_TRUCK: if (roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT) return false; break; - default: break; - } - } - - if (!HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true; - - uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, _cur_roadtype, type, 0); + uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, spec, nullptr, INVALID_TILE, _cur_roadtype, type, 0); if (cb_res == CALLBACK_FAILED) return true; - return Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res); + return Convert8bitBooleanCallback(spec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res); } void CcPlaySound_CONSTRUCTION_OTHER(Commands, const CommandCost &result, TileIndex tile) @@ -217,11 +217,11 @@ void CcRoadStop(Commands, const CommandCost &result, TileIndex tile, uint8_t wid static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg) { TileArea ta(start_tile, end_tile); - DiagDirection ddir = _roadstop_gui_settings.orientation; + DiagDirection ddir = _roadstop_gui.orientation; bool drive_through = ddir >= DIAGDIR_END; if (drive_through) ddir = static_cast(ddir - DIAGDIR_END); // Adjust picker result to actual direction. - RoadStopClassID spec_class = _roadstop_gui_settings.roadstop_class; - uint16_t spec_index = _roadstop_gui_settings.roadstop_type; + RoadStopClassID spec_class = _roadstop_gui.sel_class; + uint16_t spec_index = _roadstop_gui.sel_type; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { @@ -245,8 +245,8 @@ static void PlaceRoad_BusStation(TileIndex tile) if (_remove_button_clicked) { VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP); } else { - if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop. - VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP); + if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop. + VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP); } else { VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP); } @@ -263,8 +263,8 @@ static void PlaceRoad_TruckStation(TileIndex tile) if (_remove_button_clicked) { VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP); } else { - if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop. - VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); + if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop. + VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); } else { VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); } @@ -708,7 +708,7 @@ struct BuildRoadToolbarWindow : Window { case DDSP_BUILD_BUSSTOP: case DDSP_REMOVE_BUSSTOP: - if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_BUS, _cur_roadtype)) { + if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui.sel_class), ROADSTOP_BUS, _cur_roadtype)) { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); Command::Post(this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_BUS, _ctrl_pressed); @@ -720,7 +720,7 @@ struct BuildRoadToolbarWindow : Window { case DDSP_BUILD_TRUCKSTOP: case DDSP_REMOVE_TRUCKSTOP: - if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_TRUCK, _cur_roadtype)) { + if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui.sel_class), ROADSTOP_TRUCK, _cur_roadtype)) { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); Command::Post(this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_TRUCK, _ctrl_pressed); @@ -1093,101 +1093,126 @@ static void ShowRoadDepotPicker(Window *parent) new BuildRoadDepotWindow(&_build_road_depot_desc, parent); } -/** Enum referring to the Hotkeys in the build road stop window */ -enum BuildRoadStopHotkeys { - BROSHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string +template +class RoadStopPickerCallbacks : public PickerCallbacksNewGRFClass { +public: + StringID GetClassTooltip() const override; + StringID GetTypeTooltip() const override; + + bool IsActive() const override + { + for (const auto &cls : RoadStopClass::Classes()) { + if (IsWaypointClass(cls)) continue; + for (const auto *spec : cls.Specs()) { + if (spec == nullptr) continue; + if (roadstoptype == ROADSTOP_TRUCK && spec->stop_type != ROADSTOPTYPE_FREIGHT && spec->stop_type != ROADSTOPTYPE_ALL) continue; + if (roadstoptype == ROADSTOP_BUS && spec->stop_type != ROADSTOPTYPE_PASSENGER && spec->stop_type != ROADSTOPTYPE_ALL) continue; + return true; + } + } + return false; + } + + static bool IsClassChoice(const RoadStopClass &cls) + { + return !IsWaypointClass(cls) && GetIfClassHasNewStopsByType(&cls, roadstoptype, _cur_roadtype); + } + + bool HasClassChoice() const override + { + return std::count_if(std::begin(RoadStopClass::Classes()), std::end(RoadStopClass::Classes()), IsClassChoice); + } + + int GetSelectedClass() const override { return _roadstop_gui.sel_class; } + void SetSelectedClass(int id) const override { _roadstop_gui.sel_class = this->GetClassIndex(id); } + + StringID GetClassName(int id) const override + { + const auto *rsc = this->GetClass(id); + if (!IsClassChoice(*rsc)) return INVALID_STRING_ID; + return rsc->name; + } + + int GetSelectedType() const override { return _roadstop_gui.sel_type; } + void SetSelectedType(int id) const override { _roadstop_gui.sel_type = id; } + + StringID GetTypeName(int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + if (!IsRoadStopEverAvailable(spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK)) return INVALID_STRING_ID; + return (spec == nullptr) ? STR_STATION_CLASS_DFLT_ROADSTOP : spec->name; + } + + bool IsTypeAvailable(int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + return IsRoadStopAvailable(spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK); + } + + void DrawType(int x, int y, int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + if (spec == nullptr) { + StationPickerDrawSprite(x, y, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui.orientation); + } else { + DiagDirection orientation = _roadstop_gui.orientation; + if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END; + DrawRoadStopTile(x, y, _cur_roadtype, spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, (uint8_t)orientation); + } + } }; -struct BuildRoadStationWindow : public PickerWindowBase { +template <> StringID RoadStopPickerCallbacks::GetClassTooltip() const { return STR_PICKER_ROADSTOP_BUS_CLASS_TOOLTIP; } +template <> StringID RoadStopPickerCallbacks::GetTypeTooltip() const { return STR_PICKER_ROADSTOP_BUS_TYPE_TOOLTIP; } + +template <> StringID RoadStopPickerCallbacks::GetClassTooltip() const { return STR_PICKER_ROADSTOP_TRUCK_CLASS_TOOLTIP; } +template <> StringID RoadStopPickerCallbacks::GetTypeTooltip() const { return STR_PICKER_ROADSTOP_TRUCK_TYPE_TOOLTIP; } + +static RoadStopPickerCallbacks _bus_callback_instance; +static RoadStopPickerCallbacks _truck_callback_instance; + +static PickerCallbacks &GetRoadStopPickerCallbacks(RoadStopType rs) +{ + return rs == ROADSTOP_BUS ? static_cast(_bus_callback_instance) : static_cast(_truck_callback_instance); +} + +struct BuildRoadStationWindow : public PickerWindow { private: - RoadStopType roadStopType; ///< The RoadStopType for this Window. - uint line_height; ///< Height of a single line in the newstation selection matrix. uint coverage_height; ///< Height of the coverage texts. - Scrollbar *vscrollList; ///< Vertical scrollbar of the new station list. - Scrollbar *vscrollMatrix; ///< Vertical scrollbar of the station picker matrix. - - typedef GUIList GUIRoadStopClassList; ///< Type definition for the list to hold available road stop classes. - - static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box. - - static Listing last_sorting; ///< Default sorting of #GUIRoadStopClassList. - static Filtering last_filtering; ///< Default filtering of #GUIRoadStopClassList. - static const std::initializer_list sorter_funcs; ///< Sort functions of the #GUIRoadStopClassList. - static const std::initializer_list filter_funcs; ///< Filter functions of the #GUIRoadStopClassList. - GUIRoadStopClassList roadstop_classes; ///< Available road stop classes. - StringFilter string_filter; ///< Filter for available road stop classes. - QueryString filter_editbox; ///< Filter editbox. - - void EnsureSelectedClassIsVisible() - { - uint pos = 0; - for (auto rs_class : this->roadstop_classes) { - if (rs_class == _roadstop_gui_settings.roadstop_class) break; - pos++; - } - this->vscrollList->SetCount(this->roadstop_classes.size()); - this->vscrollList->ScrollTowards(pos); - } void CheckOrientationValid() { - const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type); /* Raise and lower to ensure the correct widget is lowered after changing displayed orientation plane. */ if (RoadTypeIsRoad(_cur_roadtype)) { - this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); + this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); this->GetWidget(WID_BROS_AVAILABLE_ORIENTATIONS)->SetDisplayedPlane((spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) ? 1 : 0); - this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); + this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); } - if (_roadstop_gui_settings.orientation >= DIAGDIR_END) return; + if (_roadstop_gui.orientation >= DIAGDIR_END) return; if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) { - this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); - _roadstop_gui_settings.orientation = DIAGDIR_END; - this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); + this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); + _roadstop_gui.orientation = DIAGDIR_END; + this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); this->SetDirty(); CloseWindowById(WC_SELECT_STATION, 0); } } public: - BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindowBase(desc, parent), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE) + BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindow(desc, parent, TRANSPORT_ROAD, GetRoadStopPickerCallbacks(rs)) { this->coverage_height = 2 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; - this->vscrollList = nullptr; - this->vscrollMatrix = nullptr; - this->roadStopType = rs; - bool newstops = GetIfNewStopsByType(rs, _cur_roadtype); - - this->CreateNestedTree(); - - /* Hide the station class filter if no stations other than the default one are available. */ - this->GetWidget(WID_BROS_SHOW_NEWST_DEFSIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE); - this->GetWidget(WID_BROS_FILTER_CONTAINER)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); - this->GetWidget(WID_BROS_SHOW_NEWST_ADDITIONS)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); - this->GetWidget(WID_BROS_SHOW_NEWST_ORIENTATION)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); - this->GetWidget(WID_BROS_SHOW_NEWST_TYPE_SEL)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); - this->GetWidget(WID_BROS_SHOW_NEWST_MATRIX)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE); - this->GetWidget(WID_BROS_SHOW_NEWST_RESIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE); - if (newstops) { - this->vscrollList = this->GetScrollbar(WID_BROS_NEWST_SCROLL); - this->vscrollMatrix = this->GetScrollbar(WID_BROS_MATRIX_SCROLL); - - this->querystrings[WID_BROS_FILTER_EDITBOX] = &this->filter_editbox; - this->roadstop_classes.SetListing(this->last_sorting); - this->roadstop_classes.SetFiltering(this->last_filtering); - this->roadstop_classes.SetSortFuncs(this->sorter_funcs); - this->roadstop_classes.SetFilterFuncs(this->filter_funcs); - } - - this->roadstop_classes.ForceRebuild(); - BuildRoadStopClassesAvailable(); /* Trams don't have non-drivethrough stations */ - if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui_settings.orientation < DIAGDIR_END) { - _roadstop_gui_settings.orientation = DIAGDIR_END; + if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui.orientation < DIAGDIR_END) { + _roadstop_gui.orientation = DIAGDIR_END; } + this->ConstructWindow(); + const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype); this->GetWidget(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs]; @@ -1195,130 +1220,24 @@ public: this->GetWidget(i)->tool_tip = rti->strings.picker_tooltip[rs]; } - this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); + this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); this->LowerWidget(WID_BROS_LT_OFF + _settings_client.gui.station_show_coverage); - this->FinishInitNested(TRANSPORT_ROAD); - this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION; - if (!newstops || _roadstop_gui_settings.roadstop_class >= RoadStopClass::GetClassCount()) { - /* There's no new stops available or the list has reduced in size. - * Now, set the default road stops as selected. */ - _roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT; - _roadstop_gui_settings.roadstop_type = 0; - } - if (newstops) { - /* The currently selected class doesn't have any stops for this RoadStopType, reset the selection. */ - if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), rs, _cur_roadtype)) { - _roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT; - _roadstop_gui_settings.roadstop_type = 0; - } - _roadstop_gui_settings.roadstop_count = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpecCount(); - _roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, _roadstop_gui_settings.roadstop_count - 1); - - /* Reset back to default class if the previously selected class is not available for this road stop type. */ - if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), roadStopType, _cur_roadtype)) { - _roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT; - } - - this->SelectFirstAvailableTypeIfUnavailable(); - - NWidgetMatrix *matrix = this->GetWidget(WID_BROS_MATRIX); - matrix->SetScrollbar(this->vscrollMatrix); - matrix->SetCount(_roadstop_gui_settings.roadstop_count); - matrix->SetClicked(_roadstop_gui_settings.roadstop_type); - - this->EnsureSelectedClassIsVisible(); - this->CheckOrientationValid(); - } } void Close([[maybe_unused]] int data = 0) override { CloseWindowById(WC_SELECT_STATION, 0); - this->PickerWindowBase::Close(); - } - - /** Sort classes by RoadStopClassID. */ - static bool RoadStopClassIDSorter(RoadStopClassID const &a, RoadStopClassID const &b) - { - return a < b; - } - - /** Filter classes by class name. */ - static bool TagNameFilter(RoadStopClassID const *sc, StringFilter &filter) - { - filter.ResetState(); - filter.AddLine(GetString(RoadStopClass::Get(*sc)->name)); - return filter.GetState(); - } - - inline bool ShowNewStops() const - { - return this->vscrollList != nullptr; - } - - void BuildRoadStopClassesAvailable() - { - if (!this->roadstop_classes.NeedRebuild()) return; - - this->roadstop_classes.clear(); - this->roadstop_classes.reserve(RoadStopClass::GetClassCount()); - - for (const auto &cls : RoadStopClass::Classes()) { - /* Skip waypoints. */ - if (IsWaypointClass(cls)) continue; - if (GetIfClassHasNewStopsByType(&cls, this->roadStopType, _cur_roadtype)) this->roadstop_classes.push_back(cls.Index()); - } - - if (this->ShowNewStops()) { - this->roadstop_classes.Filter(this->string_filter); - this->roadstop_classes.RebuildDone(); - this->roadstop_classes.Sort(); - - this->vscrollList->SetCount(this->roadstop_classes.size()); - } - } - - void SelectFirstAvailableTypeIfUnavailable() - { - const RoadStopClass *rs_class = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class); - StationType st = GetRoadStationTypeByWindowClass(this->window_class); - - if (IsRoadStopAvailable(rs_class->GetSpec(_roadstop_gui_settings.roadstop_type), st)) return; - for (uint i = 0; i < _roadstop_gui_settings.roadstop_count; i++) { - if (IsRoadStopAvailable(rs_class->GetSpec(i), st)) { - _roadstop_gui_settings.roadstop_type = i; - break; - } - } + this->PickerWindow::Close(); } void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { - if (!gui_scope) return; + this->PickerWindow::OnInvalidateData(data, gui_scope); - this->BuildRoadStopClassesAvailable(); - } - - EventState OnHotkey(int hotkey) override - { - if (hotkey == BROSHK_FOCUS_FILTER_BOX) { - this->SetFocusedWidget(WID_BROS_FILTER_EDITBOX); - SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused. - return ES_HANDLED; - } - - return ES_NOT_HANDLED; - } - - void OnEditboxChanged(WidgetID widget) override - { - if (widget == WID_BROS_FILTER_EDITBOX) { - string_filter.SetFilterTerm(this->filter_editbox.text.buf); - this->roadstop_classes.SetFilterState(!string_filter.IsEmpty()); - this->roadstop_classes.ForceRebuild(); - this->InvalidateData(); + if (gui_scope) { + this->CheckOrientationValid(); } } @@ -1353,52 +1272,23 @@ public: void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { - case WID_BROS_NEWST_LIST: { - Dimension d = { 0, 0 }; - for (auto rs_class : this->roadstop_classes) { - d = maxdim(d, GetStringBoundingBox(RoadStopClass::Get(rs_class)->name)); - } - size.width = std::max(size.width, d.width + padding.width); - this->line_height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.matrix.Vertical(); - size.height = 5 * this->line_height; - resize.height = this->line_height; - break; - } - - case WID_BROS_SHOW_NEWST_TYPE: { - Dimension d = {0, 0}; - StringID str = this->GetWidget(widget)->widget_data; - for (auto roadstop_class : this->roadstop_classes) { - RoadStopClass *rs_class = RoadStopClass::Get(roadstop_class); - for (uint j = 0; j < rs_class->GetSpecCount(); j++) { - const RoadStopSpec *roadstopspec = rs_class->GetSpec(j); - SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT_ROADSTOP); - d = maxdim(d, GetStringBoundingBox(str)); - } - } - size.width = std::max(size.width, d.width + padding.width); - break; - } - case WID_BROS_STATION_NE: case WID_BROS_STATION_SE: case WID_BROS_STATION_SW: case WID_BROS_STATION_NW: case WID_BROS_STATION_X: case WID_BROS_STATION_Y: - case WID_BROS_IMAGE: - size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal(); - size.height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical(); - break; - - case WID_BROS_MATRIX: - fill.height = 1; - resize.height = 1; + size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal(); + size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical(); break; case WID_BROS_ACCEPTANCE: size.height = this->coverage_height; break; + + default: + this->PickerWindow::UpdateWidgetSize(widget, size, padding, fill, resize); + break; } } @@ -1424,13 +1314,13 @@ public: case WID_BROS_STATION_X: case WID_BROS_STATION_Y: { StationType st = GetRoadStationTypeByWindowClass(this->window_class); - const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type); DrawPixelInfo tmp_dpi; Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); if (FillDrawPixelInfo(&tmp_dpi, ir)) { AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (ir.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31); + int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT); + int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM); if (spec == nullptr) { StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE); } else { @@ -1440,63 +1330,9 @@ public: break; } - case WID_BROS_NEWST_LIST: { - uint statclass = 0; - uint row = 0; - for (auto rs_class : this->roadstop_classes) { - if (this->vscrollList->IsVisible(statclass)) { - DrawString(r.left + WidgetDimensions::scaled.matrix.left, r.right, row * this->line_height + r.top + WidgetDimensions::scaled.matrix.top, - RoadStopClass::Get(rs_class)->name, - rs_class == _roadstop_gui_settings.roadstop_class ? TC_WHITE : TC_BLACK); - row++; - } - statclass++; - } + default: + this->PickerWindow::DrawWidget(r, widget); break; - } - - case WID_BROS_IMAGE: { - uint16_t type = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); - assert(type < _roadstop_gui_settings.roadstop_count); - - const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(type); - StationType st = GetRoadStationTypeByWindowClass(this->window_class); - - /* Set up a clipping area for the sprite preview. */ - DrawPixelInfo tmp_dpi; - Rect ir = r.Shrink(WidgetDimensions::scaled.bevel); - if (FillDrawPixelInfo(&tmp_dpi, ir)) { - AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (ir.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31); - if (spec == nullptr) { - StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui_settings.orientation); - } else { - DiagDirection orientation = _roadstop_gui_settings.orientation; - if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END; - DrawRoadStopTile(x, y, _cur_roadtype, spec, st, (uint8_t)orientation); - } - } - if (!IsRoadStopAvailable(spec, st)) { - GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER); - } - break; - } - } - } - - void OnResize() override - { - if (this->vscrollList != nullptr) { - this->vscrollList->SetCapacityFromWidget(this, WID_BROS_NEWST_LIST); - } - } - - void SetStringParameters(WidgetID widget) const override - { - if (widget == WID_BROS_SHOW_NEWST_TYPE) { - const RoadStopSpec *roadstopspec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); - SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT_ROADSTOP); } } @@ -1510,12 +1346,12 @@ public: case WID_BROS_STATION_X: case WID_BROS_STATION_Y: if (widget < WID_BROS_STATION_X) { - const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type); if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) return; } - this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); - _roadstop_gui_settings.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE); - this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation); + this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); + _roadstop_gui.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE); + this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); this->SetDirty(); CloseWindowById(WC_SELECT_STATION, 0); @@ -1531,49 +1367,8 @@ public: SetViewportCatchmentStation(nullptr, true); break; - case WID_BROS_NEWST_LIST: { - auto it = this->vscrollList->GetScrolledItemFromWidget(this->roadstop_classes, pt.y, this, WID_BROS_NEWST_LIST); - if (it == this->roadstop_classes.end()) return; - RoadStopClassID class_id = *it; - if (_roadstop_gui_settings.roadstop_class != class_id && GetIfClassHasNewStopsByType(RoadStopClass::Get(class_id), roadStopType, _cur_roadtype)) { - _roadstop_gui_settings.roadstop_class = class_id; - RoadStopClass *rsclass = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class); - _roadstop_gui_settings.roadstop_count = rsclass->GetSpecCount(); - _roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, std::max(0, (int)_roadstop_gui_settings.roadstop_count - 1)); - this->SelectFirstAvailableTypeIfUnavailable(); - - NWidgetMatrix *matrix = this->GetWidget(WID_BROS_MATRIX); - matrix->SetCount(_roadstop_gui_settings.roadstop_count); - matrix->SetClicked(_roadstop_gui_settings.roadstop_type); - } - if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); - this->SetDirty(); - CloseWindowById(WC_SELECT_STATION, 0); - this->CheckOrientationValid(); - break; - } - - case WID_BROS_IMAGE: { - uint16_t y = this->GetWidget(widget)->GetParentWidget()->GetCurrentElement(); - if (y >= _roadstop_gui_settings.roadstop_count) return; - - const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(y); - StationType st = GetRoadStationTypeByWindowClass(this->window_class); - - if (!IsRoadStopAvailable(spec, st)) return; - - _roadstop_gui_settings.roadstop_type = y; - - this->GetWidget(widget)->GetParentWidget()->SetClicked(_roadstop_gui_settings.roadstop_type); - - if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); - this->SetDirty(); - CloseWindowById(WC_SELECT_STATION, 0); - this->CheckOrientationValid(); - break; - } - default: + this->PickerWindow::OnClick(pt, widget, click_count); break; } } @@ -1583,119 +1378,66 @@ public: CheckRedrawStationCoverage(this); } - IntervalTimer yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) { - this->InvalidateData(); - }}; - static inline HotkeyList road_hotkeys{"buildroadstop", { - Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX), + Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX), }}; static inline HotkeyList tram_hotkeys{"buildtramstop", { - Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX), + Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX), }}; }; -Listing BuildRoadStationWindow::last_sorting = { false, 0 }; -Filtering BuildRoadStationWindow::last_filtering = { false, 0 }; - -const std::initializer_list BuildRoadStationWindow::sorter_funcs = { - &RoadStopClassIDSorter, -}; - -const std::initializer_list BuildRoadStationWindow::filter_funcs = { - &TagNameFilter, -}; - /** Widget definition of the build road station window */ static constexpr NWidgetPart _nested_road_station_picker_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION), NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE), - NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), - EndContainer(), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), - NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0), - SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + NWidget(NWID_VERTICAL), + NWidgetFunction(MakePickerClassWidgets), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_AVAILABLE_ORIENTATIONS), + /* 6-orientation plane. */ + NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetFill(0, 0), EndContainer(), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetFill(0, 0), EndContainer(), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(), EndContainer(), EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0), - SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL), - NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_AVAILABLE_ORIENTATIONS), - /* 6-orientation plane. */ - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetFill(0, 0), EndContainer(), - EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(), - EndContainer(), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetFill(0, 0), EndContainer(), - EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(), - EndContainer(), - EndContainer(), - /* 2-orientation plane. */ - NWidget(NWID_VERTICAL), SetPIPRatio(0, 0, 1), - NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(), - EndContainer(), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetFill(1, 0), - EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), - NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX), - /* Hidden panel as NWID_MATRIX does not support SetScrollbar() */ - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL), - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE), - SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL), - EndContainer(), + /* 2-orientation plane. */ + NWidget(NWID_VERTICAL), SetPIPRatio(0, 0, 1), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(), EndContainer(), EndContainer(), EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), + EndContainer(), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0), EndContainer(), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE), - NWidget(NWID_VERTICAL), - NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL), - NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), EndContainer(), EndContainer(), + NWidgetFunction(MakePickerTypeWidgets), EndContainer(), }; @@ -1713,67 +1455,29 @@ static constexpr NWidgetPart _nested_tram_station_picker_widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION), NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE), - NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), - EndContainer(), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER), - NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), - NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0), - SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0), - SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL), - NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0), - EndContainer(), - NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetFill(1, 0), - EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), - NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), - EndContainer(), + NWidget(NWID_VERTICAL), + NWidgetFunction(MakePickerClassWidgets), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(), EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX), - /* Hidden panel as NWID_MATRIX does not support SetScrollbar() */ - NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL), - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE), - SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL), - EndContainer(), - EndContainer(), - EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), EndContainer(), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0), EndContainer(), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal), - EndContainer(), - EndContainer(), - NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE), - NWidget(NWID_VERTICAL), - NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL), - NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), EndContainer(), EndContainer(), + NWidgetFunction(MakePickerTypeWidgets), EndContainer(), }; @@ -1793,7 +1497,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs) void InitializeRoadGui() { _road_depot_orientation = DIAGDIR_NW; - _roadstop_gui_settings.orientation = DIAGDIR_NW; + _roadstop_gui.orientation = DIAGDIR_NW; } /** diff --git a/src/waypoint.cpp b/src/waypoint.cpp index db1b36b1ec..78f9ba0df7 100644 --- a/src/waypoint.cpp +++ b/src/waypoint.cpp @@ -21,12 +21,13 @@ * Draw a waypoint * @param x coordinate * @param y coordinate - * @param stat_id station id + * @param station_class Station class. + * @param station_type Station type within class. * @param railtype RailType to use for */ -void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype) +void DrawWaypointSprite(int x, int y, StationClassID station_class, uint16_t station_type, RailType railtype) { - if (!DrawStationTile(x, y, railtype, AXIS_X, STAT_CLASS_WAYP, stat_id)) { + if (!DrawStationTile(x, y, railtype, AXIS_X, station_class, station_type)) { StationPickerDrawSprite(x, y, STATION_WAYPOINT, railtype, INVALID_ROADTYPE, AXIS_X); } } diff --git a/src/waypoint_func.h b/src/waypoint_func.h index 2906fa6369..e919390576 100644 --- a/src/waypoint_func.h +++ b/src/waypoint_func.h @@ -14,10 +14,12 @@ #include "command_type.h" #include "station_type.h" +enum StationClassID : uint16_t; + CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags); Axis GetAxisForNewWaypoint(TileIndex tile); void ShowWaypointWindow(const Waypoint *wp); -void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype); +void DrawWaypointSprite(int x, int y, StationClassID station_class, uint16_t station_type, RailType railtype); #endif /* WAYPOINT_FUNC_H */ diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 933e8c5056..a69f7e7b16 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -39,6 +39,7 @@ add_files( object_widget.h order_widget.h osk_widget.h + picker_widget.h rail_widget.h road_widget.h screenshot_widget.h diff --git a/src/widgets/object_widget.h b/src/widgets/object_widget.h index 9adf13dde7..b01e1d3ae4 100644 --- a/src/widgets/object_widget.h +++ b/src/widgets/object_widget.h @@ -12,18 +12,10 @@ /** Widgets of the #BuildObjectWindow class. */ enum BuildObjectWidgets : WidgetID { - WID_BO_FILTER, ///< The filter text box for the object list. - WID_BO_CLASS_LIST, ///< The list with classes. - WID_BO_SCROLLBAR, ///< The scrollbar associated with the list. WID_BO_OBJECT_MATRIX, ///< The matrix with preview sprites. WID_BO_OBJECT_SPRITE, ///< A preview sprite of the object. - WID_BO_OBJECT_NAME, ///< The name of the selected object. WID_BO_OBJECT_SIZE, ///< The size of the selected object. WID_BO_INFO, ///< Other information about the object (from the NewGRF). - - WID_BO_SELECT_MATRIX, ///< Selection preview matrix of objects of a given class. - WID_BO_SELECT_IMAGE, ///< Preview image in the #WID_BO_SELECT_MATRIX. - WID_BO_SELECT_SCROLL, ///< Scrollbar next to the #WID_BO_SELECT_MATRIX. }; #endif /* WIDGETS_OBJECT_WIDGET_H */ diff --git a/src/widgets/picker_widget.h b/src/widgets/picker_widget.h new file mode 100644 index 0000000000..6e89da98e8 --- /dev/null +++ b/src/widgets/picker_widget.h @@ -0,0 +1,31 @@ +/* + * This file is part of OpenTTD. + * 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. + * 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. + * 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 . + */ + +/** @file picker_widget.h Types related to the picker widgets. */ + +#ifndef WIDGETS_PICKER_WIDGET_H +#define WIDGETS_PICKER_WIDGET_H + +/** Widgets of the #PickerWindow class. */ +enum PickerClassWindowWidgets : WidgetID { + WID_PW_START = 1 << 16, ///< Dummy to ensure widgets don't overlap. + + WID_PW_CLASS_SEL, ///< Stack to hide the class picker. + WID_PW_CLASS_FILTER, ///< Editbox filter. + WID_PW_CLASS_LIST, ///< List of classes. + WID_PW_CLASS_SCROLL, ///< Scrollbar for list of classes. + + WID_PW_TYPE_SEL, ///< Stack to hide the type picker. + WID_PW_TYPE_FILTER, ///< Text filter. + WID_PW_TYPE_MATRIX, ///< Matrix with items. + WID_PW_TYPE_ITEM, ///< A single item. + WID_PW_TYPE_SCROLL, ///< Scrollbar for the matrix. + WID_PW_TYPE_NAME, ///< Name of selected item. + WID_PW_TYPE_RESIZE, ///< Type resize handle. +}; + +#endif /* WIDGETS_PICKER_WIDGET_H */ diff --git a/src/widgets/rail_widget.h b/src/widgets/rail_widget.h index a5834b2779..f90280c52b 100644 --- a/src/widgets/rail_widget.h +++ b/src/widgets/rail_widget.h @@ -60,20 +60,6 @@ enum BuildRailStationWidgets : WidgetID { WID_BRAS_HIGHLIGHT_ON, ///< Button for turning coverage highlighting on. WID_BRAS_COVERAGE_TEXTS, ///< Empty space for the coverage texts. - WID_BRAS_MATRIX, ///< Matrix widget displaying the available stations. - WID_BRAS_IMAGE, ///< Panel used at each cell of the matrix. - WID_BRAS_MATRIX_SCROLL, ///< Scrollbar of the matrix widget. - - WID_BRAS_FILTER_CONTAINER, ///< Container for the filter text box for the station class list. - WID_BRAS_FILTER_EDITBOX, ///< Filter text box for the station class list. - WID_BRAS_SHOW_NEWST_DEFSIZE, ///< Selection for default-size button for newstation. - WID_BRAS_SHOW_NEWST_ADDITIONS, ///< Selection for newstation class selection list. - WID_BRAS_SHOW_NEWST_MATRIX, ///< Selection for newstation image matrix. - WID_BRAS_SHOW_NEWST_RESIZE, ///< Selection for panel and resize at bottom right for newstation. - WID_BRAS_SHOW_NEWST_TYPE, ///< Display of selected station type. - WID_BRAS_NEWST_LIST, ///< List with available newstation classes. - WID_BRAS_NEWST_SCROLL, ///< Scrollbar of the #WID_BRAS_NEWST_LIST. - WID_BRAS_PLATFORM_NUM_BEGIN = WID_BRAS_PLATFORM_NUM_1 - 1, ///< Helper for determining the chosen platform width. WID_BRAS_PLATFORM_LEN_BEGIN = WID_BRAS_PLATFORM_LEN_1 - 1, ///< Helper for determining the chosen platform length. }; diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h index 5da57d024c..c876acbad2 100644 --- a/src/widgets/road_widget.h +++ b/src/widgets/road_widget.h @@ -53,21 +53,7 @@ enum BuildRoadStationWidgets : WidgetID { WID_BROS_LT_OFF, ///< Turn off area highlight. WID_BROS_LT_ON, ///< Turn on area highlight. WID_BROS_ACCEPTANCE, ///< Station acceptance info. - WID_BROS_MATRIX, ///< Matrix widget displaying all available road stops. - WID_BROS_IMAGE, ///< Panel used for each image of the matrix. - WID_BROS_MATRIX_SCROLL, ///< Scrollbar of the #WID_BROS_SHOW_NEWST_ADDITIONS. - WID_BROS_FILTER_CONTAINER, ///< Container for the filter text box for the road stop class list. - WID_BROS_FILTER_EDITBOX, ///< Filter text box for the road stop class list. WID_BROS_AVAILABLE_ORIENTATIONS, ///< Selection for selecting 6 or 2 orientations. - WID_BROS_SHOW_NEWST_DEFSIZE, ///< Selection for default-size button for new road stops. - WID_BROS_SHOW_NEWST_ADDITIONS, ///< Selection for new class selection list. - WID_BROS_SHOW_NEWST_MATRIX, ///< Selection for new stop image matrix. - WID_BROS_SHOW_NEWST_RESIZE, ///< Selection for panel and resize at bottom right for new stops. - WID_BROS_SHOW_NEWST_ORIENTATION, ///< Selection for the orientation string for new stops. - WID_BROS_SHOW_NEWST_TYPE_SEL, ///< Selection for the type name. - WID_BROS_SHOW_NEWST_TYPE, ///< Display of selected stop type. - WID_BROS_NEWST_LIST, ///< List with new road stops. - WID_BROS_NEWST_SCROLL, ///< Scrollbar of the #WID_BROS_NEWST_LIST. }; #endif /* WIDGETS_ROAD_WIDGET_H */