From 489b9b358bddf5f702757e51ba6925216c5c66e1 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 30 Oct 2021 09:51:46 +1100 Subject: [PATCH] [mac] menu support --- .../frontend/desktop/darwin/AppDelegate.m | 22 -- .../frontend/desktop/darwin/Application.h | 13 + .../frontend/desktop/darwin/Application.m | 63 +++- v2/internal/frontend/desktop/darwin/Role.h | 16 + .../frontend/desktop/darwin/WailsContext.h | 20 +- .../frontend/desktop/darwin/WailsContext.m | 59 +++- .../frontend/desktop/darwin/WailsMenu.h | 30 ++ .../frontend/desktop/darwin/WailsMenu.m | 315 ++++++++++++++++++ .../frontend/desktop/darwin/WailsMenuItem.h | 22 ++ .../frontend/desktop/darwin/WailsMenuItem.m | 20 ++ .../frontend/desktop/darwin/callbacks.go | 51 +++ .../frontend/desktop/darwin/frontend.go | 30 +- v2/internal/frontend/desktop/darwin/main.m | 182 +++++++++- v2/internal/frontend/desktop/darwin/menu.go | 188 ++++++----- .../frontend/desktop/darwin/menuitem.go | 51 +++ v2/internal/frontend/desktop/darwin/message.h | 1 + v2/internal/frontend/desktop/darwin/window.go | 51 ++- v2/internal/menumanager/contextmenu.go | 1 + v2/pkg/menu/keys/macmodifiers.go | 26 ++ v2/pkg/menu/keys/macmodifiers_test.go | 31 ++ v2/pkg/menu/menuitem.go | 21 +- v2/pkg/menu/menuroles.go | 69 ++-- v2/pkg/options/mac/mac.go | 21 +- 23 files changed, 1113 insertions(+), 190 deletions(-) create mode 100644 v2/internal/frontend/desktop/darwin/Role.h create mode 100644 v2/internal/frontend/desktop/darwin/WailsMenu.h create mode 100644 v2/internal/frontend/desktop/darwin/WailsMenu.m create mode 100644 v2/internal/frontend/desktop/darwin/WailsMenuItem.h create mode 100644 v2/internal/frontend/desktop/darwin/WailsMenuItem.m create mode 100644 v2/internal/frontend/desktop/darwin/callbacks.go create mode 100644 v2/internal/frontend/desktop/darwin/menuitem.go create mode 100644 v2/pkg/menu/keys/macmodifiers.go create mode 100644 v2/pkg/menu/keys/macmodifiers_test.go diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.m b/v2/internal/frontend/desktop/darwin/AppDelegate.m index 24cfaa01..0d468bbf 100644 --- a/v2/internal/frontend/desktop/darwin/AppDelegate.m +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.m @@ -25,28 +25,6 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [NSApp activateIgnoringOtherApps:YES]; } -// -//- (void) CreateMenu { -// [NSApplication sharedApplication]; -// menubar = [[NSMenu new] autorelease]; -// id appMenuItem = [[NSMenuItem new] autorelease]; -// [menubar addItem:appMenuItem]; -// [NSApp setMainMenu:menubar]; -// id appMenu = [[NSMenu new] autorelease]; -// id appName = [[NSProcessInfo processInfo] processName]; -// id quitTitle = [@"Quit " stringByAppendingString:appName]; -// id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle -// action:@selector(terminate:) keyEquivalent:@"q"] -// autorelease]; -// [appMenu addItem:quitMenuItem]; -// [appMenuItem setSubmenu:appMenu]; -//} -// -//- (void) dealloc { -// [super dealloc]; -// window = nil; -// menubar = nil; -//} @synthesize touchBar; diff --git a/v2/internal/frontend/desktop/darwin/Application.h b/v2/internal/frontend/desktop/darwin/Application.h index 05ead41a..cbb9c4bb 100644 --- a/v2/internal/frontend/desktop/darwin/Application.h +++ b/v2/internal/frontend/desktop/darwin/Application.h @@ -38,7 +38,20 @@ const char* GetPos(void *ctx); void ProcessURLResponse(void *inctx, const char *url, const char *contentType, const char *data, int datalength); +/* Dialogs */ + void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton); void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters); void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters); + +/* Application Menu */ +void* NewMenu(const char* name); +void AppendSubmenu(void* parent, void* child); +void AppendRole(void *inctx, void *inMenu, int role); +void SetAsApplicationMenu(void *inctx, void *inMenu); + +void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen); +void* AppendMenuItem(void* inctx, void* nsmenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID); +void AppendSeparator(void* inMenu); +void UpdateMenuItem(void* nsmenuitem, int checked); #endif /* Application_h */ diff --git a/v2/internal/frontend/desktop/darwin/Application.m b/v2/internal/frontend/desktop/darwin/Application.m index ac730d52..a4d5ee55 100644 --- a/v2/internal/frontend/desktop/darwin/Application.m +++ b/v2/internal/frontend/desktop/darwin/Application.m @@ -9,6 +9,8 @@ #import "WailsContext.h" #import "Application.h" #import "AppDelegate.h" +#import "WailsMenu.h" +#import "WailsMenuItem.h" WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug) { @@ -22,7 +24,7 @@ WailsContext* Create(const char* title, int width, int height, int frameless, in result.alwaysOnTop = alwaysOnTop; result.hideOnClose = hideWindowOnClose; - + return result; } @@ -194,21 +196,70 @@ void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, ) } +void AppendRole(void *inctx, void *inMenu, int role) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + WailsMenu *menu = (__bridge WailsMenu*) inMenu; + [menu appendRole :ctx :role]; +} + +void* NewMenu(const char *name) { + NSString *title = @""; + if (name != nil) { + title = [NSString stringWithUTF8String:name]; + } + WailsMenu *result = [[WailsMenu new] initWithNSTitle:title]; + return result; +} + +void AppendSubmenu(void* inparent, void* inchild) { + WailsMenu *parent = (__bridge WailsMenu*) inparent; + WailsMenu *child = (__bridge WailsMenu*) inchild; + [parent appendSubmenu:child]; +} + +void SetAsApplicationMenu(void *inctx, void *inMenu) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + WailsMenu *menu = (__bridge WailsMenu*) inMenu; + ctx.applicationMenu = menu; +} + +void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + [ctx SetAbout :title :description :imagedata :datalen]; +} + +void* AppendMenuItem(void* inctx, void* inMenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + WailsMenu *menu = (__bridge WailsMenu*) inMenu; + return [menu AppendMenuItem:ctx :label :shortcutKey :modifiers :disabled :checked :menuItemID]; +} + +void UpdateMenuItem(void* nsmenuitem, int checked) { + ON_MAIN_THREAD( + WailsMenuItem *menuItem = (__bridge WailsMenuItem*) nsmenuitem; + [menuItem setState:(checked == 1?NSControlStateValueOn:NSControlStateValueOff)]; + ) +} + + +void AppendSeparator(void* inMenu) { + WailsMenu *menu = (__bridge WailsMenu*) inMenu; + [menu AppendSeparator]; +} void Run(void *inctx) { WailsContext *ctx = (__bridge WailsContext*) inctx; - [NSApplication sharedApplication]; + NSApplication *app = [NSApplication sharedApplication]; AppDelegate* delegate = [AppDelegate new]; - [NSApp setDelegate:(id)delegate]; + [app setDelegate:(id)delegate]; ctx.appdelegate = delegate; delegate.mainWindow = ctx.mainWindow; delegate.alwaysOnTop = ctx.alwaysOnTop; [ctx loadRequest:@"wails://wails/"]; - - [NSApp run]; + [app setMainMenu:ctx.applicationMenu]; + [app run]; [ctx release]; - NSLog(@"Here"); } diff --git a/v2/internal/frontend/desktop/darwin/Role.h b/v2/internal/frontend/desktop/darwin/Role.h new file mode 100644 index 00000000..20e67068 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/Role.h @@ -0,0 +1,16 @@ +// +// Role.h +// test +// +// Created by Lea Anthony on 24/10/21. +// + +#ifndef Role_h +#define Role_h + +typedef int Role; + +static const Role AppMenu = 1; +static const Role EditMenu = 2; + +#endif /* Role_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.h b/v2/internal/frontend/desktop/darwin/WailsContext.h index c4b6e409..b98161ee 100644 --- a/v2/internal/frontend/desktop/darwin/WailsContext.h +++ b/v2/internal/frontend/desktop/darwin/WailsContext.h @@ -12,6 +12,9 @@ #import #define ON_MAIN_THREAD(str) dispatch_async(dispatch_get_main_queue(), ^{ str; }); +#define unicode(input) [NSString stringWithFormat:@"%C", input] +#define STREQ(a,b) strcmp(a, b) == 0 + @interface WailsWindow : NSWindow - (BOOL)canBecomeKeyWindow; @@ -37,9 +40,15 @@ @property bool debug; @property (retain) WKUserContentController* userContentController; - @property (retain) NSMutableDictionary *urlRequests; +@property (retain) NSMenu* applicationMenu; + +@property (retain) NSImage* aboutImage; +@property const char* aboutTitle; +@property const char* aboutDescription; +@property (retain) NSString* appName; + - (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(const char *)appearance :(bool)windowIsTranslucent; - (void) SetSize:(int)width :(int)height; - (void) SetPosition:(int)x :(int) y; @@ -58,16 +67,19 @@ - (void) ShowMouse; - (void) Hide; - (void) Show; +- (void) Quit; --(void) MessageDialog :(const char*)dialogType :(const char*)title :(const char*)message :(const char*)button1 :(const char*)button2 :(const char*)button3 :(const char*)button4 :(const char*)defaultButton :(const char*)cancelButton; --(void) OpenFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(const char*)filters; --(void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters; +- (void) MessageDialog :(const char*)dialogType :(const char*)title :(const char*)message :(const char*)button1 :(const char*)button2 :(const char*)button3 :(const char*)button4 :(const char*)defaultButton :(const char*)cancelButton; +- (void) OpenFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(const char*)filters; +- (void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters; - (void) loadRequest:(NSString*)url; - (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData*)data; - (void) ExecJS:(const char*)script; - (NSScreen*) getCurrentScreen; +- (void) SetAbout :(const char*)title :(const char*)description :(void*)imagedata :(int)datalen; + @end diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.m b/v2/internal/frontend/desktop/darwin/WailsContext.m index 967931f8..bbe26e51 100644 --- a/v2/internal/frontend/desktop/darwin/WailsContext.m +++ b/v2/internal/frontend/desktop/darwin/WailsContext.m @@ -9,8 +9,10 @@ #import #import "WailsContext.h" #import "WailsAlert.h" +#import "WailsMenu.h" #import "WindowDelegate.h" #import "message.h" +#import "Role.h" @implementation WailsWindow @@ -100,6 +102,7 @@ [self.mouseEvent release]; [self.userContentController release]; [self.urlRequests release]; + [self.applicationMenu release]; } - (NSScreen*) getCurrentScreen { @@ -149,7 +152,6 @@ } if (useToolbar) { - NSLog(@"Using Toolbar"); id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"]; [toolbar autorelease]; [toolbar setShowsBaselineSeparator:!hideToolbarSeparator]; @@ -244,6 +246,30 @@ return event; }]; + self.applicationMenu = [NSMenu new]; + +} + +- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { + NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease]; + if( flags != 0 ) { + [result setKeyEquivalentModifierMask:flags]; + } + return result; +} + +- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key { + return [self newMenuItem :title :selector :key :0]; +} + +- (NSMenu*) newMenu :(NSString*)title { + WailsMenu *result = [[[WailsMenu new] initWithTitle:title] autorelease]; + [result setAutoenablesItems:NO]; + return result; +} + +- (void) Quit { + processMessage("Q"); } - (void) loadRequest :(NSString*)url { @@ -397,6 +423,8 @@ [alert addButton:button3 :defaultButton :cancelButton]; [alert addButton:button4 :defaultButton :cancelButton]; + [alert.window setLevel:NSFloatingWindowLevel]; + long response = [alert runModal]; int result; @@ -470,7 +498,6 @@ processOpenFileDialogResponse([nsjson UTF8String]); }]; - [dialog runModal]; } @@ -522,6 +549,34 @@ } +- (void) SetAbout :(const char*)title :(const char*)description :(void*)imagedata :(int)datalen { + self.aboutTitle = title; + self.aboutDescription = description; + + NSData *imageData = [NSData dataWithBytes:imagedata length:datalen]; + self.aboutImage = [[NSImage alloc] initWithData:imageData]; +} + +-(void) About { + + WailsAlert *alert = [WailsAlert new]; + [alert setAlertStyle:NSAlertStyleInformational]; + if( self.aboutTitle != nil ) { + [alert setMessageText:[NSString stringWithUTF8String:self.aboutTitle]]; + } + if( self.aboutDescription != nil ) { + [alert setInformativeText:[NSString stringWithUTF8String:self.aboutDescription]]; + } + + + [alert.window setLevel:NSFloatingWindowLevel]; + if ( self.aboutImage != nil) { + [alert setIcon:self.aboutImage]; + } + + [alert runModal]; + +} @end diff --git a/v2/internal/frontend/desktop/darwin/WailsMenu.h b/v2/internal/frontend/desktop/darwin/WailsMenu.h new file mode 100644 index 00000000..c6dd405d --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsMenu.h @@ -0,0 +1,30 @@ +// +// WailsMenu.h +// test +// +// Created by Lea Anthony on 25/10/21. +// + +#ifndef WailsMenu_h +#define WailsMenu_h + +#import +#import "Role.h" +#import "WailsMenu.h" +#import "WailsContext.h" + +@interface WailsMenu : NSMenu + +//- (void) AddMenuByRole :(Role)role; +- (WailsMenu*) initWithNSTitle :(NSString*)title; +- (void) appendSubmenu :(WailsMenu*)child; +- (void) appendRole :(WailsContext*)ctx :(Role)role; + +- (NSMenuItem*) newMenuItemWithContext :(WailsContext*)ctx :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags; +- (void*) AppendMenuItem :(WailsContext*)ctx :(const char*)label :(const char *)shortcutKey :(int)modifiers :(bool)disabled :(bool)checked :(int)menuItemID; +- (void) AppendSeparator; + +@end + + +#endif /* WailsMenu_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsMenu.m b/v2/internal/frontend/desktop/darwin/WailsMenu.m new file mode 100644 index 00000000..6090441b --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsMenu.m @@ -0,0 +1,315 @@ +// +// WailsMenu.m +// test +// +// Created by Lea Anthony on 25/10/21. +// + +#import +#import "WailsMenu.h" +#import "WailsMenuItem.h" +#import "Role.h" + +@implementation WailsMenu + +- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { + NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease]; + [result setKeyEquivalentModifierMask:flags]; + return result; +} + +- (NSMenuItem*) newMenuItemWithContext :(WailsContext*)ctx :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { + NSMenuItem *result = [[NSMenuItem new] autorelease]; + if ( title != nil ) { + [result setTitle:title]; + } + if (selector != nil) { + [result setAction:selector]; + } + if (key) { + [result setKeyEquivalent:key]; + } + if( flags != 0 ) { + [result setKeyEquivalentModifierMask:flags]; + } + result.target = ctx; + return result; +} + +- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key { + return [self newMenuItem :title :selector :key :0]; +} + +- (WailsMenu*) initWithNSTitle:(NSString *)title { + if( title != nil ) { + [super initWithTitle:title]; + } + [self setAutoenablesItems:NO]; + return self; +} + +- (void) appendSubmenu :(WailsMenu*)child { + NSMenuItem *childMenuItem = [[NSMenuItem new] autorelease]; + [childMenuItem setTitle:[child title]]; + [self addItem:childMenuItem]; + [childMenuItem setSubmenu:child]; +} + +- (void) appendRole :(WailsContext*)ctx :(Role)role { + + switch(role) { + case AppMenu: + { + NSString *appName = [[NSProcessInfo processInfo] processName]; + NSString *cap = [appName capitalizedString]; + WailsMenu *appMenu = [[WailsMenu new] initWithNSTitle:cap]; + id quitTitle = [@"Quit " stringByAppendingString:cap]; + NSMenuItem* quitMenuItem = [self newMenuItem:quitTitle :@selector(Quit) :@"q" :NSEventModifierFlagCommand]; + quitMenuItem.target = ctx; + if (ctx.aboutTitle != nil) { + [appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:cap] :@selector(About) :nil :0]]; + } + [appMenu addItem:quitMenuItem]; + [self appendSubmenu:appMenu]; + break; + } + case EditMenu: + { + WailsMenu *editMenu = [[WailsMenu new] initWithNSTitle:@"Edit"]; + [editMenu addItem:[self newMenuItem:@"Undo" :@selector(undoActionName) :@"z" :NSEventModifierFlagCommand]]; + [editMenu addItem:[self newMenuItem:@"Redo" :@selector(redoActionName) :@"z" :(NSEventModifierFlagShift | NSEventModifierFlagCommand)]]; + [editMenu addItem:[NSMenuItem separatorItem]]; + [editMenu addItem:[self newMenuItem:@"Cut" :@selector(cut:) :@"x" :NSEventModifierFlagCommand]]; + [editMenu addItem:[self newMenuItem:@"Copy" :@selector(copy:) :@"c" :NSEventModifierFlagCommand]]; + [editMenu addItem:[self newMenuItem:@"Paste" :@selector(paste:) :@"v" :NSEventModifierFlagCommand]]; + [editMenu addItem:[self newMenuItem:@"Paste and Match Style" :@selector(pasteAsRichText:) :@"v" :(NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand)]]; + [editMenu addItem:[self newMenuItem:@"Delete" :@selector(delete:) :[self accel:"backspace"] :0]]; + [editMenu addItem:[self newMenuItem:@"Select All" :@selector(selectAll:) :@"a" :NSEventModifierFlagCommand]]; + [editMenu addItem:[NSMenuItem separatorItem]]; +// NSMenuItem *speechMenuItem = [[NSMenuItem new] autorelease]; +// [speechMenuItem setTitle:@"Speech"]; +// [editMenu addItem:speechMenuItem]; + WailsMenu *speechMenu = [[WailsMenu new] initWithNSTitle:@"Speech"]; + [speechMenu addItem:[self newMenuItem:@"Start Speaking" :@selector(startSpeaking:) :@""]]; + [speechMenu addItem:[self newMenuItem:@"Stop Speaking" :@selector(stopSpeaking:) :@""]]; + [editMenu appendSubmenu:speechMenu]; + [self appendSubmenu:editMenu]; + + break; + } + } +} + +- (void*) AppendMenuItem :(WailsContext*)ctx :(const char*)label :(const char *)shortcutKey :(int)modifiers :(bool)disabled :(bool)checked :(int)menuItemID { + NSString *nslabel = @""; + if (label != nil ) { + nslabel = [NSString stringWithUTF8String:label]; + } + WailsMenuItem *menuItem = [WailsMenuItem new]; + + // Label + menuItem.title = nslabel; + + // Process callback + menuItem.menuItemID = menuItemID; + menuItem.action = @selector(handleClick); + menuItem.target = menuItem; + + // Shortcut + if (shortcutKey != nil) { + [menuItem setKeyEquivalent:[self accel:shortcutKey]]; + [menuItem setKeyEquivalentModifierMask:modifiers]; + } + + // Enabled/Disabled + [menuItem setEnabled:!disabled]; + + // Checked + [menuItem setState:(checked ? NSControlStateValueOn : NSControlStateValueOff)]; + + [self addItem:menuItem]; + return menuItem; +} + +- (void) AppendSeparator { + [self addItem:[NSMenuItem separatorItem]]; +} + + +- (NSString*) accel :(const char *)key { + + // Guard against no accelerator key + if( key == NULL ) { + return [NSString stringWithUTF8String:""]; + } + + if( STREQ(key, "backspace") ) { + return unicode(0x0008); + } + if( STREQ(key, "tab") ) { + return unicode(0x0009); + } + if( STREQ(key, "return") ) { + return unicode(0x000d); + } + if( STREQ(key, "enter") ) { + return unicode(0x000d); + } + if( STREQ(key, "escape") ) { + return unicode(0x001b); + } + if( STREQ(key, "left") ) { + return unicode(0x001c); + } + if( STREQ(key, "right") ) { + return unicode(0x001d); + } + if( STREQ(key, "up") ) { + return unicode(0x001e); + } + if( STREQ(key, "down") ) { + return unicode(0x001f); + } + if( STREQ(key, "space") ) { + return unicode(0x0020); + } + if( STREQ(key, "delete") ) { + return unicode(0x007f); + } + if( STREQ(key, "home") ) { + return unicode(0x2196); + } + if( STREQ(key, "end") ) { + return unicode(0x2198); + } + if( STREQ(key, "page up") ) { + return unicode(0x21de); + } + if( STREQ(key, "page down") ) { + return unicode(0x21df); + } + if( STREQ(key, "f1") ) { + return unicode(0xf704); + } + if( STREQ(key, "f2") ) { + return unicode(0xf705); + } + if( STREQ(key, "f3") ) { + return unicode(0xf706); + } + if( STREQ(key, "f4") ) { + return unicode(0xf707); + } + if( STREQ(key, "f5") ) { + return unicode(0xf708); + } + if( STREQ(key, "f6") ) { + return unicode(0xf709); + } + if( STREQ(key, "f7") ) { + return unicode(0xf70a); + } + if( STREQ(key, "f8") ) { + return unicode(0xf70b); + } + if( STREQ(key, "f9") ) { + return unicode(0xf70c); + } + if( STREQ(key, "f10") ) { + return unicode(0xf70d); + } + if( STREQ(key, "f11") ) { + return unicode(0xf70e); + } + if( STREQ(key, "f12") ) { + return unicode(0xf70f); + } + if( STREQ(key, "f13") ) { + return unicode(0xf710); + } + if( STREQ(key, "f14") ) { + return unicode(0xf711); + } + if( STREQ(key, "f15") ) { + return unicode(0xf712); + } + if( STREQ(key, "f16") ) { + return unicode(0xf713); + } + if( STREQ(key, "f17") ) { + return unicode(0xf714); + } + if( STREQ(key, "f18") ) { + return unicode(0xf715); + } + if( STREQ(key, "f19") ) { + return unicode(0xf716); + } + if( STREQ(key, "f20") ) { + return unicode(0xf717); + } + if( STREQ(key, "f21") ) { + return unicode(0xf718); + } + if( STREQ(key, "f22") ) { + return unicode(0xf719); + } + if( STREQ(key, "f23") ) { + return unicode(0xf71a); + } + if( STREQ(key, "f24") ) { + return unicode(0xf71b); + } + if( STREQ(key, "f25") ) { + return unicode(0xf71c); + } + if( STREQ(key, "f26") ) { + return unicode(0xf71d); + } + if( STREQ(key, "f27") ) { + return unicode(0xf71e); + } + if( STREQ(key, "f28") ) { + return unicode(0xf71f); + } + if( STREQ(key, "f29") ) { + return unicode(0xf720); + } + if( STREQ(key, "f30") ) { + return unicode(0xf721); + } + if( STREQ(key, "f31") ) { + return unicode(0xf722); + } + if( STREQ(key, "f32") ) { + return unicode(0xf723); + } + if( STREQ(key, "f33") ) { + return unicode(0xf724); + } + if( STREQ(key, "f34") ) { + return unicode(0xf725); + } + if( STREQ(key, "f35") ) { + return unicode(0xf726); + } +// if( STREQ(key, "Insert") ) { +// return unicode(0xf727); +// } +// if( STREQ(key, "PrintScreen") ) { +// return unicode(0xf72e); +// } +// if( STREQ(key, "ScrollLock") ) { +// return unicode(0xf72f); +// } + if( STREQ(key, "numLock") ) { + return unicode(0xf739); + } + + return [NSString stringWithUTF8String:key]; +} + + +@end + + diff --git a/v2/internal/frontend/desktop/darwin/WailsMenuItem.h b/v2/internal/frontend/desktop/darwin/WailsMenuItem.h new file mode 100644 index 00000000..278bac80 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsMenuItem.h @@ -0,0 +1,22 @@ +// +// WailsMenuItem.h +// test +// +// Created by Lea Anthony on 27/10/21. +// + +#ifndef WailsMenuItem_h +#define WailsMenuItem_h + +#import + +@interface WailsMenuItem : NSMenuItem + +@property int menuItemID; + +- (void) handleClick; + +@end + + +#endif /* WailsMenuItem_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsMenuItem.m b/v2/internal/frontend/desktop/darwin/WailsMenuItem.m new file mode 100644 index 00000000..e6668223 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsMenuItem.m @@ -0,0 +1,20 @@ +// +// WailsMenuItem.m +// test +// +// Created by Lea Anthony on 27/10/21. +// + +#import + +#import "WailsMenuItem.h" +#include "message.h" + + +@implementation WailsMenuItem + +- (void) handleClick { + processCallback(self.menuItemID); +} + +@end diff --git a/v2/internal/frontend/desktop/darwin/callbacks.go b/v2/internal/frontend/desktop/darwin/callbacks.go new file mode 100644 index 00000000..7d930a2f --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/callbacks.go @@ -0,0 +1,51 @@ +//go:build darwin +// +build darwin + +package darwin + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit +#import +#import "Application.h" + +#include +*/ +import "C" +import ( + "errors" + "strconv" + + "github.com/wailsapp/wails/v2/pkg/menu" +) + +func (f *Frontend) handleCallback(menuItemID uint) error { + + menuItem := getMenuItemForID(menuItemID) + if menuItem == nil { + return errors.New("unknown menuItem ID: " + strconv.Itoa(int(menuItemID))) + } + + wailsMenuItem := menuItem.wailsMenuItem + if wailsMenuItem.Type == menu.CheckboxType { + wailsMenuItem.Checked = !wailsMenuItem.Checked + C.UpdateMenuItem(menuItem.nsmenuitem, bool2Cint(wailsMenuItem.Checked)) + } + if wailsMenuItem.Type == menu.RadioType { + // Ignore if we clicked the item that is already checked + if !wailsMenuItem.Checked { + for _, item := range menuItem.radioGroupMembers { + if item.wailsMenuItem.Checked { + item.wailsMenuItem.Checked = false + C.UpdateMenuItem(item.nsmenuitem, C.int(0)) + } + } + wailsMenuItem.Checked = true + C.UpdateMenuItem(menuItem.nsmenuitem, C.int(1)) + } + } + if wailsMenuItem.Click != nil { + go wailsMenuItem.Click(&menu.CallbackData{MenuItem: wailsMenuItem}) + } + return nil +} diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index 04f366f8..1b4e43b0 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -36,6 +36,7 @@ type request struct { var messageBuffer = make(chan string, 100) var requestBuffer = make(chan *request, 100) +var callbackBuffer = make(chan uint, 10) type Frontend struct { @@ -92,6 +93,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. go result.startMessageProcessor() go result.startRequestProcessor() + go result.startCallbackProcessor() return result } @@ -106,6 +108,14 @@ func (f *Frontend) startRequestProcessor() { f.processRequest(request) } } +func (f *Frontend) startCallbackProcessor() { + for callback := range callbackBuffer { + err := f.handleCallback(callback) + if err != nil { + println(err.Error()) + } + } +} func (f *Frontend) WindowReload() { f.ExecJS("runtime.WindowReload();") @@ -229,13 +239,6 @@ func (f *Frontend) Notify(name string, data ...interface{}) { } func (f *Frontend) processMessage(message string) { - if message == "drag" { - err := f.startDrag() - if err != nil { - f.logger.Error(err.Error()) - } - return - } result, err := f.dispatcher.ProcessMessage(message, f) if err != nil { f.logger.Error(err.Error()) @@ -259,14 +262,6 @@ func (f *Frontend) Callback(message string) { f.ExecJS(`window.wails.Callback(` + strconv.Quote(message) + `);`) } -func (f *Frontend) startDrag() error { - //if !w32.ReleaseCapture() { - // return fmt.Errorf("unable to release mouse capture") - //} - //w32.SendMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0) - return nil -} - func (f *Frontend) ExecJS(js string) { f.mainWindow.ExecJS(js) } @@ -304,3 +299,8 @@ func processURLRequest(ctx unsafe.Pointer, url *C.char) { ctx: ctx, } } + +//export processCallback +func processCallback(callbackID uint) { + callbackBuffer <- callbackID +} diff --git a/v2/internal/frontend/desktop/darwin/main.m b/v2/internal/frontend/desktop/darwin/main.m index d605d828..f98af2dc 100644 --- a/v2/internal/frontend/desktop/darwin/main.m +++ b/v2/internal/frontend/desktop/darwin/main.m @@ -12,7 +12,6 @@ void processMessage(const char*t) { NSLog(@"processMessage called"); - } void processMessageDialogResponse(int t) { @@ -22,6 +21,13 @@ void processMessageDialogResponse(int t) { void processOpenFileDialogResponse(const char *t) { NSLog(@"processMessage called %s", t); } +void processSaveFileDialogResponse(const char *t) { + NSLog(@"processMessage called %s", t); +} + +void processCallback(int callbackID) { + NSLog(@"Process callback %d", callbackID); +} void processURLRequest(void *ctx, const char* url) { NSLog(@"processURLRequest called"); @@ -29,6 +35,169 @@ void processURLRequest(void *ctx, const char* url) { ProcessURLResponse(ctx, url, "text/html", myByteArray, 21); } +unsigned char _Users_username_Pictures_SaltBae_png[] = { + +0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, +0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, +0x08, 0x06, 0x00, 0x00, 0x00, 0x8d, 0x89, 0x1d, 0x0d, 0x00, 0x00, 0x00, +0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, +0x05, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, 0x7a, +0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x80, +0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, 0x3a, +0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, 0x00, +0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, +0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x01, 0xd5, 0x69, 0x54, +0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, +0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, +0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, +0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, +0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, +0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, +0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, +0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, +0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, +0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, +0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, +0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, +0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, +0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, +0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, +0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, +0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, +0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, +0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, +0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, +0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, +0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, +0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, +0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x50, 0x68, +0x6f, 0x74, 0x6f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x49, 0x6e, 0x74, +0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, +0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x50, 0x68, 0x6f, 0x74, +0x6f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, +0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, +0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, +0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, +0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, +0x61, 0x3e, 0x0a, 0x02, 0xd8, 0x80, 0x05, 0x00, 0x00, 0x04, 0xdc, 0x49, +0x44, 0x41, 0x54, 0x38, 0x11, 0x1d, 0x94, 0x49, 0x6c, 0x1b, 0x65, 0x18, +0x86, 0x9f, 0x99, 0xf9, 0x67, 0xc6, 0x6b, 0xbc, 0x26, 0xce, 0xda, 0xa4, +0x25, 0x69, 0x0b, 0x2d, 0x28, 0x34, 0x2c, 0x95, 0x00, 0x89, 0x45, 0x08, +0x5a, 0x95, 0x03, 0x08, 0x09, 0x21, 0xe0, 0x80, 0x38, 0xc3, 0x85, 0x03, +0xe2, 0x00, 0x47, 0xc4, 0x1d, 0x38, 0x70, 0xe3, 0xc6, 0x01, 0x01, 0x42, +0x20, 0x54, 0x7a, 0x2a, 0x6b, 0x0b, 0x94, 0xd2, 0xd2, 0x25, 0x69, 0x9b, +0xa4, 0x0d, 0x2d, 0xa9, 0xb3, 0x78, 0x89, 0x9d, 0xf1, 0x2c, 0x9e, 0x85, +0x2f, 0xb5, 0x35, 0xb6, 0x35, 0x96, 0xde, 0x79, 0xdf, 0xef, 0x7f, 0x9f, +0x4f, 0xfb, 0xe0, 0xad, 0x37, 0x12, 0xfd, 0xf0, 0xb3, 0x9c, 0xfb, 0xb7, +0xc5, 0x8d, 0x46, 0x9b, 0x71, 0x5b, 0xf1, 0xd0, 0xf4, 0x18, 0xdb, 0xeb, +0x4b, 0x1c, 0xff, 0xf1, 0x57, 0x98, 0xdc, 0x87, 0x72, 0x3a, 0x8c, 0x3a, +0xcb, 0x8c, 0xea, 0x31, 0x35, 0xb7, 0xc3, 0x99, 0xba, 0xc3, 0xd7, 0xab, +0x3e, 0x87, 0x2a, 0x8a, 0xb3, 0xff, 0xdc, 0xe0, 0x9b, 0x8f, 0x5f, 0xa2, +0x1c, 0xc5, 0xfc, 0x72, 0xc9, 0x41, 0x99, 0x71, 0x48, 0xca, 0x84, 0x3c, +0x3e, 0xda, 0xd2, 0x05, 0x9a, 0xb1, 0xc7, 0x35, 0x67, 0x1c, 0xdd, 0x4c, +0x68, 0xeb, 0x26, 0xd9, 0x30, 0x26, 0x09, 0x23, 0x5c, 0x3f, 0xc2, 0xd3, +0x43, 0xc2, 0x24, 0x21, 0x4e, 0x34, 0x40, 0x27, 0x89, 0x13, 0xf9, 0x1e, +0x22, 0x6e, 0xd5, 0x45, 0x43, 0x63, 0xc6, 0xd2, 0x50, 0xa9, 0xc4, 0x67, +0x24, 0x15, 0x72, 0xa9, 0x7e, 0x95, 0xfa, 0x4f, 0x27, 0x78, 0x64, 0x76, +0x86, 0x23, 0x61, 0xc0, 0xf0, 0x58, 0x15, 0xc3, 0x29, 0x71, 0x06, 0x45, +0x2e, 0xa5, 0x48, 0xbb, 0x0a, 0x3d, 0x89, 0xa0, 0x8f, 0x08, 0x8a, 0x8e, +0x08, 0xbb, 0xc1, 0x8e, 0xb0, 0x8d, 0xdd, 0x0f, 0xc9, 0x84, 0x06, 0x65, +0x34, 0xf4, 0xed, 0x8d, 0xff, 0x58, 0xbd, 0xfc, 0x27, 0x17, 0x2f, 0x9e, +0xe3, 0xf0, 0x81, 0x49, 0x5e, 0xde, 0x5f, 0xe1, 0x9e, 0x82, 0xcd, 0xdc, +0x78, 0x8d, 0xd9, 0xb2, 0xc9, 0x56, 0x12, 0x32, 0x94, 0x4f, 0x91, 0xcb, +0x88, 0x68, 0xda, 0x42, 0x13, 0x77, 0x11, 0xa2, 0xa8, 0xc3, 0x5a, 0x5f, +0x46, 0x30, 0x65, 0x52, 0x29, 0xe4, 0x24, 0x4d, 0x8e, 0xcc, 0x68, 0x19, +0xe5, 0x76, 0xbb, 0xac, 0x5c, 0x98, 0xa7, 0xb3, 0xed, 0xd0, 0x37, 0x62, +0xa2, 0xb0, 0xc7, 0x89, 0xe5, 0x2e, 0x03, 0x0d, 0x97, 0x95, 0x46, 0x8f, +0x31, 0xd7, 0xa6, 0x63, 0x81, 0x65, 0x25, 0x84, 0xba, 0x45, 0x5f, 0x65, +0x31, 0x2c, 0x71, 0x6b, 0x77, 0x69, 0xf5, 0x7a, 0xbc, 0xb0, 0x3b, 0xcd, +0xf9, 0xa5, 0x90, 0xd1, 0xb0, 0xcd, 0xd4, 0xb0, 0xdc, 0xd7, 0xc4, 0xfa, +0xf0, 0x78, 0x95, 0x7b, 0x27, 0xab, 0x5c, 0x5e, 0x6e, 0xd2, 0xee, 0x05, +0xdc, 0xd8, 0xea, 0xf1, 0xf7, 0xe2, 0x1a, 0xc7, 0xee, 0x1a, 0x62, 0x2e, +0x1f, 0xe3, 0xe8, 0xb6, 0xc4, 0x4c, 0xd3, 0x6d, 0x6e, 0xd0, 0x6b, 0xfc, +0x4c, 0xe3, 0xd4, 0x1f, 0xc4, 0x4b, 0xf3, 0x1c, 0x2c, 0x65, 0x29, 0x67, +0x4d, 0xbe, 0xfb, 0xad, 0x45, 0x65, 0x0c, 0xea, 0x7e, 0x1f, 0x15, 0x6b, +0x09, 0x0b, 0x8b, 0xb7, 0x19, 0xc9, 0xa5, 0x78, 0x75, 0x6e, 0x18, 0xdf, +0xf5, 0x79, 0x72, 0xd0, 0xa2, 0x2d, 0xb3, 0x3a, 0xbb, 0xb4, 0x41, 0x3e, +0x53, 0xe6, 0xf4, 0xca, 0x3c, 0xa5, 0x7c, 0x86, 0xe9, 0xfd, 0x47, 0x18, +0x2e, 0xbd, 0xce, 0xd1, 0x97, 0x26, 0x78, 0xbc, 0x7e, 0x1d, 0xff, 0xcc, +0xa7, 0x5c, 0x71, 0x74, 0x16, 0xe3, 0x18, 0xd7, 0x1e, 0x23, 0xe8, 0xac, +0xa3, 0x0c, 0xcd, 0x60, 0x22, 0x6f, 0x43, 0x36, 0x43, 0x3b, 0x19, 0xc6, +0x08, 0x7a, 0xe0, 0x6c, 0xe3, 0x27, 0x8a, 0xdb, 0x4e, 0xc0, 0xd4, 0xa0, +0xcd, 0x27, 0xaf, 0xbd, 0xcb, 0x86, 0x36, 0xc6, 0xcc, 0xfe, 0x59, 0xd2, +0xca, 0x90, 0x93, 0x36, 0x70, 0xaf, 0x9c, 0xe4, 0xcb, 0x6f, 0x65, 0x54, +0xd9, 0x47, 0x59, 0x70, 0xbb, 0x74, 0x1b, 0x0e, 0x89, 0xe7, 0xa3, 0xc7, +0x12, 0x39, 0x63, 0xea, 0x68, 0x12, 0x6b, 0x53, 0x5c, 0x9e, 0xef, 0x76, +0xf0, 0x55, 0x86, 0x0d, 0x17, 0x56, 0x9a, 0x4d, 0x94, 0x95, 0x65, 0xe6, +0xbe, 0x67, 0x98, 0xbe, 0xfb, 0x21, 0x52, 0xd2, 0x43, 0xaf, 0x5d, 0x47, +0x6b, 0x5c, 0xa3, 0x59, 0xbf, 0xc2, 0x62, 0xdd, 0x26, 0xa5, 0x12, 0x6a, +0x41, 0x44, 0xdf, 0xbd, 0xcd, 0x92, 0x17, 0xa0, 0xb6, 0x03, 0x43, 0xba, +0x66, 0x91, 0xe9, 0xdc, 0xc2, 0xce, 0xed, 0xa1, 0xfc, 0xc0, 0x2b, 0x14, +0xff, 0xfd, 0x1e, 0x4b, 0xb3, 0xa9, 0x29, 0x87, 0x81, 0xd2, 0x04, 0x8e, +0x66, 0x89, 0x58, 0x00, 0x7e, 0x07, 0xaf, 0xdb, 0xa4, 0xbb, 0xb5, 0x49, +0xb9, 0xaa, 0x18, 0xb9, 0x77, 0x8e, 0xcd, 0xdb, 0x6d, 0x1e, 0x1c, 0xb5, +0x38, 0x7d, 0xa5, 0xcf, 0xaa, 0x08, 0xeb, 0x77, 0x3f, 0x35, 0xc7, 0xda, +0xfc, 0x02, 0xaa, 0xf6, 0x1c, 0xbb, 0x9f, 0x78, 0x9f, 0x89, 0x43, 0x47, +0xa4, 0x6f, 0x3d, 0x06, 0xed, 0x90, 0x92, 0x79, 0x95, 0xd4, 0xe4, 0xfd, +0x98, 0x66, 0x4a, 0x6a, 0xd7, 0xc7, 0x0b, 0x62, 0xa4, 0xe3, 0x8c, 0x4d, +0xc4, 0xe8, 0x85, 0x98, 0xe5, 0x46, 0x44, 0x26, 0x97, 0x21, 0xe9, 0xf7, +0xf9, 0x61, 0xc5, 0xe3, 0xd4, 0x66, 0x84, 0xd2, 0x70, 0xc9, 0xee, 0x79, +0x98, 0x43, 0xc7, 0x5e, 0x27, 0xb6, 0x8a, 0xd2, 0x5a, 0x1f, 0xf3, 0xa9, +0xf7, 0x88, 0xce, 0x7d, 0x85, 0x71, 0xe0, 0x79, 0x98, 0x7a, 0x90, 0x9e, +0x1b, 0xd0, 0x13, 0x52, 0x4a, 0x66, 0x97, 0x7d, 0x33, 0x1e, 0xed, 0xae, +0xc7, 0x87, 0x1f, 0x7d, 0xce, 0xc2, 0xd5, 0x3a, 0xe6, 0xde, 0x02, 0xcb, +0xdb, 0x3e, 0xbe, 0xa6, 0x91, 0x95, 0x62, 0x6b, 0x2f, 0xce, 0x90, 0x3c, +0xfd, 0xce, 0x71, 0x0e, 0xcc, 0x3e, 0x82, 0x13, 0xf4, 0x09, 0xd5, 0x00, +0x16, 0x82, 0x98, 0xb3, 0x49, 0x24, 0xb1, 0x83, 0xc8, 0xc0, 0xd6, 0x3a, +0x54, 0x33, 0xab, 0x14, 0x8c, 0x16, 0x4e, 0x38, 0xcc, 0xe5, 0xeb, 0x4d, +0x5e, 0x7b, 0xfb, 0x4d, 0xaa, 0x79, 0xa1, 0x45, 0x1c, 0x9b, 0xd2, 0x94, +0xcc, 0x0e, 0x8c, 0x52, 0x7a, 0x65, 0x17, 0xc7, 0xa9, 0x0c, 0x8e, 0xe2, +0xf7, 0xba, 0xa8, 0xc8, 0x13, 0x87, 0x32, 0x87, 0x0b, 0x27, 0x30, 0x36, +0x57, 0xe8, 0xea, 0x15, 0xce, 0x06, 0x65, 0x5e, 0x3d, 0x5a, 0x94, 0x53, +0xb7, 0x59, 0x58, 0xdf, 0x25, 0xc4, 0xe4, 0xc9, 0x65, 0x3d, 0xb4, 0xb4, +0x4e, 0x37, 0x0c, 0x29, 0x98, 0x4a, 0xe8, 0x11, 0xde, 0x85, 0x42, 0x43, +0x1c, 0xaa, 0x38, 0x55, 0xc4, 0xb4, 0x2c, 0x22, 0x3d, 0xcd, 0xfa, 0xea, +0x0d, 0xf4, 0x8d, 0x1f, 0xc9, 0x5f, 0xfa, 0x82, 0x6d, 0xc7, 0xe1, 0xa6, +0x57, 0xe3, 0x56, 0x6e, 0x96, 0xbf, 0x16, 0x1f, 0xa3, 0x54, 0xaa, 0x91, +0x16, 0x5a, 0xb2, 0xa9, 0x04, 0xaf, 0x67, 0xc9, 0xac, 0x6c, 0xfa, 0x32, +0x9e, 0x48, 0xea, 0xa5, 0x0b, 0x89, 0x3b, 0x54, 0x47, 0xf2, 0xa1, 0xf2, +0x2a, 0x4d, 0xeb, 0xf4, 0x17, 0xdc, 0xd4, 0x72, 0x6c, 0xb5, 0x36, 0x28, +0xb6, 0x7e, 0x17, 0x04, 0xd3, 0xac, 0x7a, 0x42, 0xc1, 0xf4, 0x6e, 0x9e, +0xbf, 0x6b, 0xb7, 0x3c, 0x3a, 0x21, 0x67, 0xcb, 0x41, 0x48, 0x07, 0x91, +0xde, 0x1a, 0xe2, 0xaa, 0x9c, 0xb1, 0x59, 0xdb, 0x12, 0x25, 0xc1, 0x32, +0x92, 0xea, 0xc9, 0xaf, 0x3b, 0x97, 0xca, 0xca, 0xfe, 0x5b, 0xfe, 0xe5, +0x33, 0x29, 0xeb, 0x16, 0x95, 0xd2, 0x24, 0xeb, 0xda, 0x30, 0xeb, 0x95, +0x1a, 0xd3, 0xf7, 0x0f, 0x51, 0x1c, 0xd9, 0x0b, 0x99, 0x12, 0x7a, 0x4a, +0xd0, 0xd3, 0x25, 0x9a, 0x88, 0x45, 0xb1, 0x04, 0x33, 0x2c, 0x8a, 0x99, +0x34, 0x6b, 0x75, 0x19, 0x91, 0x9d, 0x92, 0x29, 0x89, 0xa0, 0x2c, 0x8b, +0x9d, 0xd8, 0x7a, 0x5e, 0x04, 0x07, 0x87, 0x66, 0x28, 0x56, 0x67, 0xb9, +0xd6, 0xd2, 0x39, 0xd9, 0xec, 0x33, 0x30, 0xb2, 0x8b, 0xea, 0xae, 0x83, +0x18, 0xb9, 0x31, 0x34, 0xbb, 0x42, 0x22, 0x0b, 0x21, 0x96, 0x3c, 0x61, +0xac, 0xcb, 0x95, 0x60, 0x2a, 0xe9, 0x68, 0x79, 0x08, 0x36, 0x56, 0x65, +0x27, 0x4a, 0xd9, 0x83, 0x00, 0xcf, 0x0b, 0xf1, 0xfc, 0x10, 0x15, 0x0a, +0x6a, 0x75, 0x77, 0x8b, 0x86, 0xdc, 0x58, 0x57, 0x45, 0x52, 0xe9, 0x84, +0x81, 0x7c, 0x91, 0x28, 0x55, 0x23, 0x96, 0x13, 0xd7, 0x24, 0xbe, 0xac, +0x17, 0xfa, 0xf2, 0x78, 0x63, 0xc7, 0x82, 0x08, 0xda, 0xa6, 0xc5, 0x50, +0x55, 0x04, 0xe5, 0x65, 0x5b, 0x06, 0xde, 0xce, 0xf0, 0x24, 0xf3, 0x4e, +0x70, 0xb5, 0x15, 0x6a, 0x34, 0x7b, 0x11, 0x9d, 0xbe, 0x10, 0x53, 0xd0, +0xa8, 0x86, 0x2e, 0x76, 0xb6, 0x2a, 0x9d, 0x2c, 0x48, 0x3c, 0x5b, 0xa2, +0xc8, 0x3a, 0x37, 0xd4, 0x9d, 0xed, 0x6c, 0x4a, 0xab, 0x95, 0x6e, 0x08, +0x66, 0x3d, 0x5a, 0xad, 0x4d, 0x18, 0xc8, 0xca, 0xfa, 0xd5, 0x85, 0x6f, +0xf9, 0x5f, 0xde, 0x02, 0x30, 0xff, 0x03, 0x8c, 0x47, 0x35, 0xad, 0xbc, +0xbf, 0x26, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, +0x42, 0x60, 0x82 + +}; + +unsigned int _Users_username_Pictures_SaltBae_png_len = 1863; + int main(int argc, const char * argv[]) { // insert code here... int frameless = 0; @@ -48,7 +217,18 @@ int main(int argc, const char * argv[]) { int debug = 1; WailsContext *result = Create("OI OI!",400,400, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug); SetRGBA(result, 255, 0, 0, 255); + void *m = NewMenu(""); + SetAbout(result, "Fake title", "I am a description", _Users_username_Pictures_SaltBae_png, _Users_username_Pictures_SaltBae_png_len); +// AddMenuByRole(result, 1); + AppendRole(result, m, 1); + AppendRole(result, m, 2); + void* submenu = NewMenu("test"); + void* menuITem = AppendMenuItem(result, submenu, "Woohoo", "p", 0, 0, 0, 470); + AppendSubmenu(m, submenu); + UpdateMenuItem(menuITem, 1); + SetAsApplicationMenu(result, m); + Run((void*)CFBridgingRetain(result)); diff --git a/v2/internal/frontend/desktop/darwin/menu.go b/v2/internal/frontend/desktop/darwin/menu.go index 3ccb156c..722df0ea 100644 --- a/v2/internal/frontend/desktop/darwin/menu.go +++ b/v2/internal/frontend/desktop/darwin/menu.go @@ -3,94 +3,130 @@ package darwin +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit +#import +#import "Application.h" +#import "WailsContext.h" + +#include +*/ +import "C" import ( + "unsafe" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/menu/keys" ) +type NSMenu struct { + context unsafe.Pointer + nsmenu unsafe.Pointer +} + +func NewNSMenu(context unsafe.Pointer, name string) *NSMenu { + c := NewCalloc() + defer c.Free() + title := c.String(name) + nsmenu := C.NewMenu(title) + return &NSMenu{ + context: context, + nsmenu: nsmenu, + } +} + +func (m *NSMenu) AddSubMenu(label string) *NSMenu { + result := NewNSMenu(m.context, label) + C.AppendSubmenu(m.nsmenu, result.nsmenu) + return result +} + +func (m *NSMenu) AppendRole(role menu.Role) { + C.AppendRole(m.context, m.nsmenu, C.int(role)) +} + +type MenuItem struct { + id uint + nsmenuitem unsafe.Pointer + wailsMenuItem *menu.MenuItem + radioGroupMembers []*MenuItem +} + +func (m *NSMenu) AddMenuItem(menuItem *menu.MenuItem) *MenuItem { + c := NewCalloc() + defer c.Free() + var modifier C.int + var key *C.char + if menuItem.Accelerator != nil { + modifier = C.int(keys.ToMacModifier(menuItem.Accelerator)) + key = c.String(menuItem.Accelerator.Key) + } + + result := &MenuItem{ + wailsMenuItem: menuItem, + } + + result.id = createMenuItemID(result) + result.nsmenuitem = C.AppendMenuItem(m.context, m.nsmenu, c.String(menuItem.Label), key, modifier, bool2Cint(menuItem.Disabled), bool2Cint(menuItem.Checked), C.int(result.id)) + return result +} + //func (w *Window) SetApplicationMenu(menu *menu.Menu) { //w.applicationMenu = menu //processMenu(w, menu) //} -//func processMenu(window *Window, menu *menu.Menu) { -//mainMenu := window.NewMenu() -//for _, menuItem := range menu.Items { -// submenu := mainMenu.AddSubMenu(menuItem.Label) -// for _, menuItem := range menuItem.SubMenu.Items { -// processMenuItem(submenu, menuItem) -// } -//} -//mainMenu.Show() -//} +func processMenu(parent *NSMenu, wailsMenu *menu.Menu) { + var radioGroups []*MenuItem -//func processMenuItem(parent *winc.MenuItem, menuItem *menu.MenuItem) { -// if menuItem.Hidden { -// return -// } -// switch menuItem.Type { -// case menu.SeparatorType: -// parent.AddSeparator() -// case menu.TextType: -// shortcut := acceleratorToWincShortcut(menuItem.Accelerator) -// newItem := parent.AddItem(menuItem.Label, shortcut) -// if menuItem.Tooltip != "" { -// newItem.SetToolTip(menuItem.Tooltip) -// } -// if menuItem.Click != nil { -// newItem.OnClick().Bind(func(e *winc.Event) { -// menuItem.Click(&menu.CallbackData{ -// MenuItem: menuItem, -// }) -// }) -// } -// newItem.SetEnabled(!menuItem.Disabled) -// -// case menu.CheckboxType: -// shortcut := acceleratorToWincShortcut(menuItem.Accelerator) -// newItem := parent.AddItem(menuItem.Label, shortcut) -// newItem.SetCheckable(true) -// newItem.SetChecked(menuItem.Checked) -// if menuItem.Tooltip != "" { -// newItem.SetToolTip(menuItem.Tooltip) -// } -// if menuItem.Click != nil { -// newItem.OnClick().Bind(func(e *winc.Event) { -// toggleCheckBox(menuItem) -// menuItem.Click(&menu.CallbackData{ -// MenuItem: menuItem, -// }) -// }) -// } -// newItem.SetEnabled(!menuItem.Disabled) -// addCheckBoxToMap(menuItem, newItem) -// case menu.RadioType: -// shortcut := acceleratorToWincShortcut(menuItem.Accelerator) -// newItem := parent.AddItemRadio(menuItem.Label, shortcut) -// newItem.SetCheckable(true) -// newItem.SetChecked(menuItem.Checked) -// if menuItem.Tooltip != "" { -// newItem.SetToolTip(menuItem.Tooltip) -// } -// if menuItem.Click != nil { -// newItem.OnClick().Bind(func(e *winc.Event) { -// toggleRadioItem(menuItem) -// menuItem.Click(&menu.CallbackData{ -// MenuItem: menuItem, -// }) -// }) -// } -// newItem.SetEnabled(!menuItem.Disabled) -// addRadioItemToMap(menuItem, newItem) -// case menu.SubmenuType: -// submenu := parent.AddSubMenu(menuItem.Label) -// for _, menuItem := range menuItem.SubMenu.Items { -// processMenuItem(submenu, menuItem) -// } -// } -//} + for _, menuItem := range wailsMenu.Items { + if menuItem.SubMenu != nil { + if len(radioGroups) > 0 { + processRadioGroups(radioGroups) + radioGroups = []*MenuItem{} + } + submenu := parent.AddSubMenu(menuItem.Label) + processMenu(submenu, menuItem.SubMenu) + } else { + lastMenuItem := processMenuItem(parent, menuItem) + if menuItem.Type == menu.RadioType { + radioGroups = append(radioGroups, lastMenuItem) + } else { + if len(radioGroups) > 0 { + processRadioGroups(radioGroups) + radioGroups = []*MenuItem{} + } + } + } + } +} + +func processRadioGroups(groups []*MenuItem) { + for _, item := range groups { + item.radioGroupMembers = groups + } +} + +func processMenuItem(parent *NSMenu, menuItem *menu.MenuItem) *MenuItem { + if menuItem.Hidden { + return nil + } + if menuItem.Role != 0 { + parent.AppendRole(menuItem.Role) + return nil + } + if menuItem.Type == menu.SeparatorType { + C.AppendSeparator(parent.nsmenu) + return nil + } + + return parent.AddMenuItem(menuItem) + +} func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) { - //f.mainWindow.SetApplicationMenu(menu) + f.mainWindow.SetApplicationMenu(menu) } func (f *Frontend) MenuUpdateApplicationMenu() { diff --git a/v2/internal/frontend/desktop/darwin/menuitem.go b/v2/internal/frontend/desktop/darwin/menuitem.go new file mode 100644 index 00000000..00ad57aa --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/menuitem.go @@ -0,0 +1,51 @@ +//go:build darwin +// +build darwin + +package darwin + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit +#import +#import "Application.h" +#import "WailsContext.h" + +#include +*/ +import "C" +import ( + "log" + "math" + "sync" +) + +var menuItemToID = make(map[*MenuItem]uint) +var idToMenuItem = make(map[uint]*MenuItem) +var menuItemLock sync.Mutex +var menuItemIDCounter uint = 0 + +func createMenuItemID(item *MenuItem) uint { + menuItemLock.Lock() + defer menuItemLock.Unlock() + counter := 0 + for { + menuItemIDCounter++ + value := idToMenuItem[menuItemIDCounter] + if value == nil { + break + } + counter++ + if counter == math.MaxInt { + log.Fatal("insane amounts of menuitems detected! Aborting before the collapse of the world!") + } + } + idToMenuItem[menuItemIDCounter] = item + menuItemToID[item] = menuItemIDCounter + return menuItemIDCounter +} + +func getMenuItemForID(id uint) *MenuItem { + menuItemLock.Lock() + defer menuItemLock.Unlock() + return idToMenuItem[id] +} diff --git a/v2/internal/frontend/desktop/darwin/message.h b/v2/internal/frontend/desktop/darwin/message.h index 51f242cc..f58a390b 100644 --- a/v2/internal/frontend/desktop/darwin/message.h +++ b/v2/internal/frontend/desktop/darwin/message.h @@ -19,6 +19,7 @@ void processURLRequest(void*, const char *); void processMessageDialogResponse(int); void processOpenFileDialogResponse(const char*); void processSaveFileDialogResponse(const char*); +void processCallback(int); #ifdef __cplusplus } diff --git a/v2/internal/frontend/desktop/darwin/window.go b/v2/internal/frontend/desktop/darwin/window.go index b12937d4..a27bebe8 100644 --- a/v2/internal/frontend/desktop/darwin/window.go +++ b/v2/internal/frontend/desktop/darwin/window.go @@ -17,6 +17,8 @@ import ( "strings" "unsafe" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/options" ) @@ -46,10 +48,6 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window { alwaysOnTop := bool2Cint(frontendOptions.AlwaysOnTop) hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose) debug := bool2Cint(debugMode) - alpha := C.int(frontendOptions.RGBA.A) - red := C.int(frontendOptions.RGBA.R) - green := C.int(frontendOptions.RGBA.G) - blue := C.int(frontendOptions.RGBA.B) var fullSizeContent, hideTitleBar, hideTitle, useToolbar, webviewIsTransparent C.int var titlebarAppearsTransparent, hideToolbarSeparator, windowIsTranslucent C.int @@ -77,11 +75,32 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window { } var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug) - C.SetRGBA(unsafe.Pointer(context), red, green, blue, alpha) - - return &Window{ + // Create menu + result := &Window{ context: unsafe.Pointer(context), } + + if frontendOptions.RGBA != nil { + result.SetRGBA(frontendOptions.RGBA.R, frontendOptions.RGBA.G, frontendOptions.RGBA.B, frontendOptions.RGBA.A) + } + + if frontendOptions.Mac != nil && frontendOptions.Mac.About != nil { + title := c.String(frontendOptions.Mac.About.Title) + description := c.String(frontendOptions.Mac.About.Message) + var icon unsafe.Pointer + var length C.int + if frontendOptions.Mac.About.Icon != nil { + icon = unsafe.Pointer(&frontendOptions.Mac.About.Icon[0]) + length = C.int(len(frontendOptions.Mac.About.Icon)) + } + C.SetAbout(result.context, title, description, icon, length) + } + + if frontendOptions.Menu != nil { + result.SetApplicationMenu(frontendOptions.Menu) + } + + return result } func (w *Window) Center() { @@ -90,7 +109,6 @@ func (w *Window) Center() { func (w *Window) Run() { C.Run(w.context) - println("I exited!") } func (w *Window) Quit() { @@ -185,3 +203,20 @@ func (w *Window) Size() (int, int) { temp := C.GoString(_result) return parseIntDuo(temp) } + +//func (w *Window) parseMenu(inMenu *menu.Menu) { +// for index, item := range inMenu.Items { +// switch item.Type { +// case menu.TextType: +// // Create NSMenuItem +// // Append to NSMenu +// // Keep track of index +// } +// } +//} + +func (w *Window) SetApplicationMenu(inMenu *menu.Menu) { + mainMenu := NewNSMenu(w.context, "") + processMenu(mainMenu, inMenu) + C.SetAsApplicationMenu(w.context, mainMenu.nsmenu) +} diff --git a/v2/internal/menumanager/contextmenu.go b/v2/internal/menumanager/contextmenu.go index e07cc222..77c47891 100644 --- a/v2/internal/menumanager/contextmenu.go +++ b/v2/internal/menumanager/contextmenu.go @@ -3,6 +3,7 @@ package menumanager import ( "encoding/json" "fmt" + "github.com/wailsapp/wails/v2/pkg/menu" ) diff --git a/v2/pkg/menu/keys/macmodifiers.go b/v2/pkg/menu/keys/macmodifiers.go new file mode 100644 index 00000000..7da618b4 --- /dev/null +++ b/v2/pkg/menu/keys/macmodifiers.go @@ -0,0 +1,26 @@ +package keys + +const ( + NSEventModifierFlagShift = 1 << 17 // Set if Shift key is pressed. + NSEventModifierFlagControl = 1 << 18 // Set if Control key is pressed. + NSEventModifierFlagOption = 1 << 19 // Set if Option or Alternate key is pressed. + NSEventModifierFlagCommand = 1 << 20 // Set if Command key is pressed. +) + +var macModifierMap = map[Modifier]int{ + CmdOrCtrlKey: NSEventModifierFlagCommand, + ControlKey: NSEventModifierFlagControl, + OptionOrAltKey: NSEventModifierFlagOption, + ShiftKey: NSEventModifierFlagShift, +} + +func ToMacModifier(accelerator *Accelerator) int { + if accelerator == nil { + return 0 + } + result := 0 + for _, modifier := range accelerator.Modifiers { + result |= macModifierMap[modifier] + } + return result +} diff --git a/v2/pkg/menu/keys/macmodifiers_test.go b/v2/pkg/menu/keys/macmodifiers_test.go new file mode 100644 index 00000000..8be1bd05 --- /dev/null +++ b/v2/pkg/menu/keys/macmodifiers_test.go @@ -0,0 +1,31 @@ +package keys + +import "testing" + +func TestToMacModifier(t *testing.T) { + + tests := []struct { + name string + accelerator *Accelerator + want int + }{ + // TODO: Add test cases. + {"nil", nil, 0}, + {"empty", &Accelerator{}, 0}, + {"key", &Accelerator{Key: "p"}, 0}, + {"cmd", CmdOrCtrl(""), NSEventModifierFlagCommand}, + {"ctrl", Control(""), NSEventModifierFlagControl}, + {"shift", Shift(""), NSEventModifierFlagShift}, + {"option", OptionOrAlt(""), NSEventModifierFlagOption}, + {"cmd+ctrl", Combo("", CmdOrCtrlKey, ControlKey), NSEventModifierFlagCommand | NSEventModifierFlagControl}, + {"cmd+ctrl+shift", Combo("", CmdOrCtrlKey, ControlKey, ShiftKey), NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagShift}, + {"cmd+ctrl+shift+option", Combo("", CmdOrCtrlKey, ControlKey, ShiftKey, OptionOrAltKey), NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagShift | NSEventModifierFlagOption}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToMacModifier(tt.accelerator); got != tt.want { + t.Errorf("ToMacModifier() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v2/pkg/menu/menuitem.go b/v2/pkg/menu/menuitem.go index 2a55b5fb..ba9574eb 100644 --- a/v2/pkg/menu/menuitem.go +++ b/v2/pkg/menu/menuitem.go @@ -11,9 +11,9 @@ type MenuItem struct { // Label is what appears as the menu text Label string // Role is a predefined menu type - //Role Role `json:"Role,omitempty"` + Role Role // Accelerator holds a representation of a key binding - Accelerator *keys.Accelerator `json:"Accelerator,omitempty"` + Accelerator *keys.Accelerator // Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu Type Type // Disabled makes the item unselectable @@ -24,10 +24,10 @@ type MenuItem struct { Checked bool // Submenu contains a list of menu items that will be shown as a submenu //SubMenu []*MenuItem `json:"SubMenu,omitempty"` - SubMenu *Menu `json:"SubMenu,omitempty"` + SubMenu *Menu // Callback function when menu clicked - Click Callback `json:"-"` + Click Callback /* // Text Colour RGBA string @@ -267,16 +267,3 @@ func SubMenu(label string, menu *Menu) *MenuItem { return result } - -// SubMenuWithID is a helper to create Submenus with an ID -func SubMenuWithID(label string, menu *Menu) *MenuItem { - result := &MenuItem{ - Label: label, - SubMenu: menu, - Type: SubmenuType, - } - - menu.setParent(result) - - return result -} diff --git a/v2/pkg/menu/menuroles.go b/v2/pkg/menu/menuroles.go index 55f666a7..62a193c8 100644 --- a/v2/pkg/menu/menuroles.go +++ b/v2/pkg/menu/menuroles.go @@ -3,37 +3,39 @@ // Electron License: https://github.com/electron/electron/blob/master/LICENSE package menu -/* -type Role string +// Role is a type to identify menu roles +type Role int +// These constants need to be kept in sync with `v2/internal/frontend/desktop/darwin/Role.h` const ( - AboutRole Role = "about" - UndoRole Role = "undo" - RedoRole Role = "redo" - CutRole Role = "cut" - CopyRole Role = "copy" - PasteRole Role = "paste" - PasteAndMatchStyleRole Role = "pasteAndMatchStyle" - SelectAllRole Role = "selectAll" - DeleteRole Role = "delete" - MinimizeRole Role = "minimize" - QuitRole Role = "quit" - TogglefullscreenRole Role = "togglefullscreen" - FileMenuRole Role = "fileMenu" - EditMenuRole Role = "editMenu" - ViewMenuRole Role = "viewMenu" - WindowMenuRole Role = "windowMenu" - AppMenuRole Role = "appMenu" - HideRole Role = "hide" - HideOthersRole Role = "hideOthers" - UnhideRole Role = "unhide" - FrontRole Role = "front" - ZoomRole Role = "zoom" - WindowSubMenuRole Role = "windowSubMenu" - HelpSubMenuRole Role = "helpSubMenu" - SeparatorItemRole Role = "separatorItem" + AppMenuRole Role = 1 + EditMenuRole = 2 + //AboutRole Role = "about" + //UndoRole Role = "undo" + //RedoRole Role = "redo" + //CutRole Role = "cut" + //CopyRole Role = "copy" + //PasteRole Role = "paste" + //PasteAndMatchStyleRole Role = "pasteAndMatchStyle" + //SelectAllRole Role = "selectAll" + //DeleteRole Role = "delete" + //MinimizeRole Role = "minimize" + //QuitRole Role = "quit" + //TogglefullscreenRole Role = "togglefullscreen" + //FileMenuRole Role = "fileMenu" + //ViewMenuRole Role = "viewMenu" + //WindowMenuRole Role = "windowMenu" + //HideRole Role = "hide" + //HideOthersRole Role = "hideOthers" + //UnhideRole Role = "unhide" + //FrontRole Role = "front" + //ZoomRole Role = "zoom" + //WindowSubMenuRole Role = "windowSubMenu" + //HelpSubMenuRole Role = "helpSubMenu" + //SeparatorItemRole Role = "separatorItem" ) +/* // About provides a MenuItem with the About role func About() *MenuItem { return &MenuItem{ @@ -111,8 +113,8 @@ func Quit() *MenuItem { } } -// Togglefullscreen provides a MenuItem with the Togglefullscreen role -func Togglefullscreen() *MenuItem { +// ToggleFullscreen provides a MenuItem with the ToggleFullscreen role +func ToggleFullscreen() *MenuItem { return &MenuItem{ Role: TogglefullscreenRole, } @@ -124,6 +126,7 @@ func FileMenu() *MenuItem { Role: FileMenuRole, } } +*/ // EditMenu provides a MenuItem with the whole default "Edit" menu (Undo, Copy, etc.). func EditMenu() *MenuItem { @@ -132,6 +135,7 @@ func EditMenu() *MenuItem { } } +/* // ViewMenu provides a MenuItem with the whole default "View" menu (Reload, Toggle Developer Tools, etc.) func ViewMenu() *MenuItem { return &MenuItem{ @@ -145,7 +149,7 @@ func WindowMenu() *MenuItem { Role: WindowMenuRole, } } - +*/ // These roles are Mac only // AppMenu provides a MenuItem with the whole default "App" menu (About, Services, etc.) @@ -155,6 +159,7 @@ func AppMenu() *MenuItem { } } +/* // Hide provides a MenuItem that maps to the hide action. func Hide() *MenuItem { return &MenuItem{ @@ -169,8 +174,8 @@ func HideOthers() *MenuItem { } } -// Unhide provides a MenuItem that maps to the unhideAllApplications action. -func Unhide() *MenuItem { +// UnHide provides a MenuItem that maps to the unHideAllApplications action. +func UnHide() *MenuItem { return &MenuItem{ Role: UnhideRole, } diff --git a/v2/pkg/options/mac/mac.go b/v2/pkg/options/mac/mac.go index 63e4e619..033cfd1a 100644 --- a/v2/pkg/options/mac/mac.go +++ b/v2/pkg/options/mac/mac.go @@ -1,12 +1,18 @@ package mac -type ActivationPolicy int +//type ActivationPolicy int +// +//const ( +// NSApplicationActivationPolicyRegular ActivationPolicy = 0 +// NSApplicationActivationPolicyAccessory ActivationPolicy = 1 +// NSApplicationActivationPolicyProhibited ActivationPolicy = 2 +//) -const ( - NSApplicationActivationPolicyRegular ActivationPolicy = 0 - NSApplicationActivationPolicyAccessory ActivationPolicy = 1 - NSApplicationActivationPolicyProhibited ActivationPolicy = 2 -) +type AboutInfo struct { + Title string + Message string + Icon []byte +} // Options are options specific to Mac type Options struct { @@ -14,6 +20,7 @@ type Options struct { Appearance AppearanceType WebviewIsTransparent bool WindowIsTranslucent bool - ActivationPolicy ActivationPolicy + //ActivationPolicy ActivationPolicy + About *AboutInfo //URLHandlers map[string]func(string) }