From 46da56b19145e76ffa185739455ef238c6361842 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 3 Dec 2023 09:22:10 +0000 Subject: [PATCH] Change: Replace long list of cargo filter buttons with a multi-select dropdown list. As before, hold ctrl while clicking on an item to toggle selections. --- src/cargo_type.h | 3 + src/lang/english.txt | 9 +- src/station_gui.cpp | 185 ++++++++++++++--------------------- src/widgets/station_widget.h | 5 +- 4 files changed, 83 insertions(+), 119 deletions(-) diff --git a/src/cargo_type.h b/src/cargo_type.h index e3eca4772f..1fc8a0ede2 100644 --- a/src/cargo_type.h +++ b/src/cargo_type.h @@ -95,6 +95,9 @@ namespace CargoFilterCriteria { static constexpr CargoID CF_NONE = NUM_CARGO + 1; ///< Show only items which do not carry cargo (e.g. train engines) static constexpr CargoID CF_ENGINES = NUM_CARGO + 2; ///< Show only engines (for rail vehicles only) static constexpr CargoID CF_FREIGHT = NUM_CARGO + 3; ///< Show only vehicles which carry any freight (non-passenger) cargo + + static constexpr CargoID CF_NO_RATING = NUM_CARGO + 4; ///< Show items with no rating (station list) + static constexpr CargoID CF_SELECT_ALL = NUM_CARGO + 5; ///< Select all items (station list) }; /** Test whether cargo type is not CT_INVALID */ diff --git a/src/lang/english.txt b/src/lang/english.txt index 6e685acf82..e9733a1784 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -159,7 +159,6 @@ STR_ABBREV_TOFFEE :TF STR_ABBREV_BATTERIES :BA STR_ABBREV_PLASTIC :PL STR_ABBREV_FIZZY_DRINKS :FZ -STR_ABBREV_NONE :NO STR_ABBREV_ALL :ALL # 'Mode' of transport for cargoes @@ -3745,8 +3744,12 @@ STR_STATION_LIST_STATION :{YELLOW}{STATIO STR_STATION_LIST_WAYPOINT :{YELLOW}{WAYPOINT} STR_STATION_LIST_NONE :{YELLOW}- None - STR_STATION_LIST_SELECT_ALL_FACILITIES :{BLACK}Select all facilities -STR_STATION_LIST_SELECT_ALL_TYPES :{BLACK}Select all cargo types (including no waiting cargo) -STR_STATION_LIST_NO_WAITING_CARGO :{BLACK}No cargo of any type is waiting +STR_STATION_LIST_CARGO_FILTER_ALL_AND_NO_RATING :All cargo types and no rating +STR_STATION_LIST_CARGO_FILTER_MULTIPLE :Multiple cargo types +STR_STATION_LIST_CARGO_FILTER_NO_CARGO_TYPES :No cargo types +STR_STATION_LIST_CARGO_FILTER_ONLY_NO_RATING :Only no cargo rating +STR_STATION_LIST_CARGO_FILTER_SELECT_ALL :Select all and no rating +STR_STATION_LIST_CARGO_FILTER_NO_RATING :No cargo rating # Station view window STR_STATION_VIEW_CAPTION :{WHITE}{STATION} {STATION_FEATURES} diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 07553fbcd7..e580caa927 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -221,7 +221,7 @@ protected: struct FilterState { Listing last_sorting; byte facilities; ///< types of stations of interest - bool include_empty; ///< whether we should include stations without waiting cargo + bool include_no_rating; ///< Whether we should include stations with no cargo rating. CargoTypes cargoes; ///< bitmap of cargo types to include }; @@ -257,18 +257,18 @@ protected: for (const Station *st : Station::Iterate()) { if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) { if (this->filter.facilities & st->facilities) { // only stations with selected facilities - int num_waiting_cargo = 0; + bool has_rating = false; for (CargoID j = 0; j < NUM_CARGO; j++) { if (st->goods[j].HasRating()) { - num_waiting_cargo++; // count number of waiting cargo + has_rating = true; if (HasBit(this->filter.cargoes, j)) { this->stations.push_back(st); break; } } } - /* stations without waiting cargo */ - if (num_waiting_cargo == 0 && this->filter.include_empty) { + /* Stations with no cargo rating. */ + if (!has_rating && this->filter.include_no_rating) { this->stations.push_back(st); } } @@ -374,20 +374,11 @@ public: this->FinishInitNested(window_number); this->owner = (Owner)this->window_number; - uint8_t index = 0; - for (const CargoSpec *cs : _sorted_standard_cargo_specs) { - if (HasBit(this->filter.cargoes, cs->Index())) { - this->LowerWidget(WID_STL_CARGOSTART + index); - } - index++; - } - if (this->filter.cargoes == ALL_CARGOTYPES) this->filter.cargoes = _cargo_mask; for (uint i = 0; i < 5; i++) { if (HasBit(this->filter.facilities, i)) this->LowerWidget(i + WID_STL_TRAIN); } - this->SetWidgetLoweredState(WID_STL_NOCARGOWAITING, this->filter.include_empty); this->GetWidget(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()]; } @@ -433,15 +424,6 @@ public: /* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */ this->rating_width = this->rating_width * 16 / 10; break; - - default: - if (widget >= WID_STL_CARGOSTART) { - Dimension d = GetStringBoundingBox(_sorted_cargo_specs[widget - WID_STL_CARGOSTART]->abbrev, FS_SMALL); - d.width += padding.width + 2; - d.height += padding.height; - *size = maxdim(*size, d); - } - break; } } @@ -512,16 +494,6 @@ public: } break; } - - default: - if (widget >= WID_STL_CARGOSTART) { - Rect br = r.Shrink(WidgetDimensions::scaled.bevel); - const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART]; - GfxFillRect(br, cs->rating_colour); - TextColour tc = GetContrastColour(cs->rating_colour); - DrawString(br.left, br.right, CenterBounds(br.top, br.bottom, GetCharacterHeight(FS_SMALL)), cs->abbrev, tc, SA_HOR_CENTER, false, FS_SMALL); - } - break; } } @@ -531,6 +503,34 @@ public: SetDParam(0, this->window_number); SetDParam(1, this->vscroll->GetCount()); } + + if (widget == WID_STL_CARGODROPDOWN) { + if (this->filter.cargoes == 0) { + SetDParam(0, this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ONLY_NO_RATING : STR_STATION_LIST_CARGO_FILTER_NO_CARGO_TYPES); + } else if (this->filter.cargoes == _cargo_mask) { + SetDParam(0, this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ALL_AND_NO_RATING : STR_CARGO_TYPE_FILTER_ALL); + } else if (CountBits(this->filter.cargoes) == 1 && !this->filter.include_no_rating) { + SetDParam(0, CargoSpec::Get(FindFirstBit(this->filter.cargoes))->name); + } else { + SetDParam(0, STR_STATION_LIST_CARGO_FILTER_MULTIPLE); + } + } + } + + DropDownList BuildCargoDropDownList() const + { + /* Define a custom item consisting of check mark, icon and string. */ + using DropDownListCargoItem = DropDownCheck; + + DropDownList list; + list.push_back(std::make_unique(STR_STATION_LIST_CARGO_FILTER_SELECT_ALL, CargoFilterCriteria::CF_SELECT_ALL)); + list.push_back(std::make_unique(-1)); + list.push_back(std::make_unique(this->filter.include_no_rating, STR_STATION_LIST_CARGO_FILTER_NO_RATING, CargoFilterCriteria::CF_NO_RATING)); + for (const CargoSpec *cs : _sorted_cargo_specs) { + list.push_back(std::make_unique(HasBit(this->filter.cargoes, cs->Index()), cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index())); + } + + return list; } void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override @@ -581,19 +581,6 @@ public: this->SetDirty(); break; - case WID_STL_CARGOALL: { - for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) { - this->LowerWidget(WID_STL_CARGOSTART + i); - } - this->LowerWidget(WID_STL_NOCARGOWAITING); - - this->filter.cargoes = _cargo_mask; - this->filter.include_empty = true; - this->stations.ForceRebuild(); - this->SetDirty(); - break; - } - case WID_STL_SORTBY: // flip sorting method asc/desc this->stations.ToggleSortOrder(); this->SetDirty(); @@ -603,63 +590,59 @@ public: ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), WID_STL_SORTDROPBTN, 0, 0); break; - case WID_STL_NOCARGOWAITING: - if (_ctrl_pressed) { - this->filter.include_empty = !this->filter.include_empty; - this->ToggleWidgetLoweredState(WID_STL_NOCARGOWAITING); - } else { - for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) { - this->RaiseWidget(WID_STL_CARGOSTART + i); - } - - this->filter.cargoes = 0; - this->filter.include_empty = true; - - this->LowerWidget(WID_STL_NOCARGOWAITING); - } - this->stations.ForceRebuild(); - this->SetDirty(); - break; - - default: - if (widget >= WID_STL_CARGOSTART) { // change cargo_filter - /* Determine the selected cargo type */ - const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART]; - - if (_ctrl_pressed) { - ToggleBit(this->filter.cargoes, cs->Index()); - this->ToggleWidgetLoweredState(widget); - } else { - for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) { - this->RaiseWidget(WID_STL_CARGOSTART + i); - } - this->RaiseWidget(WID_STL_NOCARGOWAITING); - - this->filter.cargoes = 0; - this->filter.include_empty = false; - - SetBit(this->filter.cargoes, cs->Index()); - this->LowerWidget(widget); - } - this->stations.ForceRebuild(); - this->SetDirty(); - } + case WID_STL_CARGODROPDOWN: + /* Multiple-choice list should not have a default row selected, so use a non-present value. */ + ShowDropDownList(this, this->BuildCargoDropDownList(), -1, widget, 0, false, true); break; } } - void OnDropdownSelect(WidgetID widget, int index) override + void OnDropdownSelect(int widget, int index) override { if (widget == WID_STL_SORTDROPBTN) { if (this->stations.SortType() != index) { this->stations.SetSortType(index); /* Display the current sort variant */ - this->GetWidget(WID_STL_SORTDROPBTN)->widget_data = CompanyStationsWindow::sorter_names[this->stations.SortType()]; + this->GetWidget(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()]; this->SetDirty(); } } + + if (widget == WID_STL_CARGODROPDOWN) { + FilterState oldstate = this->filter; + + if (index >= 0 && index < NUM_CARGO) { + if (_ctrl_pressed) { + ToggleBit(this->filter.cargoes, index); + } else { + this->filter.cargoes = 1ULL << index; + this->filter.include_no_rating = false; + } + } else if (index == CargoFilterCriteria::CF_NO_RATING) { + if (_ctrl_pressed) { + this->filter.include_no_rating = !this->filter.include_no_rating; + } else { + this->filter.include_no_rating = true; + this->filter.cargoes = 0; + } + } else if (index == CargoFilterCriteria::CF_SELECT_ALL) { + this->filter.cargoes = _cargo_mask; + this->filter.include_no_rating = true; + } + + if (oldstate.cargoes != this->filter.cargoes || oldstate.include_no_rating != this->filter.include_no_rating) { + this->stations.ForceRebuild(); + this->SetDirty(); + + /* Only refresh the list if it's changed. */ + if (_ctrl_pressed) ReplaceDropDownList(this, this->BuildCargoDropDownList()); + } + + /* Always close the list if ctrl is not pressed. */ + if (!_ctrl_pressed) this->CloseChildWindows(WC_DROPDOWN_MENU); + } } void OnGameTick() override @@ -712,26 +695,6 @@ const StringID CompanyStationsWindow::sorter_names[] = { INVALID_STRING_ID }; -/** - * Make a horizontal row of cargo buttons, starting at widget #WID_STL_CARGOSTART. - * @return Horizontal row. - */ -static std::unique_ptr CargoWidgets() -{ - auto container = std::make_unique(); - - for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) { - auto panel = std::make_unique(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i); - panel->SetMinimalSize(14, 0); - panel->SetMinimalTextLines(1, 0, FS_NORMAL); - panel->SetResize(0, 0); - panel->SetFill(0, 1); - panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE); - container->Add(std::move(panel)); - } - return container; -} - static constexpr NWidgetPart _nested_company_stations_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), @@ -748,9 +711,7 @@ static constexpr NWidgetPart _nested_company_stations_widgets[] = { NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 0), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 0), SetDataTip(STR_ABBREV_ALL, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetTextStyle(TC_BLACK, FS_SMALL), SetFill(0, 1), NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 0), SetFill(0, 1), EndContainer(), - NWidgetFunction(CargoWidgets), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 0), SetDataTip(STR_ABBREV_NONE, STR_STATION_LIST_NO_WAITING_CARGO), SetTextStyle(TC_BLACK, FS_SMALL), SetFill(0, 1), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 0), SetDataTip(STR_ABBREV_ALL, STR_STATION_LIST_SELECT_ALL_TYPES), SetTextStyle(TC_BLACK, FS_SMALL), SetFill(0, 1), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_STL_CARGODROPDOWN), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), diff --git a/src/widgets/station_widget.h b/src/widgets/station_widget.h index 4ed97af79c..fda195456e 100644 --- a/src/widgets/station_widget.h +++ b/src/widgets/station_widget.h @@ -46,13 +46,10 @@ enum StationListWidgets : WidgetID { WID_STL_SHIP, ///< 'SHIP' button - list only facilities where is a dock. WID_STL_FACILALL, ///< 'ALL' button - list all facilities. - WID_STL_NOCARGOWAITING, ///< 'NO' button - list stations where no cargo is waiting. - WID_STL_CARGOALL, ///< 'ALL' button - list all stations. - WID_STL_SORTBY, ///< 'Sort by' button - reverse sort direction. WID_STL_SORTDROPBTN, ///< Dropdown button. - WID_STL_CARGOSTART, ///< Widget numbers used for list of cargo types (not present in _company_stations_widgets). + WID_STL_CARGODROPDOWN, ///< Cargo type dropdown list. }; /** Widgets of the #SelectStationWindow class. */