Feature: Add cargo filter support to vehicle list. (#8308)

This commit is contained in:
stormcone 2022-11-08 21:11:16 +01:00 committed by GitHub
parent a8a7f95665
commit 0d303d6c3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 183 additions and 11 deletions

View File

@ -81,6 +81,7 @@ static const NWidgetPart _nested_group_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_SORT_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_FILTER_BY_CARGO), SetMinimalSize(167, 12), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL),
@ -118,7 +119,6 @@ private:
VGC_END
};
VehicleID vehicle_sel; ///< Selected vehicle
GroupID group_sel; ///< Selected group (for drag/drop)
GroupID group_rename; ///< Group being renamed, INVALID_GROUP if none
GroupID group_over; ///< Group over which a vehicle is dragged, INVALID_GROUP if none
@ -349,7 +349,6 @@ public:
this->group_sb = this->GetScrollbar(WID_GL_LIST_GROUP_SCROLLBAR);
this->vli.index = ALL_GROUP;
this->vehicle_sel = INVALID_VEHICLE;
this->group_sel = INVALID_GROUP;
this->group_rename = INVALID_GROUP;
this->group_over = INVALID_GROUP;
@ -451,6 +450,10 @@ public:
void SetStringParameters(int widget) const override
{
switch (widget) {
case WID_GL_FILTER_BY_CARGO:
SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
break;
case WID_GL_AVAILABLE_VEHICLES:
SetDParam(0, STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
break;
@ -530,6 +533,9 @@ public:
/* Set text of "sort by" dropdown widget. */
this->GetWidget<NWidgetCore>(WID_GL_SORT_BY_DROPDOWN)->widget_data = this->GetVehicleSorterNames()[this->vehgroups.SortType()];
/* Set text of filter by cargo dropdown */
this->GetWidget<NWidgetCore>(WID_GL_FILTER_BY_CARGO)->widget_data = this->cargo_filter_texts[this->cargo_filter_criteria];
this->DrawWidgets();
}
@ -647,6 +653,10 @@ public:
ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_GL_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
return;
case WID_GL_FILTER_BY_CARGO: // Select filtering criteria dropdown menu
ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, WID_GL_FILTER_BY_CARGO, 0, 0);
break;
case WID_GL_ALL_VEHICLES: // All vehicles button
if (!IsAllGroupID(this->vli.index)) {
this->vli.index = ALL_GROUP;
@ -930,6 +940,10 @@ public:
this->vehgroups.SetSortType(index);
break;
case WID_GL_FILTER_BY_CARGO: // Select a cargo filter criteria
this->SetCargoFilterIndex(index);
break;
case WID_GL_MANAGE_VEHICLES_DROPDOWN:
assert(this->vehicles.size() != 0);

View File

@ -3884,6 +3884,11 @@ STR_PURCHASE_INFO_MAX_TE :{BLACK}Max. Tra
STR_PURCHASE_INFO_AIRCRAFT_RANGE :{BLACK}Range: {GOLD}{COMMA} tiles
STR_PURCHASE_INFO_AIRCRAFT_TYPE :{BLACK}Aircraft type: {GOLD}{STRING}
###length 3
STR_CARGO_TYPE_FILTER_ALL :All cargo types
STR_CARGO_TYPE_FILTER_FREIGHT :Freight
STR_CARGO_TYPE_FILTER_NONE :None
###length VEHICLE_TYPES
STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP :{BLACK}Train vehicle selection list. Click on vehicle for information. Ctrl+Click for toggling hiding of the vehicle type
STR_BUY_VEHICLE_ROAD_VEHICLE_LIST_TOOLTIP :{BLACK}Road vehicle selection list. Click on vehicle for information. Ctrl+Click for toggling hiding of the vehicle type

View File

@ -139,6 +139,7 @@ const StringID BaseVehicleListWindow::vehicle_depot_name[] = {
BaseVehicleListWindow::BaseVehicleListWindow(WindowDesc *desc, WindowNumber wno) : Window(desc), vli(VehicleListIdentifier::UnPack(wno))
{
this->vehicle_sel = INVALID_VEHICLE;
this->grouping = _grouping[vli.type][vli.vtype];
this->UpdateSortingFromGrouping();
}
@ -195,6 +196,8 @@ void BaseVehicleListWindow::BuildVehicleList()
max_unitnumber = std::max<uint>(max_unitnumber, (*it)->unitnumber);
}
this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
this->FilterVehicleList();
} else {
/* Sort by the primary vehicle; we just want all vehicles that share the same orders to form a contiguous range. */
std::stable_sort(this->vehicles.begin(), this->vehicles.end(), [](const Vehicle * const &u, const Vehicle * const &v) {
@ -223,6 +226,118 @@ void BaseVehicleListWindow::BuildVehicleList()
this->vscroll->SetCount(static_cast<int>(this->vehgroups.size()));
}
/** Cargo filter functions */
/**
* Check whether a vehicle can carry a specific cargo.
* @param vehgroup The vehicle group which contains the vehicle to be checked
* @param cid The cargo what we are looking for
* @return Whether the vehicle can carry the specified cargo or not
*/
static bool CDECL CargoFilter(const GUIVehicleGroup *vehgroup, const CargoID cid)
{
const Vehicle *v = (*vehgroup).GetSingleVehicle();
if (cid == BaseVehicleListWindow::CF_ANY) {
return true;
} else if (cid == BaseVehicleListWindow::CF_NONE) {
for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
if (w->cargo_cap > 0) {
return false;
}
}
return true;
} else if (cid == BaseVehicleListWindow::CF_FREIGHT) {
bool have_capacity = false;
for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
if (w->cargo_cap > 0) {
if (IsCargoInClass(w->cargo_type, CC_PASSENGERS)) {
return false;
} else {
have_capacity = true;
}
}
}
return have_capacity;
} else {
for (const Vehicle *w = v; w != nullptr; w = w->Next()) {
if (w->cargo_cap > 0 && w->cargo_type == cid) {
return true;
}
}
return false;
}
}
static GUIVehicleGroupList::FilterFunction * const _filter_funcs[] = {
&CargoFilter,
};
/**
* Set cargo filter list item index.
* @param index The index to be set
*/
void BaseVehicleListWindow::SetCargoFilterIndex(byte index)
{
if (this->cargo_filter_criteria != index) {
this->cargo_filter_criteria = index;
/* Deactivate filter if criteria is 'Show All', activate it otherwise. */
this->vehgroups.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
this->vehgroups.SetFilterType(0);
this->vehgroups.ForceRebuild();
}
}
/**
*Populate the filter list and set the cargo filter criteria.
*/
void BaseVehicleListWindow::SetCargoFilterArray()
{
byte filter_items = 0;
/* Add item for disabling filtering. */
this->cargo_filter[filter_items] = CF_ANY;
this->cargo_filter_texts[filter_items] = STR_CARGO_TYPE_FILTER_ALL;
this->cargo_filter_criteria = filter_items;
filter_items++;
/* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */
this->cargo_filter[filter_items] = CF_FREIGHT;
this->cargo_filter_texts[filter_items] = STR_CARGO_TYPE_FILTER_FREIGHT;
filter_items++;
/* Add item for vehicles not carrying anything, e.g. train engines. */
this->cargo_filter[filter_items] = CF_NONE;
this->cargo_filter_texts[filter_items] = STR_CARGO_TYPE_FILTER_NONE;
filter_items++;
/* Collect available cargo types for filtering. */
for (const auto &cs : _sorted_cargo_specs) {
this->cargo_filter[filter_items] = cs->Index();
this->cargo_filter_texts[filter_items] = cs->name;
filter_items++;
}
/* Terminate the filter list. */
this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
this->vehgroups.SetFilterFuncs(_filter_funcs);
this->vehgroups.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
}
/**
*Filter the engine list against the currently selected cargo filter.
*/
void BaseVehicleListWindow::FilterVehicleList()
{
this->vehgroups.Filter(this->cargo_filter[this->cargo_filter_criteria]);
if (this->vehicles.size() == 0) {
/* No vehicle passed through the filter, invalidate the previously selected vehicle */
this->vehicle_sel = INVALID_VEHICLE;
} else if (this->vehicle_sel != INVALID_VEHICLE && std::find(this->vehicles.begin(), this->vehicles.end(), Vehicle::Get(this->vehicle_sel)) == this->vehicles.end()) { // previously selected engine didn't pass the filter, remove selection
this->vehicle_sel = INVALID_VEHICLE;
}
}
/**
* Compute the size for the Action dropdown.
* @param show_autoreplace If true include the autoreplace item.
@ -248,6 +363,7 @@ Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bo
void BaseVehicleListWindow::OnInit()
{
this->order_arrow_width = GetStringBoundingBox(STR_TINY_RIGHT_ARROW).width;
this->SetCargoFilterArray();
}
/**
@ -1391,6 +1507,9 @@ static const NWidgetPart _nested_vehicle_list[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_SORT_ORDER), SetMinimalSize(81, 12), SetFill(0, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_SORT_BY_PULLDOWN), SetMinimalSize(167, 12), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VL_FILTER_BY_CARGO_SEL),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_FILTER_BY_CARGO), SetMinimalSize(167, 12), SetFill(0, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
EndContainer(),
@ -1662,6 +1781,8 @@ public:
{
this->CreateNestedTree();
this->GetWidget<NWidgetStacked>(WID_VL_FILTER_BY_CARGO_SEL)->SetDisplayedPlane((this->vli.type == VL_SHARED_ORDERS) ? SZSP_NONE : 0);
this->vscroll = this->GetScrollbar(WID_VL_SCROLLBAR);
this->BuildVehicleList();
@ -1736,6 +1857,10 @@ public:
SetDParam(0, STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
break;
case WID_VL_FILTER_BY_CARGO:
SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
break;
case WID_VL_CAPTION:
case WID_VL_CAPTION_SHARED_ORDERS: {
switch (this->vli.type) {
@ -1819,6 +1944,8 @@ public:
/* Set text of sort by dropdown widget. */
this->GetWidget<NWidgetCore>(WID_VL_SORT_BY_PULLDOWN)->widget_data = this->GetVehicleSorterNames()[this->vehgroups.SortType()];
this->GetWidget<NWidgetCore>(WID_VL_FILTER_BY_CARGO)->widget_data = this->cargo_filter_texts[this->cargo_filter_criteria];
this->DrawWidgets();
}
@ -1845,6 +1972,10 @@ public:
(this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
return;
case WID_VL_FILTER_BY_CARGO: // Cargo filter dropdown
ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, WID_VL_FILTER_BY_CARGO, 0, 0);
break;
case WID_VL_LIST: { // Matrix to show vehicles
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VL_LIST);
if (id_v >= this->vehgroups.size()) return; // click out of list bound
@ -1913,6 +2044,10 @@ public:
this->vehgroups.SetSortType(index);
break;
case WID_VL_FILTER_BY_CARGO:
this->SetCargoFilterIndex(index);
break;
case WID_VL_MANAGE_VEHICLES_DROPDOWN:
assert(this->vehicles.size() != 0);

