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/*!
5Callback that can be connected to one single handler.
6
7TODO: reconsider if we should rename that to `Event`
8but then it should also be renamed everywhere, including in the language grammar
9*/
10
11#![warn(missing_docs)]
12
13use alloc::boxed::Box;
14use 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)]
21pub 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
26impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
27 fn default() -> Self {
28 Self { handler: Default::default() }
29 }
30}
31
32impl<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]
61fn 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")]
76pub(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