From 6d4f616706b2d0aebcd81252bb0a875b54a5c67a Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 7 May 2024 17:41:37 +0100 Subject: [PATCH] Fix crash when sorting by capacity in autoreplace window Move capacity cache into GUIList sort parameter See: #690 --- src/autoreplace_gui.cpp | 2 +- src/build_vehicle_gui.cpp | 170 ++++++++++++++++++-------------------- src/engine_gui.cpp | 8 +- src/engine_gui.h | 23 +++++- 4 files changed, 107 insertions(+), 96 deletions(-) diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index c4757b9f2e..b1f1a26a37 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -32,7 +32,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group); -static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { return Engine::Get(a.engine_id)->list_position < Engine::Get(b.engine_id)->list_position; } diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 07f5bd43f6..b42a529c53 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -36,7 +36,6 @@ #include "querystring_gui.h" #include "stringfilter_type.h" #include "hotkeys.h" -#include "3rdparty/cpp-btree/btree_map.h" #include "widgets/build_vehicle_widget.h" @@ -206,29 +205,6 @@ bool _engine_sort_show_hidden_locos = false; ///< Las bool _engine_sort_show_hidden_wagons = false; ///< Last set 'show hidden wagons' setting. static CargoID _engine_sort_last_cargo_criteria[] = {CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY}; ///< Last set filter criteria, for each vehicle type. -struct BuildVehicleWindowBase; - -struct EngineCapacityCache { - const BuildVehicleWindowBase *parent = nullptr; - CargoID current_cargo = INVALID_CARGO; - btree::btree_map capacities; - - void UpdateCargoFilter(const BuildVehicleWindowBase *parent, CargoID cargo_filter_criteria) - { - this->parent = parent; - - if (cargo_filter_criteria >= NUM_CARGO) cargo_filter_criteria = INVALID_CARGO; - - if (cargo_filter_criteria != this->current_cargo) { - this->current_cargo = cargo_filter_criteria; - this->capacities.clear(); - } - } - - uint GetArticulatedCapacity(EngineID eng, bool dual_headed = false); -}; -static EngineCapacityCache *_engine_sort_capacity_cache = nullptr; - static uint8_t _last_sort_criteria_loco = 0; static bool _last_sort_order_loco = false; static CargoID _last_filter_criteria_loco = CargoFilterCriteria::CF_ANY; @@ -243,7 +219,7 @@ static CargoID _last_filter_criteria_wagon = CargoFilterCriteria::CF_ANY; * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { int r = Engine::Get(a.engine_id)->list_position - Engine::Get(b.engine_id)->list_position; @@ -256,14 +232,14 @@ static bool EngineNumberSorter(const GUIEngineListItem &a, const GUIEngineListIt * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const auto va = Engine::Get(a.engine_id)->intro_date; const auto vb = Engine::Get(b.engine_id)->intro_date; const auto r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -273,13 +249,13 @@ static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineLis * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineVehicleCountSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineVehicleCountSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const GroupStatistics &stats = GroupStatistics::Get(_local_company, ALL_GROUP, Engine::Get(a.engine_id)->type); const int r = ((int) stats.GetNumEngines(a.engine_id)) - ((int) stats.GetNumEngines(b.engine_id)); /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -292,7 +268,7 @@ static EngineID _last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE }; * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { static std::string last_name[2] = { {}, {} }; @@ -311,7 +287,7 @@ static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting). /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -321,14 +297,14 @@ static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineReliabilitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineReliabilitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const int va = Engine::Get(a.engine_id)->reliability; const int vb = Engine::Get(b.engine_id)->reliability; const int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -338,14 +314,14 @@ static bool EngineReliabilitySorter(const GUIEngineListItem &a, const GUIEngineL * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { Money va = Engine::Get(a.engine_id)->GetCost(); Money vb = Engine::Get(b.engine_id)->GetCost(); int r = ClampTo(va - vb); /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -355,14 +331,14 @@ static bool EngineCostSorter(const GUIEngineListItem &a, const GUIEngineListItem * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineSpeedSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineSpeedSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { int va = Engine::Get(a.engine_id)->GetDisplayMaxSpeed(); int vb = Engine::Get(b.engine_id)->GetDisplayMaxSpeed(); int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -372,14 +348,14 @@ static bool EngineSpeedSorter(const GUIEngineListItem &a, const GUIEngineListIte * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EnginePowerSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EnginePowerSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { int va = Engine::Get(a.engine_id)->GetPower(); int vb = Engine::Get(b.engine_id)->GetPower(); int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -389,14 +365,14 @@ static bool EnginePowerSorter(const GUIEngineListItem &a, const GUIEngineListIte * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineTractiveEffortSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineTractiveEffortSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { int va = Engine::Get(a.engine_id)->GetDisplayMaxTractiveEffort(); int vb = Engine::Get(b.engine_id)->GetDisplayMaxTractiveEffort(); int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -406,18 +382,18 @@ static bool EngineTractiveEffortSorter(const GUIEngineListItem &a, const GUIEngi * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EngineRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EngineRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { Money va = Engine::Get(a.engine_id)->GetRunningCost(); Money vb = Engine::Get(b.engine_id)->GetRunningCost(); int r = ClampTo(va - vb); /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } -static bool GenericEngineValueVsRunningCostSorter(const GUIEngineListItem &a, const uint value_a, const GUIEngineListItem &b, const uint value_b) +static bool GenericEngineValueVsRunningCostSorter(const GUIEngineListItem &a, const uint value_a, const GUIEngineListItem &b, const uint value_b, const GUIEngineListSortCache &cache) { const Engine *e_a = Engine::Get(a.engine_id); const Engine *e_b = Engine::Get(b.engine_id); @@ -429,7 +405,7 @@ static bool GenericEngineValueVsRunningCostSorter(const GUIEngineListItem &a, co if (r_a == 0) { if (r_b == 0) { /* If it is ambiguous which to return go with their ID */ - if (value_a == value_b) return EngineNumberSorter(a, b); + if (value_a == value_b) return EngineNumberSorter(a, b, cache); return _engine_sort_direction != (value_a < value_b); } return !_engine_sort_direction; @@ -444,8 +420,8 @@ static bool GenericEngineValueVsRunningCostSorter(const GUIEngineListItem &a, co * since we want consistent sorting. * Also if both have no power then sort with reverse of running cost to simulate * previous sorting behaviour for wagons. */ - if (v_a == 0 && v_b == 0) return EngineRunningCostSorter(b, a); - if (v_a == v_b) return EngineNumberSorter(a, b); + if (v_a == 0 && v_b == 0) return EngineRunningCostSorter(b, a, cache); + if (v_a == v_b) return EngineNumberSorter(a, b, cache); return _engine_sort_direction != (v_a < v_b); } @@ -455,9 +431,9 @@ static bool GenericEngineValueVsRunningCostSorter(const GUIEngineListItem &a, co * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool EnginePowerVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool EnginePowerVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { - return GenericEngineValueVsRunningCostSorter(a, Engine::Get(a.engine_id)->GetPower(), b, Engine::Get(b.engine_id)->GetPower()); + return GenericEngineValueVsRunningCostSorter(a, Engine::Get(a.engine_id)->GetPower(), b, Engine::Get(b.engine_id)->GetPower(), cache); } /* Train sorting functions */ @@ -468,17 +444,17 @@ static bool EnginePowerVsRunningCostSorter(const GUIEngineListItem &a, const GUI * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool TrainEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool TrainEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id); const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id); - int va = _engine_sort_capacity_cache->GetArticulatedCapacity(a.engine_id, rvi_a->railveh_type == RAILVEH_MULTIHEAD); - int vb = _engine_sort_capacity_cache->GetArticulatedCapacity(b.engine_id, rvi_b->railveh_type == RAILVEH_MULTIHEAD); + int va = cache.GetArticulatedCapacity(a.engine_id, rvi_a->railveh_type == RAILVEH_MULTIHEAD); + int vb = cache.GetArticulatedCapacity(b.engine_id, rvi_b->railveh_type == RAILVEH_MULTIHEAD); int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -488,15 +464,15 @@ static bool TrainEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngin * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool TrainEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool TrainEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const RailVehicleInfo *rvi_a = RailVehInfo(a.engine_id); const RailVehicleInfo *rvi_b = RailVehInfo(b.engine_id); - uint va = _engine_sort_capacity_cache->GetArticulatedCapacity(a.engine_id, rvi_a->railveh_type == RAILVEH_MULTIHEAD); - uint vb = _engine_sort_capacity_cache->GetArticulatedCapacity(b.engine_id, rvi_b->railveh_type == RAILVEH_MULTIHEAD); + uint va = cache.GetArticulatedCapacity(a.engine_id, rvi_a->railveh_type == RAILVEH_MULTIHEAD); + uint vb = cache.GetArticulatedCapacity(b.engine_id, rvi_b->railveh_type == RAILVEH_MULTIHEAD); - return GenericEngineValueVsRunningCostSorter(a, va, b, vb); + return GenericEngineValueVsRunningCostSorter(a, va, b, vb, cache); } /** @@ -505,14 +481,14 @@ static bool TrainEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, c * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool TrainEnginesThenWagonsSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool TrainEnginesThenWagonsSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { int val_a = (RailVehInfo(a.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0); int val_b = (RailVehInfo(b.engine_id)->railveh_type == RAILVEH_WAGON ? 1 : 0); int r = val_a - val_b; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -524,14 +500,14 @@ static bool TrainEnginesThenWagonsSorter(const GUIEngineListItem &a, const GUIEn * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool RoadVehEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool RoadVehEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { - int va = _engine_sort_capacity_cache->GetArticulatedCapacity(a.engine_id); - int vb = _engine_sort_capacity_cache->GetArticulatedCapacity(b.engine_id); + int va = cache.GetArticulatedCapacity(a.engine_id); + int vb = cache.GetArticulatedCapacity(b.engine_id); int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -541,11 +517,11 @@ static bool RoadVehEngineCapacitySorter(const GUIEngineListItem &a, const GUIEng * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool RoadVehEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool RoadVehEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { - int capacity_a = _engine_sort_capacity_cache->GetArticulatedCapacity(a.engine_id); - int capacity_b = _engine_sort_capacity_cache->GetArticulatedCapacity(b.engine_id); - return GenericEngineValueVsRunningCostSorter(a, capacity_a, b, capacity_b); + int capacity_a = cache.GetArticulatedCapacity(a.engine_id); + int capacity_b = cache.GetArticulatedCapacity(b.engine_id); + return GenericEngineValueVsRunningCostSorter(a, capacity_a, b, capacity_b, cache); } /* Ship vehicle sorting functions */ @@ -556,14 +532,14 @@ static bool RoadVehEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool ShipEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool ShipEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { - int va = _engine_sort_capacity_cache->GetArticulatedCapacity(a.engine_id); - int vb = _engine_sort_capacity_cache->GetArticulatedCapacity(b.engine_id); + int va = cache.GetArticulatedCapacity(a.engine_id); + int vb = cache.GetArticulatedCapacity(b.engine_id); int r = va - vb; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -573,11 +549,11 @@ static bool ShipEngineCapacitySorter(const GUIEngineListItem &a, const GUIEngine * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool ShipEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool ShipEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { - int capacity_a = _engine_sort_capacity_cache->GetArticulatedCapacity(a.engine_id); - int capacity_b = _engine_sort_capacity_cache->GetArticulatedCapacity(b.engine_id); - return GenericEngineValueVsRunningCostSorter(a, capacity_a, b, capacity_b); + int capacity_a = cache.GetArticulatedCapacity(a.engine_id); + int capacity_b = cache.GetArticulatedCapacity(b.engine_id); + return GenericEngineValueVsRunningCostSorter(a, capacity_a, b, capacity_b, cache); } /* Aircraft sorting functions */ @@ -588,7 +564,7 @@ static bool ShipEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, co * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const Engine *e_a = Engine::Get(a.engine_id); const Engine *e_b = Engine::Get(b.engine_id); @@ -604,7 +580,7 @@ static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngin if (r == 0) { /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); + return EngineNumberSorter(a, b, cache); } } return _engine_sort_direction ? r > 0 : r < 0; @@ -616,7 +592,7 @@ static bool AircraftEngineCargoSorter(const GUIEngineListItem &a, const GUIEngin * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool AircraftEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool AircraftEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { const Engine *e_a = Engine::Get(a.engine_id); const Engine *e_b = Engine::Get(b.engine_id); @@ -625,7 +601,7 @@ static bool AircraftEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a int va = e_a->GetDisplayDefaultCapacity(&mail_a); int vb = e_b->GetDisplayDefaultCapacity(&mail_b); - return GenericEngineValueVsRunningCostSorter(a, va + mail_a, b, vb + mail_b); + return GenericEngineValueVsRunningCostSorter(a, va + mail_a, b, vb + mail_b, cache); } /** @@ -634,7 +610,7 @@ static bool AircraftEngineCapacityVsRunningCostSorter(const GUIEngineListItem &a * @param b second engine to compare * @return for descending order: returns true if a < b. Vice versa for ascending order */ -static bool AircraftRangeSorter(const GUIEngineListItem &a, const GUIEngineListItem &b) +static bool AircraftRangeSorter(const GUIEngineListItem &a, const GUIEngineListItem &b, const GUIEngineListSortCache &cache) { uint16_t r_a = Engine::Get(a.engine_id)->GetRange(); uint16_t r_b = Engine::Get(b.engine_id)->GetRange(); @@ -642,7 +618,7 @@ static bool AircraftRangeSorter(const GUIEngineListItem &a, const GUIEngineListI int r = r_a - r_b; /* Use EngineID to sort instead since we want consistent sorting */ - if (r == 0) return EngineNumberSorter(a, b); + if (r == 0) return EngineNumberSorter(a, b, cache); return _engine_sort_direction ? r > 0 : r < 0; } @@ -1503,13 +1479,30 @@ struct BuildVehicleWindowBase : Window { } }; -uint EngineCapacityCache::GetArticulatedCapacity(EngineID eng, bool dual_headed) +/** + * Update cargo filter + * @param parent parent window, may be nullptr + * @param cargo_filter_criteria cargo filter criteria + */ +void GUIEngineListSortCache::UpdateCargoFilter(const BuildVehicleWindowBase *parent, CargoID cargo_filter_criteria) +{ + this->parent = parent; + + if (cargo_filter_criteria >= NUM_CARGO) cargo_filter_criteria = INVALID_CARGO; + + if (cargo_filter_criteria != this->current_cargo) { + this->current_cargo = cargo_filter_criteria; + this->capacities.clear(); + } +} + +uint GUIEngineListSortCache::GetArticulatedCapacity(EngineID eng, bool dual_headed) const { auto iter = this->capacities.insert({ eng, 0 }); if (iter.second) { /* New cache entry */ const Engine *e = Engine::Get(eng); - if (this->current_cargo != INVALID_CARGO && this->current_cargo != e->GetDefaultCargoType() && HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) && e->refit_capacity_values == nullptr) { + if (this->current_cargo != INVALID_CARGO && this->current_cargo != e->GetDefaultCargoType() && HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) && e->refit_capacity_values == nullptr && this->parent != nullptr) { /* Expensive path simulating vehicle construction is required to determine capacity */ TestedEngineDetails te{}; this->parent->FillTestedEngineCapacity(eng, this->current_cargo, te); @@ -1537,7 +1530,6 @@ struct BuildVehicleWindow : BuildVehicleWindowBase { int details_height; ///< Minimal needed height of the details panels, in text lines (found so far). Scrollbar *vscroll; TestedEngineDetails te; ///< Tested cost and capacity after refit. - EngineCapacityCache capacity_cache; ///< Engine capacity cache. StringFilter string_filter; ///< Filter for vehicle name QueryString vehicle_editbox; ///< Filter editbox @@ -1775,8 +1767,7 @@ struct BuildVehicleWindow : BuildVehicleWindowBase { _last_engine[0] = _last_engine[1] = INVALID_ENGINE; /* setup engine capacity cache */ - this->capacity_cache.UpdateCargoFilter(this, this->cargo_filter_criteria); - _engine_sort_capacity_cache = &(this->capacity_cache); + list.SortParameterData().UpdateCargoFilter(this, this->cargo_filter_criteria); /* make engines first, and then wagons, sorted by selected sort_criteria */ _engine_sort_direction = false; @@ -1916,8 +1907,7 @@ struct BuildVehicleWindow : BuildVehicleWindowBase { } /* setup engine capacity cache */ - this->capacity_cache.UpdateCargoFilter(this, this->cargo_filter_criteria); - _engine_sort_capacity_cache = &(this->capacity_cache); + this->eng_list.SortParameterData().UpdateCargoFilter(this, this->cargo_filter_criteria); _engine_sort_direction = this->descending_sort_order; EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]); @@ -2358,7 +2348,6 @@ struct BuildVehicleWindowTrainAdvanced final : BuildVehicleWindowBase { bool show_hidden; ///< State of the 'show hidden' button. int details_height; ///< Minimal needed height of the details panels (found so far). TestedEngineDetails te; ///< Tested cost and capacity after refit. - EngineCapacityCache capacity_cache; ///< Engine capacity cache. StringFilter string_filter; ///< Filter for vehicle name QueryString vehicle_editbox { MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS }; ///< Filter editbox }; @@ -2664,8 +2653,7 @@ struct BuildVehicleWindowTrainAdvanced final : BuildVehicleWindowBase { _last_engine[0] = _last_engine[1] = INVALID_ENGINE; /* setup engine capacity cache */ - state.capacity_cache.UpdateCargoFilter(this, state.cargo_filter_criteria); - _engine_sort_capacity_cache = &(state.capacity_cache); + list.SortParameterData().UpdateCargoFilter(this, state.cargo_filter_criteria); /* Sort */ _engine_sort_direction = state.descending_sort_order; diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index 3b381854c6..3488b00cfd 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -374,7 +374,9 @@ void DrawVehicleEngine(int left, int right, int preferred_x, int y, EngineID eng void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare) { if (el.size() < 2) return; - std::sort(el.begin(), el.end(), compare); + std::sort(el.begin(), el.end(), [&](const GUIEngineListItem &a, const GUIEngineListItem &b) { + return compare(a, b, el.SortParameterData()); + }); } /** @@ -389,6 +391,8 @@ void EngList_SortPartial(GUIEngineList &el, EngList_SortTypeFunction compare, si if (num_items < 2) return; assert(begin < el.size()); assert(begin + num_items <= el.size()); - std::sort(el.begin() + begin, el.begin() + begin + num_items, compare); + std::sort(el.begin() + begin, el.begin() + begin + num_items, [&](const GUIEngineListItem &a, const GUIEngineListItem &b) { + return compare(a, b, el.SortParameterData()); + }); } diff --git a/src/engine_gui.h b/src/engine_gui.h index c8ece98cfe..0308c8b074 100644 --- a/src/engine_gui.h +++ b/src/engine_gui.h @@ -16,6 +16,25 @@ #include "vehicle_type.h" #include "engine_base.h" #include "window_type.h" +#include "3rdparty/cpp-btree/btree_map.h" + +struct BuildVehicleWindowBase; + +struct GUIEngineListSortCache { + const BuildVehicleWindowBase *parent = nullptr; + CargoID current_cargo = INVALID_CARGO; + mutable btree::btree_map capacities; + + void UpdateCargoFilter(const BuildVehicleWindowBase *parent, CargoID cargo_filter_criteria); + uint GetArticulatedCapacity(EngineID eng, bool dual_headed = false) const; +}; + +template <> +struct GUIListParamConfig +{ + using SortParameterReference = GUIEngineListSortCache; + static const bool constructor_init = false; +}; struct GUIEngineListItem { EngineID engine_id; ///< Engine to display in build purchase list @@ -29,9 +48,9 @@ struct GUIEngineListItem { bool operator == (const EngineID &other) const { return this->engine_id == other; } }; -typedef GUIList GUIEngineList; +typedef GUIList GUIEngineList; -typedef bool EngList_SortTypeFunction(const GUIEngineListItem&, const GUIEngineListItem&); ///< argument type for #EngList_Sort. +typedef bool EngList_SortTypeFunction(const GUIEngineListItem&, const GUIEngineListItem&, const GUIEngineListSortCache &); ///< argument type for #EngList_Sort. void EngList_Sort(GUIEngineList &el, EngList_SortTypeFunction compare); void EngList_SortPartial(GUIEngineList &el, EngList_SortTypeFunction compare, size_t begin, size_t num_items);