#![expect(unsafe_code)]
use objc2::{MainThreadMarker, MainThreadOnly, rc::Retained, sel};
use objc2_app_kit::{NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
use objc2_foundation::{NSBundle, NSProcessInfo, NSString, ns_string};
pub(crate) fn install_application_menu() {
let Some(mtm) = MainThreadMarker::new() else {
return;
};
let app = NSApplication::sharedApplication(mtm);
if app.mainMenu().is_some() {
return;
}
let process_name = NSBundle::mainBundle()
.name()
.unwrap_or_else(|| NSProcessInfo::processInfo().processName());
let services = NSMenu::new(mtm);
let services_item = item(mtm, ns_string!("Services"), None, ns_string!(""));
services_item.setSubmenu(Some(&services));
let hide_others = item(
mtm,
ns_string!("Hide Others"),
Some(sel!(hideOtherApplications:)),
ns_string!("h"),
);
hide_others
.setKeyEquivalentModifierMask(NSEventModifierFlags::Option | NSEventModifierFlags::Command);
let app_menu = NSMenu::new(mtm);
for entry in [
item(
mtm,
&ns_string!("About ").stringByAppendingString(&process_name),
Some(sel!(orderFrontStandardAboutPanel:)),
ns_string!(""),
),
NSMenuItem::separatorItem(mtm),
services_item,
item(
mtm,
&ns_string!("Hide ").stringByAppendingString(&process_name),
Some(sel!(hide:)),
ns_string!("h"),
),
hide_others,
item(
mtm,
ns_string!("Show All"),
Some(sel!(unhideAllApplications:)),
ns_string!(""),
),
NSMenuItem::separatorItem(mtm),
item(
mtm,
&ns_string!("Quit ").stringByAppendingString(&process_name),
Some(sel!(terminate:)),
ns_string!("q"),
),
] {
app_menu.addItem(&entry);
}
let app_item = NSMenuItem::new(mtm);
app_item.setSubmenu(Some(&app_menu));
let menubar = NSMenu::new(mtm);
menubar.addItem(&app_item);
app.setServicesMenu(Some(&services));
app.setMainMenu(Some(&menubar));
}
fn item(
mtm: MainThreadMarker,
title: &NSString,
action: Option<objc2::runtime::Sel>,
key: &NSString,
) -> Retained<NSMenuItem> {
unsafe {
NSMenuItem::initWithTitle_action_keyEquivalent(NSMenuItem::alloc(mtm), title, action, key)
}
}