mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
More work on signal returning value
This commit is contained in:
parent
276e11a101
commit
e73bbbcd10
8 changed files with 107 additions and 39 deletions
|
@ -13,8 +13,46 @@ LICENSE END */
|
||||||
|
|
||||||
namespace sixtyfps {
|
namespace sixtyfps {
|
||||||
|
|
||||||
|
template<typename = void()> struct Signal;
|
||||||
|
template<typename Ret, typename... Arg>
|
||||||
|
struct Signal<Ret(Arg...)>
|
||||||
|
{
|
||||||
|
Signal() { cbindgen_private::sixtyfps_signal_init(&inner); }
|
||||||
|
~Signal() { cbindgen_private::sixtyfps_signal_drop(&inner); }
|
||||||
|
Signal(const Signal &) = delete;
|
||||||
|
Signal(Signal &&) = delete;
|
||||||
|
Signal &operator=(const Signal &) = delete;
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void set_handler(F binding) const
|
||||||
|
{
|
||||||
|
cbindgen_private::sixtyfps_signal_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);
|
||||||
|
},
|
||||||
|
new F(std::move(binding)),
|
||||||
|
[](void *user_data) { delete reinterpret_cast<F *>(user_data); });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ret emit(const Arg &...arg) const
|
||||||
|
{
|
||||||
|
Ret r{};
|
||||||
|
Pair p = std::pair{ &r, Tuple{arg...} };
|
||||||
|
cbindgen_private::sixtyfps_signal_emit(&inner, &p);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Tuple = std::tuple<Arg...>;
|
||||||
|
using Pair = std::pair<Ret *, Tuple>;
|
||||||
|
cbindgen_private::SignalOpaque inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename... Arg>
|
template<typename... Arg>
|
||||||
struct Signal
|
struct Signal<void(Arg...)>
|
||||||
{
|
{
|
||||||
Signal() { cbindgen_private::sixtyfps_signal_init(&inner); }
|
Signal() { cbindgen_private::sixtyfps_signal_init(&inner); }
|
||||||
~Signal() { cbindgen_private::sixtyfps_signal_drop(&inner); }
|
~Signal() { cbindgen_private::sixtyfps_signal_drop(&inner); }
|
||||||
|
@ -45,4 +83,8 @@ private:
|
||||||
using Tuple = std::tuple<Arg...>;
|
using Tuple = std::tuple<Arg...>;
|
||||||
cbindgen_private::SignalOpaque inner;
|
cbindgen_private::SignalOpaque inner;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -86,21 +86,31 @@ fn make_signal_handler<'cx>(
|
||||||
cx: &mut impl Context<'cx>,
|
cx: &mut impl Context<'cx>,
|
||||||
persistent_context: &persistent_context::PersistentContext<'cx>,
|
persistent_context: &persistent_context::PersistentContext<'cx>,
|
||||||
fun: Handle<'cx, JsFunction>,
|
fun: Handle<'cx, JsFunction>,
|
||||||
) -> Box<dyn Fn(&[sixtyfps_interpreter::Value])> {
|
return_type: Option<Box<Type>>,
|
||||||
|
) -> Box<dyn Fn(&[sixtyfps_interpreter::Value]) -> sixtyfps_interpreter::Value> {
|
||||||
let fun_value = fun.as_value(cx);
|
let fun_value = fun.as_value(cx);
|
||||||
let fun_idx = persistent_context.allocate(cx, fun_value);
|
let fun_idx = persistent_context.allocate(cx, fun_value);
|
||||||
Box::new(move |args| {
|
Box::new(move |args| {
|
||||||
let args = args.iter().cloned().collect::<Vec<_>>();
|
let args = args.iter().cloned().collect::<Vec<_>>();
|
||||||
|
let ret = core::cell::Cell::new(sixtyfps_interpreter::Value::Void);
|
||||||
|
let borrow_ret = &ret;
|
||||||
|
let return_type = &return_type;
|
||||||
run_with_global_contect(&move |cx, persistent_context| {
|
run_with_global_contect(&move |cx, persistent_context| {
|
||||||
let args = args.iter().map(|a| to_js_value(a.clone(), cx).unwrap()).collect::<Vec<_>>();
|
let args = args.iter().map(|a| to_js_value(a.clone(), cx).unwrap()).collect::<Vec<_>>();
|
||||||
persistent_context
|
let ret = persistent_context
|
||||||
.get(cx, fun_idx)
|
.get(cx, fun_idx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.downcast::<JsFunction>()
|
.downcast::<JsFunction>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.call::<_, _, JsValue, _>(cx, JsUndefined::new(), args)
|
.call::<_, _, JsValue, _>(cx, JsUndefined::new(), args)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
if let Some(return_type) = return_type {
|
||||||
|
borrow_ret.set(
|
||||||
|
to_eval_value(ret, (**return_type).clone(), cx, persistent_context).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ret.into_inner()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,13 +133,13 @@ fn create<'cx>(
|
||||||
cx.throw_error(format!("Property {} not found in the component", prop_name))
|
cx.throw_error(format!("Property {} not found in the component", prop_name))
|
||||||
})?
|
})?
|
||||||
.clone();
|
.clone();
|
||||||
if let Type::Signal { .. } = ty {
|
if let Type::Signal { return_type, .. } = ty {
|
||||||
let fun = value.downcast_or_throw::<JsFunction, _>(cx)?;
|
let fun = value.downcast_or_throw::<JsFunction, _>(cx)?;
|
||||||
component_type
|
component_type
|
||||||
.set_signal_handler(
|
.set_signal_handler(
|
||||||
component.borrow(),
|
component.borrow(),
|
||||||
prop_name.as_str(),
|
prop_name.as_str(),
|
||||||
make_signal_handler(cx, &persistent_context, fun),
|
make_signal_handler(cx, &persistent_context, fun, return_type),
|
||||||
)
|
)
|
||||||
.or_else(|_| cx.throw_error(format!("Cannot set signal")))?;
|
.or_else(|_| cx.throw_error(format!("Cannot set signal")))?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -414,7 +424,7 @@ declare_types! {
|
||||||
Ok(JsUndefined::new().as_value(&mut cx))
|
Ok(JsUndefined::new().as_value(&mut cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
method connect_signal(mut cx) {
|
method connect_signal(mut cx) {
|
||||||
let signal_name = cx.argument::<JsString>(0)?.value();
|
let signal_name = cx.argument::<JsString>(0)?.value();
|
||||||
let handler = cx.argument::<JsFunction>(1)?;
|
let handler = cx.argument::<JsFunction>(1)?;
|
||||||
let this = cx.this();
|
let this = cx.this();
|
||||||
|
@ -425,12 +435,24 @@ declare_types! {
|
||||||
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
|
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
|
||||||
generativity::make_guard!(guard);
|
generativity::make_guard!(guard);
|
||||||
let component = component.unerase(guard);
|
let component = component.unerase(guard);
|
||||||
component.description().set_signal_handler(
|
|
||||||
component.borrow(),
|
let ty = component.description().properties().get(&signal_name)
|
||||||
signal_name.as_str(),
|
.ok_or(())
|
||||||
make_signal_handler(&mut cx, &persistent_context, handler)
|
.or_else(|()| {
|
||||||
).or_else(|_| cx.throw_error(format!("Cannot set signal")))?;
|
cx.throw_error(format!("Signal {} not found in the component", signal_name))
|
||||||
Ok(JsUndefined::new().as_value(&mut cx))
|
})?
|
||||||
|
.clone();
|
||||||
|
if let Type::Signal {return_type, ..} = ty {
|
||||||
|
component.description().set_signal_handler(
|
||||||
|
component.borrow(),
|
||||||
|
signal_name.as_str(),
|
||||||
|
make_signal_handler(&mut cx, &persistent_context, handler, return_type)
|
||||||
|
).or_else(|_| cx.throw_error(format!("Cannot set signal")))?;
|
||||||
|
Ok(JsUndefined::new().as_value(&mut cx))
|
||||||
|
} else {
|
||||||
|
cx.throw_error(format!("{} is not a signal", signal_name))?;
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
method send_mouse_click(mut cx) {
|
method send_mouse_click(mut cx) {
|
||||||
|
|
|
@ -658,6 +658,9 @@ fn generate_component(
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| get_cpp_type(t, &property_decl.type_node, diag))
|
.map(|t| get_cpp_type(t, &property_decl.type_node, diag))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let return_type = return_type
|
||||||
|
.as_ref()
|
||||||
|
.map_or("void".into(), |t| get_cpp_type(&t, &property_decl.type_node, diag));
|
||||||
if property_decl.expose_in_public_api && is_root {
|
if property_decl.expose_in_public_api && is_root {
|
||||||
let signal_emitter = vec![format!(
|
let signal_emitter = vec![format!(
|
||||||
"return {}.emit({});",
|
"return {}.emit({});",
|
||||||
|
@ -669,17 +672,13 @@ fn generate_component(
|
||||||
Declaration::Function(Function {
|
Declaration::Function(Function {
|
||||||
name: format!("emit_{}", cpp_name),
|
name: format!("emit_{}", cpp_name),
|
||||||
signature: format!(
|
signature: format!(
|
||||||
"({}) -> {} const",
|
"({}) const -> {}",
|
||||||
param_types
|
param_types
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, ty)| format!("{} arg_{}", ty, i))
|
.map(|(i, ty)| format!("{} arg_{}", ty, i))
|
||||||
.join(", "),
|
.join(", "),
|
||||||
return_type.as_ref().map_or("void".into(), |t| get_cpp_type(
|
return_type
|
||||||
&t,
|
|
||||||
&property_decl.type_node,
|
|
||||||
diag
|
|
||||||
))
|
|
||||||
),
|
),
|
||||||
statements: Some(signal_emitter),
|
statements: Some(signal_emitter),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -699,7 +698,7 @@ fn generate_component(
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
format!("sixtyfps::Signal<{}>", param_types.join(", "))
|
format!("sixtyfps::Signal<{}({})>", return_type, param_types.join(", "))
|
||||||
} else {
|
} else {
|
||||||
let cpp_type =
|
let cpp_type =
|
||||||
get_cpp_type(&property_decl.property_type, &property_decl.type_node, diag);
|
get_cpp_type(&property_decl.property_type, &property_decl.type_node, diag);
|
||||||
|
|
|
@ -23,31 +23,34 @@ use core::cell::Cell;
|
||||||
/// The Arg represents the argument. It should always be a tuple
|
/// The Arg represents the argument. It should always be a tuple
|
||||||
///
|
///
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Signal<Arg: ?Sized> {
|
pub struct Signal<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)>>>,
|
handler: Cell<Option<Box<dyn Fn(&Arg) -> Ret>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Arg: ?Sized> Default for Signal<Arg> {
|
impl<Arg: ?Sized, Ret> Default for Signal<Arg, Ret> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { handler: Default::default() }
|
Self { handler: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Arg: ?Sized> Signal<Arg> {
|
impl<Arg: ?Sized, Ret: Default> Signal<Arg, Ret> {
|
||||||
/// Emit the signal with the given argument.
|
/// Emit the signal with the given argument.
|
||||||
pub fn emit(&self, a: &Arg) {
|
pub fn emit(&self, a: &Arg) -> Ret {
|
||||||
if let Some(h) = self.handler.take() {
|
if let Some(h) = self.handler.take() {
|
||||||
h(a);
|
let r = h(a);
|
||||||
assert!(self.handler.take().is_none(), "Signal Handler set while emitted");
|
assert!(self.handler.take().is_none(), "Signal Handler set while emitted");
|
||||||
self.handler.set(Some(h))
|
self.handler.set(Some(h));
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set an handler to be called when the signal is emited
|
/// Set an handler to be called when the signal is emited
|
||||||
///
|
///
|
||||||
/// There can only be one single handler per signal.
|
/// There can only be one single handler per signal.
|
||||||
pub fn set_handler(&self, f: impl Fn(&Arg) + '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(f)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use sixtyfps_corelib::model::Repeater;
|
||||||
use sixtyfps_corelib::properties::InterpolatedPropertyValue;
|
use sixtyfps_corelib::properties::InterpolatedPropertyValue;
|
||||||
use sixtyfps_corelib::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
|
use sixtyfps_corelib::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
|
||||||
use sixtyfps_corelib::slice::Slice;
|
use sixtyfps_corelib::slice::Slice;
|
||||||
use sixtyfps_corelib::{Color, Property, SharedString, Signal};
|
use sixtyfps_corelib::{Color, Property, SharedString};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::{pin::Pin, rc::Rc};
|
use std::{pin::Pin, rc::Rc};
|
||||||
|
|
||||||
|
@ -268,6 +268,8 @@ impl<'id> ErasedRepeaterWithinComponent<'id> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Signal = sixtyfps_corelib::Signal<[eval::Value], eval::Value>;
|
||||||
|
|
||||||
/// ComponentDescription is a representation of a component suitable for interpretation
|
/// ComponentDescription is a representation of a component suitable for interpretation
|
||||||
///
|
///
|
||||||
/// It contains information about how to create and destroy the Component.
|
/// It contains information about how to create and destroy the Component.
|
||||||
|
@ -282,7 +284,7 @@ pub struct ComponentDescription<'id> {
|
||||||
item_tree: Vec<ItemTreeNode<crate::dynamic_type::Instance<'id>>>,
|
item_tree: Vec<ItemTreeNode<crate::dynamic_type::Instance<'id>>>,
|
||||||
pub(crate) items: HashMap<String, ItemWithinComponent>,
|
pub(crate) items: HashMap<String, ItemWithinComponent>,
|
||||||
pub(crate) custom_properties: HashMap<String, PropertiesWithinComponent>,
|
pub(crate) custom_properties: HashMap<String, PropertiesWithinComponent>,
|
||||||
pub(crate) custom_signals: HashMap<String, FieldOffset<Instance<'id>, Signal<[eval::Value]>>>,
|
pub(crate) custom_signals: HashMap<String, FieldOffset<Instance<'id>, Signal>>,
|
||||||
repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
|
repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
|
||||||
/// Map the Element::id of the repeater to the index in the `repeater` vec
|
/// Map the Element::id of the repeater to the index in the `repeater` vec
|
||||||
pub repeater_names: HashMap<String, usize>,
|
pub repeater_names: HashMap<String, usize>,
|
||||||
|
@ -612,8 +614,7 @@ fn generate_component<'id>(
|
||||||
Type::Resource => property_info::<Resource>(),
|
Type::Resource => property_info::<Resource>(),
|
||||||
Type::Bool => property_info::<bool>(),
|
Type::Bool => property_info::<bool>(),
|
||||||
Type::Signal { .. } => {
|
Type::Signal { .. } => {
|
||||||
custom_signals
|
custom_signals.insert(name.clone(), builder.add_field_type::<Signal>());
|
||||||
.insert(name.clone(), builder.add_field_type::<Signal<[eval::Value]>>());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Type::Object { name: Some(name), .. } if name.ends_with("::StateInfo") => {
|
Type::Object { name: Some(name), .. } if name.ends_with("::StateInfo") => {
|
||||||
|
@ -820,7 +821,8 @@ pub fn instantiate<'id>(
|
||||||
if let Some(signal_offset) =
|
if let Some(signal_offset) =
|
||||||
item_within_component.rtti.signals.get(prop.as_str())
|
item_within_component.rtti.signals.get(prop.as_str())
|
||||||
{
|
{
|
||||||
let signal = &*(item.as_ptr().add(*signal_offset) as *const Signal<()>);
|
let signal = &*(item.as_ptr().add(*signal_offset)
|
||||||
|
as *const sixtyfps_corelib::Signal<()>);
|
||||||
signal.set_handler(move |_: &()| {
|
signal.set_handler(move |_: &()| {
|
||||||
generativity::make_guard!(guard);
|
generativity::make_guard!(guard);
|
||||||
eval::eval_expression(
|
eval::eval_expression(
|
||||||
|
@ -840,7 +842,7 @@ pub fn instantiate<'id>(
|
||||||
InstanceRef::from_pin_ref(c, guard),
|
InstanceRef::from_pin_ref(c, guard),
|
||||||
args.iter().cloned().collect(),
|
args.iter().cloned().collect(),
|
||||||
);
|
);
|
||||||
eval::eval_expression(&expr, &mut local_context);
|
eval::eval_expression(&expr, &mut local_context)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
panic!("unkown signal {}", prop)
|
panic!("unkown signal {}", prop)
|
||||||
|
|
|
@ -349,6 +349,7 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
|
||||||
let signal =
|
let signal =
|
||||||
unsafe { &*(item.as_ptr().add(*signal_offset) as *const Signal<()>) };
|
unsafe { &*(item.as_ptr().add(*signal_offset) as *const Signal<()>) };
|
||||||
signal.emit(&());
|
signal.emit(&());
|
||||||
|
Value::Void
|
||||||
} else if let Some(signal_offset) = component_type.custom_signals.get(name.as_str())
|
} else if let Some(signal_offset) = component_type.custom_signals.get(name.as_str())
|
||||||
{
|
{
|
||||||
let signal = signal_offset.apply(&*enclosing_component.instance);
|
let signal = signal_offset.apply(&*enclosing_component.instance);
|
||||||
|
@ -360,10 +361,9 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
|
||||||
}
|
}
|
||||||
ComponentInstance::GlobalComponent(global) => {
|
ComponentInstance::GlobalComponent(global) => {
|
||||||
let args = arguments.iter().map(|e| eval_expression(e, local_context));
|
let args = arguments.iter().map(|e| eval_expression(e, local_context));
|
||||||
global.as_ref().emit_signal(name.as_ref(), args.collect::<Vec<_>>().as_slice());
|
global.as_ref().emit_signal(name.as_ref(), args.collect::<Vec<_>>().as_slice())
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
Value::Void
|
|
||||||
}
|
}
|
||||||
Expression::BuiltinFunctionReference(BuiltinFunction::GetWindowScaleFactor) => {
|
Expression::BuiltinFunctionReference(BuiltinFunction::GetWindowScaleFactor) => {
|
||||||
match local_context.component_instance {
|
match local_context.component_instance {
|
||||||
|
|
|
@ -18,7 +18,7 @@ use sixtyfps_corelib::{rtti, Property, Signal};
|
||||||
use crate::eval;
|
use crate::eval;
|
||||||
|
|
||||||
pub trait GlobalComponent {
|
pub trait GlobalComponent {
|
||||||
fn emit_signal(self: Pin<&Self>, _signal_name: &str, _args: &[eval::Value]) {
|
fn emit_signal(self: Pin<&Self>, _signal_name: &str, _args: &[eval::Value]) -> eval::Value {
|
||||||
todo!("emit signal")
|
todo!("emit signal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ impl<'id> dynamic_component::ComponentDescription<'id> {
|
||||||
&self,
|
&self,
|
||||||
component: Pin<ComponentRef>,
|
component: Pin<ComponentRef>,
|
||||||
name: &str,
|
name: &str,
|
||||||
handler: Box<dyn Fn(&[Value])>,
|
handler: Box<dyn Fn(&[Value]) -> Value>,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
|
if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
|
||||||
return Err(());
|
return Err(());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue