Skip to content

Commit

Permalink
feat: Highlight main menu item when using a shortcut
Browse files Browse the repository at this point in the history
  • Loading branch information
WerWolv committed Jan 1, 2025
1 parent 0a55f4b commit 6a3b101
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 26 deletions.
15 changes: 11 additions & 4 deletions lib/libimhex/include/hex/api/shortcut_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,30 +194,34 @@ namespace hex {
class ShortcutManager {
public:
using Callback = std::function<void()>;
using EnabledCallback = std::function<bool()>;
struct ShortcutEntry {
Shortcut shortcut;
std::vector<UnlocalizedString> unlocalizedName;
Callback callback;
EnabledCallback enabledCallback;
};

/**
* @brief Add a global shortcut. Global shortcuts can be triggered regardless of what view is currently focused
* @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<UnlocalizedString> &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<UnlocalizedString> &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.
* @param view The view to add the shortcut to.
* @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<UnlocalizedString> &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<UnlocalizedString> &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; });


/**
Expand Down Expand Up @@ -252,6 +256,9 @@ namespace hex {

static void enableMacOSMode();

[[nodiscard]] static std::optional<UnlocalizedString> getLastActivatedMenu();
static void resetLastActivatedMenu();

[[nodiscard]] static std::optional<Shortcut> getPreviousShortcut();

[[nodiscard]] static std::vector<ShortcutEntry> getGlobalShortcuts();
Expand Down
11 changes: 2 additions & 9 deletions lib/libimhex/source/api/content_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
36 changes: 27 additions & 9 deletions lib/libimhex/source/api/shortcut_manager.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <hex/api/shortcut_manager.hpp>
#include <imgui.h>
#include <hex/api/content_registry.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/helpers/auto_reset.hpp>

#include <hex/ui/view.hpp>
Expand All @@ -13,6 +14,7 @@ namespace hex {
std::atomic<bool> s_paused;
std::optional<Shortcut> s_prevShortcut;
bool s_macOSMode = false;
AutoReset<std::optional<UnlocalizedString>> s_lastShortcutMainMenu;

}

Expand Down Expand Up @@ -232,27 +234,27 @@ namespace hex {
}


void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback) {
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &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<void()> &callback) {
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &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<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback) {
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &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<void()> &callback) {
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &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!");
}

Expand Down Expand Up @@ -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();
}
}
}
}

Expand All @@ -305,6 +314,15 @@ namespace hex {
processShortcut(pressedShortcut, s_globalShortcuts);
}

std::optional<UnlocalizedString> ShortcutManager::getLastActivatedMenu() {
return *s_lastShortcutMainMenu;
}

void ShortcutManager::resetLastActivatedMenu() {
s_lastShortcutMainMenu->reset();
}


void ShortcutManager::clearShortcuts() {
s_globalShortcuts->clear();
}
Expand Down
2 changes: 2 additions & 0 deletions main/gui/source/window/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions plugins/builtin/romfs/lang/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
24 changes: 20 additions & 4 deletions plugins/builtin/source/content/settings_entries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 += " -> ";
}
Expand Down Expand Up @@ -804,6 +805,7 @@ namespace hex::plugin::builtin {
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.pattern_data_row_bg", false);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.always_show_provider_tabs", false);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.show_header_command_palette", true);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.display_shortcut_highlights", true);

std::vector<std::string> languageNames;
std::vector<nlohmann::json> languageCodes;
Expand Down Expand Up @@ -957,14 +959,28 @@ namespace hex::plugin::builtin {

/* Shorcuts */
{
EventImHexStartupFinished::subscribe([]{
EventImHexStartupFinished::subscribe([] {
for (const auto &shortcutEntry : ShortcutManager::getGlobalShortcuts()) {
ContentRegistry::Settings::add<KeybindingWidget>("hex.builtin.setting.shortcuts", "hex.builtin.setting.shortcuts.global", shortcutEntry.unlocalizedName.back(), nullptr, shortcutEntry.shortcut, shortcutEntry.unlocalizedName);
ContentRegistry::Settings::add<KeybindingWidget>(
"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<KeybindingWidget>("hex.builtin.setting.shortcuts", viewName, shortcutEntry.unlocalizedName.back(), view.get(), shortcutEntry.shortcut, shortcutEntry.unlocalizedName);
ContentRegistry::Settings::add<KeybindingWidget>(
"hex.builtin.setting.shortcuts",
viewName,
shortcutEntry.unlocalizedName.back(),
view.get(),
shortcutEntry.shortcut,
shortcutEntry.unlocalizedName
);
}
}
});
Expand Down
14 changes: 14 additions & 0 deletions plugins/builtin/source/content/window_decoration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const UnlocalizedString> 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();
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<bool>(true);
});

ContentRegistry::Settings::onChange("hex.builtin.setting.interface", "hex.builtin.setting.interface.display_shortcut_highlights", [](const ContentRegistry::Settings::SettingsValue &value) {
s_displayShortcutHighlights = value.get<bool>(true);
});
}


Expand Down

0 comments on commit 6a3b101

Please sign in to comment.