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