mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
overlay: add sorting to patches table (#761)
This commit is contained in:
@@ -46,6 +46,11 @@ using namespace rapidjson;
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
// user-assigned IDs for the patches table columns, used by the sort logic
|
||||
enum PatchColumnId {
|
||||
PATCH_COLUMN_NAME = 0,
|
||||
PATCH_COLUMN_STATUS = 1,
|
||||
};
|
||||
|
||||
robin_hood::unordered_map<std::string, std::unique_ptr<std::vector<uint8_t>>> DLL_MAP;
|
||||
robin_hood::unordered_map<std::string, std::unique_ptr<std::vector<uint8_t>>> DLL_MAP_ORG;
|
||||
@@ -218,6 +223,7 @@ namespace overlay::windows {
|
||||
// patches
|
||||
std::vector<PatchData> PatchManager::patches;
|
||||
bool PatchManager::local_patches_initialized = false;
|
||||
std::vector<size_t> PatchManager::patches_sorted;
|
||||
|
||||
// loader notifications
|
||||
bool ldr_registered = false;
|
||||
@@ -649,13 +655,27 @@ namespace overlay::windows {
|
||||
// draw patches
|
||||
ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding,
|
||||
ImGui::GetStyle().CellPadding.y + overlay::apply_scaling(2));
|
||||
if (ImGui::BeginTable("PatchesTable", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("##NameColumn", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##OptionsColumn", ImGuiTableColumnFlags_WidthFixed, 240);
|
||||
if (ImGui::BeginTable("PatchesTable", 2,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_Sortable | ImGuiTableFlags_SortTristate)) {
|
||||
ImGui::TableSetupColumn(
|
||||
"Patch",
|
||||
ImGuiTableColumnFlags_WidthStretch,
|
||||
0.f, PATCH_COLUMN_NAME);
|
||||
ImGui::TableSetupColumn(
|
||||
"Status",
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
240, PATCH_COLUMN_STATUS);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// maintain a sorted view of patches so the underlying vector
|
||||
// order (used by config save and hashing) is left untouched
|
||||
update_sorted_patches();
|
||||
|
||||
const auto search_str_in_lower = strtolower(patch_name_filter);
|
||||
size_t patches_shown = 0;
|
||||
for (auto &patch : patches) {
|
||||
for (auto patch_index : patches_sorted) {
|
||||
auto &patch = patches[patch_index];
|
||||
|
||||
// get patch status
|
||||
PatchStatus patch_status = is_patch_active(patch);
|
||||
@@ -881,6 +901,55 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
void PatchManager::update_sorted_patches() {
|
||||
// Rebuild/re-sort only when the sort order changes (SpecsDirty) or when
|
||||
// the patch list count changed - not every frame. reload_local_patches()
|
||||
// clears the cache to avoid dangling pointers when `patches` is rebuilt.
|
||||
auto *sort_specs = ImGui::TableGetSortSpecs();
|
||||
const bool patches_changed = patches_sorted.size() != patches.size();
|
||||
if (!patches_changed && !(sort_specs && sort_specs->SpecsDirty)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// rebuild the view in the underlying vector order (this is also the
|
||||
// default order shown when the sort is cleared / tristate "unsorted")
|
||||
patches_sorted.clear();
|
||||
patches_sorted.reserve(patches.size());
|
||||
for (size_t i = 0; i < patches.size(); i++) {
|
||||
patches_sorted.push_back(i);
|
||||
}
|
||||
|
||||
// SpecsCount == 0 means no active sort: keep the default order
|
||||
if (sort_specs && sort_specs->SpecsCount > 0) {
|
||||
const auto &spec = sort_specs->Specs[0];
|
||||
const bool ascending = spec.SortDirection != ImGuiSortDirection_Descending;
|
||||
std::stable_sort(patches_sorted.begin(), patches_sorted.end(),
|
||||
[&](size_t ia, size_t ib) {
|
||||
const PatchData *a = &patches[ia];
|
||||
const PatchData *b = &patches[ib];
|
||||
int cmp;
|
||||
if (spec.ColumnUserID == PATCH_COLUMN_STATUS) {
|
||||
// sort by displayed status (matches the checkbox), then
|
||||
// by name as a tiebreaker
|
||||
const bool a_on = a->last_status == PatchStatus::Enabled;
|
||||
const bool b_on = b->last_status == PatchStatus::Enabled;
|
||||
if (a_on != b_on) {
|
||||
cmp = a_on ? -1 : 1;
|
||||
} else {
|
||||
cmp = a->name_in_lower_case.compare(b->name_in_lower_case);
|
||||
}
|
||||
} else {
|
||||
cmp = a->name_in_lower_case.compare(b->name_in_lower_case);
|
||||
}
|
||||
return ascending ? (cmp < 0) : (cmp > 0);
|
||||
});
|
||||
}
|
||||
|
||||
if (sort_specs) {
|
||||
sort_specs->SpecsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void PatchManager::show_patch_tooltip(const PatchData& patch) {
|
||||
if (!patch.caution.empty()) {
|
||||
ImGui::WarnTooltip(patch.description.c_str(), patch.caution.c_str());
|
||||
@@ -1753,6 +1822,9 @@ namespace overlay::windows {
|
||||
|
||||
// clear old patches
|
||||
patches.clear();
|
||||
// drop the cached sorted view so the table rebuilds it (in default order)
|
||||
// on the next frame
|
||||
patches_sorted.clear();
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
DLL_MAP.clear();
|
||||
DLL_MAP_ORG.clear();
|
||||
@@ -1807,6 +1879,7 @@ namespace overlay::windows {
|
||||
bool imported = false;
|
||||
// clear old patches
|
||||
patches.clear();
|
||||
patches_sorted.clear();
|
||||
url_fetch_errors.clear();
|
||||
|
||||
// load patches for main dll
|
||||
|
||||
@@ -133,9 +133,18 @@ namespace overlay::windows {
|
||||
static std::vector<PatchData> patches;
|
||||
static bool local_patches_initialized;
|
||||
|
||||
// cached sorted view of `patches` for the table, stored as indices so a
|
||||
// stale entry can never dangle if `patches` is rebuilt; rebuilt only when
|
||||
// the sort order changes or the patch list is reloaded (not every frame)
|
||||
static std::vector<size_t> patches_sorted;
|
||||
|
||||
void config_load();
|
||||
void config_save();
|
||||
|
||||
// rebuild the cached sorted view of `patches` from the active table's
|
||||
// sort specs; no-op unless the sort changed or the patch list changed
|
||||
void update_sorted_patches();
|
||||
|
||||
void append_patches(
|
||||
std::string &patches_json,
|
||||
bool apply_patches = false,
|
||||
|
||||
Reference in New Issue
Block a user