Skip to main content

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}