View File

@ -11,6 +11,7 @@
#define VEHICLE_GUI_BASE_H
#include "core/smallvec_type.hpp"
#include "cargo_type.h"
#include "date_type.h"
#include "economy_type.h"
#include "sortlist_type.h"
@ -22,7 +23,7 @@
#include <iterator>
#include <numeric>
typedef GUIList<const Vehicle*> GUIVehicleList;
typedef GUIList<const Vehicle*, CargoID> GUIVehicleList;
struct GUIVehicleGroup {
VehicleList::const_iterator vehicles_begin; ///< Pointer to beginning element of this vehicle group.
@ -65,7 +66,7 @@ struct GUIVehicleGroup {
}
};
typedef GUIList<GUIVehicleGroup> GUIVehicleGroupList;
typedef GUIList<GUIVehicleGroup, CargoID> GUIVehicleGroupList;
struct BaseVehicleListWindow : public Window {
@ -76,14 +77,25 @@ struct BaseVehicleListWindow : public Window {
GB_END,
};
GroupBy grouping; ///< How we want to group the list.
VehicleList vehicles; ///< List of vehicles. This is the buffer for `vehgroups` to point into; if this is structurally modified, `vehgroups` must be rebuilt.
GUIVehicleGroupList vehgroups; ///< List of (groups of) vehicles. This stores iterators of `vehicles`, and should be rebuilt if `vehicles` is structurally changed.
Listing *sorting; ///< Pointer to the vehicle type related sorting.
byte unitnumber_digits; ///< The number of digits of the highest unit number.
/** Special cargo filter criteria */
enum CargoFilterSpecialType {
CF_NONE = CT_INVALID, ///< Show only vehicles which do not carry cargo (e.g. train engines)
CF_ANY = CT_NO_REFIT, ///< Show all vehicles independent of carried cargo (i.e. no filtering)
CF_FREIGHT = CT_AUTO_REFIT, ///< Show only vehicles which carry any freight (non-passenger) cargo
};
GroupBy grouping; ///< How we want to group the list.
VehicleList vehicles; ///< List of vehicles. This is the buffer for `vehgroups` to point into; if this is structurally modified, `vehgroups` must be rebuilt.
GUIVehicleGroupList vehgroups; ///< List of (groups of) vehicles. This stores iterators of `vehicles`, and should be rebuilt if `vehicles` is structurally changed.
Listing *sorting; ///< Pointer to the vehicle type related sorting.
byte unitnumber_digits; ///< The number of digits of the highest unit number.
Scrollbar *vscroll;
VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show.
uint order_arrow_width; ///< Width of the arrow in the small order list.
VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show.
VehicleID vehicle_sel; ///< Selected vehicle
CargoID cargo_filter[NUM_CARGO + 3]; ///< Available cargo filters; CargoID or CF_ANY or CF_FREIGHT or CF_NONE
StringID cargo_filter_texts[NUM_CARGO + 4]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID
byte cargo_filter_criteria; ///< Selected cargo filter index
uint order_arrow_width; ///< Width of the arrow in the small order list.
typedef GUIVehicleGroupList::SortFunction VehicleGroupSortFunction;
typedef GUIVehicleList::SortFunction VehicleIndividualSortFunction;
@ -113,6 +125,9 @@ struct BaseVehicleListWindow : public Window {
void UpdateVehicleGroupBy(GroupBy group_by);
void SortVehicleList();
void BuildVehicleList();
void SetCargoFilterIndex(byte index);
void SetCargoFilterArray();
void FilterVehicleList();
Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group);
DropDownList BuildActionDropdownList(bool show_autoreplace, bool show_group);

View File

@ -17,6 +17,7 @@ enum GroupListWidgets {
WID_GL_GROUP_BY_DROPDOWN, ///< Group by dropdown list.
WID_GL_SORT_BY_ORDER, ///< Sort order.
WID_GL_SORT_BY_DROPDOWN, ///< Sort by dropdown list.
WID_GL_FILTER_BY_CARGO, ///< Filter vehicles by cargo type.
WID_GL_LIST_VEHICLE, ///< List of the vehicles.
WID_GL_LIST_VEHICLE_SCROLLBAR, ///< Scrollbar for the list.
WID_GL_AVAILABLE_VEHICLES, ///< Available vehicles.

View File

@ -69,6 +69,8 @@ enum VehicleListWidgets {
WID_VL_GROUP_BY_PULLDOWN, ///< Group by dropdown list.
WID_VL_SORT_ORDER, ///< Sort order.
WID_VL_SORT_BY_PULLDOWN, ///< Sort by dropdown list.
WID_VL_FILTER_BY_CARGO, ///< Cargo filter dropdown list.
WID_VL_FILTER_BY_CARGO_SEL, ///< Cargo filter dropdown list panel selector.
WID_VL_LIST, ///< List of the vehicles.
WID_VL_SCROLLBAR, ///< Scrollbar for the list.
WID_VL_HIDE_BUTTONS, ///< Selection to hide the buttons.