tauri_nspanel/
macros.rs

1#[macro_export]
2macro_rules! panel_delegate {
3    ($delegate_name:ident { $($fn_name:ident),* $(,)* }) => {{
4        use $crate::objc::{
5            class,
6            declare::ClassDecl,
7            msg_send,
8            runtime::{self, Class, Object, Protocol, Sel},
9            sel, sel_impl, Message,
10        };
11        use $crate::cocoa::base::{id, nil, BOOL, NO};
12        use $crate::objc_foundation::INSObject;
13        use $crate::objc_id::{Id, ShareId};
14        use $crate::raw_nspanel::RawNSPanel;
15        use $crate::tauri::Runtime;
16        use $crate::block::ConcreteBlock;
17        use std::ffi::{c_void, c_char};
18
19        macro_rules! snake_to_camel {
20            ($input:ident) => {{
21                let mut camel_case = String::new();
22                let mut chars = $input.chars().peekable();
23                while let Some(c) = chars.next() {
24                    if c == '_' {
25                        if let Some(next) = chars.peek() {
26                            camel_case.push(next.to_ascii_uppercase());
27                            chars.next();
28                        }
29                    } else {
30                        camel_case.push(c);
31                    }
32                }
33                camel_case
34            }};
35        }
36
37        macro_rules! sel_from_func {
38            ($func:ident) => ({
39                let func_str = stringify!($func);
40                let sel_str = snake_to_camel!(func_str);
41                sel_impl!(format!("{}:{}", sel_str, '\0').as_str())
42            });
43        }
44
45        #[allow(dead_code)]
46        const DELEGATE_CLS_NAME: &str = stringify!($delegate_name);
47
48        #[allow(dead_code)]
49        struct $delegate_name;
50
51        impl $delegate_name {
52             #[allow(dead_code)]
53            fn get_class() -> &'static Class {
54                Class::get(DELEGATE_CLS_NAME).unwrap_or_else(Self::define_class)
55            }
56
57            #[allow(dead_code)]
58            fn define_class() -> &'static Class {
59                let mut cls = ClassDecl::new(DELEGATE_CLS_NAME, class!(NSObject))
60                    .unwrap_or_else(|| panic!("Unable to register {} class", DELEGATE_CLS_NAME));
61
62                cls.add_protocol(
63                    Protocol::get("NSWindowDelegate").expect("Failed to get NSWindowDelegate protocol"),
64                );
65
66                unsafe {
67                    cls.add_ivar::<*mut c_void>("_listener");
68
69                    cls.add_method(
70                        sel!(setListener:),
71                        Self::handle_set_listener as extern "C" fn(&mut Object, Sel, *mut c_void),
72                    );
73
74                    $(
75                        cls.add_method(
76                            sel_from_func!($fn_name),
77                            Self::$fn_name as extern "C" fn(&Object, Sel, id),
78                        );
79                    )*
80
81                    cls.add_method(
82                        sel!(dealloc),
83                        Self::dealloc as extern "C" fn(&mut Object, Sel),
84                    );
85                }
86
87                cls.register()
88            }
89
90            extern "C" fn handle_set_listener(this: &mut Object, _: Sel, listener: *mut c_void) {
91                unsafe { this.set_ivar::<*mut c_void>("_listener", listener) };
92            }
93
94            $(
95                extern "C" fn $fn_name(this: &Object, _: Sel, _: id) {
96                    let delegate_name = std::ffi::CString::new(stringify!($fn_name)).unwrap();
97                    let delegate_name_ptr = delegate_name.as_ptr();
98
99                    let listener: *mut c_void = unsafe { *this.get_ivar("_listener") };
100                    let listener = listener as *const ConcreteBlock<(*const c_char, BOOL, *mut Object, *mut Sel, *mut c_void), (), ()>;
101
102                    unsafe {
103                        (*listener).call((delegate_name_ptr, NO, std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut()));
104                    }
105                }
106            )*
107
108            extern "C" fn dealloc(this: &mut Object, _cmd: Sel) {
109                unsafe {
110                    let superclass = class!(NSObject);
111                    let dealloc: extern "C" fn(&mut Object, Sel) =
112                        msg_send![super(this, superclass), dealloc];
113                    dealloc(this, _cmd);
114                }
115            }
116        }
117
118        unsafe impl Message for $delegate_name {}
119
120        impl INSObject for $delegate_name {
121            fn class() -> &'static runtime::Class {
122                Self::get_class()
123            }
124        }
125
126        impl $delegate_name {
127            pub fn set_listener(&self, callback: Box<dyn Fn(String)>) {
128                let block = ConcreteBlock::new(move |_delegate_name: *const c_char| {
129                    let delegate_name = unsafe { std::ffi::CStr::from_ptr(_delegate_name).to_string_lossy().into_owned() };
130                    callback(delegate_name);
131                });
132                let block = block.copy();
133                let _: () = unsafe { msg_send![self, setListener: block] };
134            }
135        }
136
137        $delegate_name::new()
138    }};
139}