diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 5b68238fe..c28baf810 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -424,7 +424,12 @@ namespace hex { * @param parameterCount The amount of parameters the function takes * @param func The function callback */ - void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func); + void addFunction( + const pl::api::Namespace &ns, + const std::string &name, + pl::api::FunctionParameterCount parameterCount, + const pl::api::FunctionCallback &func + ); /** * @brief Adds a new dangerous function to the pattern language @@ -434,7 +439,12 @@ namespace hex { * @param parameterCount The amount of parameters the function takes * @param func The function callback */ - void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func); + void addDangerousFunction( + const pl::api::Namespace &ns, + const std::string &name, + pl::api::FunctionParameterCount parameterCount, + const pl::api::FunctionCallback &func + ); /** * @brief Adds a new visualizer to the pattern language @@ -443,7 +453,11 @@ namespace hex { * @param function The function callback * @param parameterCount The amount of parameters the function takes */ - void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount); + void addVisualizer( + const std::string &name, + const impl::VisualizerFunctionCallback &function, + pl::api::FunctionParameterCount parameterCount + ); /** * @brief Adds a new inline visualizer to the pattern language @@ -452,7 +466,11 @@ namespace hex { * @param function The function callback * @param parameterCount The amount of parameters the function takes */ - void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount); + void addInlineVisualizer( + const std::string &name, + const impl::VisualizerFunctionCallback &function, + pl::api::FunctionParameterCount parameterCount + ); } @@ -545,7 +563,12 @@ namespace hex { * @param displayGeneratorFunction The function that will be called to generate the display function * @param editingFunction The function that will be called to edit the data */ - void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction = std::nullopt); + void add( + const UnlocalizedString &unlocalizedName, + size_t requiredSize, + impl::GeneratorFunction displayGeneratorFunction, + std::optional editingFunction = std::nullopt + ); /** * @brief Adds a new entry to the data inspector @@ -555,7 +578,14 @@ namespace hex { * @param displayGeneratorFunction The function that will be called to generate the display function * @param editingFunction The function that will be called to edit the data */ - void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction = std::nullopt); + void add( + const UnlocalizedString &unlocalizedName, + size_t requiredSize, + size_t maxSize, + impl::GeneratorFunction displayGeneratorFunction, + std::optional editingFunction = std::nullopt + ); + } /* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */ @@ -631,6 +661,7 @@ namespace hex { using DrawCallback = std::function; using MenuCallback = std::function; using EnabledCallback = std::function; + using SelectedCallback = std::function; using ClickCallback = std::function; struct MainMenuItem { @@ -644,6 +675,7 @@ namespace hex { View *view; MenuCallback callback; EnabledCallback enabledCallback; + SelectedCallback selectedCallback; }; struct SidebarItem { @@ -689,8 +721,36 @@ namespace hex { * @param enabledCallback The function to call to determine if the entry is enabled * @param view The view to use for the entry. If nullptr, the shortcut will work globally */ - void addMenuItem(const std::vector &unlocalizedMainMenuNames, const char *icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr); + void addMenuItem( + const std::vector &unlocalizedMainMenuNames, + const char *icon, + u32 priority, + const Shortcut &shortcut, + const impl::MenuCallback &function, + const impl::EnabledCallback& enabledCallback, View *view + ); + /** + * @brief Adds a new main menu entry + * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries + * @param icon The icon to use for the entry + * @param priority The priority of the entry. Lower values are displayed first + * @param shortcut The shortcut to use for the entry + * @param function The function to call when the entry is clicked + * @param enabledCallback The function to call to determine if the entry is enabled + * @param selectedCallback The function to call to determine if the entry is selected + * @param view The view to use for the entry. If nullptr, the shortcut will work globally + */ + void addMenuItem( + const std::vector &unlocalizedMainMenuNames, + const char *icon, + u32 priority, + const Shortcut &shortcut, + const impl::MenuCallback &function, + const impl::EnabledCallback& enabledCallback = []{ return true; }, + const impl::SelectedCallback &selectedCallback = []{ return false; }, + View *view = nullptr + ); /** * @brief Adds a new main menu entry @@ -699,9 +759,18 @@ namespace hex { * @param shortcut The shortcut to use for the entry * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled + * @param selectedCallback The function to call to determine if the entry is selected * @param view The view to use for the entry. If nullptr, the shortcut will work globally */ - void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr); + void addMenuItem( + const std::vector &unlocalizedMainMenuNames, + u32 priority, + const Shortcut &shortcut, + const impl::MenuCallback &function, + const impl::EnabledCallback& enabledCallback = []{ return true; }, + const impl::SelectedCallback &selectedCallback = []{ return false; }, + View *view = nullptr + ); /** * @brief Adds a new main menu sub-menu entry @@ -710,7 +779,12 @@ namespace hex { * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled */ - void addMenuItemSubMenu(std::vector unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }); + void addMenuItemSubMenu( + std::vector unlocalizedMainMenuNames, + u32 priority, + const impl::MenuCallback &function, + const impl::EnabledCallback& enabledCallback = []{ return true; } + ); /** * @brief Adds a new main menu sub-menu entry @@ -720,7 +794,13 @@ namespace hex { * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled */ - void addMenuItemSubMenu(std::vector unlocalizedMainMenuNames, const char *icon, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }); + void addMenuItemSubMenu( + std::vector unlocalizedMainMenuNames, + const char *icon, + u32 priority, + const impl::MenuCallback &function, + const impl::EnabledCallback& enabledCallback = []{ return true; } + ); /** @@ -755,7 +835,11 @@ namespace hex { * @param function The function to call to draw the item * @param enabledCallback The function */ - void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback = []{ return true; }); + void addSidebarItem( + const std::string &icon, + const impl::DrawCallback &function, + const impl::EnabledCallback &enabledCallback = []{ return true; } + ); /** * @brief Adds a new title bar button @@ -763,7 +847,11 @@ namespace hex { * @param unlocalizedTooltip The unlocalized tooltip to use for the button * @param function The function to call when the button is clicked */ - void addTitleBarButton(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function); + void addTitleBarButton( + const std::string &icon, + const UnlocalizedString &unlocalizedTooltip, + const impl::ClickCallback &function + ); } @@ -1026,7 +1114,12 @@ namespace hex { std::map &getExperiments(); } - void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = ""); + void addExperiment( + const std::string &experimentName, + const UnlocalizedString &unlocalizedName, + const UnlocalizedString &unlocalizedDescription = "" + ); + void enableExperiement(const std::string &experimentName, bool enabled); [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName); diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index c937d9036..c6d1372c3 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -15,6 +15,7 @@ using ImGuiID = unsigned int; struct ImVec2; struct ImFontAtlas; struct ImFont; +struct GLFWwindow; namespace hex { @@ -347,7 +348,11 @@ namespace hex { * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation) * @param select Whether to select the provider after adding it */ - prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface = false, bool select = true); + prv::Provider* createProvider( + const UnlocalizedString &unlocalizedName, + bool skipLoadInterface = false, + bool select = true + ); } @@ -362,6 +367,7 @@ namespace hex { void setMainWindowPosition(i32 x, i32 y); void setMainWindowSize(u32 width, u32 height); void setMainDockSpaceId(ImGuiID id); + void setMainWindowHandle(GLFWwindow *window); void setGlobalScale(float scale); void setNativeScale(float scale); @@ -459,6 +465,11 @@ namespace hex { */ ImGuiID getMainDockSpaceId(); + /** + * @brief Gets the main window's GLFW window handle + * @return GLFW window handle + */ + GLFWwindow* getMainWindowHandle(); /** * @brief Checks if borderless window mode is enabled currently diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index b12e53f1f..c06ebd47f 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -722,15 +722,23 @@ namespace hex { impl::getMainMenuItems().insert({ priority, { unlocalizedName } }); } - void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { - addMenuItem(unlocalizedMainMenuNames, nullptr, priority, shortcut, function, enabledCallback, view); + void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) { + addMenuItem(unlocalizedMainMenuNames, nullptr, priority, shortcut, function, enabledCallback, selectedCallback, view); } void addMenuItem(const std::vector &unlocalizedMainMenuNames, const char *icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { + addMenuItem(unlocalizedMainMenuNames, icon, priority, shortcut, function, enabledCallback, []{ return false; }, view); + } + + void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { + addMenuItem(unlocalizedMainMenuNames, nullptr, priority, shortcut, function, enabledCallback, []{ return false; }, view); + } + + void addMenuItem(const std::vector &unlocalizedMainMenuNames, const char *icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) { log::debug("Added new menu item to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority); impl::getMenuItems().insert({ - priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique(shortcut), view, function, enabledCallback } + priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique(shortcut), view, function, enabledCallback, selectedCallback } }); if (shortcut != Shortcut::None) { @@ -750,14 +758,14 @@ namespace hex { unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue); impl::getMenuItems().insert({ - priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique(), nullptr, function, enabledCallback } + priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique(), nullptr, function, enabledCallback, []{ return false; } } }); } void addMenuItemSeparator(std::vector unlocalizedMainMenuNames, u32 priority) { unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue); impl::getMenuItems().insert({ - priority, impl::MenuItem { unlocalizedMainMenuNames, "", std::make_unique(), nullptr, []{}, []{ return true; } } + priority, impl::MenuItem { unlocalizedMainMenuNames, "", std::make_unique(), nullptr, []{}, []{ return true; }, []{ return false; } } }); } diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 73f209f4f..2d5570b37 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -403,6 +403,11 @@ namespace hex { s_mainDockSpaceId = id; } + static GLFWwindow *s_mainWindowHandle; + void setMainWindowHandle(GLFWwindow *window) { + s_mainWindowHandle = window; + } + static float s_globalScale = 1.0; void setGlobalScale(float scale) { @@ -499,6 +504,10 @@ namespace hex { return impl::s_mainDockSpaceId; } + GLFWwindow* getMainWindowHandle() { + return impl::s_mainWindowHandle; + } + bool isBorderlessWindowModeEnabled() { return impl::s_borderlessWindowMode; } diff --git a/main/gui/source/window/win_window.cpp b/main/gui/source/window/win_window.cpp index 6b186e8fb..c7e284fdc 100644 --- a/main/gui/source/window/win_window.cpp +++ b/main/gui/source/window/win_window.cpp @@ -170,6 +170,10 @@ namespace hex { static_cast((::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)) * ImHexApi::System::getGlobalScale()) }; + if (glfwGetWindowMonitor(ImHexApi::System::getMainWindowHandle()) != nullptr) { + return HTCLIENT; + } + RECT window; if (!::GetWindowRect(hwnd, &window)) { return HTNOWHERE; diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 10c1ab8ae..4dc32cf39 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -230,7 +230,7 @@ namespace hex { } } - static void createNestedMenu(std::span menuItems, const char *icon, const Shortcut &shortcut, const std::function &callback, const std::function &enabledCallback) { + static 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(); if (name.get() == ContentRegistry::Interface::impl::SeparatorValue) { @@ -241,13 +241,13 @@ namespace hex { if (name.get() == ContentRegistry::Interface::impl::SubMenuValue) { callback(); } else if (menuItems.size() == 1) { - if (ImGui::MenuItemEx(Lang(name), icon, shortcut.toString().c_str(), false, enabledCallback())) + if (ImGui::MenuItemEx(Lang(name), icon, shortcut.toString().c_str(), selectedCallback(), enabledCallback())) callback(); } else { bool isSubmenu = (menuItems.begin() + 1)->get() == ContentRegistry::Interface::impl::SubMenuValue; if (ImGui::BeginMenuEx(Lang(name), std::next(menuItems.begin())->get() == ContentRegistry::Interface::impl::SubMenuValue ? icon : nullptr, isSubmenu ? enabledCallback() : true)) { - createNestedMenu({ std::next(menuItems.begin()), menuItems.end() }, icon, shortcut, callback, enabledCallback); + createNestedMenu({ std::next(menuItems.begin()), menuItems.end() }, icon, shortcut, callback, enabledCallback, selectedCallback); ImGui::EndMenu(); } } @@ -285,7 +285,7 @@ namespace hex { } } - if (ImHexApi::System::isBorderlessWindowModeEnabled()) { + if (ImHexApi::System::isBorderlessWindowModeEnabled() && glfwGetWindowMonitor(m_window) == nullptr) { // Draw minimize, restore and maximize buttons ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * 3); if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_MINIMIZE, buttonSize)) @@ -519,9 +519,9 @@ namespace hex { } for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) { - const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback] = menuItem; + const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback, selectedCallack] = menuItem; - createNestedMenu(unlocalizedNames, icon, *shortcut, callback, enabledCallback); + createNestedMenu(unlocalizedNames, icon, *shortcut, callback, enabledCallback, selectedCallack); } }; @@ -792,10 +792,10 @@ namespace hex { // Draw main menu popups for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) { - const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback] = menuItem; + const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback, selectedCallback] = menuItem; if (ImGui::BeginPopup(unlocalizedNames.front().get().c_str())) { - createNestedMenu({ unlocalizedNames.begin() + 1, unlocalizedNames.end() }, icon, *shortcut, callback, enabledCallback); + createNestedMenu({ unlocalizedNames.begin() + 1, unlocalizedNames.end() }, icon, *shortcut, callback, enabledCallback, selectedCallback); ImGui::EndPopup(); } } @@ -983,6 +983,8 @@ namespace hex { m_windowTitle = "ImHex"; m_window = glfwCreateWindow(1280_scaled, 720_scaled, m_windowTitle.c_str(), nullptr, nullptr); + ImHexApi::System::impl::setMainWindowHandle(m_window); + glfwSetWindowUserPointer(m_window, this); if (m_window == nullptr) { diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 5d538187d..b92b2367c 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -165,6 +165,8 @@ "hex.builtin.menu.workspace.layout.lock": "Lock Layout", "hex.builtin.menu.workspace.layout.save": "Save Layout", "hex.builtin.menu.view": "View", + "hex.builtin.menu.view.always_on_top": "Always on Top", + "hex.builtin.menu.view.fullscreen": "Full Screen Mode", "hex.builtin.menu.view.debug": "Show Debugging View", "hex.builtin.menu.view.demo": "Show ImGui Demo", "hex.builtin.menu.view.fps": "Display FPS", diff --git a/plugins/builtin/source/content/main_menu_items.cpp b/plugins/builtin/source/content/main_menu_items.cpp index a38badeea..2cb435ca7 100644 --- a/plugins/builtin/source/content/main_menu_items.cpp +++ b/plugins/builtin/source/content/main_menu_items.cpp @@ -484,7 +484,38 @@ namespace hex::plugin::builtin { static void createViewMenu() { ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.view", 3000); - ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.view" }, 1000, [] { + ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.view", "hex.builtin.menu.view.always_on_top" }, ICON_VS_PINNED, 1000, Keys::F10, [] { + static bool state = false; + + state = !state; + glfwSetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FLOATING, state); + }, []{ return true; }, []{ return glfwGetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FLOATING); }); + + ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.view", "hex.builtin.menu.view.fullscreen" }, ICON_VS_SCREEN_FULL, 2000, Keys::F11, [] { + static bool state = false; + static ImVec2 position, size; + + state = !state; + + + const auto window = ImHexApi::System::getMainWindowHandle(); + if (state) { + position = ImHexApi::System::getMainWindowPosition(); + size = ImHexApi::System::getMainWindowSize(); + + const auto monitor = glfwGetPrimaryMonitor(); + const auto videoMode = glfwGetVideoMode(monitor); + + glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate); + } else { + glfwSetWindowMonitor(window, nullptr, position.x, position.y, size.x, size.y, 0); + } + + }, []{ return true; }, []{ return glfwGetWindowMonitor(ImHexApi::System::getMainWindowHandle()) != nullptr; }); + + ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.view" }, 3000); + + ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.view" }, 4000, [] { for (auto &[name, view] : ContentRegistry::Views::impl::getEntries()) { if (view->hasViewMenuItemEntry()) { auto &state = view->getWindowOpenState();