use cocoa::appkit::{NSEventModifierFlags, NSMenu, NSMenuItem};
use cocoa::base::{id, nil, NO};
use cocoa::foundation::NSAutoreleasePool;
use crate::common_util::strip_access_key;
use crate::hotkey::{HotKey, KeyCompare};
use crate::keyboard::{KeyCode, KeyModifiers};
use crate::util::make_nsstring;
pub struct Menu {
pub menu: id,
}
fn make_menu_item(id: u32, text: &str, key: Option<&HotKey>, enabled: bool, selected: bool) -> id {
let key_equivalent = key.map(HotKey::key_equivalent).unwrap_or("");
let stripped_text = strip_access_key(text);
unsafe {
let item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
make_nsstring(&stripped_text),
sel!(handleMenuItem:),
make_nsstring(&key_equivalent),
)
.autorelease();
let () = msg_send![item, setTag: id as isize];
if let Some(mask) = key.map(HotKey::key_modifier_mask) {
let () = msg_send![item, setKeyEquivalentModifierMask: mask];
}
if !enabled {
let () = msg_send![item, setEnabled: NO];
}
if selected {
let () = msg_send![item, setState: 1_isize];
}
item
}
}
impl Menu {
pub fn new() -> Menu {
unsafe {
let menu = NSMenu::alloc(nil).autorelease();
let () = msg_send![menu, setAutoenablesItems: NO];
Menu { menu }
}
}
pub fn new_for_popup() -> Menu {
Menu::new()
}
pub fn add_dropdown(&mut self, menu: Menu, text: &str, enabled: bool) {
unsafe {
let menu_item = NSMenuItem::alloc(nil).autorelease();
let title = make_nsstring(&strip_access_key(text));
let () = msg_send![menu.menu, setTitle: title];
if !enabled {
let () = msg_send![menu_item, setEnabled: NO];
}
menu_item.setSubmenu_(menu.menu);
self.menu.addItem_(menu_item);
}
}
pub fn add_item(
&mut self,
id: u32,
text: &str,
key: Option<&HotKey>,
enabled: bool,
selected: bool,
) {
let menu_item = make_menu_item(id, text, key, enabled, selected);
unsafe {
self.menu.addItem_(menu_item);
}
}
pub fn add_separator(&mut self) {
unsafe {
let sep = id::separatorItem(self.menu);
self.menu.addItem_(sep);
}
}
}
impl Default for Menu {
fn default() -> Self {
Menu::new()
}
}
impl HotKey {
fn key_equivalent(&self) -> &str {
match self.key {
KeyCompare::Text(t) => t,
KeyCompare::Code(KeyCode::Return) => "\u{0003}",
KeyCompare::Code(KeyCode::Backspace) => "\u{0008}",
KeyCompare::Code(KeyCode::Delete) => "\u{007f}",
KeyCompare::Code(KeyCode::Insert) => "\u{F727}",
KeyCompare::Code(KeyCode::Home) => "\u{F729}",
KeyCompare::Code(KeyCode::End) => "\u{F72B}",
KeyCompare::Code(KeyCode::PageUp) => "\u{F72C}",
KeyCompare::Code(KeyCode::PageDown) => "\u{F72D}",
KeyCompare::Code(KeyCode::PrintScreen) => "\u{F72E}",
KeyCompare::Code(KeyCode::ScrollLock) => "\u{F72F}",
KeyCompare::Code(KeyCode::ArrowUp) => "\u{F700}",
KeyCompare::Code(KeyCode::ArrowDown) => "\u{F701}",
KeyCompare::Code(KeyCode::ArrowLeft) => "\u{F702}",
KeyCompare::Code(KeyCode::ArrowRight) => "\u{F703}",
KeyCompare::Code(KeyCode::F1) => "\u{F704}",
KeyCompare::Code(KeyCode::F2) => "\u{F705}",
KeyCompare::Code(KeyCode::F3) => "\u{F706}",
KeyCompare::Code(KeyCode::F4) => "\u{F707}",
KeyCompare::Code(KeyCode::F5) => "\u{F708}",
KeyCompare::Code(KeyCode::F6) => "\u{F709}",
KeyCompare::Code(KeyCode::F7) => "\u{F70A}",
KeyCompare::Code(KeyCode::F8) => "\u{F70B}",
KeyCompare::Code(KeyCode::F9) => "\u{F70C}",
KeyCompare::Code(KeyCode::F10) => "\u{F70D}",
KeyCompare::Code(other) => {
eprintln!("no key equivalent for {:?}", other);
""
}
}
}
fn key_modifier_mask(&self) -> NSEventModifierFlags {
let mods: KeyModifiers = self.mods.into();
let mut flags = NSEventModifierFlags::empty();
if mods.shift {
flags.insert(NSEventModifierFlags::NSShiftKeyMask);
}
if mods.meta {
flags.insert(NSEventModifierFlags::NSCommandKeyMask);
}
if mods.alt {
flags.insert(NSEventModifierFlags::NSAlternateKeyMask);
}
if mods.ctrl {
flags.insert(NSEventModifierFlags::NSControlKeyMask);
}
flags
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strip_access() {
assert_eq!(strip_access_key("&Exit").as_str(), "Exit");
assert_eq!(strip_access_key("&&Exit").as_str(), "&Exit");
assert_eq!(strip_access_key("E&&xit").as_str(), "E&xit");
assert_eq!(strip_access_key("E&xit").as_str(), "Exit");
}
}