Changeset - r28709:d39f67152bd6
[Not reviewed]
master
0 4 0
Peter Nelson - 12 months ago 2023-12-03 09:22:10
peter1138@openttd.org
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.
4 files changed with 83 insertions and 119 deletions:
0 comments (0 inline, 0 general)
src/cargo_type.h
Show inline comments
 
@@ -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 */
src/lang/english.txt
Show inline comments
 
@@ -159,7 +159,6 @@ STR_ABBREV_TOFFEE                       
 
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                
 
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}
src/station_gui.cpp
Show inline comments
 
@@ -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<NWidgetCore>(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<DropDownListIconItem>;
 

	
 
		DropDownList list;
 
		list.push_back(std::make_unique<DropDownListStringItem>(STR_STATION_LIST_CARGO_FILTER_SELECT_ALL, CargoFilterCriteria::CF_SELECT_ALL));
 
		list.push_back(std::make_unique<DropDownListDividerItem>(-1));
 
		list.push_back(std::make_unique<DropDownListCheckedItem>(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<DropDownListCargoItem>(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<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = CompanyStationsWindow::sorter_names[this->stations.SortType()];
 
				this->GetWidget<NWidgetCore>(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::so
 
	INVALID_STRING_ID
 
};
 

	
 
/**
 
 * Make a horizontal row of cargo buttons, starting at widget #WID_STL_CARGOSTART.
 
 * @return Horizontal row.
 
 */
 
static std::unique_ptr<NWidgetBase> CargoWidgets()
 
{
 
	auto container = std::make_unique<NWidgetHorizontal>();
 

	
 
	for (uint i = 0; i < _sorted_standard_cargo_specs.size(); i++) {
 
		auto panel = std::make_unique<NWidgetBackground>(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_com
 
		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),
src/widgets/station_widget.h
Show inline comments
 
@@ -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. */
0 comments (0 inline, 0 general)