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}