From 4f1ada7a2f727d827fc9e63da4b30aed01a623e5 Mon Sep 17 00:00:00 2001 From: bicarus-dev <202771338+bicarus-dev@users.noreply.github.com> Date: Fri, 28 Mar 2025 20:34:58 -0700 Subject: [PATCH] overlay: help tooltip updates (#279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Link to GitHub Issue, if one exists n/a ## Description of change In Options (and in Advanced/API/Search tabs), remove the help marker `(?)` and instead add a tooltip to the option name label and the control widgets like the checkbox, text input, and combo. This is to help with discoverability as many users failed to discover the `(?)` widget. Similarly, update the Patches tab to remove `(?)` and add tooltips to widgets. Patches with warnings `(!)` will continue to show it to distinguish it from other patches. Update styling for the tooltip (dark red background -> dark gray) so that it stands out from other widgets. Remove the purple option text used for game-specific options. ## Compiling 👍 ## Testing Tested in spicecfg, and in-game overlay. --- src/spice2x/overlay/imgui/extensions.cpp | 63 +++++++++++++------ src/spice2x/overlay/imgui/extensions.h | 2 + src/spice2x/overlay/windows/config.cpp | 29 +++++++-- src/spice2x/overlay/windows/patch_manager.cpp | 37 ++++++++--- src/spice2x/overlay/windows/patch_manager.h | 1 + 5 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/spice2x/overlay/imgui/extensions.cpp b/src/spice2x/overlay/imgui/extensions.cpp index 41394b6..00d5119 100644 --- a/src/spice2x/overlay/imgui/extensions.cpp +++ b/src/spice2x/overlay/imgui/extensions.cpp @@ -6,36 +6,61 @@ namespace ImGui { + const auto fg = ImVec4(0.910f, 0.914f, 0.922f, 1.0f); + const auto bg = ImVec4(0.192f, 0.212f, 0.220f, 1.0f); + + void HelpTooltip(const char* desc) { + ImGui::PushStyleColor(ImGuiCol_Border, bg); + ImGui::PushStyleColor(ImGuiCol_BorderShadow, bg); + ImGui::PushStyleColor(ImGuiCol_PopupBg, bg); + ImGui::PushStyleColor(ImGuiCol_Text, fg); + + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + + ImGui::PopStyleColor(4); + } + void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + HelpTooltip(desc); } } + void WarnTooltip(const char* desc, const char* warn) { + ImGui::PushStyleColor(ImGuiCol_Border, bg); + ImGui::PushStyleColor(ImGuiCol_BorderShadow, bg); + ImGui::PushStyleColor(ImGuiCol_PopupBg, bg); + ImGui::PushStyleColor(ImGuiCol_Text, fg); + + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + if (desc) { + ImGui::TextUnformatted(desc); + ImGui::TextUnformatted(""); + } + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f)); + if (warn) { + ImGui::TextUnformatted("WARNING:"); + ImGui::TextUnformatted(warn); + } + ImGui::PopStyleColor(); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + + ImGui::PopStyleColor(4); + } + void WarnMarker(const char* desc, const char* warn) { ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f)); ImGui::TextDisabled("(!)"); ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - if (desc) { - ImGui::TextUnformatted(desc); - ImGui::TextUnformatted(""); - } - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f)); - if (warn) { - ImGui::TextUnformatted("WARNING:"); - ImGui::TextUnformatted(warn); - } - ImGui::PopStyleColor(); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + WarnTooltip(desc, warn); } } diff --git a/src/spice2x/overlay/imgui/extensions.h b/src/spice2x/overlay/imgui/extensions.h index d79d858..3395292 100644 --- a/src/spice2x/overlay/imgui/extensions.h +++ b/src/spice2x/overlay/imgui/extensions.h @@ -2,7 +2,9 @@ namespace ImGui { + void HelpTooltip(const char* desc); void HelpMarker(const char* desc); + void WarnTooltip(const char* desc, const char* warn); void WarnMarker(const char* desc, const char* warn); void DummyMarker(); void Knob(float fraction, float size, float thickness = 2.f, diff --git a/src/spice2x/overlay/windows/config.cpp b/src/spice2x/overlay/windows/config.cpp index 948f474..c34707b 100644 --- a/src/spice2x/overlay/windows/config.cpp +++ b/src/spice2x/overlay/windows/config.cpp @@ -2629,9 +2629,8 @@ namespace overlay::windows { // list entry ImGui::PushID(&option); ImGui::AlignTextToFramePadding(); - ImGui::HelpMarker(definition.desc.c_str()); - ImGui::SameLine(); if (option.is_active()) { + // active option if (option.disabled || definition.disabled) { ImGui::TextColored(ImVec4(1.f, 0.4f, 0.f, 1.f), "%s", definition.title.c_str()); } else { @@ -2639,18 +2638,21 @@ namespace overlay::windows { } } else if (definition.hidden || (!definition.game_name.empty() && definition.game_name != this->games_selected_name)) { + // wrong game - grayed out ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.f), "%s", definition.title.c_str()); - } else if (definition.game_name == this->games_selected_name) { - ImGui::TextColored(ImVec4(0.8f, 0, 0.8f, 1.f), "%s", definition.title.c_str()); } else { + // normal text ImGui::Text("%s", definition.title.c_str()); } + if (ImGui::IsItemHovered()) { + ImGui::HelpTooltip(definition.desc.c_str()); + } ImGui::NextColumn(); ImGui::AlignTextToFramePadding(); if (definition.display_name.empty()) { - ImGui::Text("-%s", definition.name.c_str()); + ImGui::TextDisabled("-%s", definition.name.c_str()); } else { - ImGui::Text("-%s", definition.display_name.c_str()); + ImGui::TextDisabled("-%s", definition.display_name.c_str()); } ImGui::NextColumn(); if (option.disabled || definition.disabled) { @@ -2665,6 +2667,9 @@ namespace overlay::windows { option.value = state ? "/ENABLED" : ""; ::Config::getInstance().updateBinding(games_list[games_selected], option); } + if (ImGui::IsItemHovered()) { + ImGui::HelpTooltip(definition.desc.c_str()); + } break; } case OptionType::Integer: { @@ -2691,6 +2696,9 @@ namespace overlay::windows { option.value = buffer; ::Config::getInstance().updateBinding(games_list[games_selected], option); } + if (ImGui::IsItemHovered()) { + ImGui::HelpTooltip(definition.desc.c_str()); + } break; } case OptionType::Hex: { @@ -2724,6 +2732,9 @@ namespace overlay::windows { option.value = buffer; ::Config::getInstance().updateBinding(games_list[games_selected], option); } + if (ImGui::IsItemHovered()) { + ImGui::HelpTooltip(definition.desc.c_str()); + } break; } case OptionType::Text: { @@ -2741,6 +2752,9 @@ namespace overlay::windows { option.value = buffer; ::Config::getInstance().updateBinding(games_list[games_selected], option); } + if (ImGui::IsItemHovered()) { + ImGui::HelpTooltip(definition.desc.c_str()); + } break; } case OptionType::Enum: { @@ -2771,6 +2785,9 @@ namespace overlay::windows { } ImGui::EndCombo(); } + if (ImGui::IsItemHovered()) { + ImGui::HelpTooltip(definition.desc.c_str()); + } break; } default: { diff --git a/src/spice2x/overlay/windows/patch_manager.cpp b/src/spice2x/overlay/windows/patch_manager.cpp index 35391f1..1d53b92 100644 --- a/src/spice2x/overlay/windows/patch_manager.cpp +++ b/src/spice2x/overlay/windows/patch_manager.cpp @@ -649,17 +649,9 @@ namespace overlay::windows { // first column, part 1: help / caution marker ImGui::TableNextColumn(); - const std::string description = patch.description; - const std::string caution = patch.caution; - if (!description.empty() && !caution.empty()) { + if (!patch.caution.empty()) { ImGui::AlignTextToFramePadding(); - ImGui::WarnMarker(description.c_str(), caution.c_str()); - } else if (!description.empty()) { - ImGui::AlignTextToFramePadding(); - ImGui::HelpMarker(description.c_str()); - } else if (!caution.empty()) { - ImGui::AlignTextToFramePadding(); - ImGui::WarnMarker(nullptr, caution.c_str()); + ImGui::WarnMarker(nullptr, patch.caution.c_str()); } else { ImGui::DummyMarker(); } @@ -705,6 +697,11 @@ namespace overlay::windows { if (style_color_pushed) { ImGui::PopStyleColor(style_color_pushed); } + if (ImGui::IsItemHovered()) { + show_patch_tooltip(patch); + } + + // show range after label for integer patches if (patch.type == PatchType::Integer) { ImGui::SameLine(); auto& numpatch = patch.patch_number; @@ -741,6 +738,9 @@ namespace overlay::windows { patch.last_status = is_patch_active(patch); } ImGui::EndDisabled(); + if (ImGui::IsItemHovered()) { + show_patch_tooltip(patch); + } // second column, part 2: additional options UI (dropdown, text input) ImGui::SameLine(); @@ -767,6 +767,9 @@ namespace overlay::windows { } ImGui::EndCombo(); } + if (ImGui::IsItemHovered()) { + show_patch_tooltip(patch); + } } else if (patch.type == PatchType::Integer) { ImGui::SetNextItemWidth(200.0f); auto& numpatch = patch.patch_number; @@ -780,6 +783,9 @@ namespace overlay::windows { apply_patch(patch, true); config_dirty = true; } + if (ImGui::IsItemHovered()) { + show_patch_tooltip(patch); + } } } else if (patch_status == PatchStatus::Disabled) { ImGui::SetNextItemWidth(200.0f); @@ -794,6 +800,9 @@ namespace overlay::windows { ImGui::InputInt("##dummy_int_input", &patch.patch_number.value); } ImGui::EndDisabled(); + if (ImGui::IsItemHovered()) { + show_patch_tooltip(patch); + } } } else { ImGui::AlignTextToFramePadding(); @@ -822,6 +831,14 @@ namespace overlay::windows { } } + void PatchManager::show_patch_tooltip(const PatchData& patch) { + if (!patch.caution.empty()) { + ImGui::WarnTooltip(patch.description.c_str(), patch.caution.c_str()); + } else if (!patch.description.empty()) { + ImGui::HelpTooltip(patch.description.c_str()); + } + } + void PatchManager::hard_apply_patches() { std::vector written_list; for (auto& patch : patches) { diff --git a/src/spice2x/overlay/windows/patch_manager.h b/src/spice2x/overlay/windows/patch_manager.h index 1d22799..105f199 100644 --- a/src/spice2x/overlay/windows/patch_manager.h +++ b/src/spice2x/overlay/windows/patch_manager.h @@ -142,6 +142,7 @@ namespace overlay::windows { std::function filter = std::function(), std::string pe_identifier_for_patch = ""); + void show_patch_tooltip(const PatchData& patch); }; PatchStatus is_patch_active(PatchData &patch);