1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
3 | |
4 | /*! |
5 | Callback that can be connected to one single handler. |
6 | |
7 | TODO: reconsider if we should rename that to `Event` |
8 | but then it should also be renamed everywhere, including in the language grammar |
9 | */ |
10 | |
11 | #![warn (missing_docs)] |
12 | |
13 | use alloc::boxed::Box; |
14 | use core::cell::Cell; |
15 | |
16 | /// A Callback that can be connected to a handler. |
17 | /// |
18 | /// The Arg represents the argument. It should always be a tuple |
19 | /// |
20 | #[repr (C)] |
21 | pub struct Callback<Arg: ?Sized, Ret = ()> { |
22 | /// FIXME: `Box<dyn>` is a fat object and we probably want to put an erased type in there |
23 | handler: Cell<Option<Box<dyn FnMut(&Arg, &mut Ret)>>>, |
24 | } |
25 | |
26 | impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> { |
27 | fn default() -> Self { |
28 | Self { handler: Default::default() } |
29 | } |
30 | } |
31 | |
32 | impl<Arg: ?Sized, Ret: Default> Callback<Arg, Ret> { |
33 | /// Call the callback with the given argument. |
34 | pub fn call(&self, a: &Arg) -> Ret { |
35 | let mut r = Ret::default(); |
36 | if let Some(mut h) = self.handler.take() { |
37 | h(a, &mut r); |
38 | assert!(self.handler.take().is_none(), "Callback Handler set while called" ); |
39 | self.handler.set(Some(h)); |
40 | } |
41 | r |
42 | } |
43 | |
44 | /// Return whether a callback is registered or not. |
45 | pub fn has_handler(&self) -> bool { |
46 | let handler = self.handler.take(); |
47 | let result = handler.is_some(); |
48 | self.handler.set(handler); |
49 | result |
50 | } |
51 | |
52 | /// Set an handler to be called when the callback is called |
53 | /// |
54 | /// There can only be one single handler per callback. |
55 | pub fn set_handler(&self, mut f: impl FnMut(&Arg) -> Ret + 'static) { |
56 | self.handler.set(Some(Box::new(move |a: &Arg, r: &mut Ret| *r = f(a)))); |
57 | } |
58 | } |
59 | |
60 | #[test ] |
61 | fn callback_simple_test() { |
62 | use std::rc::Rc; |
63 | #[derive (Default)] |
64 | struct Component { |
65 | pressed: core::cell::Cell<bool>, |
66 | clicked: Callback<()>, |
67 | } |
68 | let c = Rc::new(Component::default()); |
69 | let weak = Rc::downgrade(&c); |
70 | c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true)); |
71 | c.clicked.call(&()); |
72 | assert!(c.pressed.get()); |
73 | } |
74 | |
75 | #[cfg (feature = "ffi" )] |
76 | pub(crate) mod ffi { |
77 | #![allow (unsafe_code)] |
78 | |
79 | use super::*; |
80 | |
81 | #[allow (non_camel_case_types)] |
82 | type c_void = (); |
83 | #[repr (C)] |
84 | /// Has the same layout as Callback<_> |
85 | pub struct CallbackOpaque(*const c_void, *const c_void); |
86 | |
87 | static_assertions::assert_eq_align!(CallbackOpaque, Callback<()>); |
88 | static_assertions::assert_eq_size!(CallbackOpaque, Callback<()>); |
89 | static_assertions::assert_eq_align!(CallbackOpaque, Callback<(alloc::string::String,)>); |
90 | static_assertions::assert_eq_size!(CallbackOpaque, Callback<(alloc::string::String,)>); |
91 | |
92 | /// Initialize the callback. |
93 | /// slint_callback_drop must be called. |
94 | #[no_mangle ] |
95 | pub unsafe extern "C" fn slint_callback_init(out: *mut CallbackOpaque) { |
96 | assert_eq!(core::mem::size_of::<CallbackOpaque>(), core::mem::size_of::<Callback<()>>()); |
97 | core::ptr::write(out as *mut Callback<()>, Default::default()); |
98 | } |
99 | |
100 | /// Emit the callback |
101 | #[no_mangle ] |
102 | pub unsafe extern "C" fn slint_callback_call( |
103 | sig: *const CallbackOpaque, |
104 | arg: *const c_void, |
105 | ret: *mut c_void, |
106 | ) { |
107 | let sig = &*(sig as *const Callback<c_void>); |
108 | if let Some(mut h) = sig.handler.take() { |
109 | h(&*arg, &mut *ret); |
110 | assert!(sig.handler.take().is_none(), "Callback Handler set while called" ); |
111 | sig.handler.set(Some(h)); |
112 | } |
113 | } |
114 | |
115 | /// Set callback handler. |
116 | /// |
117 | /// The binding has signature fn(user_data) |
118 | #[no_mangle ] |
119 | pub unsafe extern "C" fn slint_callback_set_handler( |
120 | sig: *const CallbackOpaque, |
121 | binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void), |
122 | user_data: *mut c_void, |
123 | drop_user_data: Option<extern "C" fn(*mut c_void)>, |
124 | ) { |
125 | let sig = &mut *(sig as *mut Callback<c_void>); |
126 | |
127 | struct UserData { |
128 | user_data: *mut c_void, |
129 | drop_user_data: Option<extern "C" fn(*mut c_void)>, |
130 | binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void), |
131 | } |
132 | |
133 | impl Drop for UserData { |
134 | fn drop(&mut self) { |
135 | if let Some(x) = self.drop_user_data { |
136 | x(self.user_data) |
137 | } |
138 | } |
139 | } |
140 | |
141 | impl UserData { |
142 | /// Safety: the arguments must be valid pointers |
143 | unsafe fn call(&self, arg: *const c_void, ret: *mut c_void) { |
144 | (self.binding)(self.user_data, arg, ret) |
145 | } |
146 | } |
147 | |
148 | let ud = UserData { user_data, drop_user_data, binding }; |
149 | sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| { |
150 | ud.call(a as *const c_void, r as *mut c_void) |
151 | }))); |
152 | } |
153 | |
154 | /// Destroy callback |
155 | #[no_mangle ] |
156 | pub unsafe extern "C" fn slint_callback_drop(handle: *mut CallbackOpaque) { |
157 | core::ptr::drop_in_place(handle as *mut Callback<()>); |
158 | } |
159 | } |
160 | |