mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-11-03 21:24:17 +00:00 
			
		
		
		
	To be compatible with the 2024 edition, we need to wrap the `no_mangle` attribute in `unsafe()`. The parsing for that in cbindgen was only added in the version 0.28, but we couldn't upgrade cbindgen before because of a regression in cbindgen 0.27 that prevented us from upgrading. Now that cbindgen 0.29 is released with a fix, we can prepare for the 2024 edition
		
			
				
	
	
		
			159 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// Copyright © SixtyFPS GmbH <info@slint.dev>
 | 
						|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
 | 
						|
 | 
						|
/*!
 | 
						|
Callback that can be connected to one single handler.
 | 
						|
 | 
						|
TODO: reconsider if we should rename that to `Event`
 | 
						|
but then it should also be renamed everywhere, including in the language grammar
 | 
						|
*/
 | 
						|
 | 
						|
#![warn(missing_docs)]
 | 
						|
 | 
						|
use alloc::boxed::Box;
 | 
						|
use core::cell::Cell;
 | 
						|
 | 
						|
/// A Callback that can be connected to a handler.
 | 
						|
///
 | 
						|
/// The Arg represents the argument. It should always be a tuple
 | 
						|
///
 | 
						|
#[repr(C)]
 | 
						|
pub struct Callback<Arg: ?Sized, Ret = ()> {
 | 
						|
    /// FIXME: `Box<dyn>` is a fat object and we probably want to put an erased type in there
 | 
						|
    handler: Cell<Option<Box<dyn FnMut(&Arg, &mut Ret)>>>,
 | 
						|
}
 | 
						|
 | 
						|
impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
 | 
						|
    fn default() -> Self {
 | 
						|
        Self { handler: Default::default() }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl<Arg: ?Sized, Ret: Default> Callback<Arg, Ret> {
 | 
						|
    /// Call the callback with the given argument.
 | 
						|
    pub fn call(&self, a: &Arg) -> Ret {
 | 
						|
        let mut r = Ret::default();
 | 
						|
        if let Some(mut h) = self.handler.take() {
 | 
						|
            h(a, &mut r);
 | 
						|
            assert!(self.handler.take().is_none(), "Callback Handler set while called");
 | 
						|
            self.handler.set(Some(h));
 | 
						|
        }
 | 
						|
        r
 | 
						|
    }
 | 
						|
 | 
						|
    /// Return whether a callback is registered or not.
 | 
						|
    pub fn has_handler(&self) -> bool {
 | 
						|
        let handler = self.handler.take();
 | 
						|
        let result = handler.is_some();
 | 
						|
        self.handler.set(handler);
 | 
						|
        result
 | 
						|
    }
 | 
						|
 | 
						|
    /// Set an handler to be called when the callback is called
 | 
						|
    ///
 | 
						|
    /// There can only be one single handler per callback.
 | 
						|
    pub fn set_handler(&self, mut f: impl FnMut(&Arg) -> Ret + 'static) {
 | 
						|
        self.handler.set(Some(Box::new(move |a: &Arg, r: &mut Ret| *r = f(a))));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn callback_simple_test() {
 | 
						|
    use std::rc::Rc;
 | 
						|
    #[derive(Default)]
 | 
						|
    struct Component {
 | 
						|
        pressed: core::cell::Cell<bool>,
 | 
						|
        clicked: Callback<()>,
 | 
						|
    }
 | 
						|
    let c = Rc::new(Component::default());
 | 
						|
    let weak = Rc::downgrade(&c);
 | 
						|
    c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true));
 | 
						|
    c.clicked.call(&());
 | 
						|
    assert!(c.pressed.get());
 | 
						|
}
 | 
						|
 | 
						|
#[cfg(feature = "ffi")]
 | 
						|
pub(crate) mod ffi {
 | 
						|
    #![allow(unsafe_code)]
 | 
						|
 | 
						|
    use super::*;
 | 
						|
 | 
						|
    #[allow(non_camel_case_types)]
 | 
						|
    type c_void = ();
 | 
						|
    #[repr(C)]
 | 
						|
    /// Has the same layout as Callback<_>
 | 
						|
    pub struct CallbackOpaque(*const c_void, *const c_void);
 | 
						|
 | 
						|
    static_assertions::assert_eq_align!(CallbackOpaque, Callback<()>);
 | 
						|
    static_assertions::assert_eq_size!(CallbackOpaque, Callback<()>);
 | 
						|
    static_assertions::assert_eq_align!(CallbackOpaque, Callback<(alloc::string::String,)>);
 | 
						|
    static_assertions::assert_eq_size!(CallbackOpaque, Callback<(alloc::string::String,)>);
 | 
						|
 | 
						|
    /// Initialize the callback.
 | 
						|
    /// slint_callback_drop must be called.
 | 
						|
    #[unsafe(no_mangle)]
 | 
						|
    pub unsafe extern "C" fn slint_callback_init(out: *mut CallbackOpaque) {
 | 
						|
        assert_eq!(core::mem::size_of::<CallbackOpaque>(), core::mem::size_of::<Callback<()>>());
 | 
						|
        core::ptr::write(out as *mut Callback<()>, Default::default());
 | 
						|
    }
 | 
						|
 | 
						|
    /// Emit the callback
 | 
						|
    #[unsafe(no_mangle)]
 | 
						|
    pub unsafe extern "C" fn slint_callback_call(
 | 
						|
        sig: *const CallbackOpaque,
 | 
						|
        arg: *const c_void,
 | 
						|
        ret: *mut c_void,
 | 
						|
    ) {
 | 
						|
        let sig = &*(sig as *const Callback<c_void>);
 | 
						|
        if let Some(mut h) = sig.handler.take() {
 | 
						|
            h(&*arg, &mut *ret);
 | 
						|
            assert!(sig.handler.take().is_none(), "Callback Handler set while called");
 | 
						|
            sig.handler.set(Some(h));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// Set callback handler.
 | 
						|
    ///
 | 
						|
    /// The binding has signature fn(user_data)
 | 
						|
    #[unsafe(no_mangle)]
 | 
						|
    pub unsafe extern "C" fn slint_callback_set_handler(
 | 
						|
        sig: *const CallbackOpaque,
 | 
						|
        binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void),
 | 
						|
        user_data: *mut c_void,
 | 
						|
        drop_user_data: Option<extern "C" fn(*mut c_void)>,
 | 
						|
    ) {
 | 
						|
        let sig = &mut *(sig as *mut Callback<c_void>);
 | 
						|
 | 
						|
        struct UserData {
 | 
						|
            user_data: *mut c_void,
 | 
						|
            drop_user_data: Option<extern "C" fn(*mut c_void)>,
 | 
						|
            binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void),
 | 
						|
        }
 | 
						|
 | 
						|
        impl Drop for UserData {
 | 
						|
            fn drop(&mut self) {
 | 
						|
                if let Some(x) = self.drop_user_data {
 | 
						|
                    x(self.user_data)
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        impl UserData {
 | 
						|
            /// Safety: the arguments must be valid pointers
 | 
						|
            unsafe fn call(&self, arg: *const c_void, ret: *mut c_void) {
 | 
						|
                (self.binding)(self.user_data, arg, ret)
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        let ud = UserData { user_data, drop_user_data, binding };
 | 
						|
        sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| {
 | 
						|
            ud.call(a as *const c_void, r as *mut c_void)
 | 
						|
        })));
 | 
						|
    }
 | 
						|
 | 
						|
    /// Destroy callback
 | 
						|
    #[unsafe(no_mangle)]
 | 
						|
    pub unsafe extern "C" fn slint_callback_drop(handle: *mut CallbackOpaque) {
 | 
						|
        core::ptr::drop_in_place(handle as *mut Callback<()>);
 | 
						|
    }
 | 
						|
}
 |