patcher: make the checkbox label toggleable (#882)
Requested by a certain sea creature. Don't know why ImGui doesn't do this by default. Also, use ImGui internal mixed state (tri-state) checkbox instead rendering our own.
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
|
#include "external/imgui/imgui_internal.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "misc/clipboard.h"
|
#include "misc/clipboard.h"
|
||||||
@@ -215,19 +216,14 @@ namespace overlay::windows {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw a square checkmark for mixed-state groups (neither checked or unchecked)
|
// the state text next to a toggle is the checkbox label, so it has to dim itself instead
|
||||||
void render_patch_group_mixed_checkbox_mark() {
|
// of being wrapped in BeginDisabled (which would also kill the click area)
|
||||||
const auto check_min = ImGui::GetItemRectMin();
|
void push_toggle_label_color(bool checked) {
|
||||||
const float check_size = ImGui::GetFrameHeight();
|
auto color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||||
const float calculated_padding = check_size / 3.6f;
|
if (!checked) {
|
||||||
const float padding = calculated_padding < 1.0f ? 1.0f : calculated_padding;
|
color.w *= ImGui::GetStyle().DisabledAlpha;
|
||||||
ImGui::GetWindowDrawList()->AddRectFilled(
|
}
|
||||||
ImVec2(check_min.x + padding, check_min.y + padding),
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
ImVec2(
|
|
||||||
check_min.x + check_size - padding,
|
|
||||||
check_min.y + check_size - padding),
|
|
||||||
ImGui::GetColorU32(ImGuiCol_CheckMark),
|
|
||||||
ImGui::GetStyle().FrameRounding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// render a tri-state checkbox for groups
|
// render a tri-state checkbox for groups
|
||||||
@@ -237,18 +233,24 @@ namespace overlay::windows {
|
|||||||
const std::vector<size_t>& members,
|
const std::vector<size_t>& members,
|
||||||
bool& checked) {
|
bool& checked) {
|
||||||
const bool mixed = state.status == PatchGroupStatus::Mixed;
|
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) {
|
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) {
|
if (mixed) {
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopItemFlag();
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
if (mixed && !changed) {
|
|
||||||
render_patch_group_mixed_checkbox_mark();
|
|
||||||
}
|
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -266,11 +268,11 @@ namespace overlay::windows {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// render aggregate status or error text beside the group checkbox
|
// render error text beside the group checkbox; other states are shown as its label
|
||||||
void render_patch_group_status(const PatchGroupState& state, bool checked) {
|
void render_patch_group_status(const PatchGroupState& state) {
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::AlignTextToFramePadding();
|
|
||||||
if (state.status == PatchGroupStatus::Error) {
|
if (state.status == PatchGroupStatus::Error) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
const auto& error_patch = patcher::patches[state.first_error_index];
|
const auto& error_patch = patcher::patches[state.first_error_index];
|
||||||
const auto error_reason = error_patch.error_reason.empty()
|
const auto error_reason = error_patch.error_reason.empty()
|
||||||
? "Unknown error"
|
? "Unknown error"
|
||||||
@@ -279,12 +281,6 @@ namespace overlay::windows {
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f));
|
||||||
ImGui::TextUnformatted(error_text.c_str());
|
ImGui::TextUnformatted(error_text.c_str());
|
||||||
ImGui::PopStyleColor();
|
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)) {
|
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||||
show_patch_group_tooltip(group);
|
show_patch_group_tooltip(group);
|
||||||
}
|
}
|
||||||
render_patch_group_status(group_state, group_checked);
|
render_patch_group_status(group_state);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
const auto group_name = get_patch_group_display_name(
|
const auto group_name = get_patch_group_display_name(
|
||||||
@@ -874,8 +870,18 @@ namespace overlay::windows {
|
|||||||
render_patch_group_child_gutter(last_group_child);
|
render_patch_group_child_gutter(last_group_child);
|
||||||
ImGui::SameLine();
|
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);
|
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;
|
patcher::config_dirty = true;
|
||||||
switch (patch_status) {
|
switch (patch_status) {
|
||||||
case patcher::PatchStatus::Enabled:
|
case patcher::PatchStatus::Enabled:
|
||||||
@@ -901,13 +907,14 @@ namespace overlay::windows {
|
|||||||
patch.last_status = patcher::is_patch_active(patch);
|
patch.last_status = patcher::is_patch_active(patch);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||||
show_patch_tooltip(patch);
|
show_patch_tooltip(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// second column, part 2: additional options UI (dropdown, text input)
|
// second column, part 2: additional options UI (dropdown, text input)
|
||||||
ImGui::SameLine();
|
|
||||||
if (patch_status == patcher::PatchStatus::Error){
|
if (patch_status == patcher::PatchStatus::Error){
|
||||||
|
ImGui::SameLine();
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 0.f, 0.f, 1.f));
|
||||||
if (patch.error_reason.empty()) {
|
if (patch.error_reason.empty()) {
|
||||||
@@ -917,6 +924,7 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
} else if (patch.type == patcher::PatchType::Union || patch.type == patcher::PatchType::Integer) {
|
} else if (patch.type == patcher::PatchType::Union || patch.type == patcher::PatchType::Integer) {
|
||||||
|
ImGui::SameLine();
|
||||||
if (patch_status == patcher::PatchStatus::Enabled) {
|
if (patch_status == patcher::PatchStatus::Enabled) {
|
||||||
if (patch.type == patcher::PatchType::Union) {
|
if (patch.type == patcher::PatchType::Union) {
|
||||||
set_patch_option_width();
|
set_patch_option_width();
|
||||||
@@ -970,11 +978,6 @@ namespace overlay::windows {
|
|||||||
show_patch_tooltip(patch);
|
show_patch_tooltip(patch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ImGui::AlignTextToFramePadding();
|
|
||||||
ImGui::BeginDisabled(!patch_checked);
|
|
||||||
ImGui::TextUnformatted(patch_checked ? "ON" : "off");
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::HighlightTableRowOnHover();
|
ImGui::HighlightTableRowOnHover();
|
||||||
|
|||||||
Reference in New Issue
Block a user