feat: Add create and open options to macOS dock icon context menu

This commit is contained in:
WerWolv
2025-12-22 16:20:23 +01:00
parent 8b53b36b20
commit 646ebcdd00
11 changed files with 175 additions and 14 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -32,6 +32,7 @@
void macosInstallEventListener();
void toastMessageMacos(const char *title, const char *message);
void macosSetupDockMenu(void);
}
#endif

View File

@@ -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();

View File

@@ -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");
}
}
}

View File

@@ -407,5 +407,4 @@
}
}
#endif