i_slint_core/
callbacks.rs

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    #[unsafe(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    #[unsafe(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    #[unsafe(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    #[unsafe(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}