22 static NSString*
const kIdKey =
@"id";
65 @0x00100000008 : [NSNumber numberWithInt:NSBackspaceCharacter],
66 @0x00100000009 : [NSNumber numberWithInt:NSTabCharacter],
67 @0x0010000000a : [NSNumber numberWithInt:NSNewlineCharacter],
68 @0x0010000000c : [NSNumber numberWithInt:NSFormFeedCharacter],
69 @0x0010000000d : [NSNumber numberWithInt:NSCarriageReturnCharacter],
70 @0x0010000007f : [NSNumber numberWithInt:NSDeleteCharacter],
71 @0x00100000801 : [NSNumber numberWithInt:NSF1FunctionKey],
72 @0x00100000802 : [NSNumber numberWithInt:NSF2FunctionKey],
73 @0x00100000803 : [NSNumber numberWithInt:NSF3FunctionKey],
74 @0x00100000804 : [NSNumber numberWithInt:NSF4FunctionKey],
75 @0x00100000805 : [NSNumber numberWithInt:NSF5FunctionKey],
76 @0x00100000806 : [NSNumber numberWithInt:NSF6FunctionKey],
77 @0x00100000807 : [NSNumber numberWithInt:NSF7FunctionKey],
78 @0x00100000808 : [NSNumber numberWithInt:NSF8FunctionKey],
79 @0x00100000809 : [NSNumber numberWithInt:NSF9FunctionKey],
80 @0x0010000080a : [NSNumber numberWithInt:NSF10FunctionKey],
81 @0x0010000080b : [NSNumber numberWithInt:NSF11FunctionKey],
82 @0x0010000080c : [NSNumber numberWithInt:NSF12FunctionKey],
83 @0x0010000080d : [NSNumber numberWithInt:NSF13FunctionKey],
84 @0x0010000080e : [NSNumber numberWithInt:NSF14FunctionKey],
85 @0x0010000080f : [NSNumber numberWithInt:NSF15FunctionKey],
86 @0x00100000810 : [NSNumber numberWithInt:NSF16FunctionKey],
87 @0x00100000811 : [NSNumber numberWithInt:NSF17FunctionKey],
88 @0x00100000812 : [NSNumber numberWithInt:NSF18FunctionKey],
89 @0x00100000813 : [NSNumber numberWithInt:NSF19FunctionKey],
90 @0x00100000814 : [NSNumber numberWithInt:NSF20FunctionKey],
95 @0x00100000302 : @0xf702,
96 @0x00100000303 : @0xf703,
97 @0x00100000304 : @0xf700,
98 @0x00100000301 : @0xf701,
99 @0x00100000306 : @0xf729,
100 @0x00100000305 : @0xf72B,
101 @0x00100000308 : @0xf72c,
102 @0x00100000307 : @0xf72d,
103 @0x0010000001b : @0x001B,
136 int flutterModifierFlags = modifiers.intValue;
137 NSEventModifierFlags flags = 0;
139 flags |= NSEventModifierFlagCommand;
142 flags |= NSEventModifierFlagShift;
145 flags |= NSEventModifierFlagOption;
148 flags |= NSEventModifierFlagControl;
183 - (void)menuWillOpen:(NSMenu*)menu {
184 [_channel invokeMethod:kMenuOpenedMethod arguments:@(_identifier)];
187 - (void)menuDidClose:(NSMenu*)menu {
188 [_channel invokeMethod:kMenuClosedMethod arguments:@(_identifier)];
198 - (void)replaceAppName:(NSArray<NSMenuItem*>*)items;
202 - (NSMenuItem*)findProvidedMenuItem:(NSMenu*)menu ofType:(
SEL)selector;
208 - (NSMenuItem*)menuItemFromFlutterRepresentation:(NSDictionary*)representation;
213 - (void)flutterMenuItemSelected:(
id)sender;
217 - (void)setMenus:(nonnull NSDictionary*)representation;
232 #pragma mark - Private Methods
246 [
self replaceAppName:_platformProvidedItems];
255 - (void)replaceAppName:(NSArray<NSMenuItem*>*)items {
256 NSString* appName = [NSRunningApplication currentApplication].localizedName;
257 for (NSMenuItem* item in items) {
258 if ([[item title] containsString:
kAppName]) {
259 [item setTitle:[[item title] stringByReplacingOccurrencesOfString:kAppName
260 withString:appName]];
262 if ([item hasSubmenu]) {
263 [
self replaceAppName:[[item submenu] itemArray]];
268 - (NSMenuItem*)findProvidedMenuItem:(NSMenu*)menu ofType:(
SEL)selector {
270 for (NSMenuItem* item in items) {
271 if ([item action] == selector) {
274 if ([[item submenu] numberOfItems] > 0) {
275 NSMenuItem* foundChild = [
self findProvidedMenuItem:[item submenu] ofType:selector];
286 auto found_type = providedMenus.find(type);
287 if (found_type == providedMenus.end()) {
290 SEL selectorTarget = found_type->second;
296 ? [_platformProvidedItems[0] submenu]
298 NSMenuItem* found = [
self findProvidedMenuItem:startingMenu ofType:selectorTarget];
305 - (NSMenuItem*)menuItemFromFlutterRepresentation:(NSDictionary*)representation {
306 if ([(NSNumber*)([representation valueForKey:
kDividerKey]) intValue] == YES) {
307 return [NSMenuItem separatorItem];
309 NSNumber* platformProvidedMenuId = representation[kPlatformProvidedMenuKey];
310 NSString* keyEquivalent =
@"";
312 if (platformProvidedMenuId) {
314 createPlatformProvidedMenu:(flutter::PlatformProvidedMenu)platformProvidedMenuId.intValue];
317 keyEquivalent = representation[kShortcutCharacterKey];
319 NSNumber* triggerKeyId = representation[kShortcutTriggerKey];
321 NSNumber* trigger = specialKeys[triggerKeyId];
323 keyEquivalent = [NSString stringWithFormat:@"%C", [trigger unsignedShortValue]];
327 keyEquivalent = [[NSString
328 stringWithFormat:@"%C", (unichar)([triggerKeyId unsignedLongLongValue] &
329 kFlutterKeyIdValueMask)] lowercaseString];
335 NSNumber* identifier = representation[kIdKey];
336 SEL action = (identifier ?
@selector(flutterMenuItemSelected:) : NULL);
337 NSString* appName = [NSRunningApplication currentApplication].localizedName;
338 NSString* title = [representation[kLabelKey] stringByReplacingOccurrencesOfString:kAppName
340 NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:title
342 keyEquivalent:keyEquivalent];
343 if ([keyEquivalent length] > 0) {
344 item.keyEquivalentModifierMask =
348 item.tag = identifier.longLongValue;
351 NSNumber* enabled = representation[kEnabledKey];
353 item.enabled = enabled.boolValue;
356 NSArray* children = representation[kChildrenKey];
357 if (children && children.count > 0) {
358 NSMenu* submenu = [[NSMenu alloc] initWithTitle:title];
361 [_menuDelegates addObject:delegate];
362 submenu.delegate = delegate;
363 submenu.autoenablesItems = NO;
364 for (NSDictionary* child in children) {
365 NSMenuItem* newItem = [
self menuItemFromFlutterRepresentation:child];
367 [submenu addItem:newItem];
370 item.submenu = submenu;
375 - (void)flutterMenuItemSelected:(
id)sender {
376 NSMenuItem* item = sender;
377 [_channel invokeMethod:kMenuSelectedCallbackMethod arguments:@(item.tag)];
385 [
self setMenus:menus];
392 - (void)setMenus:(NSDictionary*)representation {
393 [_menuDelegates removeAllObjects];
394 NSMenu* newMenu = [[NSMenu alloc] init];
397 for (NSDictionary* item in representation[
@"0"]) {
398 NSMenuItem* menuItem = [
self menuItemFromFlutterRepresentation:item];
399 menuItem.representedObject =
self;
400 NSNumber* identifier = item[kIdKey];
402 [[
FlutterMenuDelegate alloc] initWithIdentifier:identifier.longLongValue channel:_channel];
403 [_menuDelegates addObject:delegate];
404 [menuItem submenu].delegate = delegate;
405 [newMenu addItem:menuItem];
407 NSApp.mainMenu = newMenu;
410 #pragma mark - Public Class Methods