From 6a3b10111fd260a599265655f127c0e6ce3540ea Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 1 Jan 2025 16:19:38 +0100 Subject: [PATCH] feat: Highlight main menu item when using a shortcut --- .../include/hex/api/shortcut_manager.hpp | 15 +++++--- lib/libimhex/source/api/content_registry.cpp | 11 ++---- lib/libimhex/source/api/shortcut_manager.cpp | 36 ++++++++++++++----- main/gui/source/window/window.cpp | 2 ++ plugins/builtin/romfs/lang/en_US.json | 1 + .../source/content/settings_entries.cpp | 24 ++++++++++--- .../source/content/window_decoration.cpp | 14 ++++++++ 7 files changed, 77 insertions(+), 26 deletions(-) diff --git a/lib/libimhex/include/hex/api/shortcut_manager.hpp b/lib/libimhex/include/hex/api/shortcut_manager.hpp index 757471c7c..c58646c60 100644 --- a/lib/libimhex/include/hex/api/shortcut_manager.hpp +++ b/lib/libimhex/include/hex/api/shortcut_manager.hpp @@ -194,10 +194,12 @@ namespace hex { class ShortcutManager { public: using Callback = std::function; + using EnabledCallback = std::function; struct ShortcutEntry { Shortcut shortcut; std::vector unlocalizedName; Callback callback; + EnabledCallback enabledCallback; }; /** @@ -205,9 +207,10 @@ namespace hex { * @param shortcut The shortcut to add. * @param unlocalizedName The unlocalized name of the shortcut * @param callback The callback to call when the shortcut is triggered. + * @param enabledCallback Callback that's called to check if this shortcut is enabled */ - static void addGlobalShortcut(const Shortcut &shortcut, const std::vector &unlocalizedName, const Callback &callback); - static void addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback); + static void addGlobalShortcut(const Shortcut &shortcut, const std::vector &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); + static void addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); /** * @brief Add a view-specific shortcut. View-specific shortcuts can only be triggered when the specified view is focused. @@ -215,9 +218,10 @@ namespace hex { * @param shortcut The shortcut to add. * @param unlocalizedName The unlocalized name of the shortcut * @param callback The callback to call when the shortcut is triggered. + * @param enabledCallback Callback that's called to check if this shortcut is enabled */ - static void addShortcut(View *view, const Shortcut &shortcut, const std::vector &unlocalizedName, const Callback &callback); - static void addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback); + static void addShortcut(View *view, const Shortcut &shortcut, const std::vector &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); + static void addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; }); /** @@ -252,6 +256,9 @@ namespace hex { static void enableMacOSMode(); + [[nodiscard]] static std::optional getLastActivatedMenu(); + static void resetLastActivatedMenu(); + [[nodiscard]] static std::optional getPreviousShortcut(); [[nodiscard]] static std::vector getGlobalShortcuts(); diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index d12814eab..87ae13c2f 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -933,17 +933,10 @@ namespace hex { }); if (shortcut != Shortcut::None) { - auto callbackIfEnabled = [enabledCallback, function]{ if (enabledCallback()) { function(); } }; - - const auto unlocalizedShortcutName = - unlocalizedMainMenuNames.size() == 1 ? - std::vector { unlocalizedMainMenuNames.back() } : - std::vector(unlocalizedMainMenuNames.begin() + 1, unlocalizedMainMenuNames.end()); - if (shortcut.isLocal() && view != nullptr) - ShortcutManager::addShortcut(view, shortcut, unlocalizedShortcutName, callbackIfEnabled); + ShortcutManager::addShortcut(view, shortcut, unlocalizedMainMenuNames, function, enabledCallback); else - ShortcutManager::addGlobalShortcut(shortcut, unlocalizedShortcutName, callbackIfEnabled); + ShortcutManager::addGlobalShortcut(shortcut, unlocalizedMainMenuNames, function, enabledCallback); } } diff --git a/lib/libimhex/source/api/shortcut_manager.cpp b/lib/libimhex/source/api/shortcut_manager.cpp index a5dbee456..369de01f0 100644 --- a/lib/libimhex/source/api/shortcut_manager.cpp +++ b/lib/libimhex/source/api/shortcut_manager.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ namespace hex { std::atomic s_paused; std::optional s_prevShortcut; bool s_macOSMode = false; + AutoReset> s_lastShortcutMainMenu; } @@ -232,27 +234,27 @@ namespace hex { } - void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector &unlocalizedName, const std::function &callback) { + void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get()); - auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { shortcut, unlocalizedName, callback } }); + auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { shortcut, unlocalizedName, callback, enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } - void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function &callback) { + void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding global shortcut {} for {}", shortcut.toString(), unlocalizedName.get()); - auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { shortcut, { unlocalizedName }, callback } }); + auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { shortcut, { unlocalizedName }, callback, enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } - void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector &unlocalizedName, const std::function &callback) { + void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.back().get()); - auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, unlocalizedName, callback } }); + auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, unlocalizedName, callback, enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } - void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function &callback) { + void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function &callback, const EnabledCallback &enabledCallback) { log::debug("Adding shortcut {} for {}", shortcut.toString(), unlocalizedName.get()); - auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, { unlocalizedName }, callback } }); + auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { shortcut, { unlocalizedName }, callback, enabledCallback } }); if (!inserted) log::error("Failed to add shortcut!"); } @@ -285,7 +287,14 @@ namespace hex { if (auto it = shortcuts.find(shortcut); it != shortcuts.end()) { const auto &[foundShortcut, entry] = *it; - entry.callback(); + + if (entry.enabledCallback()) { + entry.callback(); + + if (!entry.unlocalizedName.empty()) { + s_lastShortcutMainMenu = entry.unlocalizedName.front(); + } + } } } @@ -305,6 +314,15 @@ namespace hex { processShortcut(pressedShortcut, s_globalShortcuts); } + std::optional ShortcutManager::getLastActivatedMenu() { + return *s_lastShortcutMainMenu; + } + + void ShortcutManager::resetLastActivatedMenu() { + s_lastShortcutMainMenu->reset(); + } + + void ShortcutManager::clearShortcuts() { s_globalShortcuts->clear(); } diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 0703a410a..191cb3a07 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -591,6 +591,8 @@ namespace hex { void Window::frame() { auto &io = ImGui::GetIO(); + ShortcutManager::resetLastActivatedMenu(); + // Loop through all views and draw them for (auto &[name, view] : ContentRegistry::Views::impl::getEntries()) { ImGui::GetCurrentContext()->NextWindowData.ClearFlags(); diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 0dce48a26..db34fc4fb 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -503,6 +503,7 @@ "hex.builtin.setting.interface.native_window_decorations": "Use OS Window decorations", "hex.builtin.setting.interface.color": "Color theme", "hex.builtin.setting.interface.crisp_scaling": "Enable crisp scaling", + "hex.builtin.setting.interface.display_shortcut_highlights": "Highlight menu when using shortcuts", "hex.builtin.setting.interface.fps": "FPS Limit", "hex.builtin.setting.interface.fps.unlocked": "Unlocked", "hex.builtin.setting.interface.fps.native": "Native", diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index dec3ce61f..2c00074e3 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -302,7 +302,8 @@ namespace hex::plugin::builtin { ImGui::SameLine(); std::string fullName; - for (const auto &part : m_fullName) { + for (u32 i = m_fullName.size() == 1 ? 0 : 1; i < m_fullName.size(); i += 1) { + const auto &part = m_fullName[i]; fullName += Lang(part).get(); fullName += " -> "; } @@ -804,6 +805,7 @@ namespace hex::plugin::builtin { ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.pattern_data_row_bg", false); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.always_show_provider_tabs", false); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.show_header_command_palette", true); + ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.display_shortcut_highlights", true); std::vector languageNames; std::vector languageCodes; @@ -957,14 +959,28 @@ namespace hex::plugin::builtin { /* Shorcuts */ { - EventImHexStartupFinished::subscribe([]{ + EventImHexStartupFinished::subscribe([] { for (const auto &shortcutEntry : ShortcutManager::getGlobalShortcuts()) { - ContentRegistry::Settings::add("hex.builtin.setting.shortcuts", "hex.builtin.setting.shortcuts.global", shortcutEntry.unlocalizedName.back(), nullptr, shortcutEntry.shortcut, shortcutEntry.unlocalizedName); + ContentRegistry::Settings::add( + "hex.builtin.setting.shortcuts", + "hex.builtin.setting.shortcuts.global", + shortcutEntry.unlocalizedName.back(), + nullptr, + shortcutEntry.shortcut, + shortcutEntry.unlocalizedName + ); } for (auto &[viewName, view] : ContentRegistry::Views::impl::getEntries()) { for (const auto &shortcutEntry : ShortcutManager::getViewShortcuts(view.get())) { - ContentRegistry::Settings::add("hex.builtin.setting.shortcuts", viewName, shortcutEntry.unlocalizedName.back(), view.get(), shortcutEntry.shortcut, shortcutEntry.unlocalizedName); + ContentRegistry::Settings::add( + "hex.builtin.setting.shortcuts", + viewName, + shortcutEntry.unlocalizedName.back(), + view.get(), + shortcutEntry.shortcut, + shortcutEntry.unlocalizedName + ); } } }); diff --git a/plugins/builtin/source/content/window_decoration.cpp b/plugins/builtin/source/content/window_decoration.cpp index d017e3fbc..0dbfb2a15 100644 --- a/plugins/builtin/source/content/window_decoration.cpp +++ b/plugins/builtin/source/content/window_decoration.cpp @@ -26,6 +26,7 @@ namespace hex::plugin::builtin { u32 s_searchBarPosition = 0; ImGuiExt::Texture s_logoTexture; bool s_showSearchBar = true; + bool s_displayShortcutHighlights = true; void createNestedMenu(std::span menuItems, const char *icon, const Shortcut &shortcut, const ContentRegistry::Interface::impl::MenuCallback &callback, const ContentRegistry::Interface::impl::EnabledCallback &enabledCallback, const ContentRegistry::Interface::impl::SelectedCallback &selectedCallback) { const auto &name = menuItems.front(); @@ -287,7 +288,16 @@ namespace hex::plugin::builtin { if (ImGui::BeginMenu(Lang(menuName))) { populateMenu(menuName); ImGui::EndMenu(); + } else { + if (s_displayShortcutHighlights) { + if (const auto lastShortcutMenu = ShortcutManager::getLastActivatedMenu(); lastShortcutMenu.has_value()) { + if (menuName == *lastShortcutMenu) { + ImGui::NavHighlightActivated(ImGui::GetItemID()); + } + } + } } + } void drawMenu() { @@ -588,6 +598,10 @@ namespace hex::plugin::builtin { ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.show_header_command_palette", [](const ContentRegistry::Settings::SettingsValue &value) { s_showSearchBar = value.get(true); }); + + ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.display_shortcut_highlights", [](const ContentRegistry::Settings::SettingsValue &value) { + s_displayShortcutHighlights = value.get(true); + }); }