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