diff --git a/src/Menu.cpp b/src/Menu.cpp index f36e4d3c03..f5d3820bed 100644 --- a/src/Menu.cpp +++ b/src/Menu.cpp @@ -155,6 +155,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( EnableShaderBlocking, FirstTimeSetupCompleted, SkipClearCacheConfirmation, + AutoHideFeatureList, Theme, SelectedThemePreset) diff --git a/src/Menu.h b/src/Menu.h index 5220f014e4..af06ba78fd 100644 --- a/src/Menu.h +++ b/src/Menu.h @@ -379,6 +379,7 @@ class Menu bool EnableShaderBlocking = false; // Enable shader blocking hotkeys for debugging bool FirstTimeSetupCompleted = false; // Track if first-time setup has been completed bool SkipClearCacheConfirmation = false; // Skip confirmation dialog when clearing shader cache + bool AutoHideFeatureList = false; // Auto-hide left feature list panel, show on hover ThemeSettings Theme; std::string SelectedThemePreset = ""; // Currently selected theme preset (empty = custom/user theme) }; diff --git a/src/Menu/FeatureListRenderer.cpp b/src/Menu/FeatureListRenderer.cpp index 622f16485c..9ba483d1e0 100644 --- a/src/Menu/FeatureListRenderer.cpp +++ b/src/Menu/FeatureListRenderer.cpp @@ -29,6 +29,66 @@ namespace return std::find(CORE_MENU_NAMES.begin(), CORE_MENU_NAMES.end(), menuName) != CORE_MENU_NAMES.end(); } + /** + * @brief Determines if the left feature panel should be visible based on auto-hide settings and mouse position + * @return true if panel should be visible, false if it should be hidden + */ + bool ShouldShowLeftPanel() + { + bool autoHideEnabled = globals::menu->GetSettings().AutoHideFeatureList; + static bool leftPanelVisible = true; + static float hoverStartTime = 0.0f; + static bool wasHovering = false; + + if (!autoHideEnabled) { + leftPanelVisible = true; + return true; + } + + // Get mouse position and window bounds + ImVec2 mousePos = ImGui::GetMousePos(); + ImVec2 windowPos = ImGui::GetWindowPos(); + ImVec2 windowSize = ImGui::GetWindowSize(); + float currentTime = static_cast(ImGui::GetTime()); + + // Use constants for auto-hide behavior + const float activationZoneWidth = ThemeManager::Constants::AUTOHIDE_ACTIVATION_ZONE_WIDTH; + const float expandDelay = ThemeManager::Constants::AUTOHIDE_EXPAND_DELAY; + const float panelWidth = windowSize.x * ThemeManager::Constants::AUTOHIDE_PANEL_WIDTH_RATIO; + + // Calculate relative X position + const float relativeX = mousePos.x - windowPos.x; + + // For activation: only check if mouse is at left edge (allow any Y position for easier triggering) + // Prevent negative X from triggering, but don't restrict Y-axis for activation + bool mouseInActivationZone = relativeX >= 0.0f && relativeX < activationZoneWidth; + + // For staying visible: check both X and Y to ensure mouse is actually over the panel area + const bool mouseOverPanelX = relativeX >= 0.0f && relativeX < panelWidth; + const bool mouseOverPanelY = mousePos.y >= windowPos.y && mousePos.y <= (windowPos.y + windowSize.y); + bool mouseOverPanel = leftPanelVisible && mouseOverPanelX && mouseOverPanelY; + + // Track hover start time + if (mouseInActivationZone && !wasHovering) { + hoverStartTime = currentTime; + wasHovering = true; + } else if (!mouseInActivationZone) { + wasHovering = false; + } + + // Expand only after delay has elapsed + bool shouldExpand = mouseInActivationZone && (currentTime - hoverStartTime >= expandDelay); + + // Update visibility: expand with delay, or stay visible while mouse is over panel + if (shouldExpand || mouseOverPanel) { + leftPanelVisible = true; + } else if (!mouseOverPanel && !mouseInActivationZone) { + leftPanelVisible = false; + } + + return leftPanelVisible; + } + void SeparatorTextWithFont(const char* text, Menu::FontRole role) { MenuFonts::FontRoleGuard guard(role); @@ -134,13 +194,22 @@ void FeatureListRenderer::RenderFeatureList( HandlePendingFeatureSelection(pendingFeatureSelection, menuList, selectedMenu); - // Create the table with two columns - if (ImGui::BeginTable("Menus Table", 2, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable)) { - ImGui::TableSetupColumn("##ListOfMenus", 0, 2); - ImGui::TableSetupColumn("##MenuConfig", 0, 8); - - RenderLeftColumn(menuList, selectedMenu, featureSearch, categoryExpansionStates); - RenderRightColumn(menuList, selectedMenu); + // Determine if left panel should be visible based on auto-hide settings + bool leftPanelVisible = ShouldShowLeftPanel(); + + // Create the table with appropriate number of columns based on visibility + int numColumns = leftPanelVisible ? 2 : 1; + if (ImGui::BeginTable("Menus Table", numColumns, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable)) { + if (leftPanelVisible) { + ImGui::TableSetupColumn("##ListOfMenus", 0, 2); + ImGui::TableSetupColumn("##MenuConfig", 0, 8); + RenderLeftColumn(menuList, selectedMenu, featureSearch, categoryExpansionStates); + RenderRightColumn(menuList, selectedMenu); + } else { + // When left panel is hidden, right column takes full width + ImGui::TableSetupColumn("##MenuConfig", 0, 1); + RenderRightColumn(menuList, selectedMenu); + } ImGui::EndTable(); } diff --git a/src/Menu/SettingsTabRenderer.cpp b/src/Menu/SettingsTabRenderer.cpp index 7e3698c75e..2c0785a592 100644 --- a/src/Menu/SettingsTabRenderer.cpp +++ b/src/Menu/SettingsTabRenderer.cpp @@ -319,6 +319,7 @@ void SettingsTabRenderer::RenderInterfaceTab() if (BeginTabItemWithFont("Interface", Menu::FontRole::Heading)) { MenuFonts::TabBarPaddingGuard tabPaddingGuard(Menu::FontRole::Subheading); if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) { + RenderBehaviorTab(); RenderThemesTab(); RenderFontsTab(); RenderStylingTab(); @@ -329,6 +330,71 @@ void SettingsTabRenderer::RenderInterfaceTab() } } +void SettingsTabRenderer::RenderBehaviorTab() +{ + if (BeginTabItemWithFont("Behavior", Menu::FontRole::Heading)) { + auto& themeSettings = globals::menu->GetSettings().Theme; + + SeparatorTextWithFont("UI Behavior", Menu::FontRole::Subheading); + + ImGui::Checkbox("Show Icon Buttons in Header", &themeSettings.ShowActionIcons); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text( + "When enabled: Shows action buttons (Save, Load, Clear Cache) as icons in the header\n" + "When disabled: Shows as text buttons below the header"); + } + + if (themeSettings.ShowActionIcons) { + ImGui::Indent(); + if (ImGui::Checkbox("Use Monochrome Icons", &themeSettings.UseMonochromeIcons)) { + globals::menu->pendingIconReload = true; + } + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Uses white monochrome icons that adapt to your theme's text color"); + } + ImGui::SameLine(); + if (ImGui::Checkbox("Use Monochrome CS Logo", &themeSettings.UseMonochromeLogo)) { + globals::menu->pendingIconReload = true; + } + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Uses monochrome version of the Community Shaders logo"); + } + ImGui::Unindent(); + } + + ImGui::Checkbox("Show Footer", &themeSettings.ShowFooter); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Shows the footer with game version, swap chain, and GPU information at the bottom of the window"); + } + + ImGui::Checkbox("Center Header Title", &themeSettings.CenterHeader); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Centers the Community Shaders title and logo in the header title bar"); + } + + ImGui::Checkbox("Auto-hide Feature List", &globals::menu->GetSettings().AutoHideFeatureList); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Automatically hides the left feature list panel. Move cursor to the left edge to show it."); + } + + ImGui::SliderFloat("Tooltip Hover Delay", &themeSettings.TooltipHoverDelay, 0.0f, 2.0f, "%.2f s", ImGuiSliderFlags_AlwaysClamp); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::TextUnformatted("Time in seconds to wait before a tooltip appears when hovering over an item."); + } + + SeparatorTextWithFont("Visual Effects", Menu::FontRole::Subheading); + + if (ImGui::Checkbox("Background Blur", &themeSettings.BackgroundBlurEnabled)) { + BackgroundBlur::SetEnabled(themeSettings.BackgroundBlurEnabled); + } + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("Applies a blur effect to the background behind the menu window."); + } + + ImGui::EndTabItem(); + } +} + void SettingsTabRenderer::RenderThemesTab() { if (BeginTabItemWithFont("Themes", Menu::FontRole::Heading)) { @@ -851,56 +917,6 @@ void SettingsTabRenderer::RenderStylingTab() auto& themeSettings = globals::menu->GetSettings().Theme; auto& style = themeSettings.Style; - SeparatorTextWithFont("Styling Options", Menu::FontRole::Subheading); - - ImGui::Checkbox("Show Icon Buttons in Header", &themeSettings.ShowActionIcons); - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text( - "When enabled: Shows action buttons (Save, Load, Clear Cache) as icons in the header\n" - "When disabled: Shows as text buttons below the header"); - } - - if (themeSettings.ShowActionIcons) { - ImGui::Indent(); - if (ImGui::Checkbox("Use Monochrome Icons", &themeSettings.UseMonochromeIcons)) { - // Defer icon reload to next frame to avoid rendering with released textures - globals::menu->pendingIconReload = true; - } - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Uses white monochrome icons that adapt to your theme's text color"); - } - ImGui::SameLine(); - if (ImGui::Checkbox("Use Monochrome CS Logo", &themeSettings.UseMonochromeLogo)) { - globals::menu->pendingIconReload = true; - } - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Uses monochrome version of the Community Shaders logo"); - } - ImGui::Unindent(); - } - - ImGui::Checkbox("Show Footer", &themeSettings.ShowFooter); - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Shows the footer with game version, swap chain, and GPU information at the bottom of the window"); - } - - ImGui::Checkbox("Center Header Title", &themeSettings.CenterHeader); - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Centers the Community Shaders title and logo in the header title bar"); - } - - ImGui::SliderFloat("Tooltip Hover Delay", &themeSettings.TooltipHoverDelay, 0.0f, 2.0f, "%.2f s", ImGuiSliderFlags_AlwaysClamp); - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::TextUnformatted("Time in seconds to wait before a tooltip appears when hovering over an item."); - } - - if (ImGui::Checkbox("Background Blur", &themeSettings.BackgroundBlurEnabled)) { - BackgroundBlur::SetEnabled(themeSettings.BackgroundBlurEnabled); - } - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Applies a blur effect to the background behind the menu window."); - } - SeparatorTextWithFont("Main", Menu::FontRole::Subheading); if (ImGui::SliderFloat("Global Scale", &themeSettings.GlobalScale, -1.f, 1.f, "%.2f")) { float trueScale = exp2(themeSettings.GlobalScale); diff --git a/src/Menu/SettingsTabRenderer.h b/src/Menu/SettingsTabRenderer.h index 2247a0c6cb..a3476d4eda 100644 --- a/src/Menu/SettingsTabRenderer.h +++ b/src/Menu/SettingsTabRenderer.h @@ -31,6 +31,7 @@ class SettingsTabRenderer static void RenderInterfaceTab(); // Interface sub-tabs + static void RenderBehaviorTab(); static void RenderThemesTab(); static void RenderFontsTab(); static void RenderStylingTab(); diff --git a/src/Menu/ThemeManager.h b/src/Menu/ThemeManager.h index 86ca010b7a..c7aecf1258 100644 --- a/src/Menu/ThemeManager.h +++ b/src/Menu/ThemeManager.h @@ -184,6 +184,11 @@ class ThemeManager // Feature header constants static constexpr float DEFAULT_FEATURE_TITLE_SCALE = 1.5f; // Default scale for feature title text static constexpr float VERSION_TEXT_OPACITY = 0.6f; // Opacity for version text next to feature title + + // Auto-hide feature list constants + static constexpr float AUTOHIDE_ACTIVATION_ZONE_WIDTH = 50.0f; // Width of hover zone at left edge (px) + static constexpr float AUTOHIDE_EXPAND_DELAY = 0.25f; // Delay before expanding panel (seconds) + static constexpr float AUTOHIDE_PANEL_WIDTH_RATIO = 0.2f; // Ratio of window width for panel (2/10) }; static ThemeManager* GetSingleton()