diff --git a/src/spice2x/overlay/windows/patch_manager.cpp b/src/spice2x/overlay/windows/patch_manager.cpp index c69b235..c790fee 100644 --- a/src/spice2x/overlay/windows/patch_manager.cpp +++ b/src/spice2x/overlay/windows/patch_manager.cpp @@ -7,6 +7,7 @@ #include #include "avs/game.h" #include "cfg/configurator.h" +#include "external/imgui/imgui_internal.h" #include "games/io.h" #include "launcher/launcher.h" #include "misc/clipboard.h" @@ -215,19 +216,14 @@ namespace overlay::windows { return name; } - // draw a square checkmark for mixed-state groups (neither checked or unchecked) - void render_patch_group_mixed_checkbox_mark() { - const auto check_min = ImGui::GetItemRectMin(); - const float check_size = ImGui::GetFrameHeight(); - const float calculated_padding = check_size / 3.6f; - const float padding = calculated_padding < 1.0f ? 1.0f : calculated_padding; - ImGui::GetWindowDrawList()->AddRectFilled( - ImVec2(check_min.x + padding, check_min.y + padding), - ImVec2( - check_min.x + check_size - padding, - check_min.y + check_size - padding), - ImGui::GetColorU32(ImGuiCol_CheckMark), - ImGui::GetStyle().FrameRounding); + // the state text next to a toggle is the checkbox label, so it has to dim itself instead + // of being wrapped in BeginDisabled (which would also kill the click area) + void push_toggle_label_color(bool checked) { + auto color = ImGui::GetStyleColorVec4(ImGuiCol_Text); + if (!checked) { + color.w *= ImGui::GetStyle().DisabledAlpha; + } + ImGui::PushStyleColor(ImGuiCol_Text, color); } // render a tri-state checkbox for groups @@ -237,18 +233,24 @@ namespace overlay::windows { const std::vector& members, bool& checked) { const bool mixed = state.status == PatchGroupStatus::Mixed; - ImGui::BeginDisabled(state.status == PatchGroupStatus::Error); + const bool error = state.status == PatchGroupStatus::Error; + ImGui::BeginDisabled(error); if (mixed) { - ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushItemFlag(ImGuiItemFlags_MixedValue, true); } - const bool changed = ImGui::Checkbox("##group_checked_checkbox", &checked); + // the state text doubles as the checkbox label so clicking it toggles the group; + // errored groups show the failing patch there instead + const char *label = "##group_checked_checkbox"; + if (!error) { + label = mixed ? "mixed" : (checked ? "ON" : "off"); + } + push_toggle_label_color(checked); + const bool changed = ImGui::Checkbox(label, &checked); + ImGui::PopStyleColor(); if (mixed) { - ImGui::PopStyleColor(); + ImGui::PopItemFlag(); } ImGui::EndDisabled(); - if (mixed && !changed) { - render_patch_group_mixed_checkbox_mark(); - } if (!changed) { return false; } @@ -266,11 +268,11 @@ namespace overlay::windows { return true; } - // render aggregate status or error text beside the group checkbox - void render_patch_group_status(const PatchGroupState& state, bool checked) { - ImGui::SameLine(); - ImGui::AlignTextToFramePadding(); + // render error text beside the group checkbox; other states are shown as its label + void render_patch_group_status(const PatchGroupState& state) { if (state.status == PatchGroupStatus::Error) { + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); const auto& error_patch = patcher::patches[state.first_error_index]; const auto error_reason = error_patch.error_reason.empty() ? "Unknown error" @@ -279,12 +281,6 @@ namespace overlay::windows { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f)); ImGui::TextUnformatted(error_text.c_str()); ImGui::PopStyleColor(); - } else if (state.status == PatchGroupStatus::Mixed) { - ImGui::TextUnformatted("mixed"); - } else { - ImGui::BeginDisabled(!checked); - ImGui::TextUnformatted(checked ? "ON" : "off"); - ImGui::EndDisabled(); } } @@ -311,7 +307,7 @@ namespace overlay::windows { if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) { show_patch_group_tooltip(group); } - render_patch_group_status(group_state, group_checked); + render_patch_group_status(group_state); ImGui::TableSetColumnIndex(0); const auto group_name = get_patch_group_display_name( @@ -874,8 +870,18 @@ namespace overlay::windows { render_patch_group_child_gutter(last_group_child); ImGui::SameLine(); } + // plain on/off patches have no extra widget, so the state text doubles as the + // checkbox label - clicking the text toggles it, like the Options tab + const bool has_extra_widget = + patch_status == patcher::PatchStatus::Error + || patch.type == patcher::PatchType::Union + || patch.type == patcher::PatchType::Integer; + const char *checkbox_label = has_extra_widget + ? "##patch_checked_checkbox" + : (patch_checked ? "ON" : "off"); + push_toggle_label_color(patch_checked); ImGui::BeginDisabled(patch_status == patcher::PatchStatus::Error); - if (ImGui::Checkbox("##patch_checked_checkbox", &patch_checked)) { + if (ImGui::Checkbox(checkbox_label, &patch_checked)) { patcher::config_dirty = true; switch (patch_status) { case patcher::PatchStatus::Enabled: @@ -901,13 +907,14 @@ namespace overlay::windows { patch.last_status = patcher::is_patch_active(patch); } ImGui::EndDisabled(); + ImGui::PopStyleColor(); if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) { show_patch_tooltip(patch); } // second column, part 2: additional options UI (dropdown, text input) - ImGui::SameLine(); if (patch_status == patcher::PatchStatus::Error){ + ImGui::SameLine(); ImGui::AlignTextToFramePadding(); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f)); if (patch.error_reason.empty()) { @@ -917,6 +924,7 @@ namespace overlay::windows { } ImGui::PopStyleColor(); } else if (patch.type == patcher::PatchType::Union || patch.type == patcher::PatchType::Integer) { + ImGui::SameLine(); if (patch_status == patcher::PatchStatus::Enabled) { if (patch.type == patcher::PatchType::Union) { set_patch_option_width(); @@ -970,11 +978,6 @@ namespace overlay::windows { show_patch_tooltip(patch); } } - } else { - ImGui::AlignTextToFramePadding(); - ImGui::BeginDisabled(!patch_checked); - ImGui::TextUnformatted(patch_checked ? "ON" : "off"); - ImGui::EndDisabled(); } ImGui::HighlightTableRowOnHover();