Open ghostman2013 opened 6 months ago
This is a common pattern used in Apple's frameworks, see the documentation on Target-Action, and it's a bit of a pain to handle when you're used to Rust's closures.
There's a similar issue here: https://github.com/madsmtm/objc2/issues/585
Try to have a look at that, in short you have to make a method on your delegate with a selector like onClick:
, place it on the menu item with .setAction(Some(sel!(onClick:)))
and .setTarget(Some(self))
, and then you have to run your click handler in onClick:
.
I know that's not a real explanation, but I don't have time to write up a full example right now, will do so later.
This is a common pattern used in Apple's frameworks, see the documentation on Target-Action, and it's a bit of a pain to handle when you're used to Rust's closures.
There's a similar issue here: #585
Try to have a look at that, in short you have to make a method on your delegate with a selector like
onClick:
, place it on the menu item with.setAction(Some(sel!(onClick:)))
and.setTarget(Some(self))
, and then you have to run your click handler inonClick:
.I know that's not a real explanation, but I don't have time to write up a full example right now, will do so later.
No, you have explained it very clearly. Thank you! I got how to make.
Since you said in https://github.com/rust-windowing/winit/issues/1751 that the object containing the selector methods doesn't need to be an NSApplicationDelegate
, I tried following this with a custom class like this
pub fn create_menu() {
let mtm = MainThreadMarker::new().unwrap();
let menu_bar_handler = MenuBarHandler::new(mtm);
let app = NSApplication::sharedApplication(mtm);
let menu_bar = NSMenu::new(mtm);
app.setMainMenu(Some(&menu_bar));
let app_menu_header = NSMenuItem::new(mtm);
let app_menu = NSMenu::new(mtm);
let quit_item = unsafe {
NSMenuItem::initWithTitle_action_keyEquivalent(
mtm.alloc(),
ns_string!("Test"),
Some(sel!(testaction:)),
ns_string!("t"),
)
};
unsafe { quit_item.setTarget(Some(&menu_bar_handler)) };
app_menu.addItem(&quit_item);
app_menu_header.setSubmenu(Some(&app_menu));
menu_bar.addItem(&app_menu_header);
}
declare_class!(
struct MenuBarHandler;
unsafe impl ClassType for MenuBarHandler {
type Super = NSObject;
type Mutability = mutability::MainThreadOnly;
const NAME: &'static str = "MenuBarHandler";
}
impl DeclaredClass for MenuBarHandler {}
unsafe impl MenuBarHandler {
#[method(testaction:)]
fn open(&self, _: &NSNotification) {
println!("This is a test");
}
}
);
impl MenuBarHandler {
fn new(mtm: MainThreadMarker) -> Retained<Self> {
unsafe { msg_send_id![mtm.alloc(), init] }
}
}
However, while the menu bar appears as expected, the Test item is grated out, which means that the selector isn't linked correctly to the method. Can you tell me what I'm doing wrong?
See the explanation in https://github.com/madsmtm/objc2/issues/585#issuecomment-1965482253, the issue is that you don't store menu_bar_handler
anywhere, so it's deallocated at the end of create_menu
.
Thank you for the reply! Yes, that was the issue. I guess it's not bad to have an (otherwise) working example here in case other people stumble on this too though.
Hello!
I use a little modified code that's based on examples from objc2-app-kit crate sources:
Just a trivial
AppDelegate
with menu builder. However, I hit troubles at this step:The
on_click: fn()
is a typical Rust function but I can't get how to pass it as a selector, toNSMenuItem
called it on click.Unfortunately, I couldn't find any examples. Could you explain, please, how I can make it?