rust_macios/appkit/
action_handler.rs1#![allow(trivial_casts)]
2
3use std::{fmt, sync::Once};
4
5use crate::{
6 foundation::NSString,
7 objective_c_runtime::{
8 class,
9 declare::ClassDecl,
10 id, msg_send,
11 runtime::{Class, Object, Sel},
12 sel, sel_impl,
13 traits::FromId,
14 ShareId,
15 },
16 utils::get_variable,
17};
18
19pub static ACTION_CALLBACK_PTR: &str = "RUST_Action_Callback_Ptr";
21
22pub struct Action(Box<dyn Fn() + Send + Sync + 'static>);
24
25pub struct ActionHandler {
27 pub invoker: ShareId<Object>,
29 pub action: Box<Action>,
31}
32
33impl ActionHandler {
34 pub fn new<F: Fn() + Send + Sync + 'static>(control: &Object, action: F) -> Self {
36 let block = Box::new(Action(Box::new(action)));
37 let ptr = Box::into_raw(block);
38
39 let invoker = unsafe {
40 ShareId::from_ptr({
41 let invoker: id = msg_send![Self::register_handler_class::<F>(), alloc];
42 let invoker: id = msg_send![invoker, init];
43 (*invoker).set_ivar(ACTION_CALLBACK_PTR, ptr as usize);
44 let _: () = msg_send![control, setAction: sel!(perform:)];
45 let _: () = msg_send![control, setTarget: invoker];
46 invoker
47 })
48 };
49
50 ActionHandler {
51 invoker,
52 action: unsafe { Box::from_raw(ptr) },
53 }
54 }
55
56 fn register_handler_class<F>() -> *const Class
57 where
58 F: Fn() + 'static,
59 {
60 static mut CLASS: *const Class = 0 as *const Class;
61 static INIT: Once = Once::new();
62
63 INIT.call_once(|| unsafe {
64 let mut decl = ClassDecl::new("RUST_ActionHandler", class!(NSObject)).unwrap();
65
66 decl.add_ivar::<usize>(ACTION_CALLBACK_PTR);
67 decl.add_method(
68 sel!(perform:),
69 perform::<F> as extern "C" fn(&mut Object, _, id),
70 );
71
72 CLASS = decl.register();
73 });
74
75 unsafe { CLASS }
76 }
77}
78
79impl fmt::Debug for Action {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 write!(f, "Action")
82 }
83}
84
85impl fmt::Debug for ActionHandler {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "{}", unsafe {
88 NSString::from_id(msg_send![self.invoker, debugDescription])
89 })
90 }
91}
92
93extern "C" fn perform<F: Fn() + 'static>(this: &mut Object, _: Sel, _sender: id) {
95 let action = get_variable::<Action>(this, ACTION_CALLBACK_PTR);
96 (action.0)();
97}