diff options
author | Bernard Teo <bernard.14916@gmail.com> | 2019-01-11 16:09:37 +0800 |
---|---|---|
committer | Patric Stout <github@truebrain.nl> | 2020-12-21 23:15:53 +0100 |
commit | 584df548f1bdfa27482aa995d151542abe4f0bca (patch) | |
tree | 93992876f366068c75fa5e887d8c3a8db982cf4f /src | |
parent | 86e08aa8ef57822e3080b859ffe2802c8996bd9e (diff) | |
download | openttd-584df548f1bdfa27482aa995d151542abe4f0bca.tar.xz |
Codechange: Make vehicle lists internally support grouping of vehicles
This is in preparation for the new UI feature that allows grouping by shared orders.
Diffstat (limited to 'src')
-rw-r--r-- | src/group_gui.cpp | 62 | ||||
-rw-r--r-- | src/vehicle_gui.cpp | 233 | ||||
-rw-r--r-- | src/vehicle_gui_base.h | 76 |
3 files changed, 235 insertions, 136 deletions
diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 8d0bba8fb..5323b65f2 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -341,24 +341,12 @@ public: this->vscroll = this->GetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR); this->group_sb = this->GetScrollbar(WID_GL_LIST_GROUP_SCROLLBAR); - switch (this->vli.vtype) { - default: NOT_REACHED(); - case VEH_TRAIN: this->sorting = &_sorting.train; break; - case VEH_ROAD: this->sorting = &_sorting.roadveh; break; - case VEH_SHIP: this->sorting = &_sorting.ship; break; - case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break; - } - 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; - this->vehicles.SetListing(*this->sorting); - this->vehicles.ForceRebuild(); - this->vehicles.NeedResort(); - this->BuildVehicleList(); this->SortVehicleList(); @@ -382,7 +370,7 @@ public: ~VehicleGroupWindow() { - *this->sorting = this->vehicles.GetListing(); + *this->sorting = this->vehgroups.GetListing(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -453,10 +441,10 @@ public: { if (data == 0) { /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */ - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); this->groups.ForceRebuild(); } else { - this->vehicles.ForceResort(); + this->vehgroups.ForceResort(); this->groups.ForceResort(); } @@ -509,8 +497,8 @@ public: this->BuildGroupList(this->owner); - this->group_sb->SetCount((uint)this->groups.size()); - this->vscroll->SetCount((uint)this->vehicles.size()); + this->group_sb->SetCount(static_cast<int>(this->groups.size())); + this->vscroll->SetCount(static_cast<int>(this->vehgroups.size())); /* The drop down menu is out, *but* it may not be used, retract it. */ if (this->vehicles.size() == 0 && this->IsWidgetLowered(WID_GL_MANAGE_VEHICLES_DROPDOWN)) { @@ -550,7 +538,7 @@ public: this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype; /* Set text of sort by dropdown */ - this->GetWidget<NWidgetCore>(WID_GL_SORT_BY_DROPDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()]; + this->GetWidget<NWidgetCore>(WID_GL_SORT_BY_DROPDOWN)->widget_data = this->vehicle_group_none_sorter_names[this->vehgroups.SortType()]; this->DrawWidgets(); } @@ -623,16 +611,16 @@ public: } case WID_GL_SORT_BY_ORDER: - this->DrawSortButtonState(WID_GL_SORT_BY_ORDER, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP); + this->DrawSortButtonState(WID_GL_SORT_BY_ORDER, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP); break; case WID_GL_LIST_VEHICLE: - if (this->vli.index != ALL_GROUP) { - /* Mark vehicles which are in sub-groups */ + if (this->vli.index != ALL_GROUP && this->grouping == GB_NONE) { + /* Mark vehicles which are in sub-groups (only if we are not using shared order coalescing) */ int y = r.top; - uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), (uint)this->vehicles.size()); + uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), static_cast<uint>(this->vehgroups.size())); for (uint i = this->vscroll->GetPosition(); i < max; ++i) { - const Vehicle *v = this->vehicles[i]; + const Vehicle *v = this->vehgroups[i].GetSingleVehicle(); if (v->group_id != this->vli.index) { GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->resize.step_height - 2, _colour_gradient[COLOUR_GREY][3], FILLRECT_CHECKER); } @@ -658,18 +646,18 @@ public: { switch (widget) { case WID_GL_SORT_BY_ORDER: // Flip sorting method ascending/descending - this->vehicles.ToggleSortOrder(); + this->vehgroups.ToggleSortOrder(); this->SetDirty(); break; case WID_GL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu - ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), WID_GL_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10)); + 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_ALL_VEHICLES: // All vehicles button if (!IsAllGroupID(this->vli.index)) { this->vli.index = ALL_GROUP; - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); this->SetDirty(); } break; @@ -677,7 +665,7 @@ public: case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles button if (!IsDefaultGroupID(this->vli.index)) { this->vli.index = DEFAULT_GROUP; - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); this->SetDirty(); } break; @@ -717,17 +705,17 @@ public: SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this); - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); this->SetDirty(); break; } case WID_GL_LIST_VEHICLE: { // Matrix Vehicle uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE); - if (id_v >= this->vehicles.size()) return; // click out of list bound + if (id_v >= this->vehgroups.size()) return; // click out of list bound - const Vehicle *v = this->vehicles[id_v]; - if (VehicleClicked(v)) break; + const GUIVehicleGroup &vehgroup = this->vehgroups[id_v]; + const Vehicle * const v = vehgroup.GetSingleVehicle(); this->vehicle_sel = v->index; @@ -851,13 +839,13 @@ public: this->SetDirty(); uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE); - if (id_v >= this->vehicles.size()) return; // click out of list bound + if (id_v >= this->vehgroups.size()) return; // click out of list bound - const Vehicle *v = this->vehicles[id_v]; + const GUIVehicleGroup &vehgroup = this->vehgroups[id_v]; + const Vehicle *v = vehgroup.GetSingleVehicle(); if (!VehicleClicked(v) && vindex == v->index) { ShowVehicleViewWindow(v); } - break; } } } @@ -886,7 +874,7 @@ public: { switch (widget) { case WID_GL_SORT_BY_DROPDOWN: - this->vehicles.SetSortType(index); + this->vehgroups.SetSortType(index); break; case WID_GL_MANAGE_VEHICLES_DROPDOWN: @@ -924,7 +912,7 @@ public: void OnGameTick() override { - if (this->groups.NeedResort() || this->vehicles.NeedResort()) { + if (this->groups.NeedResort() || this->vehgroups.NeedResort()) { this->SetDirty(); } } @@ -1019,7 +1007,7 @@ public: } this->group_sb->ScrollTowards(id_g); } - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); this->SetDirty(); } diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 84ff890d1..135da393a 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -39,40 +39,50 @@ #include "safeguards.h" +#include <algorithm> + + +BaseVehicleListWindow::GroupBy _grouping[VLT_END][VEH_COMPANY_END]; +Sorting _sorting[BaseVehicleListWindow::GB_END]; + +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNumberSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleNameSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleAgeSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitThisYearSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleProfitLastYearSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleCargoSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleReliabilitySorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleMaxSpeedSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleModelSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleValueSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleLengthSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimeToLiveSorter; +static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimetableDelaySorter; + +/** Wrapper to convert a VehicleIndividualSortFunction to a VehicleGroupSortFunction */ +template <BaseVehicleListWindow::VehicleIndividualSortFunction func> +static bool VehicleIndividualToGroupSorterWrapper(GUIVehicleGroup const &a, GUIVehicleGroup const &b) +{ + return func(*(a.vehicles_begin), *(b.vehicles_begin)); +} -Sorting _sorting; - -static GUIVehicleList::SortFunction VehicleNumberSorter; -static GUIVehicleList::SortFunction VehicleNameSorter; -static GUIVehicleList::SortFunction VehicleAgeSorter; -static GUIVehicleList::SortFunction VehicleProfitThisYearSorter; -static GUIVehicleList::SortFunction VehicleProfitLastYearSorter; -static GUIVehicleList::SortFunction VehicleCargoSorter; -static GUIVehicleList::SortFunction VehicleReliabilitySorter; -static GUIVehicleList::SortFunction VehicleMaxSpeedSorter; -static GUIVehicleList::SortFunction VehicleModelSorter; -static GUIVehicleList::SortFunction VehicleValueSorter; -static GUIVehicleList::SortFunction VehicleLengthSorter; -static GUIVehicleList::SortFunction VehicleTimeToLiveSorter; -static GUIVehicleList::SortFunction VehicleTimetableDelaySorter; - -GUIVehicleList::SortFunction * const BaseVehicleListWindow::vehicle_sorter_funcs[] = { - &VehicleNumberSorter, - &VehicleNameSorter, - &VehicleAgeSorter, - &VehicleProfitThisYearSorter, - &VehicleProfitLastYearSorter, - &VehicleCargoSorter, - &VehicleReliabilitySorter, - &VehicleMaxSpeedSorter, - &VehicleModelSorter, - &VehicleValueSorter, - &VehicleLengthSorter, - &VehicleTimeToLiveSorter, - &VehicleTimetableDelaySorter, +BaseVehicleListWindow::VehicleGroupSortFunction * const BaseVehicleListWindow::vehicle_group_none_sorter_funcs[] = { + &VehicleIndividualToGroupSorterWrapper<VehicleNumberSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleNameSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleAgeSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleProfitThisYearSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleProfitLastYearSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleCargoSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleReliabilitySorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleMaxSpeedSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleModelSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleValueSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleLengthSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleTimeToLiveSorter>, + &VehicleIndividualToGroupSorterWrapper<VehicleTimetableDelaySorter>, }; -const StringID BaseVehicleListWindow::vehicle_sorter_names[] = { +const StringID BaseVehicleListWindow::vehicle_group_none_sorter_names[] = { STR_SORT_BY_NUMBER, STR_SORT_BY_NAME, STR_SORT_BY_AGE, @@ -96,6 +106,31 @@ const StringID BaseVehicleListWindow::vehicle_depot_name[] = { STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR }; +BaseVehicleListWindow::BaseVehicleListWindow(WindowDesc *desc, WindowNumber wno) : Window(desc), vli(VehicleListIdentifier::UnPack(wno)) +{ + this->grouping = _grouping[vli.type][vli.vtype]; + this->UpdateSortingFromGrouping(); +} + +/** + * Get the number of digits of space required for the given number. + * @param number The number. + * @return The number of digits to allocate space for. + */ +uint CountDigitsForAllocatingSpace(uint number) +{ + if (number >= 10000) return 5; + if (number >= 1000) return 4; + if (number >= 100) return 3; + + /* + * When the smallest unit number is less than 10, it is + * quite likely that it will expand to become more than + * 10 quite soon. + */ + return 2; +} + /** * Get the number of digits the biggest unit number of a set of vehicles has. * @param vehicles The list of vehicles. @@ -108,30 +143,29 @@ uint GetUnitNumberDigits(VehicleList &vehicles) unitnumber = max<uint>(unitnumber, v->unitnumber); } - if (unitnumber >= 10000) return 5; - if (unitnumber >= 1000) return 4; - if (unitnumber >= 100) return 3; - - /* - * When the smallest unit number is less than 10, it is - * quite likely that it will expand to become more than - * 10 quite soon. - */ - return 2; + return CountDigitsForAllocatingSpace(unitnumber); } void BaseVehicleListWindow::BuildVehicleList() { - if (!this->vehicles.NeedRebuild()) return; + if (!this->vehgroups.NeedRebuild()) return; DEBUG(misc, 3, "Building vehicle list type %d for company %d given index %d", this->vli.type, this->vli.company, this->vli.index); + this->vehgroups.clear(); + GenerateVehicleSortList(&this->vehicles, this->vli); - this->unitnumber_digits = GetUnitNumberDigits(this->vehicles); + uint max_unitnumber = 0; + for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) { + this->vehgroups.emplace_back(it, it + 1, (*it)->GetDisplayProfitThisYear(), (*it)->GetDisplayProfitLastYear(), (*it)->age); + + max_unitnumber = max<uint>(max_unitnumber, (*it)->unitnumber); + } + this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber); - this->vehicles.RebuildDone(); - this->vscroll->SetCount((uint)this->vehicles.size()); + this->vehgroups.RebuildDone(); + this->vscroll->SetCount(this->vehgroups.size()); } /** @@ -183,7 +217,7 @@ static const Vehicle *_last_vehicle[2] = { nullptr, nullptr }; void BaseVehicleListWindow::SortVehicleList() { - if (this->vehicles.Sort()) return; + if (this->vehgroups.Sort()) return; /* invalidate cached values for name sorter - vehicle names could change */ _last_vehicle[0] = _last_vehicle[1] = nullptr; @@ -196,16 +230,16 @@ void DepotSortList(VehicleList *list) } /** draw the vehicle profit button in the vehicle list window. */ -static void DrawVehicleProfitButton(const Vehicle *v, int x, int y) +static void DrawVehicleProfitButton(Date age, Money display_profit_last_year, uint num_vehicles, int x, int y) { SpriteID spr; /* draw profit-based coloured icons */ - if (v->age <= VEHICLE_PROFIT_MIN_AGE) { + if (age <= VEHICLE_PROFIT_MIN_AGE) { spr = SPR_PROFIT_NA; - } else if (v->GetDisplayProfitLastYear() < 0) { + } else if (display_profit_last_year < 0) { spr = SPR_PROFIT_NEGATIVE; - } else if (v->GetDisplayProfitLastYear() < VEHICLE_PROFIT_THRESHOLD) { + } else if (display_profit_last_year < VEHICLE_PROFIT_THRESHOLD * num_vehicles) { spr = SPR_PROFIT_SOME; } else { spr = SPR_PROFIT_LOT; @@ -1204,6 +1238,7 @@ static bool VehicleTimetableDelaySorter(const Vehicle * const &a, const Vehicle void InitializeGUI() { + MemSetT(&_grouping, 0); MemSetT(&_sorting, 0); } @@ -1384,17 +1419,21 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int int vehicle_button_x = rtl ? right - GetSpriteSize(SPR_PROFIT_LOT).width : left; int y = r.top; - uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), (uint)this->vehicles.size()); + uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->vehgroups.size()); for (uint i = this->vscroll->GetPosition(); i < max; ++i) { - const Vehicle *v = this->vehicles[i]; - StringID str; - SetDParam(0, v->GetDisplayProfitThisYear()); - SetDParam(1, v->GetDisplayProfitLastYear()); + const GUIVehicleGroup &vehgroup = this->vehgroups[i]; - DrawVehicleImage(v, image_left, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0); + SetDParam(0, vehgroup.display_profit_this_year); + SetDParam(1, vehgroup.display_profit_last_year); DrawString(text_left, text_right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1, STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR); + DrawVehicleProfitButton(vehgroup.age, vehgroup.display_profit_last_year, vehgroup.NumVehicles(), vehicle_button_x, y + FONT_HEIGHT_NORMAL + 3); + + const Vehicle *v = vehgroup.GetSingleVehicle(); + + DrawVehicleImage(v, image_left, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0); + if (!v->name.empty()) { /* The vehicle got a name so we will print it */ SetDParam(0, v->index); @@ -1407,6 +1446,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_real_order_index); + StringID str; if (v->IsChainInDepot()) { str = STR_BLUE_COMMA; } else { @@ -1416,12 +1456,41 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int SetDParam(0, v->unitnumber); DrawString(left, right, y + 2, str); - DrawVehicleProfitButton(v, vehicle_button_x, y + FONT_HEIGHT_NORMAL + 3); - y += line_height; } } +void BaseVehicleListWindow::UpdateSortingFromGrouping() +{ + /* Set up sorting. Make the window-specific _sorting variable + * point to the correct global _sorting struct so we are freed + * from having conditionals during window operation */ + switch (this->vli.vtype) { + case VEH_TRAIN: this->sorting = &_sorting[this->grouping].train; break; + case VEH_ROAD: this->sorting = &_sorting[this->grouping].roadveh; break; + case VEH_SHIP: this->sorting = &_sorting[this->grouping].ship; break; + case VEH_AIRCRAFT: this->sorting = &_sorting[this->grouping].aircraft; break; + default: NOT_REACHED(); + } + this->vehgroups.SetSortFuncs(this->vehicle_group_none_sorter_funcs); + this->vehgroups.SetListing(*this->sorting); + this->vehgroups.ForceRebuild(); + this->vehgroups.NeedResort(); +} + +void BaseVehicleListWindow::UpdateVehicleGroupBy(GroupBy group_by) +{ + if (this->grouping != group_by) { + /* Save the old sorting option, so that if we change the grouping option back later on, + * UpdateSortingFromGrouping() will automatically restore the saved sorting option. */ + *this->sorting = this->vehgroups.GetListing(); + + this->grouping = group_by; + _grouping[this->vli.type][this->vli.vtype] = group_by; + this->UpdateSortingFromGrouping(); + } +} + /** * Window for the (old) vehicle listing. * @@ -1442,24 +1511,10 @@ private: public: VehicleListWindow(WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow(desc, window_number) { - /* Set up sorting. Make the window-specific _sorting variable - * point to the correct global _sorting struct so we are freed - * from having conditionals during window operation */ - switch (this->vli.vtype) { - case VEH_TRAIN: this->sorting = &_sorting.train; break; - case VEH_ROAD: this->sorting = &_sorting.roadveh; break; - case VEH_SHIP: this->sorting = &_sorting.ship; break; - case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break; - default: NOT_REACHED(); - } - this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_VL_SCROLLBAR); - this->vehicles.SetListing(*this->sorting); - this->vehicles.ForceRebuild(); - this->vehicles.NeedResort(); this->BuildVehicleList(); this->SortVehicleList(); @@ -1478,7 +1533,7 @@ public: ~VehicleListWindow() { - *this->sorting = this->vehicles.GetListing(); + *this->sorting = this->vehgroups.GetListing(); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -1533,26 +1588,26 @@ public: * and we should close the window when deleting the order. */ NOT_REACHED(); } - SetDParam(0, this->vscroll->GetCount()); + SetDParam(0, this->vehicles.size()); break; case VL_STANDARD: // Company Name SetDParam(0, STR_COMPANY_NAME); SetDParam(1, this->vli.index); - SetDParam(3, this->vscroll->GetCount()); + SetDParam(3, this->vehicles.size()); break; case VL_STATION_LIST: // Station/Waypoint Name SetDParam(0, Station::IsExpected(BaseStation::Get(this->vli.index)) ? STR_STATION_NAME : STR_WAYPOINT_NAME); SetDParam(1, this->vli.index); - SetDParam(3, this->vscroll->GetCount()); + SetDParam(3, this->vehicles.size()); break; case VL_DEPOT_LIST: SetDParam(0, STR_DEPOT_CAPTION); SetDParam(1, this->vli.vtype); SetDParam(2, this->vli.index); - SetDParam(3, this->vscroll->GetCount()); + SetDParam(3, this->vehicles.size()); break; default: NOT_REACHED(); } @@ -1566,7 +1621,7 @@ public: switch (widget) { case WID_VL_SORT_ORDER: /* draw arrow pointing up/down for ascending/descending sorting */ - this->DrawSortButtonState(widget, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP); + this->DrawSortButtonState(widget, this->vehgroups.IsDescSortOrder() ? SBS_DOWN : SBS_UP); break; case WID_VL_LIST: @@ -1602,7 +1657,7 @@ public: } /* Set text of sort by dropdown widget. */ - this->GetWidget<NWidgetCore>(WID_VL_SORT_BY_PULLDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()]; + this->GetWidget<NWidgetCore>(WID_VL_SORT_BY_PULLDOWN)->widget_data = this->vehicle_group_none_sorter_names[this->vehgroups.SortType()]; this->DrawWidgets(); } @@ -1611,20 +1666,22 @@ public: { switch (widget) { case WID_VL_SORT_ORDER: // Flip sorting method ascending/descending - this->vehicles.ToggleSortOrder(); + this->vehgroups.ToggleSortOrder(); this->SetDirty(); break; case WID_VL_SORT_BY_PULLDOWN:// Select sorting criteria dropdown menu - ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), WID_VL_SORT_BY_PULLDOWN, 0, + ShowDropDownMenu(this, this->vehicle_group_none_sorter_names, this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10)); return; case WID_VL_LIST: { // Matrix to show vehicles uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_VL_LIST); - if (id_v >= this->vehicles.size()) return; // click out of list bound + if (id_v >= this->vehgroups.size()) return; // click out of list bound + + const GUIVehicleGroup &vehgroup = this->vehgroups[id_v]; + const Vehicle *v = vehgroup.GetSingleVehicle(); - const Vehicle *v = this->vehicles[id_v]; if (!VehicleClicked(v)) { if (_ctrl_pressed) { ShowCompanyGroupForVehicle(v); @@ -1655,7 +1712,7 @@ public: { switch (widget) { case WID_VL_SORT_BY_PULLDOWN: - this->vehicles.SetSortType(index); + this->vehgroups.SetSortType(index); break; case WID_VL_MANAGE_VEHICLES_DROPDOWN: assert(this->vehicles.size() != 0); @@ -1679,7 +1736,7 @@ public: void OnGameTick() override { - if (this->vehicles.NeedResort()) { + if (this->vehgroups.NeedResort()) { StationID station = (this->vli.type == VL_STATION_LIST) ? this->vli.index : INVALID_STATION; DEBUG(misc, 3, "Periodic resort %d list company %d at station %d", this->vli.vtype, this->owner, station); @@ -1703,15 +1760,15 @@ public: /* Needs to be done in command-scope, so everything stays valid */ this->vli.index = GB(data, 0, 20); this->window_number = this->vli.Pack(); - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); return; } if (data == 0) { /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */ - this->vehicles.ForceRebuild(); + this->vehgroups.ForceRebuild(); } else { - this->vehicles.ForceResort(); + this->vehgroups.ForceResort(); } } }; diff --git a/src/vehicle_gui_base.h b/src/vehicle_gui_base.h index 89410b17e..c63d5ec18 100644 --- a/src/vehicle_gui_base.h +++ b/src/vehicle_gui_base.h @@ -10,19 +10,59 @@ #ifndef VEHICLE_GUI_BASE_H #define VEHICLE_GUI_BASE_H +#include "core/smallvec_type.hpp" +#include "date_type.h" +#include "economy_type.h" #include "sortlist_type.h" #include "vehiclelist.h" #include "window_gui.h" #include "widgets/dropdown_type.h" +#include <iterator> + typedef GUIList<const Vehicle*> GUIVehicleList; +struct GUIVehicleGroup { + VehicleList::const_iterator vehicles_begin; ///< Pointer to beginning element of this vehicle group. + VehicleList::const_iterator vehicles_end; ///< Pointer to past-the-end element of this vehicle group. + Money display_profit_this_year; ///< Total profit for the vehicle group this year. + Money display_profit_last_year; ///< Total profit for the vehicle group laste year. + Date age; ///< Age in days of oldest vehicle in the group. + + GUIVehicleGroup(VehicleList::const_iterator vehicles_begin, VehicleList::const_iterator vehicles_end, Money display_profit_this_year, Money display_profit_last_year, Date age) + : vehicles_begin(vehicles_begin), vehicles_end(vehicles_end), display_profit_this_year(display_profit_this_year), display_profit_last_year(display_profit_last_year), age(age) {} + + std::ptrdiff_t NumVehicles() const + { + return std::distance(vehicles_begin, vehicles_end); + } + const Vehicle *GetSingleVehicle() const + { + assert(NumVehicles() == 1); + return vehicles_begin[0]; + } +}; + +typedef GUIList<GUIVehicleGroup> GUIVehicleGroupList; + struct BaseVehicleListWindow : public Window { - GUIVehicleList vehicles; ///< The list of vehicles - Listing *sorting; ///< Pointer to the vehicle type related sorting. - byte unitnumber_digits; ///< The number of digits of the highest unit number + + enum GroupBy : byte { + GB_NONE, + + 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. Scrollbar *vscroll; - VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show. + VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show. + + typedef GUIVehicleGroupList::SortFunction VehicleGroupSortFunction; + typedef GUIVehicleList::SortFunction VehicleIndividualSortFunction; enum ActionDropdownItem { ADI_REPLACE, @@ -33,19 +73,32 @@ struct BaseVehicleListWindow : public Window { }; static const StringID vehicle_depot_name[]; - static const StringID vehicle_sorter_names[]; - static GUIVehicleList::SortFunction * const vehicle_sorter_funcs[]; + static const StringID vehicle_group_by_names[]; + static const StringID vehicle_group_none_sorter_names[]; + static const StringID vehicle_group_shared_orders_sorter_names[]; + static VehicleGroupSortFunction * const vehicle_group_none_sorter_funcs[]; + static VehicleGroupSortFunction * const vehicle_group_shared_orders_sorter_funcs[]; - BaseVehicleListWindow(WindowDesc *desc, WindowNumber wno) : Window(desc), vli(VehicleListIdentifier::UnPack(wno)) - { - this->vehicles.SetSortFuncs(this->vehicle_sorter_funcs); - } + BaseVehicleListWindow(WindowDesc *desc, WindowNumber wno); + + void UpdateSortingFromGrouping(); void DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const; + void UpdateVehicleGroupBy(GroupBy group_by); void SortVehicleList(); void BuildVehicleList(); Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group); DropDownList BuildActionDropdownList(bool show_autoreplace, bool show_group); + + const StringID *GetVehicleSorterNames() + { + return vehicle_group_none_sorter_names; + } + + VehicleGroupSortFunction * const *GetVehicleSorterFuncs() + { + return vehicle_group_none_sorter_funcs; + } }; uint GetVehicleListHeight(VehicleType type, uint divisor = 1); @@ -57,6 +110,7 @@ struct Sorting { Listing train; }; -extern Sorting _sorting; +extern BaseVehicleListWindow::GroupBy _grouping[VLT_END][VEH_COMPANY_END]; +extern Sorting _sorting[BaseVehicleListWindow::GB_END]; #endif /* VEHICLE_GUI_BASE_H */ |