From d11d689d07df2b5e507110bc5f22ad51ea4cb3cc Mon Sep 17 00:00:00 2001 From: nxroot Date: Thu, 22 Oct 2026 14:22:35 +0100 Subject: [PATCH] Mod manager: a checkbox per modpack dependency in the uninstall confirmation Each dependency the confirmation offers has its own checkbox, all ticked, in a list that scrolls past eight, so a player can keep any of them. The first checkbox ticks or unticks them all, shows a mixed mark while only some are ticked, and says how many go ("Uninstall 12 of 19 modpack dependencies?"). What it frees counts only the ticked ones. Co-Authored-By: Claude Opus 5.5 --- Launcher/gui_internal.h | 2 +- Launcher/gui_mods.cpp | 42 ++++++++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/Launcher/gui_internal.h b/Launcher/gui_internal.h index 5118f80a..25db42af 100644 --- a/Launcher/gui_internal.h +++ b/Launcher/gui_internal.h @@ -611,7 +611,7 @@ struct ModsPanel { // The mods those brought in that nothing else needs (launcher_mods::orphaned_dependencies), // offered with them; worked out once for the folders in remove_extras_for. std::vector remove_extras, remove_extras_for; - bool remove_extras_on{true}; + std::vector remove_extras_picked; // per entry of remove_extras: ticked to go too fs::path conflict_source; // install waiting for "Replace" confirmation std::string conflict_name; diff --git a/Launcher/gui_mods.cpp b/Launcher/gui_mods.cpp index 958a6968..e0699716 100644 --- a/Launcher/gui_mods.cpp +++ b/Launcher/gui_mods.cpp @@ -1922,10 +1922,15 @@ void mods_window(Launcher& launcher, const Fonts& fonts, ImVec2 size, Ui& ui, Mo for (const auto& entry : entries) everything.push_back(entry.mod.name); panel.remove_extras = launcher_mods::orphaned_dependencies(panel.root, chosen, everything); panel.remove_extras_for = chosen; - panel.remove_extras_on = true; + panel.remove_extras_picked.assign(panel.remove_extras.size(), 1); } auto names = chosen; - if (panel.remove_extras_on) names.insert(names.end(), panel.remove_extras.begin(), panel.remove_extras.end()); + int picked = 0; + for (std::size_t i = 0; i < panel.remove_extras.size(); ++i) + if (panel.remove_extras_picked[i]) { + names.push_back(panel.remove_extras[i]); + ++picked; + } std::uint64_t bytes{}; for (const auto& name : names) if (const auto found = panel.sizes.find(name); found != panel.sizes.end()) bytes += found->second; @@ -1942,17 +1947,32 @@ void mods_window(Launcher& launcher, const Fonts& fonts, ImVec2 size, Ui& ui, Mo if (!panel.remove_extras.empty()) { ImGui::Spacing(); const auto count = static_cast(panel.remove_extras.size()); - const auto label = count == 1 ? std::string("Uninstall 1 modpack dependency?") - : std::format("Uninstall {} modpack dependencies?", count); - // The label as far from the box as the settings page has it (an item's spacing, not the inner one). + const auto label = picked == count || picked == 0 + ? (count == 1 ? std::string("Uninstall 1 modpack dependency?") : std::format("Uninstall {} modpack dependencies?", count)) + : std::format("Uninstall {} of {} modpack dependencies?", picked, count); + // The labels as far from their boxes as the settings page has them (an item's spacing, not the inner one). ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemInnerSpacing.y)); - ImGui::Checkbox(label.c_str(), &panel.remove_extras_on); + // The first box ticks or unticks them all; it shows a mark of its own while only some are ticked. + bool all = picked == count; + ImGui::PushItemFlag(ImGuiItemFlags_MixedValue, picked > 0 && picked < count); + if (ImGui::Checkbox((label + "###all_extras").c_str(), &all)) + std::fill(panel.remove_extras_picked.begin(), panel.remove_extras_picked.end(), static_cast(all)); + ImGui::PopItemFlag(); + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Mods it brought in that no other installed mod needs. Untick any to keep."); + // One box each, in a list that scrolls past eight. + const float row = ImGui::GetFrameHeightWithSpacing(); + const float shown = std::min(static_cast(count), 8.0f) * row + ImGui::GetStyle().WindowPadding.y; + ImGui::Indent(S(28)); + ImGui::BeginChild("##extras", ImVec2(0, shown), ImGuiChildFlags_NavFlattened); + for (std::size_t i = 0; i < panel.remove_extras.size(); ++i) { + ImGui::PushID(static_cast(i)); + bool on = panel.remove_extras_picked[i] != 0; + if (ImGui::Checkbox(title(panel.remove_extras[i]).c_str(), &on)) panel.remove_extras_picked[i] = on; + ImGui::PopID(); + } + ImGui::EndChild(); + ImGui::Unindent(S(28)); ImGui::PopStyleVar(); - if (ImGui::IsItemHovered()) ImGui::SetTooltip("Mods it brought in that no other installed mod needs"); - const std::size_t listed = std::min(panel.remove_extras.size(), 8); - for (std::size_t i = 0; i < listed; ++i) ImGui::TextDisabled("%s", title(panel.remove_extras[i]).c_str()); - if (panel.remove_extras.size() > listed) - ImGui::TextDisabled("and %d more", static_cast(panel.remove_extras.size() - listed)); } if (bytes) ImGui::TextDisabled("That frees %s.", size_text(bytes).c_str()); ImGui::PopTextWrapPos();