200 lines
6 KiB
Rust
200 lines
6 KiB
Rust
//! A wrapper for NSMenuItem. Currently only supports menus going
|
|
//! one level deep; this could change in the future but is fine for
|
|
//! now.
|
|
|
|
use objc::{class, msg_send, sel, sel_impl};
|
|
use objc::runtime::{Object, Sel};
|
|
use objc_id::ShareId;
|
|
|
|
use crate::foundation::{id, nil, NSString, NSUInteger};
|
|
use crate::events::EventModifierFlag;
|
|
|
|
/// Internal method (shorthand) for generating `NSMenuItem` holders.
|
|
fn make_menu_item(
|
|
title: &str,
|
|
key: Option<&str>,
|
|
action: Option<Sel>,
|
|
modifiers: Option<&[EventModifierFlag]>
|
|
) -> MenuItem {
|
|
unsafe {
|
|
let cls = class!(NSMenuItem);
|
|
let alloc: id = msg_send![cls, alloc];
|
|
let title = NSString::new(title);
|
|
|
|
// Note that AppKit requires a blank string if nil, not nil.
|
|
let key = NSString::new(match key {
|
|
Some(s) => s,
|
|
None => ""
|
|
});
|
|
|
|
let item = ShareId::from_ptr(match action {
|
|
Some(a) => msg_send![alloc, initWithTitle:title action:a keyEquivalent:key],
|
|
None => msg_send![alloc, initWithTitle:title action:nil keyEquivalent:key]
|
|
});
|
|
|
|
if let Some(modifiers) = modifiers {
|
|
let mut key_mask: NSUInteger = 0;
|
|
|
|
for modifier in modifiers {
|
|
let y: NSUInteger = modifier.into();
|
|
key_mask = key_mask | y;
|
|
}
|
|
|
|
let _: () = msg_send![&*item, setKeyEquivalentModifierMask:key_mask];
|
|
}
|
|
|
|
MenuItem::Action(item)
|
|
}
|
|
}
|
|
|
|
/// Represents varying `NSMenuItem` types - e.g, a separator vs an action.
|
|
#[derive(Debug)]
|
|
pub enum MenuItem {
|
|
/// Represents a Menu item that's not a separator - for all intents and purposes, you can consider
|
|
/// this the real `NSMenuItem`.
|
|
Action(ShareId<Object>),
|
|
|
|
/// Represents a Separator. You can't do anything with this, but it's useful nonetheless for
|
|
/// separating out pieces of the `NSMenu` structure.
|
|
Separator
|
|
}
|
|
|
|
impl MenuItem {
|
|
/// Creates and returns a `MenuItem::Action` with the specified title.
|
|
pub fn action(title: &str) -> Self {
|
|
make_menu_item(title, None, None, None)
|
|
}
|
|
|
|
/// Configures the menu item, if it's not a separator, to support a key equivalent.
|
|
pub fn key(self, key: &str) -> Self {
|
|
match self {
|
|
MenuItem::Separator => MenuItem::Separator,
|
|
|
|
MenuItem::Action(item) => {
|
|
unsafe {
|
|
let key = NSString::new(key);
|
|
let _: () = msg_send![&*item, setKeyEquivalent:key];
|
|
}
|
|
|
|
MenuItem::Action(item)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns a standard "About" item.
|
|
pub fn about(name: &str) -> Self {
|
|
let title = format!("About {}", name);
|
|
make_menu_item(&title, None, Some(sel!(orderFrontStandardAboutPanel:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Hide" item.
|
|
pub fn hide() -> Self {
|
|
make_menu_item("Hide", Some("h"), Some(sel!(hide:)), None)
|
|
}
|
|
|
|
/// Returns the standard "Services" item. This one does some extra work to link in the default
|
|
/// Services submenu.
|
|
pub fn services() -> Self {
|
|
match make_menu_item("Services", None, None, None) {
|
|
// Link in the services menu, which is part of NSApp
|
|
MenuItem::Action(item) => {
|
|
unsafe {
|
|
let app: id = msg_send![class!(RSTApplication), sharedApplication];
|
|
let services: id = msg_send![app, servicesMenu];
|
|
let _: () = msg_send![&*item, setSubmenu:services];
|
|
}
|
|
|
|
MenuItem::Action(item)
|
|
},
|
|
|
|
// Should never be hit
|
|
MenuItem::Separator => MenuItem::Separator
|
|
}
|
|
}
|
|
|
|
/// Returns a standard "Hide" item.
|
|
pub fn hide_others() -> Self {
|
|
make_menu_item(
|
|
"Hide Others",
|
|
Some("h"),
|
|
Some(sel!(hide:)),
|
|
Some(&[EventModifierFlag::Command, EventModifierFlag::Option])
|
|
)
|
|
}
|
|
|
|
/// Returns a standard "Hide" item.
|
|
pub fn show_all() -> Self {
|
|
make_menu_item("Show All", None, Some(sel!(unhideAllApplications:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Close Window" item.
|
|
pub fn close_window() -> Self {
|
|
make_menu_item("Close Window", Some("w"), Some(sel!(performClose:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Quit" item.
|
|
pub fn quit() -> Self {
|
|
make_menu_item("Quit", Some("q"), Some(sel!(terminate:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Copy" item.
|
|
pub fn copy() -> Self {
|
|
make_menu_item("Copy", Some("c"), Some(sel!(copy:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Undo" item.
|
|
pub fn undo() -> Self {
|
|
make_menu_item("Undo", Some("z"), Some(sel!(undo:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Enter Full Screen" item
|
|
pub fn enter_full_screen() -> Self {
|
|
make_menu_item(
|
|
"Enter Full Screen",
|
|
Some("f"),
|
|
Some(sel!(toggleFullScreen:)),
|
|
Some(&[EventModifierFlag::Command, EventModifierFlag::Control])
|
|
)
|
|
}
|
|
|
|
/// Returns a standard "Miniaturize" item
|
|
pub fn minimize() -> Self {
|
|
make_menu_item(
|
|
"Minimize",
|
|
Some("m"),
|
|
Some(sel!(performMiniaturize:)),
|
|
None
|
|
)
|
|
}
|
|
|
|
/// Returns a standard "Zoom" item
|
|
pub fn zoom() -> Self {
|
|
make_menu_item(
|
|
"Zoom",
|
|
None,
|
|
Some(sel!(performZoom:)),
|
|
None
|
|
)
|
|
}
|
|
|
|
/// Returns a standard "Redo" item.
|
|
pub fn redo() -> Self {
|
|
make_menu_item("Redo", Some("Z"), Some(sel!(redo:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Cut" item.
|
|
pub fn cut() -> Self {
|
|
make_menu_item("Cut", Some("x"), Some(sel!(cut:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Select All" item.
|
|
pub fn select_all() -> Self {
|
|
make_menu_item("Select All", Some("a"), Some(sel!(selectAll:)), None)
|
|
}
|
|
|
|
/// Returns a standard "Paste" item.
|
|
pub fn paste() -> Self {
|
|
make_menu_item("Paste", Some("v"), Some(sel!(paste:)), None)
|
|
}
|
|
}
|