我有下一个代码用于在
AppDelegate
中构建应用程序菜单(AppDelegate
代码取自官方示例):
impl AppDelegate {
fn new(mtm: MainThreadMarker, menu: Option<Menu>) -> Id<Self> {
let this = mtm.alloc();
let this = this.set_ivars(Ivars {
mtm,
menu,
});
unsafe { msg_send_id![super(this), init] }
}
fn build_menu(&self, menu: &Menu, ns_menu: &Id<NSMenu>) {
let ivars = self.ivars();
for item in menu.items.iter() {
let ns_menu_item = NSMenuItem::new(ivars.mtm);
let title = NSString::from_str(&item.name);
unsafe { ns_menu_item.setTitle(&title) };
if let Some(on_click) = &item.on_click {
unsafe { ns_menu_item.setAction(Some(on_click)) };
}
if let Some(submenu) = &item.submenu {
let ns_submenu = NSMenu::new(ivars.mtm);
self.build_menu(submenu, &ns_submenu);
ns_menu_item.setSubmenu(Some(&ns_submenu));
}
ns_menu.addItem(&ns_menu_item);
}
}
fn create_menu(&self, application: Id<NSApplication>) {
let ivars = self.ivars();
if let Some(menu) = &ivars.menu {
let main_menu = NSMenu::new(ivars.mtm);
application.setMainMenu(Some(&main_menu));
self.build_menu(menu, &main_menu);
}
}
}
问题出在这部分代码:
if let Some(on_click) = &item.on_click {
unsafe { ns_menu_item.setAction(Some(on_click)) };
}
on_click: fn()
是一个Rust函数,我不知道如何使用objc2将其作为Objective-C选择器传递。
在生成的 AppKit 中,该方法如下所示:
#[method(setAction:)]
pub unsafe fn setAction(&self, action: Option<Sel>);
我很确定有一种更简单的方法,但是...我已经使用自定义
NSMenuItem
类解决了我的问题:
#[derive(Debug)]
#[allow(unused)]
struct MacMenuItemIvars {
action: Option<fn()>,
}
declare_class!(
struct MacMenuItem;
unsafe impl ClassType for MacMenuItem {
type Super = NSMenuItem;
type Mutability = mutability::MainThreadOnly;
const NAME: &'static str = "MenuItem";
}
impl DeclaredClass for MacMenuItem {
type Ivars = MacMenuItemIvars;
}
unsafe impl MacMenuItem {
#[method(callback)]
fn __callback(&self) {
if let Some(action) = self.ivars().action {
action();
}
}
}
unsafe impl NSObjectProtocol for MacMenuItem {}
);
impl MacMenuItem {
fn new<S>(mtm: MainThreadMarker, title: S, action: Option<fn()>) -> Id<Self>
where
S: Into<String>,
{
let this = mtm.alloc();
let this = this.set_ivars(MacMenuItemIvars {
action,
});
let item: Id<MacMenuItem> = unsafe { msg_send_id![super(this), init] };
let title: String = title.into();
let title = NSString::from_str(&title);
unsafe { item.setTitle(&title) };
if action.is_some() {
unsafe { item.setTarget(Some(&item)) };
unsafe { item.setAction(Some(sel!(callback))) };
}
item
}
}
必须有一种方法可以使用 Objective-C 块达到相同的结果。不过我没明白怎么办。