overlay: redesign Options tab(s) (#747)

## Link to GitHub Issue or related Pull Request, if one exists
#0

## Description of change
Combine API/Options/Advanced/Development/Search tabs into one Options
tab and add a left nav for navigation categories.

Reduce information density for options table by making each row double
the height and slightly increasing widget sizes.

## Testing
This commit is contained in:
bicarus
2026-06-11 00:17:54 -07:00
committed by GitHub
parent 5c244eaca7
commit a170627f52
5 changed files with 675 additions and 457 deletions
+2
View File
@@ -46,6 +46,8 @@ struct OptionDefinition {
// for OptionPickerType::FilePath // for OptionPickerType::FilePath
std::string file_extension = ""; std::string file_extension = "";
std::string quick_setting_category = "";
}; };
class Option { class Option {
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -40,6 +40,7 @@ namespace launcher {
spice2x_Dx9On12, spice2x_Dx9On12,
NoLegacy, NoLegacy,
RichPresence, RichPresence,
DiscordAppID,
SmartEAmusement, SmartEAmusement,
EAmusementMaintenance, EAmusementMaintenance,
spice2x_EAmusementMaintenance, spice2x_EAmusementMaintenance,
@@ -218,7 +219,6 @@ namespace launcher {
LogLevel, LogLevel,
EAAutomap, EAAutomap,
EANetdump, EANetdump,
DiscordAppID,
BlockingLogger, BlockingLogger,
DebugCreateFile, DebugCreateFile,
VerboseGraphicsLogging, VerboseGraphicsLogging,
@@ -309,7 +309,11 @@ namespace launcher {
enum class OptionsCategory { enum class OptionsCategory {
Everything, Everything,
Basic, GameOptions,
Display,
Audio,
Network,
Overlay,
Advanced, Advanced,
Dev, Dev,
API API
@@ -319,7 +323,9 @@ namespace launcher {
extern bool USE_CMD_OVERRIDE; extern bool USE_CMD_OVERRIDE;
const std::vector<std::string> &get_categories(Options::OptionsCategory category); const std::vector<std::string> &get_categories(Options::OptionsCategory category);
const std::vector<std::string> &get_quick_setting_categories();
const std::vector<OptionDefinition> &get_option_definitions(); const std::vector<OptionDefinition> &get_option_definitions();
void validate_option_categories();
std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]); std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]);
std::vector<Option> merge_options(const std::vector<Option> &options, const std::vector<Option> &overrides); std::vector<Option> merge_options(const std::vector<Option> &options, const std::vector<Option> &overrides);
+348 -254
View File
@@ -1,6 +1,7 @@
#include "config.h" #include "config.h"
#include <algorithm> #include <algorithm>
#include <set>
#include <thread> #include <thread>
#include <windows.h> #include <windows.h>
@@ -63,6 +64,21 @@ namespace overlay::windows {
constexpr ImVec4 TEXT_COLOR_RED(1.f, 0.f, 0.f, 1.f); constexpr ImVec4 TEXT_COLOR_RED(1.f, 0.f, 0.f, 1.f);
constexpr uint32_t OPTION_INPUT_TEXT_WIDTH = 512; constexpr uint32_t OPTION_INPUT_TEXT_WIDTH = 512;
// subtab groups shown in the Options tab left navigation, in display order
static const std::vector<std::pair<const char *, launcher::Options::OptionsCategory>> OPTIONS_TAB_GROUPS = {
{ "Game Options", launcher::Options::OptionsCategory::GameOptions },
{ "Display", launcher::Options::OptionsCategory::Display },
{ "Audio", launcher::Options::OptionsCategory::Audio },
{ "Network", launcher::Options::OptionsCategory::Network },
{ "Overlay", launcher::Options::OptionsCategory::Overlay },
{ "Others", launcher::Options::OptionsCategory::Advanced },
{ "Development", launcher::Options::OptionsCategory::Dev },
{ "API", launcher::Options::OptionsCategory::API },
};
static const char *OPTIONS_TAB_SEARCH = "Search";
static const char *OPTIONS_TAB_QUICK = "Quick Settings";
std::optional<std::vector<hooks::audio::AsioDriverScanEntry>> asio_driver_list; std::optional<std::vector<hooks::audio::AsioDriverScanEntry>> asio_driver_list;
Config::Config(overlay::SpiceOverlay *overlay) : Window(overlay) { Config::Config(overlay::SpiceOverlay *overlay) : Window(overlay) {
@@ -222,6 +238,245 @@ namespace overlay::windows {
} }
} }
void Config::build_options_tab(float page_offset) {
const float content_height = ImGui::GetWindowContentRegionMax().y - page_offset;
const float nav_width = overlay::apply_scaling(130);
// default to the first group on first display
if (this->options_group_selected.empty()) {
this->options_group_selected = OPTIONS_TAB_QUICK;
}
auto options = games::get_options(this->games_selected_name);
// collect the set of categories that currently have at least one option that
// would be rendered by build_options(), in a single pass. used to keep the nav
// in sync with the content so categories that only contain hidden options appear
// in the nav exactly when "Show Hidden" is enabled. quick_categories tracks the
// distinct quick_setting_category names (quick settings group under their own
// categories, separate from the regular option categories).
std::set<std::string> visible_categories;
std::set<std::string> quick_categories;
if (options) {
for (auto &option : *options) {
auto &definition = option.get_definition();
if (!this->options_show_hidden && option.value.empty() && definition.hidden) {
continue;
}
if (!definition.game_name.empty() && definition.game_name != this->games_selected_name) {
continue;
}
visible_categories.insert(definition.category);
if (!definition.quick_setting_category.empty()) {
quick_categories.insert(definition.quick_setting_category);
}
}
}
// left navigation: one header per subtab, each listing its categories. clicking a
// header selects that group; clicking a category scrolls the content to that section.
ImGui::BeginChild("OptionsNav", ImVec2(nav_width, content_height), false);
// exact height of the global option controls pinned at the bottom of the nav
const bool show_restart = !cfg::CONFIGURATOR_STANDALONE && this->options_dirty;
const float ctrl_spacing = ImGui::GetStyle().ItemSpacing.y;
float nav_controls_height = ImGui::GetFrameHeight(); // "Show Hidden" checkbox
if (show_restart) {
nav_controls_height += ctrl_spacing + ImGui::GetFrameHeight(); // "Restart Game" button
}
nav_controls_height += ctrl_spacing * 2.0f + 1.0f; // separator + padding
// scrollable nav list (group headers and categories)
ImGui::BeginChild("OptionsNavList",
ImVec2(0, content_height - nav_controls_height - ctrl_spacing), false);
// extra vertical padding between nav rows (headers and tree items)
const ImVec2 nav_item_spacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,
ImVec2(nav_item_spacing.x, nav_item_spacing.y + overlay::apply_scaling(2)));
// arrow-less, non-collapsible nav header; selecting it clears any highlighted category
auto nav_header = [this](const char *label) {
const bool active = this->options_group_selected == label;
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf;
if (active) {
flags |= ImGuiTreeNodeFlags_Selected;
}
// give the active header a clear, distinct highlight
int colors_pushed = 0;
if (active) {
const ImVec4 accent = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
ImGui::PushStyleColor(ImGuiCol_Header, accent);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, accent);
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text));
colors_pushed = 3;
}
// taller frame padding so headers match the height of the category rows below
const ImVec2 frame_padding = ImGui::GetStyle().FramePadding;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
ImVec2(frame_padding.x, frame_padding.y + overlay::apply_scaling(4)));
ImGui::CollapsingHeader(label, flags);
ImGui::PopStyleVar();
if (colors_pushed) {
ImGui::PopStyleColor(colors_pushed);
}
if (ImGui::IsItemClicked()) {
this->options_group_selected = label;
this->options_category_selected.clear();
this->options_scroll_top = true;
}
};
// indented, selectable category row under a header; clicking it flags the
// content pane to scroll to that category's section
auto nav_category = [this](const char *group_label, const std::string &category) {
ImGui::Indent(INDENT * 0.5f);
if (ImGui::Selectable(category.c_str(), this->options_category_selected == category)) {
this->options_group_selected = group_label;
this->options_category_selected = category;
this->options_scroll_pending = true;
}
ImGui::Unindent(INDENT * 0.5f);
};
// search entry: selecting it shows the search box in the content pane
nav_header(OPTIONS_TAB_SEARCH);
// quick settings entry: selecting it shows the quick settings in the content pane
nav_header(OPTIONS_TAB_QUICK);
// when quick settings is selected, list its categories so they can be jumped to
if (this->options_group_selected == OPTIONS_TAB_QUICK) {
ImGui::PushID(OPTIONS_TAB_QUICK);
for (const auto &category : launcher::get_quick_setting_categories()) {
if (quick_categories.contains(category)) {
nav_category(OPTIONS_TAB_QUICK, category);
}
}
ImGui::PopID();
}
for (const auto &group : OPTIONS_TAB_GROUPS) {
nav_header(group.first);
// only the selected group expands to show its categories
if (this->options_group_selected != group.first) {
continue;
}
ImGui::PushID(group.first);
for (const auto &category : launcher::get_categories(group.second)) {
if (!category.empty() && visible_categories.contains(category)) {
nav_category(group.first, category);
}
}
ImGui::PopID();
}
ImGui::PopStyleVar();
ImGui::EndChild(); // OptionsNavList
// global option controls pinned to the bottom of the nav
ImGui::SetCursorPosY(content_height - nav_controls_height);
ImGui::Separator();
ImGui::Checkbox("Show Hidden", &this->options_show_hidden);
// left-align the text inside the full-width control buttons
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f));
if (show_restart) {
if (ImGui::Button("Restart Game")) {
launcher::restart();
}
ImGui::SameLine();
ImGui::HelpMarker("You need to restart the game to apply the changed settings.");
}
ImGui::PopStyleVar(); // ButtonTextAlign
ImGui::EndChild(); // OptionsNav
ImGui::SameLine();
// content: only the options belonging to the selected group.
ImGui::BeginChild("OptionsContent", ImVec2(0, content_height), false);
if (this->options_scroll_top) {
ImGui::SetScrollY(0.0f);
this->options_scroll_top = false;
}
// breathing room at the top of the content area
ImGui::Dummy(ImVec2(0.0f, overlay::apply_scaling(4)));
// when a category was clicked in the nav, scroll the content to its section
auto scroll_anchor = [this](const std::string &category) {
if (this->options_scroll_pending && this->options_category_selected == category) {
ImGui::Dummy(ImVec2(0.0f, 0.0f));
ImGui::SetScrollHereY(0.0f);
this->options_scroll_pending = false;
}
};
if (this->options_group_selected == OPTIONS_TAB_SEARCH) {
// search from all options
ImGui::SetNextItemWidth(420.f);
if (ImGui::InputTextWithHint(
"", "Type here to search in options..", &this->search_filter,
ImGuiInputTextFlags_EscapeClearsAll)) {
this->search_filter_in_lower_case = strtolower(this->search_filter);
}
if (!this->search_filter.empty()) {
ImGui::SameLine();
if (ImGui::Button("Clear")) {
this->search_filter.clear();
this->search_filter_in_lower_case.clear();
}
}
ImGui::Spacing();
// draw matching options
if (!this->search_filter.empty()) {
for (auto category : launcher::get_categories(launcher::Options::OptionsCategory::Everything)) {
this->build_options(
options,
category,
const_cast<std::string *>(&this->search_filter_in_lower_case));
}
}
} else if (this->options_group_selected == OPTIONS_TAB_QUICK) {
for (const auto &category : launcher::get_quick_setting_categories()) {
if (!quick_categories.contains(category)) {
continue;
}
scroll_anchor(category);
this->build_options(options, category, nullptr, true);
}
} else {
for (const auto &group : OPTIONS_TAB_GROUPS) {
if (this->options_group_selected != group.first) {
continue;
}
for (const auto &category : launcher::get_categories(group.second)) {
if (category.empty()) {
continue;
}
scroll_anchor(category);
this->build_options(options, category);
}
}
}
ImGui::EndChild();
}
void Config::build_content() { void Config::build_content() {
// if standalone then fullscreen window // if standalone then fullscreen window
@@ -268,7 +523,6 @@ namespace overlay::windows {
// tab selection // tab selection
auto tab_selected_new = ConfigTab::CONFIG_TAB_INVALID; auto tab_selected_new = ConfigTab::CONFIG_TAB_INVALID;
if (ImGui::BeginTabBar("Config Tabs", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabBar("Config Tabs", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
const int page_offset = overlay::apply_scaling(cfg::CONFIGURATOR_STANDALONE ? 88 : 110);
const int page_offset2 = overlay::apply_scaling(cfg::CONFIGURATOR_STANDALONE ? 65 : 87); const int page_offset2 = overlay::apply_scaling(cfg::CONFIGURATOR_STANDALONE ? 65 : 87);
if (ImGui::BeginTabItem("Buttons")) { if (ImGui::BeginTabItem("Buttons")) {
@@ -399,163 +653,9 @@ namespace overlay::windows {
ImGui::EndChild(); ImGui::EndChild();
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem("API")) {
tab_selected_new = ConfigTab::CONFIG_TAB_API;
// API options list
ImGui::BeginChild("ApiTab", ImVec2(
0, ImGui::GetWindowContentRegionMax().y - page_offset2), false);
auto options = games::get_options(this->games_selected_name);
for (auto category : launcher::get_categories(launcher::Options::OptionsCategory::API)) {
this->build_options(options, category);
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Options")) { if (ImGui::BeginTabItem("Options")) {
tab_selected_new = ConfigTab::CONFIG_TAB_OPTIONS; tab_selected_new = ConfigTab::CONFIG_TAB_OPTIONS;
this->build_options_tab(page_offset2);
// options list
ImGui::BeginChild("Options", ImVec2(
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
auto options = games::get_options(this->games_selected_name);
for (auto category : launcher::get_categories(launcher::Options::OptionsCategory::Basic)) {
this->build_options(options, category);
}
ImGui::EndChild();
// hidden options checkbox
ImGui::Checkbox("Show Hidden Options", &this->options_show_hidden);
if (!cfg::CONFIGURATOR_STANDALONE && this->options_dirty) {
ImGui::SameLine();
if (ImGui::Button("Restart Game")) {
launcher::restart();
}
ImGui::SameLine();
ImGui::HelpMarker("You need to restart the game to apply the changed settings.");
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Advanced")) {
tab_selected_new = ConfigTab::CONFIG_TAB_ADVANCED;
// advanced options list
ImGui::BeginChild("AdvancedOptions", ImVec2(
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
auto options = games::get_options(this->games_selected_name);
for (auto category : launcher::get_categories(launcher::Options::OptionsCategory::Advanced)) {
this->build_options(options, category);
}
ImGui::EndChild();
// hidden options checkbox
ImGui::Checkbox("Show Hidden Options", &this->options_show_hidden);
if (!cfg::CONFIGURATOR_STANDALONE && this->options_dirty) {
ImGui::SameLine();
if (ImGui::Button("Restart Game")) {
launcher::restart();
}
ImGui::SameLine();
ImGui::HelpMarker("You need to restart the game to apply the changed settings.");
}
// reset configuration button
ImGui::SameLine();
if (ImGui::Button("Reset Configuration")) {
ImGui::OpenPopup("Reset Config");
}
if (ImGui::BeginPopupModal("Reset Config", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
"Do you really want to reset your configuration for all games?\n"
"Warning: This can't be reverted!");
if (ImGui::Button("Yes")) {
::Config::getInstance().createConfigFile();
launcher::restart();
}
ImGui::SameLine();
if (ImGui::Button("Nope")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Development")) {
tab_selected_new = ConfigTab::CONFIG_TAB_DEV;
// dev options list
ImGui::BeginChild("DevOptions", ImVec2(
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
auto options = games::get_options(this->games_selected_name);
for (auto category : launcher::get_categories(launcher::Options::OptionsCategory::Dev)) {
this->build_options(options, category);
}
ImGui::EndChild();
// hidden options checkbox
ImGui::Checkbox("Show Hidden Options", &this->options_show_hidden);
if (!cfg::CONFIGURATOR_STANDALONE && this->options_dirty) {
ImGui::SameLine();
if (ImGui::Button("Restart Game")) {
launcher::restart();
}
ImGui::SameLine();
ImGui::HelpMarker("You need to restart the game to apply the changed settings.");
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Search")) {
tab_selected_new = ConfigTab::CONFIG_TAB_SEARCH;
ImGui::BeginChild("SearchOptions", ImVec2(
0, ImGui::GetWindowContentRegionMax().y - page_offset), false);
// search from all options
ImGui::Spacing();
ImGui::SetNextItemWidth(420.f);
if (ImGui::InputTextWithHint(
"", "Type here to search in options..", &this->search_filter,
ImGuiInputTextFlags_EscapeClearsAll)) {
this->search_filter_in_lower_case = strtolower(this->search_filter);
}
if (!this->search_filter.empty()) {
ImGui::SameLine();
if (ImGui::Button("Clear")) {
this->search_filter.clear();
this->search_filter_in_lower_case.clear();
}
}
ImGui::Spacing();
// draw all options
auto options = games::get_options(this->games_selected_name);
if (!this->search_filter.empty()) {
for (auto category : launcher::get_categories(launcher::Options::OptionsCategory::Everything)) {
this->build_options(
options,
category,
const_cast<std::string *>(&this->search_filter_in_lower_case));
}
}
ImGui::EndChild();
// hidden options checkbox
ImGui::Checkbox("Show Hidden Options", &this->options_show_hidden);
if (!cfg::CONFIGURATOR_STANDALONE && this->options_dirty) {
ImGui::SameLine();
if (ImGui::Button("Restart Game")) {
launcher::restart();
}
ImGui::SameLine();
ImGui::HelpMarker("You need to restart the game to apply the changed settings.");
}
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
ImGui::EndTabBar(); ImGui::EndTabBar();
@@ -4668,13 +4768,52 @@ namespace overlay::windows {
} }
void Config::build_options( void Config::build_options(
std::vector<Option> *options, const std::string &category, const std::string *filter) { std::vector<Option> *options, const std::string &category, const std::string *filter, bool quick_only) {
int options_count;
// collect the options that match the current filters. doing this once lets us
// skip rendering an empty header + table for categories with no matches, and
// avoids iterating the full option list a second time during rendering.
std::vector<Option *> matching;
for (auto &option : *options) {
auto &definition = option.get_definition();
if (quick_only) {
// quick settings group under quick_setting_category, not the regular category
if (definition.quick_setting_category.empty()) {
continue;
}
if (!category.empty() && definition.quick_setting_category != category) {
continue;
}
} else if (!category.empty() && definition.category != category) {
continue;
}
if (!this->options_show_hidden && option.value.empty() && definition.hidden) {
continue;
}
if (!definition.game_name.empty() && definition.game_name != this->games_selected_name) {
continue;
}
if (filter != nullptr) {
if (filter->empty() || !option.search_match(*filter)) {
continue;
}
// limit to 30 results
if (matching.size() > 30) {
continue;
}
}
matching.push_back(&option);
}
if (matching.empty()) {
return;
}
// category name // category name
std::string cat = "Options"; std::string cat = "Options";
if (!category.empty()) { if (!category.empty()) {
cat = category; cat = category;
} else if (quick_only) {
cat = "Quick Settings";
} else if (filter != nullptr) { } else if (filter != nullptr) {
cat = "Search results"; cat = "Search results";
} }
@@ -4683,84 +4822,28 @@ namespace overlay::windows {
// render table // render table
// tables must share the same ID to have synced column settings // tables must share the same ID to have synced column settings
const int num_columns = (filter != nullptr) ? 3 : 2; ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding,
if (ImGui::BeginTable("OptionsTable", num_columns, ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) { ImGui::GetStyle().CellPadding.y + overlay::apply_scaling(2));
auto widget_col_width = 0; if (ImGui::BeginTable("OptionsTable", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
if (filter != nullptr) { const int widget_col_width = overlay::apply_scaling(260);
ImGui::TableSetupColumn("Category", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(160)); ImGui::TableSetupColumn("Option", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Option", ImGuiTableColumnFlags_WidthStretch);
widget_col_width = overlay::apply_scaling(220);
} else {
ImGui::TableSetupColumn("Option", ImGuiTableColumnFlags_WidthStretch);
widget_col_width = overlay::apply_scaling(264);
}
ImGui::TableSetupColumn("Setting", ImGuiTableColumnFlags_WidthFixed, widget_col_width); ImGui::TableSetupColumn("Setting", ImGuiTableColumnFlags_WidthFixed, widget_col_width);
// iterate options // iterate options
options_count = 0; for (auto *option_ptr : matching) {
for (auto &option : *options) { auto &option = *option_ptr;
// get option definition
auto &definition = option.get_definition(); auto &definition = option.get_definition();
// check category
if (!category.empty() && definition.category != category) {
continue;
}
// check hidden option
if (!this->options_show_hidden && option.value.empty()) {
// skip hidden entries
if (definition.hidden) {
continue;
}
}
// check for game exclusivity
if (!definition.game_name.empty()) {
if (definition.game_name != this->games_selected_name) {
continue;
}
}
// filter
if (filter != nullptr) {
if (filter->empty()) {
continue;
}
if (!option.search_match(*filter)) {
continue;
}
// limit to 30 results
if (30 < options_count) {
continue;
}
}
options_count += 1;
// list entry // list entry
ImGui::PushID(&option); ImGui::PushID(&option);
ImGui::TableNextRow(); ImGui::TableNextRow();
// category (only shown for search results)
if (filter != nullptr) {
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
if (definition.category.empty()) {
ImGui::TextDisabled("-");
} else {
ImGui::TextDisabled("%s", definition.category.c_str());
}
}
// option name // option name
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding(); ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0);
if (filter == nullptr) { const float row_indent = overlay::apply_scaling(8);
// add indent to make options align under category header ImGui::Indent(row_indent);
ImGui::Indent(INDENT);
}
if (option.is_active()) { if (option.is_active()) {
// active option // active option
if (option.disabled || definition.disabled) { if (option.disabled || definition.disabled) {
@@ -4776,9 +4859,6 @@ namespace overlay::windows {
// normal text // normal text
ImGui::TextUnformatted(definition.title.c_str()); ImGui::TextUnformatted(definition.title.c_str());
} }
if (filter == nullptr) {
ImGui::Unindent(INDENT);
}
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) { if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
ImGui::HelpTooltip(definition.desc.c_str()); ImGui::HelpTooltip(definition.desc.c_str());
} }
@@ -4790,8 +4870,7 @@ namespace overlay::windows {
} else { } else {
param += definition.display_name; param += definition.display_name;
} }
ImGui::SameLine(); ImGui::TextDisabled("%s", param.c_str());
ImGui::TextColored(ImVec4(0.27f, 0.27f, 0.27f, 1.f), " %s", param.c_str());
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) { if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
const auto help = const auto help =
param + param +
@@ -4805,8 +4884,21 @@ namespace overlay::windows {
clipboard::copy_text(param.c_str()); clipboard::copy_text(param.c_str());
} }
ImGui::Unindent(row_indent);
ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
// option widgets // option widgets
ImGui::TableNextColumn(); ImGui::TableNextColumn();
// make widgets span two text lines tall to match the two-line name column
ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, ImGui::GetFontSize() * 0.36f);
// vertically center the widget within the two-line row
float row_h = ImGui::GetTextLineHeight() * 2.f;
float widget_h = ImGui::GetFrameHeight();
if (row_h > widget_h) {
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (row_h - widget_h) * 0.5f);
}
ImGui::PushStyleVarX(ImGuiStyleVar_ItemSpacing, 4.f); ImGui::PushStyleVarX(ImGuiStyleVar_ItemSpacing, 4.f);
if (option.disabled || definition.disabled) { if (option.disabled || definition.disabled) {
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
@@ -4979,6 +5071,7 @@ namespace overlay::windows {
} }
ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
ImGui::PopStyleVar(); // ImGuiStyleVar_FramePadding
// clean up disabled item flags // clean up disabled item flags
if (option.disabled || definition.disabled) { if (option.disabled || definition.disabled) {
@@ -5039,32 +5132,9 @@ namespace overlay::windows {
ImGui::PopID(); ImGui::PopID();
} }
// check if empty
if (options_count == 0) {
ImGui::TableNextRow();
// option category
if (filter != nullptr) {
ImGui::TableNextColumn();
ImGui::TextDisabled("-");
}
// name of option
ImGui::TableNextColumn();
if (filter == nullptr) {
ImGui::Indent(INDENT);
}
ImGui::TextDisabled("-");
if (filter == nullptr) {
ImGui::Unindent(INDENT);
}
// widget
ImGui::TableNextColumn();
ImGui::TextDisabled("-");
}
ImGui::EndTable(); ImGui::EndTable();
} }
ImGui::PopStyleVar(); // ImGuiStyleVar_CellPadding
ImGui::TextUnformatted(""); ImGui::TextUnformatted("");
} }
@@ -5118,6 +5188,7 @@ namespace overlay::windows {
void Config::build_menu(int *game_selected) { void Config::build_menu(int *game_selected) {
bool about_popup = false; bool about_popup = false;
bool reset_config_popup = false;
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 1.0f));
if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenuBar()) {
@@ -5131,6 +5202,10 @@ namespace overlay::windows {
if (ImGui::MenuItem("About")) { if (ImGui::MenuItem("About")) {
about_popup = true; about_popup = true;
} }
ImGui::Separator();
if (ImGui::MenuItem("Reset to Default")) {
reset_config_popup = true;
}
ImGui::EndMenu(); ImGui::EndMenu();
} else { } else {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@@ -5189,6 +5264,25 @@ namespace overlay::windows {
if (about_popup) { if (about_popup) {
ImGui::OpenPopup("About##topbarpopup"); ImGui::OpenPopup("About##topbarpopup");
} }
if (reset_config_popup) {
ImGui::OpenPopup("Reset Config");
}
// reset configuration confirmation popup
if (ImGui::BeginPopupModal("Reset Config", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
"Do you really want to reset your configuration for all games?\n"
"Warning: This can't be reverted!");
if (ImGui::Button("Yes")) {
::Config::getInstance().createConfigFile();
launcher::restart();
}
ImGui::SameLine();
if (ImGui::Button("Nope")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
// draw popups // draw popups
{ {
+9 -5
View File
@@ -20,12 +20,8 @@ namespace overlay::windows {
CONFIG_TAB_LIGHTS, CONFIG_TAB_LIGHTS,
CONFIG_TAB_CARDS, CONFIG_TAB_CARDS,
CONFIG_TAB_PATCHES, CONFIG_TAB_PATCHES,
CONFIG_TAB_API,
CONFIG_TAB_OPTIONS, CONFIG_TAB_OPTIONS,
CONFIG_TAB_ADVANCED,
CONFIG_TAB_DEV,
CONFIG_TAB_PRESETS, CONFIG_TAB_PRESETS,
CONFIG_TAB_SEARCH,
}; };
struct MatchEntry { struct MatchEntry {
@@ -134,6 +130,12 @@ namespace overlay::windows {
std::string search_filter = ""; std::string search_filter = "";
std::string search_filter_in_lower_case = ""; std::string search_filter_in_lower_case = "";
// Options tab left-nav: selected group, currently highlighted category, and a pending scroll
std::string options_group_selected = "";
std::string options_category_selected = "";
bool options_scroll_pending = false;
bool options_scroll_top = false;
std::filesystem::path file_picker_path; std::filesystem::path file_picker_path;
std::thread *file_picker_thread = nullptr; std::thread *file_picker_thread = nullptr;
bool file_picker_done = false; bool file_picker_done = false;
@@ -190,7 +192,9 @@ namespace overlay::windows {
std::string build_option_value_picker_title(const OptionDefinition& option); std::string build_option_value_picker_title(const OptionDefinition& option);
void build_option_value_picker(Option& option); void build_option_value_picker(Option& option);
void build_options( void build_options(
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr); std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr,
bool quick_only=false);
void build_options_tab(float page_offset);
void build_about(); void build_about();
void build_launcher(); void build_launcher();
void build_keypad_warning(); void build_keypad_warning();