feat(macos): implement CheckMenuItem, NativeMenuItem (#14)

* feat(macos): implement `CheckMenuItem`

* feat(macos): implement `NativeMenuItem`

* Update src/platform_impl/macos/mod.rs

Co-authored-by: Ngo Iok Ui (Wu Yu Wei) <wusyong9104@gmail.com>

* Fix enum variant

Co-authored-by: Ngo Iok Ui (Wu Yu Wei) <wusyong9104@gmail.com>
This commit is contained in:
Jason Tsai 2022-06-19 17:47:48 +08:00 committed by GitHub
parent 00a0270b8f
commit a125e8a58c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 171 additions and 7 deletions

View file

@ -82,6 +82,93 @@ impl MenuItem {
}
}
#[derive(Debug, Clone)]
pub struct CheckMenuItem {
pub(crate) id: u64,
pub(crate) ns_menu_item: id,
label: Rc<str>,
}
impl CheckMenuItem {
pub fn new<S: AsRef<str>>(
label: S,
enabled: bool,
checked: bool,
selector: Sel,
accelerator: Option<&str>,
) -> Self {
let (id, ns_menu_item) = make_menu_item(&remove_mnemonic(&label), selector, accelerator);
unsafe {
(&mut *ns_menu_item).set_ivar(MENU_IDENTITY, id);
let () = msg_send![&*ns_menu_item, setTarget:&*ns_menu_item];
if !enabled {
let () = msg_send![ns_menu_item, setEnabled: NO];
}
if checked {
let () = msg_send![ns_menu_item, setState: 1_isize];
}
}
Self {
id,
ns_menu_item,
label: Rc::from(label.as_ref()),
}
}
pub fn label(&self) -> String {
self.label.to_string()
}
pub fn set_label<S: AsRef<str>>(&mut self, label: S) {
unsafe {
let title = NSString::alloc(nil).init_str(&remove_mnemonic(&label));
self.ns_menu_item.setTitle_(title);
}
self.label = Rc::from(label.as_ref());
}
pub fn enabled(&self) -> bool {
unsafe {
let enabled: BOOL = msg_send![self.ns_menu_item, isEnabled];
enabled == YES
}
}
pub fn set_enabled(&mut self, enabled: bool) {
unsafe {
let status = match enabled {
true => YES,
false => NO,
};
let () = msg_send![self.ns_menu_item, setEnabled: status];
}
}
pub fn checked(&self) -> bool {
unsafe {
let checked: isize = msg_send![self.ns_menu_item, state];
checked == 1_isize
}
}
pub fn set_checked(&mut self, checked: bool) {
unsafe {
let state = match checked {
true => 1_isize,
false => 0_isize,
};
let () = msg_send![self.ns_menu_item, setState: state];
}
}
pub fn id(&self) -> u64 {
self.id
}
}
pub fn make_menu_item(title: &str, selector: Sel, accelerator: Option<&str>) -> (u64, *mut Object) {
let alloc = make_menu_item_alloc();
let menu_id = COUNTER.next();

View file

@ -1,17 +1,18 @@
mod accelerator;
mod menu_item;
use crate::platform_impl::platform_impl::menu_item::make_menu_item;
use crate::NativeMenuItem;
use cocoa::{
appkit::{NSApp, NSApplication, NSMenu, NSMenuItem},
base::{id, nil, NO},
base::{id, nil, selector, NO},
foundation::{NSAutoreleasePool, NSString},
};
use objc::{msg_send, sel, sel_impl};
pub use menu_item::MenuItem;
use objc::{class, msg_send, sel, sel_impl};
use self::accelerator::remove_mnemonic;
pub use menu_item::CheckMenuItem;
pub use menu_item::MenuItem;
#[derive(Debug, Clone)]
pub struct Menu(id);
@ -98,8 +99,84 @@ impl Submenu {
item
}
pub fn add_native_item(&mut self, _item: NativeMenuItem) {
// TODO
return;
pub fn add_native_item(&mut self, item: NativeMenuItem) {
let (_, native_menu_item) = match item {
NativeMenuItem::Separator => unsafe { (0, NSMenuItem::separatorItem(nil)) },
NativeMenuItem::About(app_name, _) => {
let title = format!("About {}", app_name);
make_menu_item(
title.as_str(),
selector("orderFrontStandardAboutPanel:"),
None,
)
}
NativeMenuItem::CloseWindow => {
make_menu_item("Close Window", selector("performClose:"), Some("Command+W"))
}
NativeMenuItem::Quit => {
make_menu_item("Quit", selector("terminate:"), Some("Command+Q"))
}
NativeMenuItem::Hide => make_menu_item("Hide", selector("hide:"), Some("Command+H")),
NativeMenuItem::HideOthers => make_menu_item(
"Hide Others",
selector("hideOtherApplications:"),
Some("Alt+H"),
),
NativeMenuItem::ShowAll => {
make_menu_item("Show All", selector("unhideAllApplications:"), None)
}
NativeMenuItem::EnterFullScreen => make_menu_item(
"Toggle Full Screen",
selector("toggleFullScreen:"),
Some("Ctrl+F"),
),
NativeMenuItem::Minimize => make_menu_item(
"Minimize",
selector("performMiniaturize:"),
Some("Command+M"),
),
NativeMenuItem::Zoom => make_menu_item("Zoom", selector("performZoom:"), None),
NativeMenuItem::Copy => make_menu_item("Copy", selector("copy:"), Some("Command+C")),
NativeMenuItem::Cut => make_menu_item("Cut", selector("cut:"), Some("Command+X")),
NativeMenuItem::Paste => make_menu_item("Paste", selector("paste:"), Some("Command+V")),
NativeMenuItem::Undo => make_menu_item("Undo", selector("undo:"), Some("Command+Z")),
NativeMenuItem::Redo => {
make_menu_item("Redo", selector("redo:"), Some("Command+Shift+Z"))
}
NativeMenuItem::SelectAll => {
make_menu_item("Select All", selector("selectAll:"), Some("Command+A"))
}
NativeMenuItem::Services => unsafe {
let (_, item) = make_menu_item("Services", sel!(fireMenubarAction:), None);
let app_class = class!(NSApplication);
let app: id = msg_send![app_class, sharedApplication];
let services: id = msg_send![app, servicesMenu];
let _: () = msg_send![&*item, setSubmenu: services];
(0, item)
},
};
unsafe {
self.menu.0.addItem_(native_menu_item);
}
}
pub fn add_check_item<S: AsRef<str>>(
&mut self,
label: S,
enabled: bool,
checked: bool,
accelerator: Option<&str>,
) -> CheckMenuItem {
let item = CheckMenuItem::new(
label,
enabled,
checked,
sel!(fireMenubarAction:),
accelerator,
);
unsafe {
self.menu.0.addItem_(item.ns_menu_item);
}
item
}
}