mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-29 00:10:02 -05:00
feat: Add create and open options to macOS dock icon context menu
This commit is contained in:
@@ -68,6 +68,7 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
constexpr static auto SeparatorValue = "$SEPARATOR$";
|
||||
constexpr static auto SubMenuValue = "$SUBMENU$";
|
||||
constexpr static auto TaskBarMenuValue = "$TASKBAR$";
|
||||
|
||||
const std::multimap<u32, MainMenuItem>& getMainMenuItems();
|
||||
|
||||
@@ -199,6 +200,19 @@ EXPORT_MODULE namespace hex {
|
||||
*/
|
||||
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Adds a new main menu entry
|
||||
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
|
||||
* @param priority The priority of the entry. Lower values are displayed first
|
||||
* @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 addTaskBarMenuItem(
|
||||
std::vector<UnlocalizedString> unlocalizedMainMenuNames,
|
||||
u32 priority,
|
||||
const impl::MenuCallback &function,
|
||||
const impl::EnabledCallback& enabledCallback
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new welcome screen entry
|
||||
@@ -220,10 +234,10 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
/**
|
||||
* @brief Adds a menu item to the toolbar
|
||||
* @param unlocalizedName Unlocalized name of the menu item
|
||||
* @param unlocalizedNames Unlocalized name of the menu item
|
||||
* @param color Color of the toolbar icon
|
||||
*/
|
||||
void addMenuItemToToolbar(const UnlocalizedString &unlocalizedName, ImGuiCustomCol color);
|
||||
void addMenuItemToToolbar(const std::vector<UnlocalizedString> &unlocalizedNames, ImGuiCustomCol color);
|
||||
|
||||
/**
|
||||
* @brief Reconstructs the toolbar items list after they have been modified
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace hex::menu {
|
||||
bool beginMenu(const char *label, bool enabled = true);
|
||||
void endMenu();
|
||||
|
||||
bool beginTaskBarMenu();
|
||||
void endTaskBarMenu();
|
||||
|
||||
bool beginMenuEx(const char* label, const char* icon, bool enabled = true);
|
||||
|
||||
bool menuItem(const char *label, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
void macosInstallEventListener();
|
||||
|
||||
void toastMessageMacos(const char *title, const char *message);
|
||||
void macosSetupDockMenu(void);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1044,6 +1044,15 @@ namespace hex {
|
||||
});
|
||||
}
|
||||
|
||||
void addTaskBarMenuItem(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {
|
||||
log::debug("Added new taskbar menu item to menu {} ", unlocalizedMainMenuNames[0].get());
|
||||
|
||||
unlocalizedMainMenuNames.insert(unlocalizedMainMenuNames.begin(), impl::TaskBarMenuValue);
|
||||
impl::s_menuItems->insert({
|
||||
priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon="", .shortcut=Shortcut::None, .view=nullptr, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }
|
||||
});
|
||||
}
|
||||
|
||||
void addWelcomeScreenEntry(const impl::DrawCallback &function) {
|
||||
impl::s_welcomeScreenEntries->push_back(function);
|
||||
}
|
||||
@@ -1056,13 +1065,13 @@ namespace hex {
|
||||
impl::s_toolbarItems->push_back(function);
|
||||
}
|
||||
|
||||
void addMenuItemToToolbar(const UnlocalizedString& unlocalizedName, ImGuiCustomCol color) {
|
||||
void addMenuItemToToolbar(const std::vector<UnlocalizedString>& unlocalizedNames, ImGuiCustomCol color) {
|
||||
const auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) {
|
||||
return a.second.toolbarIndex < b.second.toolbarIndex;
|
||||
})->second.toolbarIndex;
|
||||
|
||||
for (auto &[priority, menuItem] : *impl::s_menuItems) {
|
||||
if (menuItem.unlocalizedNames.back() == unlocalizedName) {
|
||||
if (menuItem.unlocalizedNames == unlocalizedNames) {
|
||||
menuItem.toolbarIndex = maxIndex + 1;
|
||||
menuItem.icon.color = color;
|
||||
updateToolbarItems();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
struct KeyEquivalent {
|
||||
bool valid;
|
||||
@@ -192,6 +193,11 @@ bool macosBeginMenu(const char* label, const char *icon, bool enabled) {
|
||||
menuItem.title = title;
|
||||
[menuItem setSubmenu:newMenu];
|
||||
|
||||
// Hide menus starting with '$' (used for special menus)
|
||||
if (label[0] == '$') {
|
||||
[menuItem setHidden:YES];
|
||||
}
|
||||
|
||||
if (icon != NULL) {
|
||||
NSString *iconString = [NSString stringWithUTF8String:icon];
|
||||
NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]);
|
||||
@@ -305,3 +311,94 @@ void macosSeparator(void) {
|
||||
[s_menuStack[s_menuStackSize - 1] addItem:separator];
|
||||
}
|
||||
}
|
||||
|
||||
@interface NSObject (DockMenuAddition)
|
||||
- (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender;
|
||||
@end
|
||||
|
||||
@implementation NSObject (DockMenuAddition)
|
||||
|
||||
- (NSMenu *)cloneMenu:(NSMenu *)originalMenu {
|
||||
NSMenu *clonedMenu = [[NSMenu alloc] initWithTitle:[originalMenu title]];
|
||||
|
||||
for (NSMenuItem *item in [originalMenu itemArray]) {
|
||||
NSMenuItem *clonedItem = [self cloneMenuItem:item];
|
||||
[clonedMenu addItem:clonedItem];
|
||||
}
|
||||
|
||||
return clonedMenu;
|
||||
}
|
||||
|
||||
- (NSMenuItem *)cloneMenuItem:(NSMenuItem *)original {
|
||||
if ([original isSeparatorItem]) {
|
||||
return [NSMenuItem separatorItem];
|
||||
}
|
||||
|
||||
// Create new item with same properties
|
||||
NSMenuItem *clone = [[NSMenuItem alloc] initWithTitle:[original title]
|
||||
action:[original action]
|
||||
keyEquivalent:[original keyEquivalent]];
|
||||
|
||||
// Copy other properties
|
||||
[clone setTarget:[original target]];
|
||||
[clone setEnabled:[original isEnabled]];
|
||||
[clone setImage:[original image]];
|
||||
[clone setTag:[original tag]];
|
||||
[clone setRepresentedObject:[original representedObject]];
|
||||
[clone setToolTip:[original toolTip]];
|
||||
[clone setState:[original state]];
|
||||
|
||||
// Handle submenus recursively
|
||||
if ([original hasSubmenu]) {
|
||||
NSMenu *clonedSubmenu = [self cloneMenu:[original submenu]];
|
||||
[clone setSubmenu:clonedSubmenu];
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
- (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender {
|
||||
NSMenu *dockMenu = [[NSMenu alloc] init];
|
||||
|
||||
NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:@"$TASKBAR$"];
|
||||
if (menuIndex == -1) {
|
||||
return dockMenu;
|
||||
}
|
||||
NSMenuItem *fileMenuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex];
|
||||
|
||||
// Get the File submenu
|
||||
NSMenu *fileSubmenu = [fileMenuItem submenu];
|
||||
|
||||
if (fileSubmenu) {
|
||||
// Clone each item from the File submenu directly into the dock menu
|
||||
for (NSMenuItem *item in [fileSubmenu itemArray]) {
|
||||
NSMenuItem *clonedItem = [self cloneMenuItem:item];
|
||||
[dockMenu addItem:clonedItem];
|
||||
}
|
||||
}
|
||||
|
||||
return dockMenu;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void macosSetupDockMenu(void) {
|
||||
@autoreleasepool {
|
||||
// Get GLFW's delegate class
|
||||
Class delegateClass = objc_getClass("GLFWApplicationDelegate");
|
||||
|
||||
if (delegateClass != nil) {
|
||||
// Get our custom implementation
|
||||
Method customMethod = class_getInstanceMethod([NSObject class],
|
||||
@selector(imhexApplicationDockMenu:));
|
||||
|
||||
// Add the method to GLFW's delegate class
|
||||
class_addMethod(delegateClass,
|
||||
@selector(applicationDockMenu:),
|
||||
method_getImplementation(customMethod),
|
||||
method_getTypeEncoding(customMethod));
|
||||
} else {
|
||||
NSLog(@"ERROR: Could not find GLFWApplicationDelegate class");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,5 +407,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
enumerateFontsMacos();
|
||||
macosSetupDockMenu();
|
||||
}
|
||||
|
||||
void Window::setupNativeWindow() {
|
||||
|
||||
@@ -375,19 +375,34 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::UserInterface::registerMainMenuItem("hex.builtin.menu.file", 1000);
|
||||
|
||||
/* Create File */
|
||||
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.create_file" }, ICON_VS_FILE, 1050, CTRLCMD + Keys::N + AllowWhileTyping + ShowOnWelcomeScreen, [] {
|
||||
const auto createFile = [] {
|
||||
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
|
||||
if (newProvider != nullptr && newProvider->open().isFailure())
|
||||
hex::ImHexApi::Provider::remove(newProvider.get());
|
||||
else
|
||||
EventProviderOpened::post(newProvider.get());
|
||||
}, noRunningTasks, ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name"));
|
||||
};
|
||||
|
||||
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.create_file" }, ICON_VS_FILE, 1050, CTRLCMD + Keys::N + AllowWhileTyping + ShowOnWelcomeScreen,
|
||||
createFile,
|
||||
noRunningTasks,
|
||||
ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name")
|
||||
);
|
||||
|
||||
ContentRegistry::UserInterface::addTaskBarMenuItem({ "hex.builtin.menu.file.create_file" }, 100,
|
||||
createFile,
|
||||
noRunningTasks
|
||||
);
|
||||
|
||||
/* Open File */
|
||||
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.open_file" }, ICON_VS_FOLDER_OPENED, 1100, CTRLCMD + Keys::O + AllowWhileTyping + ShowOnWelcomeScreen, [] {
|
||||
RequestOpenWindow::post("Open File");
|
||||
}, noRunningTasks, ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name"));
|
||||
|
||||
ContentRegistry::UserInterface::addTaskBarMenuItem({ "hex.builtin.menu.file.open_file" }, 200, [] {
|
||||
RequestOpenWindow::post("Open File");
|
||||
}, noRunningTasks);
|
||||
|
||||
/* Open Other */
|
||||
ContentRegistry::UserInterface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.builtin.menu.file.open_other"}, ICON_VS_TELESCOPE, 1150, [] {
|
||||
for (const auto &[unlocalizedProviderName, icon] : ContentRegistry::Provider::impl::getEntries()) {
|
||||
|
||||
@@ -577,13 +577,13 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
EventImHexStartupFinished::subscribe([] {
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.view.hex_editor.menu.edit.undo", ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.view.hex_editor.menu.edit.redo", ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.menu.file.create_file", ImGuiCustomCol_ToolbarGray);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.menu.file.open_file", ImGuiCustomCol_ToolbarBrown);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.view.hex_editor.menu.file.save", ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.view.hex_editor.menu.file.save_as", ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar("hex.builtin.menu.edit.bookmark.create", ImGuiCustomCol_ToolbarGreen);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.undo" }, ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.redo" }, ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.file", "hex.builtin.menu.file.create_file" }, ImGuiCustomCol_ToolbarGray);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.file", "hex.builtin.menu.file.open_file" }, ImGuiCustomCol_ToolbarBrown);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.file", "hex.builtin.view.hex_editor.menu.file.save" }, ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.file", "hex.builtin.view.hex_editor.menu.file.save_as" }, ImGuiCustomCol_ToolbarBlue);
|
||||
ContentRegistry::UserInterface::addMenuItemToToolbar({ "hex.builtin.menu.edit", "hex.builtin.menu.edit.bookmark.create" }, ImGuiCustomCol_ToolbarGreen);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -470,6 +470,11 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (menu::beginTaskBarMenu()) {
|
||||
populateMenu(ContentRegistry::UserInterface::impl::TaskBarMenuValue);
|
||||
menu::endTaskBarMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void drawMainMenu([[maybe_unused]] float menuBarHeight) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <hex/api/shortcut_manager.hpp>
|
||||
#include <hex/api/content_registry/user_interface.hpp>
|
||||
|
||||
#if defined(OS_MACOS)
|
||||
extern "C" {
|
||||
@@ -98,6 +99,22 @@ namespace hex::menu {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
bool beginTaskBarMenu() {
|
||||
#if defined(OS_MACOS)
|
||||
return beginMenu(ContentRegistry::UserInterface::impl::TaskBarMenuValue, true);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void endTaskBarMenu() {
|
||||
#if defined(OS_MACOS)
|
||||
endMenu();
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool menuItem(const char *label, const Shortcut &shortcut, bool selected, bool enabled) {
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
Reference in New Issue
Block a user