如何使用 objc2 将 Rust 函数作为选择器传递到 NSMenuItem.action 中?

问题描述 投票:0回答:1

我有下一个代码用于在

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>);
rust appkit nsmenu objc2
1个回答
0
投票

我很确定有一种更简单的方法,但是...我已经使用自定义

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 块达到相同的结果。不过我没明白怎么办。

© www.soinside.com 2019 - 2024. All rights reserved.