github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/darwin/WailsMenu.m (about) 1 //go:build darwin 2 // 3 // WailsMenu.m 4 // test 5 // 6 // Created by Lea Anthony on 25/10/21. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "WailsMenu.h" 11 #import "WailsMenuItem.h" 12 #import "Role.h" 13 14 @implementation WailsMenu 15 16 - (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { 17 NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease]; 18 [result setKeyEquivalentModifierMask:flags]; 19 return result; 20 } 21 22 - (NSMenuItem*) newMenuItemWithContext :(WailsContext*)ctx :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { 23 NSMenuItem *result = [NSMenuItem new]; 24 if ( title != nil ) { 25 [result setTitle:title]; 26 } 27 if (selector != nil) { 28 [result setAction:selector]; 29 } 30 if (key) { 31 [result setKeyEquivalent:key]; 32 } 33 if( flags != 0 ) { 34 [result setKeyEquivalentModifierMask:flags]; 35 } 36 result.target = ctx; 37 return result; 38 } 39 40 - (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key { 41 return [self newMenuItem :title :selector :key :0]; 42 } 43 44 - (WailsMenu*) initWithNSTitle:(NSString *)title { 45 if( title != nil ) { 46 [super initWithTitle:title]; 47 } else { 48 [self init]; 49 } 50 [self setAutoenablesItems:NO]; 51 return self; 52 } 53 54 - (void) appendSubmenu :(WailsMenu*)child { 55 NSMenuItem *childMenuItem = [[NSMenuItem new] autorelease]; 56 [childMenuItem setTitle:child.title]; 57 [self addItem:childMenuItem]; 58 [childMenuItem setSubmenu:child]; 59 } 60 61 - (void) appendRole :(WailsContext*)ctx :(Role)role { 62 63 switch(role) { 64 case AppMenu: 65 { 66 NSString *appName = [NSRunningApplication currentApplication].localizedName; 67 if( appName == nil ) { 68 appName = [[NSProcessInfo processInfo] processName]; 69 } 70 WailsMenu *appMenu = [[[WailsMenu new] initWithNSTitle:appName] autorelease]; 71 72 if (ctx.aboutTitle != nil) { 73 [appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:appName] :@selector(About) :nil :0]]; 74 [appMenu addItem:[NSMenuItem separatorItem]]; 75 } 76 77 [appMenu addItem:[self newMenuItem:[@"Hide " stringByAppendingString:appName] :@selector(hide:) :@"h" :NSEventModifierFlagCommand]]; 78 [appMenu addItem:[self newMenuItem:@"Hide Others" :@selector(hideOtherApplications:) :@"h" :(NSEventModifierFlagOption | NSEventModifierFlagCommand)]]; 79 [appMenu addItem:[self newMenuItem:@"Show All" :@selector(unhideAllApplications:) :@""]]; 80 [appMenu addItem:[NSMenuItem separatorItem]]; 81 82 id quitTitle = [@"Quit " stringByAppendingString:appName]; 83 NSMenuItem* quitMenuItem = [self newMenuItem:quitTitle :@selector(Quit) :@"q" :NSEventModifierFlagCommand]; 84 quitMenuItem.target = ctx; 85 [appMenu addItem:quitMenuItem]; 86 [self appendSubmenu:appMenu]; 87 break; 88 } 89 case EditMenu: 90 { 91 WailsMenu *editMenu = [[[WailsMenu new] initWithNSTitle:@"Edit"] autorelease]; 92 [editMenu addItem:[self newMenuItem:@"Undo" :@selector(undo:) :@"z" :NSEventModifierFlagCommand]]; 93 [editMenu addItem:[self newMenuItem:@"Redo" :@selector(redo:) :@"z" :(NSEventModifierFlagShift | NSEventModifierFlagCommand)]]; 94 [editMenu addItem:[NSMenuItem separatorItem]]; 95 [editMenu addItem:[self newMenuItem:@"Cut" :@selector(cut:) :@"x" :NSEventModifierFlagCommand]]; 96 [editMenu addItem:[self newMenuItem:@"Copy" :@selector(copy:) :@"c" :NSEventModifierFlagCommand]]; 97 [editMenu addItem:[self newMenuItem:@"Paste" :@selector(paste:) :@"v" :NSEventModifierFlagCommand]]; 98 [editMenu addItem:[self newMenuItem:@"Paste and Match Style" :@selector(pasteAsRichText:) :@"v" :(NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand)]]; 99 [editMenu addItem:[self newMenuItem:@"Delete" :@selector(delete:) :[self accel:@"backspace"] :0]]; 100 [editMenu addItem:[self newMenuItem:@"Select All" :@selector(selectAll:) :@"a" :NSEventModifierFlagCommand]]; 101 [editMenu addItem:[NSMenuItem separatorItem]]; 102 // NSMenuItem *speechMenuItem = [[NSMenuItem new] autorelease]; 103 // [speechMenuItem setTitle:@"Speech"]; 104 // [editMenu addItem:speechMenuItem]; 105 WailsMenu *speechMenu = [[[WailsMenu new] initWithNSTitle:@"Speech"] autorelease]; 106 [speechMenu addItem:[self newMenuItem:@"Start Speaking" :@selector(startSpeaking:) :@""]]; 107 [speechMenu addItem:[self newMenuItem:@"Stop Speaking" :@selector(stopSpeaking:) :@""]]; 108 [editMenu appendSubmenu:speechMenu]; 109 [self appendSubmenu:editMenu]; 110 111 break; 112 } 113 case WindowMenu: 114 { 115 WailsMenu *windowMenu = [[[WailsMenu new] initWithNSTitle:@"Window"] autorelease]; 116 [windowMenu addItem:[self newMenuItem:@"Minimize" :@selector(performMiniaturize:) :@"m" :NSEventModifierFlagCommand]]; 117 [windowMenu addItem:[self newMenuItem:@"Zoom" :@selector(performZoom:) :@""]]; 118 [windowMenu addItem:[NSMenuItem separatorItem]]; 119 [windowMenu addItem:[self newMenuItem:@"Full Screen" :@selector(enterFullScreenMode:) :@"f" :(NSEventModifierFlagControl | NSEventModifierFlagCommand)]]; 120 [self appendSubmenu:windowMenu]; 121 122 break; 123 } 124 } 125 } 126 127 - (void*) AppendMenuItem :(WailsContext*)ctx :(NSString*)label :(NSString *)shortcutKey :(int)modifiers :(bool)disabled :(bool)checked :(int)menuItemID { 128 129 NSString *nslabel = @""; 130 if (label != nil ) { 131 nslabel = label; 132 } 133 WailsMenuItem *menuItem = [WailsMenuItem new]; 134 135 // Label 136 menuItem.title = nslabel; 137 138 // Process callback 139 menuItem.menuItemID = menuItemID; 140 menuItem.action = @selector(handleClick); 141 menuItem.target = menuItem; 142 143 // Shortcut 144 if (shortcutKey != nil) { 145 [menuItem setKeyEquivalent:[self accel:shortcutKey]]; 146 [menuItem setKeyEquivalentModifierMask:modifiers]; 147 } 148 149 // Enabled/Disabled 150 [menuItem setEnabled:!disabled]; 151 152 // Checked 153 [menuItem setState:(checked ? NSControlStateValueOn : NSControlStateValueOff)]; 154 155 [self addItem:menuItem]; 156 return menuItem; 157 } 158 159 - (void) AppendSeparator { 160 [self addItem:[NSMenuItem separatorItem]]; 161 } 162 163 164 - (NSString*) accel :(NSString*)key { 165 166 // Guard against no accelerator key 167 if( key == NULL ) { 168 return @""; 169 } 170 171 if( [key isEqualToString:@"backspace"] ) { 172 return unicode(0x0008); 173 } 174 if( [key isEqualToString:@"tab"] ) { 175 return unicode(0x0009); 176 } 177 if( [key isEqualToString:@"return"] ) { 178 return unicode(0x000d); 179 } 180 if( [key isEqualToString:@"enter"] ) { 181 return unicode(0x000d); 182 } 183 if( [key isEqualToString:@"escape"] ) { 184 return unicode(0x001b); 185 } 186 if( [key isEqualToString:@"left"] ) { 187 return unicode(0x001c); 188 } 189 if( [key isEqualToString:@"right"] ) { 190 return unicode(0x001d); 191 } 192 if( [key isEqualToString:@"up"] ) { 193 return unicode(0x001e); 194 } 195 if( [key isEqualToString:@"down"] ) { 196 return unicode(0x001f); 197 } 198 if( [key isEqualToString:@"space"] ) { 199 return unicode(0x0020); 200 } 201 if( [key isEqualToString:@"delete"] ) { 202 return unicode(0x007f); 203 } 204 if( [key isEqualToString:@"home"] ) { 205 return unicode(0x2196); 206 } 207 if( [key isEqualToString:@"end"] ) { 208 return unicode(0x2198); 209 } 210 if( [key isEqualToString:@"page up"] ) { 211 return unicode(0x21de); 212 } 213 if( [key isEqualToString:@"page down"] ) { 214 return unicode(0x21df); 215 } 216 if( [key isEqualToString:@"f1"] ) { 217 return unicode(0xf704); 218 } 219 if( [key isEqualToString:@"f2"] ) { 220 return unicode(0xf705); 221 } 222 if( [key isEqualToString:@"f3"] ) { 223 return unicode(0xf706); 224 } 225 if( [key isEqualToString:@"f4"] ) { 226 return unicode(0xf707); 227 } 228 if( [key isEqualToString:@"f5"] ) { 229 return unicode(0xf708); 230 } 231 if( [key isEqualToString:@"f6"] ) { 232 return unicode(0xf709); 233 } 234 if( [key isEqualToString:@"f7"] ) { 235 return unicode(0xf70a); 236 } 237 if( [key isEqualToString:@"f8"] ) { 238 return unicode(0xf70b); 239 } 240 if( [key isEqualToString:@"f9"] ) { 241 return unicode(0xf70c); 242 } 243 if( [key isEqualToString:@"f10"] ) { 244 return unicode(0xf70d); 245 } 246 if( [key isEqualToString:@"f11"] ) { 247 return unicode(0xf70e); 248 } 249 if( [key isEqualToString:@"f12"] ) { 250 return unicode(0xf70f); 251 } 252 if( [key isEqualToString:@"f13"] ) { 253 return unicode(0xf710); 254 } 255 if( [key isEqualToString:@"f14"] ) { 256 return unicode(0xf711); 257 } 258 if( [key isEqualToString:@"f15"] ) { 259 return unicode(0xf712); 260 } 261 if( [key isEqualToString:@"f16"] ) { 262 return unicode(0xf713); 263 } 264 if( [key isEqualToString:@"f17"] ) { 265 return unicode(0xf714); 266 } 267 if( [key isEqualToString:@"f18"] ) { 268 return unicode(0xf715); 269 } 270 if( [key isEqualToString:@"f19"] ) { 271 return unicode(0xf716); 272 } 273 if( [key isEqualToString:@"f20"] ) { 274 return unicode(0xf717); 275 } 276 if( [key isEqualToString:@"f21"] ) { 277 return unicode(0xf718); 278 } 279 if( [key isEqualToString:@"f22"] ) { 280 return unicode(0xf719); 281 } 282 if( [key isEqualToString:@"f23"] ) { 283 return unicode(0xf71a); 284 } 285 if( [key isEqualToString:@"f24"] ) { 286 return unicode(0xf71b); 287 } 288 if( [key isEqualToString:@"f25"] ) { 289 return unicode(0xf71c); 290 } 291 if( [key isEqualToString:@"f26"] ) { 292 return unicode(0xf71d); 293 } 294 if( [key isEqualToString:@"f27"] ) { 295 return unicode(0xf71e); 296 } 297 if( [key isEqualToString:@"f28"] ) { 298 return unicode(0xf71f); 299 } 300 if( [key isEqualToString:@"f29"] ) { 301 return unicode(0xf720); 302 } 303 if( [key isEqualToString:@"f30"] ) { 304 return unicode(0xf721); 305 } 306 if( [key isEqualToString:@"f31"] ) { 307 return unicode(0xf722); 308 } 309 if( [key isEqualToString:@"f32"] ) { 310 return unicode(0xf723); 311 } 312 if( [key isEqualToString:@"f33"] ) { 313 return unicode(0xf724); 314 } 315 if( [key isEqualToString:@"f34"] ) { 316 return unicode(0xf725); 317 } 318 if( [key isEqualToString:@"f35"] ) { 319 return unicode(0xf726); 320 } 321 // if( [key isEqualToString:@"Insert"] ) { 322 // return unicode(0xf727); 323 // } 324 // if( [key isEqualToString:@"PrintScreen"] ) { 325 // return unicode(0xf72e); 326 // } 327 // if( [key isEqualToString:@"ScrollLock"] ) { 328 // return unicode(0xf72f); 329 // } 330 if( [key isEqualToString:@"numLock"] ) { 331 return unicode(0xf739); 332 } 333 334 return key; 335 } 336 337 338 @end 339 340