mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Handle native callbacks with return value
This commit is contained in:
parent
e0e3a1aaad
commit
2e23b5bbdd
6 changed files with 77 additions and 48 deletions
|
@ -28,9 +28,9 @@ struct Callback<Ret(Arg...)>
|
|||
{
|
||||
cbindgen_private::sixtyfps_callback_set_handler(
|
||||
&inner,
|
||||
[](void *user_data, const void *arg) {
|
||||
auto *p = reinterpret_cast<const Pair*>(arg);
|
||||
*p->first = std::apply(*reinterpret_cast<F *>(user_data), p->second);
|
||||
[](void *user_data, const void *arg, void *ret) {
|
||||
*reinterpret_cast<Ret*>(ret) = std::apply(*reinterpret_cast<F *>(user_data),
|
||||
*reinterpret_cast<const Tuple*>(arg));
|
||||
},
|
||||
new F(std::move(binding)),
|
||||
[](void *user_data) { delete reinterpret_cast<F *>(user_data); });
|
||||
|
@ -39,14 +39,13 @@ struct Callback<Ret(Arg...)>
|
|||
Ret call(const Arg &...arg) const
|
||||
{
|
||||
Ret r{};
|
||||
Pair p = std::pair{ &r, Tuple{arg...} };
|
||||
cbindgen_private::sixtyfps_callback_call(&inner, &p);
|
||||
Tuple tuple{arg...};
|
||||
cbindgen_private::sixtyfps_callback_call(&inner, &tuple, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
using Tuple = std::tuple<Arg...>;
|
||||
using Pair = std::pair<Ret *, Tuple>;
|
||||
cbindgen_private::CallbackOpaque inner;
|
||||
};
|
||||
|
||||
|
@ -65,7 +64,7 @@ struct Callback<void(Arg...)>
|
|||
{
|
||||
cbindgen_private::sixtyfps_callback_set_handler(
|
||||
&inner,
|
||||
[](void *user_data, const void *arg) {
|
||||
[](void *user_data, const void *arg, void *) {
|
||||
std::apply(*reinterpret_cast<F *>(user_data),
|
||||
*reinterpret_cast<const Tuple*>(arg));
|
||||
},
|
||||
|
@ -76,7 +75,7 @@ struct Callback<void(Arg...)>
|
|||
void call(const Arg &...arg) const
|
||||
{
|
||||
Tuple tuple{arg...};
|
||||
cbindgen_private::sixtyfps_callback_call(&inner, &tuple);
|
||||
cbindgen_private::sixtyfps_callback_call(&inner, &tuple, reinterpret_cast<void *>(0x1));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -84,7 +83,11 @@ private:
|
|||
cbindgen_private::CallbackOpaque inner;
|
||||
};
|
||||
|
||||
template<typename A, typename R> struct CallbackSignatureHelper { using Result = R(A); };
|
||||
template<typename R> struct CallbackSignatureHelper<void, R> { using Result = R(); };
|
||||
template<typename A, typename R = void> using CallbackHelper =
|
||||
Callback<typename CallbackSignatureHelper<A, R>::Result>;
|
||||
|
||||
}
|
||||
} // namespace sixtyfps
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ use core::cell::Cell;
|
|||
#[repr(C)]
|
||||
pub struct Callback<Arg: ?Sized, Ret = ()> {
|
||||
/// FIXME: Box<dyn> is a fat object and we probaly want to put an erased type in there
|
||||
handler: Cell<Option<Box<dyn Fn(&Arg) -> Ret>>>,
|
||||
handler: Cell<Option<Box<dyn Fn(&Arg, &mut Ret)>>>,
|
||||
}
|
||||
|
||||
impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
|
||||
|
@ -37,21 +37,20 @@ impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
|
|||
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(h) = self.handler.take() {
|
||||
let r = h(a);
|
||||
h(a, &mut r);
|
||||
assert!(self.handler.take().is_none(), "Callback Handler set while callted");
|
||||
self.handler.set(Some(h));
|
||||
r
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// 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, f: impl Fn(&Arg) -> Ret + 'static) {
|
||||
self.handler.set(Some(Box::new(f)));
|
||||
self.handler.set(Some(Box::new(move |a: &Arg, r: &mut Ret| *r = f(a))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,9 +98,14 @@ pub(crate) mod ffi {
|
|||
pub unsafe extern "C" fn sixtyfps_callback_call(
|
||||
sig: *const CallbackOpaque,
|
||||
arg: *const c_void,
|
||||
ret: *mut c_void,
|
||||
) {
|
||||
let sig = &*(sig as *const Callback<c_void>);
|
||||
sig.call(&*arg);
|
||||
if let Some(h) = sig.handler.take() {
|
||||
h(&*arg, &mut *ret);
|
||||
assert!(sig.handler.take().is_none(), "Callback Handler set while callted");
|
||||
sig.handler.set(Some(h));
|
||||
}
|
||||
}
|
||||
|
||||
/// Set callback handler.
|
||||
|
@ -110,7 +114,7 @@ pub(crate) mod ffi {
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_callback_set_handler(
|
||||
sig: *const CallbackOpaque,
|
||||
binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void),
|
||||
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)>,
|
||||
) {
|
||||
|
@ -129,11 +133,9 @@ pub(crate) mod ffi {
|
|||
}
|
||||
}
|
||||
let ud = UserData { user_data, drop_user_data };
|
||||
|
||||
let real_binding = move |arg: &()| {
|
||||
binding(ud.user_data, arg as *const c_void);
|
||||
};
|
||||
sig.set_handler(real_binding);
|
||||
sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| {
|
||||
binding(ud.user_data, a as *const c_void, r as *mut c_void)
|
||||
})));
|
||||
}
|
||||
|
||||
/// Destroy callback
|
||||
|
|
|
@ -23,6 +23,7 @@ macro_rules! declare_ValueType {
|
|||
};
|
||||
}
|
||||
declare_ValueType![
|
||||
(),
|
||||
bool,
|
||||
u32,
|
||||
u64,
|
||||
|
@ -228,12 +229,14 @@ pub trait CallbackInfo<Item, Value> {
|
|||
) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
impl<Item, Value: Default + 'static> CallbackInfo<Item, Value>
|
||||
for FieldOffset<Item, crate::Callback<()>>
|
||||
impl<Item, Value: Default + 'static, Ret: Default> CallbackInfo<Item, Value>
|
||||
for FieldOffset<Item, crate::Callback<(), Ret>>
|
||||
where
|
||||
Value: TryInto<Ret>,
|
||||
Ret: TryInto<Value>,
|
||||
{
|
||||
fn call(&self, item: Pin<&Item>, _args: &[Value]) -> Result<Value, ()> {
|
||||
self.apply_pin(item).call(&());
|
||||
Ok(Default::default())
|
||||
self.apply_pin(item).call(&()).try_into().map_err(|_| ())
|
||||
}
|
||||
|
||||
fn set_handler(
|
||||
|
@ -241,24 +244,23 @@ impl<Item, Value: Default + 'static> CallbackInfo<Item, Value>
|
|||
item: Pin<&Item>,
|
||||
handler: Box<dyn Fn(&[Value]) -> Value>,
|
||||
) -> Result<(), ()> {
|
||||
self.apply_pin(item).set_handler(move |()| {
|
||||
handler(&[]);
|
||||
});
|
||||
self.apply_pin(item).set_handler(move |()| handler(&[]).try_into().ok().unwrap());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item, Value: Clone + Default + 'static, T: Clone> CallbackInfo<Item, Value>
|
||||
for FieldOffset<Item, crate::Callback<(T,)>>
|
||||
impl<Item, Value: Clone + Default + 'static, T: Clone, Ret: Default> CallbackInfo<Item, Value>
|
||||
for FieldOffset<Item, crate::Callback<(T,), Ret>>
|
||||
where
|
||||
Value: TryInto<T>,
|
||||
T: TryInto<Value>,
|
||||
Value: TryInto<Ret>,
|
||||
Ret: TryInto<Value>,
|
||||
{
|
||||
fn call(&self, item: Pin<&Item>, args: &[Value]) -> Result<Value, ()> {
|
||||
let value = args.first().ok_or(())?;
|
||||
let value = value.clone().try_into().map_err(|_| ())?;
|
||||
self.apply_pin(item).call(&(value,));
|
||||
Ok(Value::default())
|
||||
self.apply_pin(item).call(&(value,)).try_into().map_err(|_| ())
|
||||
}
|
||||
|
||||
fn set_handler(
|
||||
|
@ -268,8 +270,7 @@ where
|
|||
) -> Result<(), ()> {
|
||||
self.apply_pin(item).set_handler(move |(val,)| {
|
||||
let val: Value = val.clone().try_into().ok().unwrap();
|
||||
handler(&[val]);
|
||||
Value::default();
|
||||
handler(&[val]).try_into().ok().unwrap()
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -80,12 +80,14 @@ pub fn sixtyfps_element(input: TokenStream) -> TokenStream {
|
|||
|
||||
let mut callback_field_names = Vec::new();
|
||||
let mut callback_args = Vec::new();
|
||||
let mut callback_rets = Vec::new();
|
||||
for field in fields {
|
||||
if let Some(arg) = callback_arg(&field.ty) {
|
||||
if let Some((arg, ret)) = callback_arg(&field.ty) {
|
||||
if matches!(field.vis, syn::Visibility::Public(_)) {
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
callback_field_names.push(name);
|
||||
callback_args.push(arg);
|
||||
callback_rets.push(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +116,7 @@ pub fn sixtyfps_element(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
fn callbacks<Value: ValueType>() -> Vec<(&'static str, &'static dyn CallbackInfo<Self, Value>)> {
|
||||
vec![#( {
|
||||
const O : const_field_offset::FieldOffset<#item_name, Callback<#callback_args>, const_field_offset::AllowPin> =
|
||||
const O : const_field_offset::FieldOffset<#item_name, Callback<#callback_args, #callback_rets>, const_field_offset::AllowPin> =
|
||||
#item_name::FIELD_OFFSETS.#callback_field_names;
|
||||
(stringify!(#callback_field_names), &O as &'static dyn CallbackInfo<Self, Value>)
|
||||
} ),*]
|
||||
|
@ -154,8 +156,8 @@ fn property_type(ty: &syn::Type) -> Option<&syn::Type> {
|
|||
None
|
||||
}
|
||||
|
||||
// Try to match `Callback<Args>` on the syn tree and return Args if found
|
||||
fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> {
|
||||
// Try to match `Callback<Args, Ret>` on the syn tree and return Args and Ret if found
|
||||
fn callback_arg(ty: &syn::Type) -> Option<(&syn::Type, Option<&syn::Type>)> {
|
||||
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty {
|
||||
if let Some(syn::PathSegment {
|
||||
ident,
|
||||
|
@ -163,14 +165,19 @@ fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> {
|
|||
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
|
||||
}) = segments.first()
|
||||
{
|
||||
match args.first() {
|
||||
Some(syn::GenericArgument::Type(property_type))
|
||||
if ident.to_string() == "Callback" =>
|
||||
{
|
||||
return Some(property_type)
|
||||
}
|
||||
_ => {}
|
||||
if ident != "Callback" {
|
||||
return None;
|
||||
}
|
||||
let mut it = args.iter();
|
||||
let first = match it.next() {
|
||||
Some(syn::GenericArgument::Type(ty)) => ty,
|
||||
_ => return None,
|
||||
};
|
||||
let sec = match it.next() {
|
||||
Some(syn::GenericArgument::Type(ty)) => Some(ty),
|
||||
_ => None,
|
||||
};
|
||||
return Some((first, sec));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -264,6 +264,21 @@ impl TryInto<corelib::animations::Instant> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<()> for Value {
|
||||
type Error = ();
|
||||
#[inline]
|
||||
fn try_from(_: ()) -> Result<Self, ()> {
|
||||
Ok(Value::Void)
|
||||
}
|
||||
}
|
||||
impl TryInto<()> for Value {
|
||||
type Error = ();
|
||||
#[inline]
|
||||
fn try_into(self) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum ComponentInstance<'a, 'id> {
|
||||
InstanceRef(InstanceRef<'a, 'id>),
|
||||
|
|
|
@ -33,8 +33,9 @@ fn default_config() -> cbindgen::Config {
|
|||
documentation: true,
|
||||
export: cbindgen::ExportConfig {
|
||||
rename: [
|
||||
("VoidArg".into(), "void()".into()),
|
||||
("KeyEventArg".into(), "void(KeyEvent)".into()),
|
||||
("Callback".into(), "CallbackHelper".into()),
|
||||
("VoidArg".into(), "void".into()),
|
||||
("KeyEventArg".into(), "KeyEvent".into()),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue