Skip to content

Instantly share code, notes, and snippets.

@lunacookies
Created March 19, 2025 01:57
Show Gist options
  • Select an option

  • Save lunacookies/c83e08bda32eb25870402775d3ed5605 to your computer and use it in GitHub Desktop.

Select an option

Save lunacookies/c83e08bda32eb25870402775d3ed5605 to your computer and use it in GitHub Desktop.
AppKit programmatic main menu
@implementation AppDelegate
- (instancetype)init {
self = [super init];
NSString *displayName = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"Main Menu"];
{
NSMenu *appMenu = [[NSMenu alloc] initWithTitle:displayName];
[mainMenu addItemWithTitle:displayName action:nil keyEquivalent:@""].submenu = appMenu;
[appMenu addItemWithTitle:[NSString localizedStringWithFormat:NSLocalizedString(@"MainMenu.AboutApp", nil),
displayName]
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Preferences", nil) action:nil keyEquivalent:@","];
[appMenu addItem:[NSMenuItem separatorItem]];
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.Services", nil)];
[appMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Services", nil) action:nil keyEquivalent:@""].submenu =
servicesMenu;
NSApp.servicesMenu = servicesMenu;
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:[NSString localizedStringWithFormat:NSLocalizedString(@"MainMenu.HideApp", nil),
displayName]
action:@selector(hide:)
keyEquivalent:@"h"];
[appMenu addItemWithTitle:NSLocalizedString(@"MainMenu.HideOthers", nil)
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"]
.keyEquivalentModifierMask |= NSEventModifierFlagOption;
[appMenu addItemWithTitle:NSLocalizedString(@"MainMenu.ShowAll", nil)
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:[NSString localizedStringWithFormat:NSLocalizedString(@"MainMenu.QuitApp", nil),
displayName]
action:@selector(terminate:)
keyEquivalent:@"q"];
}
{
NSMenu *fileMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.File", nil)];
[mainMenu addItemWithTitle:NSLocalizedString(@"MainMenu.File", nil) action:nil keyEquivalent:@""].submenu =
fileMenu;
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.New", nil)
action:@selector(newDocument:)
keyEquivalent:@"n"];
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Open", nil)
action:@selector(openDocument:)
keyEquivalent:@"o"];
{
NSMenu *openRecentMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.OpenRecent", nil)];
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.OpenRecent", nil) action:nil keyEquivalent:@""]
.submenu = openRecentMenu;
[openRecentMenu addItemWithTitle:NSLocalizedString(@"MainMenu.ClearMenu", nil)
action:@selector(clearRecentDocuments:)
keyEquivalent:@""];
}
[fileMenu addItem:[NSMenuItem separatorItem]];
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Close", nil)
action:@selector(performClose:)
keyEquivalent:@"w"];
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Save", nil)
action:@selector(saveDocument:)
keyEquivalent:@"s"];
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SaveAs", nil)
action:@selector(saveDocumentAs:)
keyEquivalent:@"s"]
.keyEquivalentModifierMask |= NSEventModifierFlagShift;
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.RevertToSaved", nil)
action:@selector(revertDocumentToSaved:)
keyEquivalent:@"r"];
[fileMenu addItem:[NSMenuItem separatorItem]];
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.PageSetup", nil)
action:@selector(runPageLayout:)
keyEquivalent:@"p"]
.keyEquivalentModifierMask |= NSEventModifierFlagShift;
[fileMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Print", nil)
action:@selector(print:)
keyEquivalent:@"p"];
}
{
NSMenu *editMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.Edit", nil)];
[mainMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Edit", nil) action:nil keyEquivalent:@""].submenu =
editMenu;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Undo", nil) action:@selector(undo:) keyEquivalent:@"z"];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Redo", nil) action:@selector(redo:) keyEquivalent:@"z"]
.keyEquivalentModifierMask |= NSEventModifierFlagShift;
#pragma clang diagnostic pop
[editMenu addItem:[NSMenuItem separatorItem]];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Cut", nil) action:@selector(cut:) keyEquivalent:@"x"];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Copy", nil) action:@selector(copy:) keyEquivalent:@"c"];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Paste", nil)
action:@selector(paste:)
keyEquivalent:@"v"];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.PasteAndMatchStyle", nil)
action:@selector(pasteAsPlainText:)
keyEquivalent:@"v"]
.keyEquivalentModifierMask |= NSEventModifierFlagOption | NSEventModifierFlagShift;
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Delete", nil)
action:@selector(delete:)
keyEquivalent:@""];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SelectAll", nil)
action:@selector(selectAll:)
keyEquivalent:@"a"];
[editMenu addItem:[NSMenuItem separatorItem]];
{
NSMenu *findMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.FindSubmenu", nil)];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.FindSubmenu", nil) action:nil keyEquivalent:@""]
.submenu = findMenu;
[findMenu addItemWithTitle:NSLocalizedString(@"MainMenu.FindMenuItem", nil)
action:@selector(performFindPanelAction:)
keyEquivalent:@"f"]
.tag = 1;
NSMenuItem *findAndReplaceItem =
[findMenu addItemWithTitle:NSLocalizedString(@"MainMenu.FindAndReplace", nil)
action:@selector(performFindPanelAction:)
keyEquivalent:@"f"];
findAndReplaceItem.tag = 12;
findAndReplaceItem.keyEquivalentModifierMask |= NSEventModifierFlagOption;
[findMenu addItemWithTitle:NSLocalizedString(@"MainMenu.FindNext", nil)
action:@selector(performFindPanelAction:)
keyEquivalent:@"g"]
.tag = 2;
NSMenuItem *findPreviousItem = [findMenu addItemWithTitle:NSLocalizedString(@"MainMenu.FindPrevious", nil)
action:@selector(performFindPanelAction:)
keyEquivalent:@"g"];
findPreviousItem.tag = 3;
findPreviousItem.keyEquivalentModifierMask |= NSEventModifierFlagShift;
[findMenu addItemWithTitle:NSLocalizedString(@"MainMenu.UseSelectionForFind", nil)
action:@selector(performFindPanelAction:)
keyEquivalent:@"e"]
.tag = 7;
[findMenu addItemWithTitle:NSLocalizedString(@"MainMenu.JumpToSelection", nil)
action:@selector(centerSelectionInVisibleArea:)
keyEquivalent:@"j"];
}
{
NSMenu *spellingAndGrammarMenu =
[[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.SpellingAndGrammar", nil)];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SpellingAndGrammar", nil)
action:nil
keyEquivalent:@""]
.submenu = spellingAndGrammarMenu;
[spellingAndGrammarMenu addItemWithTitle:NSLocalizedString(@"MainMenu.ShowSpellingAndGrammar", nil)
action:@selector(showGuessPanel:)
keyEquivalent:@":"];
[spellingAndGrammarMenu addItemWithTitle:NSLocalizedString(@"MainMenu.CheckDocumentNow", nil)
action:@selector(checkSpelling:)
keyEquivalent:@";"];
[spellingAndGrammarMenu addItem:[NSMenuItem separatorItem]];
[spellingAndGrammarMenu addItemWithTitle:NSLocalizedString(@"MainMenu.CheckSpellingWhileTyping", nil)
action:@selector(toggleContinuousSpellChecking:)
keyEquivalent:@""];
[spellingAndGrammarMenu addItemWithTitle:NSLocalizedString(@"MainMenu.CheckGrammarWithSpelling", nil)
action:@selector(toggleGrammarChecking:)
keyEquivalent:@""];
[spellingAndGrammarMenu addItemWithTitle:NSLocalizedString(@"MainMenu.CorrectSpellingAutomatically", nil)
action:@selector(toggleAutomaticSpellingCorrection:)
keyEquivalent:@""];
}
{
NSMenu *substitutionsMenu =
[[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.Substitutions", nil)];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Substitutions", nil) action:nil keyEquivalent:@""]
.submenu = substitutionsMenu;
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.ShowSubstitutions", nil)
action:@selector(orderFrontSubstitutionsPanel:)
keyEquivalent:@""];
[substitutionsMenu addItem:[NSMenuItem separatorItem]];
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SmartCopyPaste", nil)
action:@selector(toggleSmartInsertDelete:)
keyEquivalent:@""];
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SmartQuotes", nil)
action:@selector(toggleAutomaticQuoteSubstitution:)
keyEquivalent:@""];
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SmartDashes", nil)
action:@selector(toggleAutomaticDashSubstitution:)
keyEquivalent:@""];
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.SmartLinks", nil)
action:@selector(toggleAutomaticLinkDetection:)
keyEquivalent:@""];
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.DataDetectors", nil)
action:@selector(toggleAutomaticDataDetection:)
keyEquivalent:@""];
[substitutionsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.TextReplacement", nil)
action:@selector(toggleAutomaticTextReplacement:)
keyEquivalent:@""];
}
{
NSMenu *transformationsMenu =
[[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.Transformations", nil)];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Transformations", nil) action:nil keyEquivalent:@""]
.submenu = transformationsMenu;
[transformationsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.MakeUpperCase", nil)
action:@selector(uppercaseWord:)
keyEquivalent:@""];
[transformationsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.MakeLowerCase", nil)
action:@selector(lowercaseWord:)
keyEquivalent:@""];
[transformationsMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Capitalize", nil)
action:@selector(capitalizeWord:)
keyEquivalent:@""];
}
{
NSMenu *speechMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.Speech", nil)];
[editMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Speech", nil) action:nil keyEquivalent:@""]
.submenu = speechMenu;
[speechMenu addItemWithTitle:NSLocalizedString(@"MainMenu.StartSpeaking", nil)
action:@selector(startSpeaking:)
keyEquivalent:@""];
[speechMenu addItemWithTitle:NSLocalizedString(@"MainMenu.StopSpeaking", nil)
action:@selector(stopSpeaking:)
keyEquivalent:@""];
}
}
{
NSMenu *viewMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.View", nil)];
[mainMenu addItemWithTitle:NSLocalizedString(@"MainMenu.View", nil) action:nil keyEquivalent:@""].submenu =
viewMenu;
[viewMenu addItemWithTitle:NSLocalizedString(@"MainMenu.ShowToolbar", nil)
action:@selector(toggleToolbarShown:)
keyEquivalent:@"t"]
.keyEquivalentModifierMask |= NSEventModifierFlagOption;
[viewMenu addItemWithTitle:NSLocalizedString(@"MainMenu.CustomizeToolbar", nil)
action:@selector(runToolbarCustomizationPalette:)
keyEquivalent:@""];
[viewMenu addItem:[NSMenuItem separatorItem]];
[viewMenu addItemWithTitle:NSLocalizedString(@"MainMenu.ShowSidebar", nil)
action:@selector(toggleSidebar:)
keyEquivalent:@"s"]
.keyEquivalentModifierMask |= NSEventModifierFlagControl;
[viewMenu addItemWithTitle:NSLocalizedString(@"MainMenu.EnterFullScreen", nil)
action:@selector(toggleFullScreen:)
keyEquivalent:@"f"]
.keyEquivalentModifierMask |= NSEventModifierFlagControl;
}
{
NSMenu *windowMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"MainMenu.Window", nil)];
[mainMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Window", nil) action:nil keyEquivalent:@""].submenu =
windowMenu;
[windowMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Minimize", nil)
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[windowMenu addItemWithTitle:NSLocalizedString(@"MainMenu.Zoom", nil)
action:@selector(performZoom:)
keyEquivalent:@""];
[windowMenu addItem:[NSMenuItem separatorItem]];
[windowMenu addItemWithTitle:NSLocalizedString(@"MainMenu.BringAllToFront", nil)
action:@selector(arrangeInFront:)
keyEquivalent:@""];
NSApp.windowsMenu = windowMenu;
}
NSApp.mainMenu = mainMenu;
return self;
}
@end
{
"MainMenu.AboutApp": "About %@",
"MainMenu.Preferences": "Preferences…",
"MainMenu.Services": "Services",
"MainMenu.HideApp": "Hide %@",
"MainMenu.HideOthers": "Hide Others",
"MainMenu.ShowAll": "Show All",
"MainMenu.QuitApp": "Quit %@",
"MainMenu.File": "File",
"MainMenu.New": "New",
"MainMenu.Open": "Open",
"MainMenu.OpenRecent": "Open Recent",
"MainMenu.ClearMenu": "Clear Menu",
"MainMenu.Close": "Close",
"MainMenu.Save": "Save…",
"MainMenu.SaveAs": "Save As…",
"MainMenu.RevertToSaved": "Revert to Saved",
"MainMenu.PageSetup": "Page Setup…",
"MainMenu.Print": "Print…",
"MainMenu.Edit": "Edit",
"MainMenu.Undo": "Undo",
"MainMenu.Redo": "Redo",
"MainMenu.Cut": "Cut",
"MainMenu.Copy": "Copy",
"MainMenu.Paste": "Paste",
"MainMenu.PasteAndMatchStyle": "Paste and Match Style",
"MainMenu.Delete": "Delete",
"MainMenu.SelectAll": "Select All",
"MainMenu.FindSubmenu": "Find",
"MainMenu.FindMenuItem": "Find…",
"MainMenu.FindAndReplace": "Find and Replace…",
"MainMenu.FindNext": "Find Next",
"MainMenu.FindPrevious": "Find Previous",
"MainMenu.UseSelectionForFind": "Use Selection for Find",
"MainMenu.JumpToSelection": "Jump to Selection",
"MainMenu.SpellingAndGrammar": "Spelling and Grammar",
"MainMenu.ShowSpellingAndGrammar": "Show Spelling and Grammar",
"MainMenu.CheckDocumentNow": "Check Document Now",
"MainMenu.CheckSpellingWhileTyping": "Check Spelling While Typing",
"MainMenu.CheckGrammarWithSpelling": "Check Grammar With Spelling",
"MainMenu.CorrectSpellingAutomatically": "Correct Spelling Automatically",
"MainMenu.Substitutions": "Substitutions",
"MainMenu.ShowSubstitutions": "Show Substitutions",
"MainMenu.SmartCopyPaste": "Smart Copy/Paste",
"MainMenu.SmartQuotes": "Smart Quotes",
"MainMenu.SmartDashes": "Smart Dashes",
"MainMenu.SmartLinks": "Smart Links",
"MainMenu.DataDetectors": "Data Detectors",
"MainMenu.TextReplacement": "Text Replacement",
"MainMenu.Transformations": "Transformations",
"MainMenu.MakeUpperCase": "Make Upper Case",
"MainMenu.MakeLowerCase": "Make Lower Case",
"MainMenu.Capitalize": "Capitalize",
"MainMenu.Speech": "Speech",
"MainMenu.StartSpeaking": "Start Speaking",
"MainMenu.StopSpeaking": "Stop Speaking",
"MainMenu.View": "View",
"MainMenu.ShowToolbar": "Show Toolbar",
"MainMenu.CustomizeToolbar": "Customize Toolbar…",
"MainMenu.ShowSidebar": "Show Sidebar",
"MainMenu.EnterFullScreen": "Enter Full Screen",
"MainMenu.Window": "Window",
"MainMenu.Minimize": "Minimize",
"MainMenu.Zoom": "Zoom",
"MainMenu.BringAllToFront": "Bring All to Front"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment