afia_component/callback.rs
1//! Types and functions used for handling events and adding event listeners to dom nodes.
2
3use crate::dom::event::DomEvent;
4use crate::ComponentImports;
5
6/// Generates an `__afia__$handle_event` function that expects that a pointer to a [`Callback`]
7/// was used as the context when calling [`crate::DomElement::add_event_listener`]'s context.
8///
9/// The generated functions behaves similar to the following:
10/// ```ignore
11/// use afia_component::callback::Callback;
12/// use afia_component::dom::event::DomEvent;
13///
14/// #[export_name = "__afia__$handle_event"]
15/// pub extern "C" fn handle_event(event: DomEvent, callback: &'static Callback) {
16/// callback.call(event);
17/// }
18/// ```
19#[macro_export]
20macro_rules! handle_events_using_callbacks {
21 () => {
22 #[export_name = "__afia__$handle_event"]
23 #[allow(missing_docs)]
24 pub extern "C" fn handle_event(event_handle: i64, callback: *mut Callback) {
25 let callback: &'static Callback = unsafe { &*callback };
26
27 let event_handle =
28 $crate::dom::event::DomEvent::_private_new(event_handle, callback.imports());
29
30 (callback.callback())(event_handle, callback.context());
31 }
32 };
33}
34
35/// A callback that can be used for event listeners in the function
36/// [`add_event_listener`], it gets passed down to the exported
37/// `__afia__$handle_event` function.
38pub struct Callback {
39 context: isize,
40 imports: &'static ComponentImports,
41 callback: fn(DomEvent, isize),
42}
43
44impl Callback {
45 /// TODO: Delete this. Just a way for us to let some of our older code make use of this newer
46 /// [`Callback`] abstraction.
47 /// This is currently used in one place. We should not use it anywhere else. Once we delete
48 /// that place we can delete this method.
49 pub fn temporary_way_to_call_using_integer_event_handle(&self, event_handle: i64) {
50 (self.callback)(
51 DomEvent::_private_new(event_handle, self.imports),
52 self.context,
53 )
54 }
55
56 /// Create a [`Callback`] by providing a function that takes an event handle
57 /// and a pointer to the associated context.
58 pub fn new_with_context_pointer<T>(
59 context: *mut T,
60 imports: &'static ComponentImports,
61 // TODO: Change think about whether to change this to.
62 // Just a quick idea that I'm jotting down... this commit is already large to trying to land
63 // it without any new stuff ...
64 // `callback: fn(DomEvent, ContextPtr<T>)`
65 // Using `ContextPtr<T>` allows the user to access the `T` without needing to write unsafe
66 // pointer dereferencing code.
67 callback: fn(DomEvent, *mut T),
68 ) -> Self {
69 // SAFETY: safe because we are casting between two pointer types of the same size
70 let callback: fn(DomEvent, isize) = unsafe { std::mem::transmute(callback) };
71
72 Self {
73 context: context as isize,
74 callback,
75 imports,
76 }
77 }
78
79 /// Create a [`Callback`] by providing a function that takes an event
80 /// handle as an argument.
81 pub fn new_without_context(imports: &'static ComponentImports, callback: fn(DomEvent)) -> Self {
82 Self {
83 context: callback as isize,
84 imports,
85 callback: |event: DomEvent, context: isize| {
86 // SAFETY: Safe because we stored the callback function pointer as the context.
87 let callback: fn(DomEvent) = unsafe { std::mem::transmute(context) };
88
89 callback(event)
90 },
91 }
92 }
93
94 // PRIVATE: Used by the `handle_events_using_callbacks` macro.
95 #[doc(hidden)]
96 pub fn callback(&self) -> fn(DomEvent, isize) {
97 self.callback
98 }
99
100 // PRIVATE: Used by the `handle_events_using_callbacks` macro.
101 #[doc(hidden)]
102 pub fn context(&self) -> isize {
103 self.context
104 }
105
106 // PRIVATE: Used by the `handle_events_using_callbacks` macro.
107 #[doc(hidden)]
108 pub fn imports(&self) -> &'static ComponentImports {
109 self.imports
110 }
111}