Get rid of the context in properties/signal

This commit is contained in:
Olivier Goffart 2020-07-13 18:49:06 +02:00
parent ab7ae9f3e2
commit e00491811b
25 changed files with 389 additions and 653 deletions

View file

@ -46,7 +46,6 @@ private:
internal::ComponentWindowOpaque inner; internal::ComponentWindowOpaque inner;
}; };
using internal::EvaluationContext;
using internal::Image; using internal::Image;
using internal::Path; using internal::Path;
using internal::Rectangle; using internal::Rectangle;
@ -69,14 +68,6 @@ constexpr inline ItemTreeNode<uint8_t> make_dyn_node(std::uintptr_t offset)
using internal::sixtyfps_visit_item_tree; using internal::sixtyfps_visit_item_tree;
template<typename Component>
EvaluationContext evaluation_context_for_root_component(const Component *component) {
return EvaluationContext{
VRef<ComponentVTable> { &Component::component_type, const_cast<Component *>(component)},
nullptr,
};
}
// layouts: // layouts:
using internal::Constraint; using internal::Constraint;
using internal::GridLayoutCellData; using internal::GridLayoutCellData;

View file

@ -45,8 +45,8 @@ void Property<Color>::set_animated_binding(F binding,
{ {
internal::sixtyfps_property_set_animated_binding_color( internal::sixtyfps_property_set_animated_binding_color(
&inner, &inner,
[](void *user_data, const internal::EvaluationContext *context, Color *value) { [](void *user_data, Color *value) {
*reinterpret_cast<Color *>(value) = (*reinterpret_cast<F *>(user_data))(context); *reinterpret_cast<Color *>(value) = (*reinterpret_cast<F *>(user_data))();
}, },
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); }, new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data); &animation_data);

View file

@ -31,9 +31,9 @@ struct Property
internal::sixtyfps_property_set_changed(&inner); internal::sixtyfps_property_set_changed(&inner);
} }
const T &get(const internal::EvaluationContext *context) const const T &get() const
{ {
internal::sixtyfps_property_update(&inner, context, &value); internal::sixtyfps_property_update(&inner, &value);
return value; return value;
} }
@ -42,8 +42,8 @@ struct Property
{ {
internal::sixtyfps_property_set_binding( internal::sixtyfps_property_set_binding(
&inner, &inner,
[](void *user_data, const internal::EvaluationContext *context, void *value) { [](void *user_data, void *value) {
*reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))(context); *reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))();
}, },
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); }); new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); });
} }
@ -79,8 +79,8 @@ void Property<int32_t>::set_animated_binding(F binding,
{ {
internal::sixtyfps_property_set_animated_binding_int( internal::sixtyfps_property_set_animated_binding_int(
&inner, &inner,
[](void *user_data, const internal::EvaluationContext *context, int32_t *value) { [](void *user_data, int32_t *value) {
*reinterpret_cast<int32_t *>(value) = (*reinterpret_cast<F *>(user_data))(context); *reinterpret_cast<int32_t *>(value) = (*reinterpret_cast<F *>(user_data))();
}, },
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); }, new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data); &animation_data);
@ -93,8 +93,8 @@ void Property<float>::set_animated_binding(F binding,
{ {
internal::sixtyfps_property_set_animated_binding_float( internal::sixtyfps_property_set_animated_binding_float(
&inner, &inner,
[](void *user_data, const internal::EvaluationContext *context, float *value) { [](void *user_data, float *value) {
*reinterpret_cast<float *>(value) = (*reinterpret_cast<F *>(user_data))(context); *reinterpret_cast<float *>(value) = (*reinterpret_cast<F *>(user_data))();
}, },
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); }, new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data); &animation_data);

View file

@ -18,15 +18,15 @@ struct Signal
{ {
internal::sixtyfps_signal_set_handler( internal::sixtyfps_signal_set_handler(
&inner, &inner,
[](void *user_data, const internal::EvaluationContext *value) { [](void *user_data) {
(*reinterpret_cast<F *>(user_data))(value); (*reinterpret_cast<F *>(user_data))();
}, },
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); }); new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); });
} }
void emit(const internal::EvaluationContext *context) const void emit() const
{ {
internal::sixtyfps_signal_emit(&inner, context); internal::sixtyfps_signal_emit(&inner);
} }
private: private:

View file

@ -2,7 +2,7 @@ use core::cell::RefCell;
use neon::prelude::*; use neon::prelude::*;
use sixtyfps_compilerlib::typeregister::Type; use sixtyfps_compilerlib::typeregister::Type;
use sixtyfps_corelib::abi::datastructures::Resource; use sixtyfps_corelib::abi::datastructures::Resource;
use sixtyfps_corelib::{ComponentRefPin, EvaluationContext}; use sixtyfps_corelib::ComponentRefPin;
use std::rc::Rc; use std::rc::Rc;
@ -65,7 +65,7 @@ fn create<'cx>(
.set_signal_handler( .set_signal_handler(
component.borrow_mut(), component.borrow_mut(),
prop_name.as_str(), prop_name.as_str(),
Box::new(move |_eval_ctx, ()| { Box::new(move |()| {
GLOBAL_CONTEXT.with(|cx_fn| { GLOBAL_CONTEXT.with(|cx_fn| {
cx_fn(&move |cx, presistent_context| { cx_fn(&move |cx, presistent_context| {
presistent_context presistent_context
@ -243,7 +243,7 @@ declare_types! {
let x = this.borrow(&lock).0.clone(); let x = this.borrow(&lock).0.clone();
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
let value = component.description() let value = component.description()
.get_property(&EvaluationContext::for_root_component(component.borrow()), prop_name.as_str()) .get_property(component.borrow(), prop_name.as_str())
.or_else(|_| cx.throw_error(format!("Cannot read property")))?; .or_else(|_| cx.throw_error(format!("Cannot read property")))?;
to_js_value(value, &mut cx) to_js_value(value, &mut cx)
} }
@ -275,7 +275,7 @@ declare_types! {
let x = this.borrow(&lock).0.clone(); let x = this.borrow(&lock).0.clone();
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
component.description() component.description()
.emit_signal(&EvaluationContext::for_root_component(component.borrow()), signal_name.as_str()) .emit_signal(component.borrow(), signal_name.as_str())
.or_else(|_| cx.throw_error(format!("Cannot emit signal")))?; .or_else(|_| cx.throw_error(format!("Cannot emit signal")))?;
Ok(JsUndefined::new().as_value(&mut cx)) Ok(JsUndefined::new().as_value(&mut cx))

View file

@ -86,7 +86,6 @@ pub mod re_exports {
}; };
pub use sixtyfps_corelib::Color; pub use sixtyfps_corelib::Color;
pub use sixtyfps_corelib::ComponentVTable_static; pub use sixtyfps_corelib::ComponentVTable_static;
pub use sixtyfps_corelib::EvaluationContext;
pub use sixtyfps_corelib::Resource; pub use sixtyfps_corelib::Resource;
pub use sixtyfps_corelib::SharedArray; pub use sixtyfps_corelib::SharedArray;
pub use sixtyfps_corelib::SharedString; pub use sixtyfps_corelib::SharedString;

View file

@ -7,14 +7,14 @@ int main()
component.foobar.set_handler([](auto...) { std::cout << "Hello from C++" << std::endl; }); component.foobar.set_handler([](auto...) { std::cout << "Hello from C++" << std::endl; });
component.plus_clicked.set_handler([](auto ctx) { component.plus_clicked.set_handler([]() {
auto &counter = component.counter; auto &counter = component.counter;
counter.set(counter.get(ctx) + 1); counter.set(counter.get() + 1);
}); });
component.minus_clicked.set_handler([](auto ctx) { component.minus_clicked.set_handler([]() {
auto &counter = component.counter; auto &counter = component.counter;
counter.set(counter.get(ctx) - 1); counter.set(counter.get() - 1);
}); });
sixtyfps::ComponentWindow window; sixtyfps::ComponentWindow window;

View file

@ -128,15 +128,17 @@ Hello := Rectangle {
fn main() { fn main() {
let app = Hello::new(); let app = Hello::new();
app.plus_clicked.set_handler(|context, ()| { let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
let app = context.get_component::<Hello>().unwrap(); app.plus_clicked.set_handler(move |()| {
let counter = Hello::field_offsets().counter.apply_pin(app); let app = app_weak.upgrade().unwrap();
counter.set(counter.get(context) + 1); let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() + 1);
}); });
app.minus_clicked.set_handler(|context, ()| { let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
let app = context.get_component::<Hello>().unwrap(); app.minus_clicked.set_handler(move |()| {
let counter = Hello::field_offsets().counter.apply_pin(app); let app = app_weak.upgrade().unwrap();
counter.set(counter.get(context) - 1); let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() - 1);
}); });
app.run(); app.run();
} }

View file

@ -4,16 +4,17 @@ sixtyfps::include_modules!();
fn main() { fn main() {
let app = Hello::new(); let app = Hello::new();
let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
app.plus_clicked.set_handler(|context, ()| { app.plus_clicked.set_handler(move |()| {
let app = context.get_component::<Hello>().unwrap(); let app = app_weak.upgrade().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app); let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get(context) + 1); counter.set(counter.get() + 1);
}); });
app.minus_clicked.set_handler(|context, ()| { let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
let app = context.get_component::<Hello>().unwrap(); app.minus_clicked.set_handler(move |()| {
let counter = Hello::field_offsets().counter.apply_pin(app); let app = app_weak.upgrade().unwrap();
counter.set(counter.get(context) - 1); let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() - 1);
}); });
app.run(); app.run();
} }

View file

@ -247,13 +247,12 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
format!( format!(
"{signal_accessor_prefix}{prop}.set_handler( "{signal_accessor_prefix}{prop}.set_handler(
[]([[maybe_unused]] const sixtyfps::EvaluationContext *context) {{ [this]() {{
[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(context->component.instance); [[maybe_unused]] auto self = this;
{code}; {code};
}});", }});",
signal_accessor_prefix = signal_accessor_prefix, signal_accessor_prefix = signal_accessor_prefix,
prop = s, prop = s,
ty = main_struct.name,
code = compile_expression(i, &item.enclosing_component.upgrade().unwrap()) code = compile_expression(i, &item.enclosing_component.upgrade().unwrap())
) )
} else { } else {
@ -267,7 +266,7 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
let init = compile_expression(i, component); let init = compile_expression(i, component);
if i.is_constant() { if i.is_constant() {
let setter = property_set_value_code(&component, item, s, init); let setter = property_set_value_code(&component, item, s, init);
format!( format!(
"{accessor_prefix}{cpp_prop}.{setter};", "{accessor_prefix}{cpp_prop}.{setter};",
accessor_prefix = accessor_prefix, accessor_prefix = accessor_prefix,
@ -276,11 +275,10 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
) )
} else { } else {
let binding_code = format!( let binding_code = format!(
"[]([[maybe_unused]] const sixtyfps::EvaluationContext *context) {{ "[this]() {{
[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(context->component.instance); [[maybe_unused]] auto self = this;
return {init}; return {init};
}}", }}",
ty = main_struct.name,
init = init init = init
); );
@ -385,10 +383,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
for (cpp_name, property_decl) in component.root_element.borrow().property_declarations.iter() { for (cpp_name, property_decl) in component.root_element.borrow().property_declarations.iter() {
let ty = if property_decl.property_type == Type::Signal { let ty = if property_decl.property_type == Type::Signal {
if property_decl.expose_in_public_api && is_root { if property_decl.expose_in_public_api && is_root {
let signal_emitter: Vec<String> = vec![ let signal_emitter: Vec<String> = vec![format!("{}.emit();", cpp_name)];
"[[maybe_unused]] auto context = sixtyfps::evaluation_context_for_root_component(this);".into(),
format!("{}.emit(&context);", cpp_name)
];
component_struct.members.push(Declaration::Function(Function { component_struct.members.push(Declaration::Function(Function {
name: format!("emit_{}", cpp_name), name: format!("emit_{}", cpp_name),
@ -411,10 +406,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
}); });
if property_decl.expose_in_public_api && is_root { if property_decl.expose_in_public_api && is_root {
let prop_getter: Vec<String> = vec![ let prop_getter: Vec<String> = vec![format!("return {}.get();", cpp_name)];
"[[maybe_unused]] auto context = sixtyfps::evaluation_context_for_root_component(this);".into(),
format!("return {}.get(&context);", cpp_name)
];
component_struct.members.push(Declaration::Function(Function { component_struct.members.push(Declaration::Function(Function {
name: format!("get_{}", cpp_name), name: format!("get_{}", cpp_name),
@ -567,9 +559,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
component_struct.members.push(Declaration::Function(Function { component_struct.members.push(Declaration::Function(Function {
name: "compute_layout".into(), name: "compute_layout".into(),
signature: signature: "(sixtyfps::ComponentRef component) -> void".into(),
"(sixtyfps::ComponentRef component, [[maybe_unused]] const sixtyfps::EvaluationContext *context) -> void"
.into(),
is_static: true, is_static: true,
statements: Some(compute_layout(component)), statements: Some(compute_layout(component)),
..Default::default() ..Default::default()
@ -601,8 +591,6 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
"static const auto dyn_visit = [] (const uint8_t *base, [[maybe_unused]] sixtyfps::ItemVisitorRefMut visitor, uintptr_t dyn_index) {".to_owned(), "static const auto dyn_visit = [] (const uint8_t *base, [[maybe_unused]] sixtyfps::ItemVisitorRefMut visitor, uintptr_t dyn_index) {".to_owned(),
format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", component_id), format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", component_id),
// Fixme: this is not the root component // Fixme: this is not the root component
"auto context_ = sixtyfps::evaluation_context_for_root_component(self);".into(),
"[[maybe_unused]] auto context = &context_;".into(),
format!(" switch(dyn_index) {{ {} }}\n }};", children_visitor_case.join("")), format!(" switch(dyn_index) {{ {} }}\n }};", children_visitor_case.join("")),
"return sixtyfps::sixtyfps_visit_item_tree(component, { const_cast<sixtyfps::ItemTreeNode<uint8_t>*>(children), std::size(children)}, index, visitor, dyn_visit);".to_owned(), "return sixtyfps::sixtyfps_visit_item_tree(component, { const_cast<sixtyfps::ItemTreeNode<uint8_t>*>(children), std::size(children)}, index, visitor, dyn_visit);".to_owned(),
]), ]),
@ -627,28 +615,27 @@ fn component_id(component: &Rc<Component>) -> String {
} }
} }
/// Returns the code that can access the given property or signal from the context (but without the set or get) /// Returns the code that can access the given property (but without the set or get)
/// ///
/// to be used like: /// to be used like:
/// ```ignore /// ```ignore
/// let (access, context) = access_member(...) /// let access = access_member(...);
/// format!("context.{access}.get({context})", ...) /// format!("{}.get()", access)
/// ``` /// ```
fn access_member( fn access_member(
element: &ElementRc, element: &ElementRc,
name: &str, name: &str,
component: &Rc<Component>, component: &Rc<Component>,
context: &str,
component_cpp: &str, component_cpp: &str,
) -> (String, String) { ) -> String {
let e = element.borrow(); let e = element.borrow();
let enclosing_component = e.enclosing_component.upgrade().unwrap(); let enclosing_component = e.enclosing_component.upgrade().unwrap();
if Rc::ptr_eq(component, &enclosing_component) { if Rc::ptr_eq(component, &enclosing_component) {
let e = element.borrow(); let e = element.borrow();
if e.property_declarations.contains_key(name) { if e.property_declarations.contains_key(name) {
(format!("{}->{}", component_cpp, name), context.into()) format!("{}->{}", component_cpp, name)
} else { } else {
(format!("{}->{}.{}", component_cpp, e.id.as_str(), name), context.into()) format!("{}->{}.{}", component_cpp, e.id.as_str(), name)
} }
} else { } else {
access_member( access_member(
@ -662,7 +649,6 @@ fn access_member(
.enclosing_component .enclosing_component
.upgrade() .upgrade()
.unwrap(), .unwrap(),
&format!("{}->parent_context", context),
&format!("{}->parent", component_cpp), &format!("{}->parent", component_cpp),
) )
} }
@ -676,35 +662,25 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
NumberLiteral(n) => n.to_string(), NumberLiteral(n) => n.to_string(),
BoolLiteral(b) => b.to_string(), BoolLiteral(b) => b.to_string(),
PropertyReference(NamedReference { element, name }) => { PropertyReference(NamedReference { element, name }) => {
let (access, context) = access_member( let access =
&element.upgrade().unwrap(), access_member(&element.upgrade().unwrap(), name.as_str(), component, "self");
name.as_str(), format!(r#"{}.get()"#, access)
component,
"context",
"self",
);
format!(r#"{}.get({})"#, access, context)
} }
SignalReference(NamedReference { element, name }) => { SignalReference(NamedReference { element, name }) => {
let (access, context) = access_member( let access =
&element.upgrade().unwrap(), access_member(&element.upgrade().unwrap(), name.as_str(), component, "self");
name.as_str(), format!(r#"{}.emit()"#, access)
component,
"context",
"self",
);
format!(r#"{}.emit({})"#, access, context)
} }
RepeaterIndexReference { element } => { RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) { if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
"self->index.get(context)".to_owned() "self->index.get()".to_owned()
} else { } else {
todo!(); todo!();
} }
} }
RepeaterModelReference { element } => { RepeaterModelReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) { if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
"self->model_data.get(context)".to_owned() "self->model_data.get()".to_owned()
} else { } else {
todo!(); todo!();
} }
@ -748,19 +724,13 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
} }
SelfAssignment { lhs, rhs, op } => match &**lhs { SelfAssignment { lhs, rhs, op } => match &**lhs {
PropertyReference(NamedReference { element, name }) => { PropertyReference(NamedReference { element, name }) => {
let (access, context) = access_member( let access =
&element.upgrade().unwrap(), access_member(&element.upgrade().unwrap(), name.as_str(), component, "self");
name.as_str(),
component,
"context",
"self",
);
format!( format!(
r#"{lhs}.set({lhs}.get({context}) {op} {rhs})"#, r#"{lhs}.set({lhs}.get() {op} {rhs})"#,
lhs = access, lhs = access,
rhs = compile_expression(&*rhs, component), rhs = compile_expression(&*rhs, component),
op = op, op = op,
context = context
) )
} }
_ => panic!("typechecking should make sure this was a PropertyReference"), _ => panic!("typechecking should make sure this was a PropertyReference"),
@ -894,8 +864,8 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
// FIXME: add auto conversion from std::array* to Slice // FIXME: add auto conversion from std::array* to Slice
res.push(" { row_constr.data(), row_constr.size() },".to_owned()); res.push(" { row_constr.data(), row_constr.size() },".to_owned());
res.push(" { col_constr.data(), col_constr.size() },".to_owned()); res.push(" { col_constr.data(), col_constr.size() },".to_owned());
res.push(format!(" self->{}.width.get(context),", grid.within.borrow().id)); res.push(format!(" self->{}.width.get(),", grid.within.borrow().id));
res.push(format!(" self->{}.height.get(context),", grid.within.borrow().id)); res.push(format!(" self->{}.height.get(),", grid.within.borrow().id));
res.push(" x, y,".to_owned()); res.push(" x, y,".to_owned());
res.push(" {cells, std::size(cells)}".to_owned()); res.push(" {cells, std::size(cells)}".to_owned());
res.push(" };".to_owned()); res.push(" };".to_owned());

View file

@ -52,10 +52,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
quote!( quote!(
#[allow(dead_code)] #[allow(dead_code)]
fn #emitter_ident(self: ::core::pin::Pin<&Self>) { fn #emitter_ident(self: ::core::pin::Pin<&Self>) {
let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component( Self::field_offsets().#prop_ident.apply_pin(self).emit(())
sixtyfps::re_exports::ComponentRef::new_pin(self)
);
Self::field_offsets().#prop_ident.apply_pin(self).emit(&eval_context, ())
} }
) )
.into(), .into(),
@ -79,10 +76,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
quote!( quote!(
#[allow(dead_code)] #[allow(dead_code)]
fn #getter_ident(self: ::core::pin::Pin<&Self>) -> #rust_property_type { fn #getter_ident(self: ::core::pin::Pin<&Self>) -> #rust_property_type {
let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component( Self::field_offsets().#prop_ident.apply_pin(self).get()
sixtyfps::re_exports::ComponentRef::new_pin(self)
);
Self::field_offsets().#prop_ident.apply_pin(self).get(&eval_context)
} }
) )
.into(), .into(),
@ -180,11 +174,6 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
if self_pinned.#model_name.is_dirty() { if self_pinned.#model_name.is_dirty() {
#component_id::field_offsets().#model_name.apply_pin(self_pinned).evaluate(|| { #component_id::field_offsets().#model_name.apply_pin(self_pinned).evaluate(|| {
let _self = self_pinned.clone(); let _self = self_pinned.clone();
// FIXME: this should not be the root_component (but that's fine as we no longer access the parent)
let context = sixtyfps::re_exports::EvaluationContext::for_root_component(
sixtyfps::re_exports::ComponentRef::new_pin(_self)
);
let context = &context;
self_pinned.#repeater_id.update_model(#model, || { self_pinned.#repeater_id.update_model(#model, || {
#rep_component_id::new(self_pinned.self_weak.get().unwrap().clone()) #rep_component_id::new(self_pinned.self_weak.get().unwrap().clone())
}); });
@ -226,9 +215,13 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
if matches!(item.lookup_property(k.as_str()), Type::Signal) { if matches!(item.lookup_property(k.as_str()), Type::Signal) {
init.push(quote!( init.push(quote!(
self_pinned.#rust_property.set_handler(|context, ()| { self_pinned.#rust_property.set_handler({
let _self = context.get_component::<#component_id>().unwrap(); let self_weak = sixtyfps::re_exports::WeakPin::downgrade(self_pinned.clone());
#tokens_for_expression; move |()| {
let self_pinned = self_weak.upgrade().unwrap();
let _self = self_pinned.as_ref();
#tokens_for_expression;
}
}); });
)); ));
} else { } else {
@ -246,7 +239,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
k, k,
quote!({ quote!({
let self_weak = sixtyfps::re_exports::WeakPin::downgrade(self_pinned.clone()); let self_weak = sixtyfps::re_exports::WeakPin::downgrade(self_pinned.clone());
move |context| { move || {
let self_pinned = self_weak.upgrade().unwrap(); let self_pinned = self_weak.upgrade().unwrap();
let _self = self_pinned.as_ref(); let _self = self_pinned.as_ref();
(#tokens_for_expression) as _ (#tokens_for_expression) as _
@ -450,20 +443,19 @@ fn property_set_binding_tokens(
} }
} }
/// Returns the code that can access the given property or signal from the context (but without the set or get) /// Returns the code that can access the given property or signal (but without the set or get)
/// ///
/// to be used like: /// to be used like:
/// ```ignore /// ```ignore
/// let (access, context) = access_member(...) /// let access = access_member(...)
/// quote!(#access.get(#context)) /// quote!(#access.get())
/// ``` /// ```
fn access_member( fn access_member(
element: &ElementRc, element: &ElementRc,
name: &str, name: &str,
component: &Rc<Component>, component: &Rc<Component>,
context: TokenStream,
component_rust: TokenStream, component_rust: TokenStream,
) -> (TokenStream, TokenStream) { ) -> TokenStream {
let e = element.borrow(); let e = element.borrow();
let enclosing_component = e.enclosing_component.upgrade().unwrap(); let enclosing_component = e.enclosing_component.upgrade().unwrap();
@ -471,15 +463,13 @@ fn access_member(
let component_id = component_id(&enclosing_component); let component_id = component_id(&enclosing_component);
let name_ident = quote::format_ident!("{}", name); let name_ident = quote::format_ident!("{}", name);
if e.property_declarations.contains_key(name) { if e.property_declarations.contains_key(name) {
(quote!(#component_id::field_offsets().#name_ident.apply_pin(#component_rust)), context) quote!(#component_id::field_offsets().#name_ident.apply_pin(#component_rust))
} else { } else {
let elem_ident = quote::format_ident!("{}", e.id); let elem_ident = quote::format_ident!("{}", e.id);
let elem_ty = quote::format_ident!("{}", e.base_type.as_builtin().class_name); let elem_ty = quote::format_ident!("{}", e.base_type.as_builtin().class_name);
(
quote!((#component_id::field_offsets().#elem_ident + #elem_ty::field_offsets().#name_ident) quote!((#component_id::field_offsets().#elem_ident + #elem_ty::field_offsets().#name_ident)
.apply_pin(#component_rust) .apply_pin(#component_rust)
),
context,
) )
} }
} else { } else {
@ -494,7 +484,6 @@ fn access_member(
.enclosing_component .enclosing_component
.upgrade() .upgrade()
.unwrap(), .unwrap(),
quote!((&sixtyfps::re_exports::EvaluationContext::for_root_component(#context.component))),
quote!(#component_rust.parent.upgrade().unwrap().as_ref()), quote!(#component_rust.parent.upgrade().unwrap().as_ref()),
) )
} }
@ -520,19 +509,14 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
} }
} }
Expression::PropertyReference(NamedReference { element, name }) => { Expression::PropertyReference(NamedReference { element, name }) => {
let (access, context) = access_member( let access =
&element.upgrade().unwrap(), access_member(&element.upgrade().unwrap(), name.as_str(), component, quote!(_self));
name.as_str(), quote!(#access.get())
component,
quote!(context),
quote!(_self),
);
quote!(#access.get(#context))
} }
Expression::RepeaterIndexReference { element } => { Expression::RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) { if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
let component_id = component_id(&component); let component_id = component_id(&component);
quote!({ #component_id::field_offsets().index.apply_pin(_self).get(context) }) quote!({ #component_id::field_offsets().index.apply_pin(_self).get() })
} else { } else {
todo!(); todo!();
} }
@ -540,7 +524,7 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
Expression::RepeaterModelReference { element } => { Expression::RepeaterModelReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) { if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
let component_id = component_id(&component); let component_id = component_id(&component);
quote!({ #component_id::field_offsets().model_data.apply_pin(_self).get(context) }) quote!({ #component_id::field_offsets().model_data.apply_pin(_self).get() })
} else { } else {
todo!(); todo!();
} }
@ -562,14 +546,9 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
quote!({ #(#map);* }) quote!({ #(#map);* })
} }
Expression::SignalReference(NamedReference { element, name, .. }) => { Expression::SignalReference(NamedReference { element, name, .. }) => {
let (access, context) = access_member( let access =
&element.upgrade().unwrap(), access_member(&element.upgrade().unwrap(), name.as_str(), component, quote!(_self));
name.as_str(), quote!(#access.emit(()))
component,
quote!(context),
quote!(_self),
);
quote!(#access.emit(#context, ()))
} }
Expression::FunctionCall { function } => { Expression::FunctionCall { function } => {
if matches!(function.ty(), Type::Signal) { if matches!(function.ty(), Type::Signal) {
@ -581,16 +560,15 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
} }
Expression::SelfAssignment { lhs, rhs, op } => match &**lhs { Expression::SelfAssignment { lhs, rhs, op } => match &**lhs {
Expression::PropertyReference(NamedReference { element, name }) => { Expression::PropertyReference(NamedReference { element, name }) => {
let (lhs, context) = access_member( let lhs = access_member(
&element.upgrade().unwrap(), &element.upgrade().unwrap(),
name.as_str(), name.as_str(),
component, component,
quote!(context),
quote!(_self), quote!(_self),
); );
let rhs = compile_expression(&*rhs, &component); let rhs = compile_expression(&*rhs, &component);
let op = proc_macro2::Punct::new(*op, proc_macro2::Spacing::Alone); let op = proc_macro2::Punct::new(*op, proc_macro2::Spacing::Alone);
quote!( #lhs.set(#lhs.get(#context) #op &((#rhs) as _) )) quote!( #lhs.set(#lhs.get() #op &((#rhs) as _) ))
} }
_ => panic!("typechecking should make sure this was a PropertyReference"), _ => panic!("typechecking should make sure this was a PropertyReference"),
}, },
@ -725,9 +703,9 @@ fn compute_layout(component: &Rc<Component>) -> TokenStream {
row_constraint: Slice::from_slice(&[#(#row_constraint),*]), row_constraint: Slice::from_slice(&[#(#row_constraint),*]),
col_constraint: Slice::from_slice(&[#(#col_constraint),*]), col_constraint: Slice::from_slice(&[#(#col_constraint),*]),
width: (Self::field_offsets().#within + #within_ty::field_offsets().width) width: (Self::field_offsets().#within + #within_ty::field_offsets().width)
.apply_pin(self).get(context), .apply_pin(self).get(),
height: (Self::field_offsets().#within + #within_ty::field_offsets().height) height: (Self::field_offsets().#within + #within_ty::field_offsets().height)
.apply_pin(self).get(context), .apply_pin(self).get(),
x: #x_pos, x: #x_pos,
y: #y_pos, y: #y_pos,
cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]), cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]),
@ -781,7 +759,7 @@ fn compute_layout(component: &Rc<Component>) -> TokenStream {
fn layout_info(self: ::core::pin::Pin<&Self>) -> sixtyfps::re_exports::LayoutInfo { fn layout_info(self: ::core::pin::Pin<&Self>) -> sixtyfps::re_exports::LayoutInfo {
todo!("Implement in rust.rs") todo!("Implement in rust.rs")
} }
fn compute_layout(self: ::core::pin::Pin<&Self>, context: &sixtyfps::re_exports::EvaluationContext) { fn compute_layout(self: ::core::pin::Pin<&Self>) {
#![allow(unused)] #![allow(unused)]
use sixtyfps::re_exports::*; use sixtyfps::re_exports::*;
let dummy = Property::<f32>::default(); let dummy = Property::<f32>::default();

View file

@ -1,7 +1,6 @@
//! This module contains the basic datastructures that are exposed to the C API //! This module contains the basic datastructures that are exposed to the C API
use super::slice::Slice; use super::slice::Slice;
use crate::EvaluationContext;
use core::pin::Pin; use core::pin::Pin;
use std::cell::Cell; use std::cell::Cell;
use vtable::*; use vtable::*;
@ -53,8 +52,7 @@ pub struct ComponentVTable {
pub layout_info: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>) -> LayoutInfo, pub layout_info: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>) -> LayoutInfo,
/// Will compute the layout of /// Will compute the layout of
pub compute_layout: pub compute_layout: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>),
extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, eval_context: &EvaluationContext),
} }
/// This structure must be present in items that are Rendered and contains information. /// This structure must be present in items that are Rendered and contains information.
@ -111,8 +109,7 @@ pub enum ItemTreeNode<T> {
#[repr(C)] #[repr(C)]
pub struct ItemVTable { pub struct ItemVTable {
/// Returns the geometry of this item (relative to its parent item) /// Returns the geometry of this item (relative to its parent item)
pub geometry: pub geometry: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> Rect,
extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, context: &crate::EvaluationContext) -> Rect,
/// offset in bytes fromthe *const ItemImpl. /// offset in bytes fromthe *const ItemImpl.
/// isize::MAX means None /// isize::MAX means None
@ -121,17 +118,13 @@ pub struct ItemVTable {
pub cached_rendering_data_offset: usize, pub cached_rendering_data_offset: usize,
/// Return the rendering primitive used to display this item. /// Return the rendering primitive used to display this item.
pub rendering_primitive: extern "C" fn( pub rendering_primitive: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> RenderingPrimitive,
core::pin::Pin<VRef<ItemVTable>>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive,
/// We would need max/min/preferred size, and all layout info /// We would need max/min/preferred size, and all layout info
pub layouting_info: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> LayoutInfo, pub layouting_info: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> LayoutInfo,
/// input event /// input event
pub input_event: pub input_event: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, MouseEvent),
extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, MouseEvent, &crate::EvaluationContext),
} }
/// The constraint that applies to an item /// The constraint that applies to an item

View file

@ -21,7 +21,7 @@ use super::datastructures::{
}; };
#[cfg(feature = "rtti")] #[cfg(feature = "rtti")]
use crate::rtti::*; use crate::rtti::*;
use crate::{EvaluationContext, Property, SharedString, Signal}; use crate::{Property, SharedString, Signal};
use const_field_offset::FieldOffsets; use const_field_offset::FieldOffsets;
use core::pin::Pin; use core::pin::Pin;
use corelib_macro::*; use corelib_macro::*;
@ -40,27 +40,24 @@ pub struct Rectangle {
} }
impl Item for Rectangle { impl Item for Rectangle {
fn geometry(self: Pin<&Self>, context: &EvaluationContext) -> Rect { fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect( euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context), Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(context), Self::field_offsets().y.apply_pin(self).get(),
Self::field_offsets().width.apply_pin(self).get(context), Self::field_offsets().width.apply_pin(self).get(),
Self::field_offsets().height.apply_pin(self).get(context), Self::field_offsets().height.apply_pin(self).get(),
) )
} }
fn rendering_primitive( fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
self: Pin<&Self>, let width = Self::field_offsets().width.apply_pin(self).get();
context: &crate::EvaluationContext, let height = Self::field_offsets().height.apply_pin(self).get();
) -> RenderingPrimitive {
let width = Self::field_offsets().width.apply_pin(self).get(context);
let height = Self::field_offsets().height.apply_pin(self).get(context);
if width > 0. && height > 0. { if width > 0. && height > 0. {
RenderingPrimitive::Rectangle { RenderingPrimitive::Rectangle {
x: Self::field_offsets().x.apply_pin(self).get(context), x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(context), y: Self::field_offsets().y.apply_pin(self).get(),
width, width,
height, height,
color: Self::field_offsets().color.apply_pin(self).get(context), color: Self::field_offsets().color.apply_pin(self).get(),
} }
} else { } else {
RenderingPrimitive::NoContents RenderingPrimitive::NoContents
@ -71,12 +68,7 @@ impl Item for Rectangle {
Default::default() Default::default()
} }
fn input_event( fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
} }
impl ItemConsts for Rectangle { impl ItemConsts for Rectangle {
@ -102,22 +94,19 @@ pub struct Image {
} }
impl Item for Image { impl Item for Image {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect { fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect( euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context), Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(context), Self::field_offsets().y.apply_pin(self).get(),
Self::field_offsets().width.apply_pin(self).get(context), Self::field_offsets().width.apply_pin(self).get(),
Self::field_offsets().height.apply_pin(self).get(context), Self::field_offsets().height.apply_pin(self).get(),
) )
} }
fn rendering_primitive( fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
RenderingPrimitive::Image { RenderingPrimitive::Image {
x: Self::field_offsets().x.apply_pin(self).get(context), x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(context), y: Self::field_offsets().y.apply_pin(self).get(),
source: Self::field_offsets().source.apply_pin(self).get(context), source: Self::field_offsets().source.apply_pin(self).get(),
} }
} }
@ -126,12 +115,7 @@ impl Item for Image {
Default::default() Default::default()
} }
fn input_event( fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
} }
impl ItemConsts for Image { impl ItemConsts for Image {
@ -159,25 +143,22 @@ pub struct Text {
impl Item for Text { impl Item for Text {
// FIXME: width / height. or maybe it doesn't matter? ( // FIXME: width / height. or maybe it doesn't matter? (
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect { fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect( euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context), Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(context), Self::field_offsets().y.apply_pin(self).get(),
0., 0.,
0., 0.,
) )
} }
fn rendering_primitive( fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
RenderingPrimitive::Text { RenderingPrimitive::Text {
x: Self::field_offsets().x.apply_pin(self).get(context), x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(context), y: Self::field_offsets().y.apply_pin(self).get(),
text: Self::field_offsets().text.apply_pin(self).get(context), text: Self::field_offsets().text.apply_pin(self).get(),
font_family: Self::field_offsets().font_family.apply_pin(self).get(context), font_family: Self::field_offsets().font_family.apply_pin(self).get(),
font_pixel_size: Self::field_offsets().font_pixel_size.apply_pin(self).get(context), font_pixel_size: Self::field_offsets().font_pixel_size.apply_pin(self).get(),
color: Self::field_offsets().color.apply_pin(self).get(context), color: Self::field_offsets().color.apply_pin(self).get(),
} }
} }
@ -185,12 +166,7 @@ impl Item for Text {
todo!() todo!()
} }
fn input_event( fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
} }
impl ItemConsts for Text { impl ItemConsts for Text {
@ -217,18 +193,15 @@ pub struct TouchArea {
} }
impl Item for TouchArea { impl Item for TouchArea {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect { fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect( euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context), Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(context), Self::field_offsets().y.apply_pin(self).get(),
Self::field_offsets().width.apply_pin(self).get(context), Self::field_offsets().width.apply_pin(self).get(),
Self::field_offsets().height.apply_pin(self).get(context), Self::field_offsets().height.apply_pin(self).get(),
) )
} }
fn rendering_primitive( fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
self: Pin<&Self>,
_context: &crate::EvaluationContext,
) -> RenderingPrimitive {
RenderingPrimitive::NoContents RenderingPrimitive::NoContents
} }
@ -236,11 +209,7 @@ impl Item for TouchArea {
todo!() todo!()
} }
fn input_event( fn input_event(self: Pin<&Self>, event: super::datastructures::MouseEvent) {
self: Pin<&Self>,
event: super::datastructures::MouseEvent,
context: &crate::EvaluationContext,
) {
println!("Touch Area Event {:?}", event); println!("Touch Area Event {:?}", event);
Self::field_offsets().pressed.apply_pin(self).set(match event.what { Self::field_offsets().pressed.apply_pin(self).set(match event.what {
super::datastructures::MouseEventType::MousePressed => true, super::datastructures::MouseEventType::MousePressed => true,
@ -248,7 +217,7 @@ impl Item for TouchArea {
super::datastructures::MouseEventType::MouseMoved => return, super::datastructures::MouseEventType::MouseMoved => return,
}); });
if matches!(event.what, super::datastructures::MouseEventType::MouseReleased) { if matches!(event.what, super::datastructures::MouseEventType::MouseReleased) {
Self::field_offsets().clicked.apply_pin(self).emit(context, ()) Self::field_offsets().clicked.apply_pin(self).emit(())
} }
} }
} }
@ -278,27 +247,24 @@ pub struct Path {
} }
impl Item for Path { impl Item for Path {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect { fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect( euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context), Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(context), Self::field_offsets().y.apply_pin(self).get(),
0., 0.,
0., 0.,
) )
} }
fn rendering_primitive( fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
RenderingPrimitive::Path { RenderingPrimitive::Path {
x: Self::field_offsets().x.apply_pin(self).get(context), x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(context), y: Self::field_offsets().y.apply_pin(self).get(),
width: Self::field_offsets().width.apply_pin(self).get(context), width: Self::field_offsets().width.apply_pin(self).get(),
height: Self::field_offsets().height.apply_pin(self).get(context), height: Self::field_offsets().height.apply_pin(self).get(),
elements: Self::field_offsets().elements.apply_pin(self).get(context), elements: Self::field_offsets().elements.apply_pin(self).get(),
fill_color: Self::field_offsets().fill_color.apply_pin(self).get(context), fill_color: Self::field_offsets().fill_color.apply_pin(self).get(),
stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(context), stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(),
stroke_width: Self::field_offsets().stroke_width.apply_pin(self).get(context), stroke_width: Self::field_offsets().stroke_width.apply_pin(self).get(),
} }
} }
@ -306,12 +272,7 @@ impl Item for Path {
todo!() todo!()
} }
fn input_event( fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
} }
impl ItemConsts for Path { impl ItemConsts for Path {

View file

@ -66,45 +66,6 @@ use core::{marker::PhantomPinned, pin::Pin};
use crate::abi::datastructures::Color; use crate::abi::datastructures::Color;
use crate::abi::primitives::PropertyAnimation; use crate::abi::primitives::PropertyAnimation;
use crate::ComponentRefPin;
/// This structure contains what is required for the property engine to evaluate properties
///
/// One must pass it to the getter of the property, or emit of signals, and it can
/// be accessed from the bindings
#[repr(C)]
pub struct EvaluationContext<'a> {
/// The component which contains the Property or the Signal
pub component: core::pin::Pin<vtable::VRef<'a, crate::abi::datastructures::ComponentVTable>>,
/// The context of the parent component
pub parent_context: Option<&'a EvaluationContext<'a>>,
}
impl<'a> EvaluationContext<'a> {
/// Create a new context related to the root component
///
/// The component need to be a root component, otherwise fetching properties
/// might panic.
pub fn for_root_component(component: ComponentRefPin<'a>) -> Self {
Self { component, parent_context: None }
}
/// Create a context for a child component of a component within the current
/// context.
pub fn child_context(&'a self, child: ComponentRefPin<'a>) -> Self {
Self { component: child, parent_context: Some(self) }
}
/// Attempt to cast the component to the given type
pub fn get_component<
T: vtable::HasStaticVTable<crate::abi::datastructures::ComponentVTable>,
>(
&'a self,
) -> Option<core::pin::Pin<&'a T>> {
vtable::VRef::downcast_pin(self.component)
}
}
/// The return value of a binding /// The return value of a binding
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -118,11 +79,7 @@ enum BindingResult {
struct BindingVTable { struct BindingVTable {
drop: unsafe fn(_self: *mut BindingHolder), drop: unsafe fn(_self: *mut BindingHolder),
evaluate: unsafe fn( evaluate: unsafe fn(_self: *mut BindingHolder, value: *mut ()) -> BindingResult,
_self: *mut BindingHolder,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult,
mark_dirty: unsafe fn(_self: *const BindingHolder), mark_dirty: unsafe fn(_self: *const BindingHolder),
} }
@ -130,24 +87,16 @@ struct BindingVTable {
trait BindingCallable { trait BindingCallable {
/// This function is called by the property to evaluate the binding and produce a new value. The /// This function is called by the property to evaluate the binding and produce a new value. The
/// previous property value is provided in the value parameter. /// previous property value is provided in the value parameter.
unsafe fn evaluate( unsafe fn evaluate(self: Pin<&Self>, value: *mut ()) -> BindingResult;
self: Pin<&Self>,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult;
/// This function is used to notify the binding that one of the dependencies was changed /// This function is used to notify the binding that one of the dependencies was changed
/// and therefore this binding may evaluate to a different value, too. /// and therefore this binding may evaluate to a different value, too.
fn mark_dirty(self: Pin<&Self>) {} fn mark_dirty(self: Pin<&Self>) {}
} }
impl<F: Fn(*mut (), &EvaluationContext) -> BindingResult> BindingCallable for F { impl<F: Fn(*mut ()) -> BindingResult> BindingCallable for F {
unsafe fn evaluate( unsafe fn evaluate(self: Pin<&Self>, value: *mut ()) -> BindingResult {
self: Pin<&Self>, self(value)
value: *mut (),
context: &EvaluationContext,
) -> BindingResult {
self(value, context)
} }
} }
@ -178,12 +127,10 @@ fn alloc_binding_holder<B: BindingCallable + 'static>(binding: B) -> *mut Bindin
unsafe fn evaluate<B: BindingCallable>( unsafe fn evaluate<B: BindingCallable>(
_self: *mut BindingHolder, _self: *mut BindingHolder,
value: *mut (), value: *mut (),
context: &EvaluationContext,
) -> BindingResult { ) -> BindingResult {
let pinned_holder = Pin::new_unchecked(&*_self); let pinned_holder = Pin::new_unchecked(&*_self);
CURRENT_BINDING.set(pinned_holder, || { CURRENT_BINDING.set(pinned_holder, || {
Pin::new_unchecked(&((*(_self as *mut BindingHolder<B>)).binding)) Pin::new_unchecked(&((*(_self as *mut BindingHolder<B>)).binding)).evaluate(value)
.evaluate(value, context)
}) })
} }
@ -384,7 +331,7 @@ impl PropertyHandle {
// `value` is the content of the unsafe cell and will be only dereferenced if the // `value` is the content of the unsafe cell and will be only dereferenced if the
// handle is not locked. (Upholding the requirements of UnsafeCell) // handle is not locked. (Upholding the requirements of UnsafeCell)
unsafe fn update<T>(&self, value: *mut T, context: &EvaluationContext) { unsafe fn update<T>(&self, value: *mut T) {
let remove = self.access(|binding| { let remove = self.access(|binding| {
if let Some(mut binding) = binding { if let Some(mut binding) = binding {
if binding.dirty.get() { if binding.dirty.get() {
@ -393,7 +340,6 @@ impl PropertyHandle {
let r = (binding.vtable.evaluate)( let r = (binding.vtable.evaluate)(
binding.as_mut().get_unchecked_mut() as *mut BindingHolder, binding.as_mut().get_unchecked_mut() as *mut BindingHolder,
value as *mut (), value as *mut (),
context,
); );
binding.dirty.set(false); binding.dirty.set(false);
if r == BindingResult::RemoveBinding { if r == BindingResult::RemoveBinding {
@ -489,8 +435,8 @@ impl<T: Clone> Property<T> {
/// ///
/// Panics if this property is get while evaluating its own binding or /// Panics if this property is get while evaluating its own binding or
/// cloning the value. /// cloning the value.
pub fn get(self: Pin<&Self>, context: &EvaluationContext) -> T { pub fn get(self: Pin<&Self>) -> T {
unsafe { self.handle.update(self.value.get(), context) }; unsafe { self.handle.update(self.value.get()) };
self.handle.register_as_dependency_to_current_binding(); self.handle.register_as_dependency_to_current_binding();
self.get_internal() self.get_internal()
} }
@ -522,10 +468,10 @@ impl<T: Clone> Property<T> {
/// ///
/// If other properties have bindings depending of this property, these properties will /// If other properties have bindings depending of this property, these properties will
/// be marked as dirty. /// be marked as dirty.
//FIXME pub fn set_binding(self: Pin<&Self>, f: impl (Fn(&EvaluationContext) -> T) + 'static) { //FIXME pub fn set_binding(self: Pin<&Self>, f: impl (Fn() -> T) + 'static) {
pub fn set_binding(&self, f: impl (Fn(&EvaluationContext) -> T) + 'static) { pub fn set_binding(&self, f: impl (Fn() -> T) + 'static) {
self.handle.set_binding(move |val: *mut (), context: &EvaluationContext| unsafe { self.handle.set_binding(move |val: *mut ()| unsafe {
*(val as *mut T) = f(context); *(val as *mut T) = f();
BindingResult::KeepBinding BindingResult::KeepBinding
}); });
self.handle.mark_dirty(); self.handle.mark_dirty();
@ -542,7 +488,7 @@ impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
pub fn set_animated_value(&self, value: T, animation_data: &PropertyAnimation) { pub fn set_animated_value(&self, value: T, animation_data: &PropertyAnimation) {
// FIXME if the current value is a dirty binding, we must run it, but we do not have the context // FIXME if the current value is a dirty binding, we must run it, but we do not have the context
let d = PropertyValueAnimationData::new(self.get_internal(), value, animation_data.clone()); let d = PropertyValueAnimationData::new(self.get_internal(), value, animation_data.clone());
self.handle.set_binding(move |val: *mut (), _context: &EvaluationContext| unsafe { self.handle.set_binding(move |val: *mut ()| unsafe {
let (value, finished) = d.compute_interpolated_value(); let (value, finished) = d.compute_interpolated_value();
*(val as *mut T) = value; *(val as *mut T) = value;
if finished { if finished {
@ -559,14 +505,14 @@ impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
/// ///
pub fn set_animated_binding( pub fn set_animated_binding(
&self, &self,
f: impl (Fn(&EvaluationContext) -> T) + 'static, f: impl (Fn() -> T) + 'static,
animation_data: &PropertyAnimation, animation_data: &PropertyAnimation,
) { ) {
self.handle.set_binding(AnimatedBindingCallable::<T> { self.handle.set_binding(AnimatedBindingCallable::<T> {
original_binding: PropertyHandle { original_binding: PropertyHandle {
handle: Cell::new( handle: Cell::new(
(alloc_binding_holder(move |val: *mut (), context: &EvaluationContext| unsafe { (alloc_binding_holder(move |val: *mut ()| unsafe {
*(val as *mut T) = f(context); *(val as *mut T) = f();
BindingResult::KeepBinding BindingResult::KeepBinding
}) as usize) }) as usize)
| 0b10, | 0b10,
@ -626,11 +572,7 @@ struct AnimatedBindingCallable<T> {
} }
impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T> { impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T> {
unsafe fn evaluate( unsafe fn evaluate(self: Pin<&Self>, value: *mut ()) -> BindingResult {
self: Pin<&Self>,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult {
self.original_binding.register_as_dependency_to_current_binding(); self.original_binding.register_as_dependency_to_current_binding();
match self.state.get() { match self.state.get() {
AnimatedBindingState::Animating => { AnimatedBindingState::Animating => {
@ -644,15 +586,14 @@ impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T
} }
} }
AnimatedBindingState::NotAnimating => { AnimatedBindingState::NotAnimating => {
self.original_binding.update(value, context); self.original_binding.update(value);
} }
AnimatedBindingState::ShouldStart => { AnimatedBindingState::ShouldStart => {
let value = &mut *(value as *mut T); let value = &mut *(value as *mut T);
self.state.set(AnimatedBindingState::Animating); self.state.set(AnimatedBindingState::Animating);
let mut animation_data = self.animation_data.borrow_mut(); let mut animation_data = self.animation_data.borrow_mut();
animation_data.from_value = value.clone(); animation_data.from_value = value.clone();
self.original_binding self.original_binding.update((&mut animation_data.to_value) as *mut T as *mut ());
.update((&mut animation_data.to_value) as *mut T as *mut (), context);
let (val, finished) = animation_data.compute_interpolated_value(); let (val, finished) = animation_data.compute_interpolated_value();
*value = val; *value = val;
if finished { if finished {
@ -682,8 +623,8 @@ impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T
fn properties_simple_test() { fn properties_simple_test() {
use std::rc::Rc; use std::rc::Rc;
use weak_pin::rc::WeakPin; use weak_pin::rc::WeakPin;
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 { fn g(prop: &Property<i32>) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) } unsafe { Pin::new_unchecked(prop).get() }
} }
#[derive(Default)] #[derive(Default)]
@ -692,32 +633,27 @@ fn properties_simple_test() {
height: Property<i32>, height: Property<i32>,
area: Property<i32>, area: Property<i32>,
} }
let dummy_eval_context = EvaluationContext::for_root_component(unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
});
let compo = Rc::pin(Component::default()); let compo = Rc::pin(Component::default());
let w = WeakPin::downgrade(compo.clone()); let w = WeakPin::downgrade(compo.clone());
compo.area.set_binding(move |ctx| { compo.area.set_binding(move || {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
g(&compo.width, ctx) * g(&compo.height, ctx) g(&compo.width) * g(&compo.height)
}); });
compo.width.set(4); compo.width.set(4);
compo.height.set(8); compo.height.set(8);
assert_eq!(g(&compo.width, &dummy_eval_context), 4); assert_eq!(g(&compo.width), 4);
assert_eq!(g(&compo.height, &dummy_eval_context), 8); assert_eq!(g(&compo.height), 8);
assert_eq!(g(&compo.area, &dummy_eval_context), 4 * 8); assert_eq!(g(&compo.area), 4 * 8);
let w = WeakPin::downgrade(compo.clone()); let w = WeakPin::downgrade(compo.clone());
compo.width.set_binding(move |ctx| { compo.width.set_binding(move || {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
g(&compo.height, ctx) * 2 g(&compo.height) * 2
}); });
assert_eq!(g(&compo.width, &dummy_eval_context), 8 * 2); assert_eq!(g(&compo.width), 8 * 2);
assert_eq!(g(&compo.height, &dummy_eval_context), 8); assert_eq!(g(&compo.height), 8);
assert_eq!(g(&compo.area, &dummy_eval_context), 8 * 8 * 2); assert_eq!(g(&compo.area), 8 * 8 * 2);
} }
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -735,12 +671,8 @@ pub unsafe extern "C" fn sixtyfps_property_init(out: *mut PropertyHandleOpaque)
/// To be called before accessing the value /// To be called before accessing the value
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_update( pub unsafe extern "C" fn sixtyfps_property_update(handle: &PropertyHandleOpaque, val: *mut c_void) {
handle: &PropertyHandleOpaque, handle.0.update(val);
context: &EvaluationContext,
val: *mut c_void,
) {
handle.0.update(val, context);
handle.0.register_as_dependency_to_current_binding(); handle.0.register_as_dependency_to_current_binding();
} }
@ -753,12 +685,12 @@ pub unsafe extern "C" fn sixtyfps_property_set_changed(handle: &PropertyHandleOp
} }
fn make_c_function_binding( fn make_c_function_binding(
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut c_void), binding: extern "C" fn(*mut c_void, *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)>,
) -> impl Fn(*mut (), &EvaluationContext) -> BindingResult { ) -> impl Fn(*mut ()) -> BindingResult {
struct CFunctionBinding<T> { struct CFunctionBinding<T> {
binding_function: extern "C" fn(*mut c_void, &EvaluationContext, *mut T), binding_function: extern "C" fn(*mut c_void, *mut T),
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)>,
} }
@ -773,14 +705,13 @@ fn make_c_function_binding(
let b = CFunctionBinding { binding_function: binding, user_data, drop_user_data }; let b = CFunctionBinding { binding_function: binding, user_data, drop_user_data };
move |value_ptr, context| { move |value_ptr| {
(b.binding_function)(b.user_data, context, value_ptr); (b.binding_function)(b.user_data, value_ptr);
BindingResult::KeepBinding BindingResult::KeepBinding
} }
} }
/// Set a binding /// Set a binding
/// The binding has signature fn(user_data, context, pointer_to_value)
/// ///
/// The current implementation will do usually two memory alocation: /// The current implementation will do usually two memory alocation:
/// 1. the allocation from the calling code to allocate user_data /// 1. the allocation from the calling code to allocate user_data
@ -790,11 +721,7 @@ fn make_c_function_binding(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_binding( pub unsafe extern "C" fn sixtyfps_property_set_binding(
handle: &PropertyHandleOpaque, handle: &PropertyHandleOpaque,
binding: extern "C" fn( binding: extern "C" fn(user_data: *mut c_void, pointer_to_value: *mut c_void),
user_data: *mut c_void,
context: &EvaluationContext,
pointer_to_value: *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)>,
) { ) {
@ -845,7 +772,7 @@ fn c_set_animated_value<T: InterpolatedPropertyValue>(
animation_data: &crate::abi::primitives::PropertyAnimation, animation_data: &crate::abi::primitives::PropertyAnimation,
) { ) {
let d = PropertyValueAnimationData::new(from, to, animation_data.clone()); let d = PropertyValueAnimationData::new(from, to, animation_data.clone());
handle.0.set_binding(move |val: *mut (), _: &EvaluationContext| { handle.0.set_binding(move |val: *mut ()| {
let (value, finished) = d.compute_interpolated_value(); let (value, finished) = d.compute_interpolated_value();
unsafe { unsafe {
*(val as *mut T) = value; *(val as *mut T) = value;
@ -895,14 +822,14 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_value_color(
unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue>( unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue>(
handle: &PropertyHandleOpaque, handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut T), binding: extern "C" fn(*mut c_void, *mut T),
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)>,
animation_data: &crate::abi::primitives::PropertyAnimation, animation_data: &crate::abi::primitives::PropertyAnimation,
) { ) {
let binding = core::mem::transmute::< let binding = core::mem::transmute::<
extern "C" fn(*mut c_void, &EvaluationContext, *mut T), extern "C" fn(*mut c_void, *mut T),
extern "C" fn(*mut c_void, &EvaluationContext, *mut ()), extern "C" fn(*mut c_void, *mut ()),
>(binding); >(binding);
handle.0.set_binding(AnimatedBindingCallable::<T> { handle.0.set_binding(AnimatedBindingCallable::<T> {
original_binding: PropertyHandle { original_binding: PropertyHandle {
@ -926,7 +853,7 @@ unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue>(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_int( pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_int(
handle: &PropertyHandleOpaque, handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut i32), binding: extern "C" fn(*mut c_void, *mut i32),
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)>,
animation_data: &crate::abi::primitives::PropertyAnimation, animation_data: &crate::abi::primitives::PropertyAnimation,
@ -938,7 +865,7 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_int(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float( pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float(
handle: &PropertyHandleOpaque, handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut f32), binding: extern "C" fn(*mut c_void, *mut f32),
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)>,
animation_data: &crate::abi::primitives::PropertyAnimation, animation_data: &crate::abi::primitives::PropertyAnimation,
@ -950,7 +877,7 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_color( pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_color(
handle: &PropertyHandleOpaque, handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut Color), binding: extern "C" fn(*mut c_void, *mut Color),
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)>,
animation_data: &crate::abi::primitives::PropertyAnimation, animation_data: &crate::abi::primitives::PropertyAnimation,
@ -975,52 +902,44 @@ mod animation_tests {
#[test] #[test]
fn properties_test_animation_triggered_by_set() { fn properties_test_animation_triggered_by_set() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 { fn g(prop: &Property<i32>) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) } unsafe { Pin::new_unchecked(prop).get() }
} }
let dummy_eval_context = EvaluationContext {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
},
parent_context: None,
};
let compo = Rc::new(Component::default()); let compo = Rc::new(Component::default());
let w = Rc::downgrade(&compo); let w = Rc::downgrade(&compo);
compo.width_times_two.set_binding(move |context| { compo.width_times_two.set_binding(move || {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
g(&compo.width, context) * 2 g(&compo.width) * 2
}); });
let animation_details = PropertyAnimation { duration: DURATION.as_millis() as _ }; let animation_details = PropertyAnimation { duration: DURATION.as_millis() as _ };
compo.width.set(100); compo.width.set(100);
assert_eq!(g(&compo.width, &dummy_eval_context), 100); assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two), 200);
let start_time = let start_time =
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick()); crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick());
compo.width.set_animated_value(200, &animation_details); compo.width.set_animated_value(200, &animation_details);
assert_eq!(g(&compo.width, &dummy_eval_context), 100); assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two), 200);
crate::animations::CURRENT_ANIMATION_DRIVER crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION / 2)); .with(|driver| driver.update_animations(start_time + DURATION / 2));
assert_eq!(g(&compo.width, &dummy_eval_context), 150); assert_eq!(g(&compo.width), 150);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300); assert_eq!(g(&compo.width_times_two), 300);
crate::animations::CURRENT_ANIMATION_DRIVER crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION)); .with(|driver| driver.update_animations(start_time + DURATION));
assert_eq!(g(&compo.width, &dummy_eval_context), 200); assert_eq!(g(&compo.width), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400); assert_eq!(g(&compo.width_times_two), 400);
crate::animations::CURRENT_ANIMATION_DRIVER crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION * 2)); .with(|driver| driver.update_animations(start_time + DURATION * 2));
assert_eq!(g(&compo.width, &dummy_eval_context), 200); assert_eq!(g(&compo.width), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400); assert_eq!(g(&compo.width_times_two), 400);
// the binding should be removed // the binding should be removed
compo.width.handle.access(|binding| assert!(binding.is_none())); compo.width.handle.access(|binding| assert!(binding.is_none()));
@ -1028,24 +947,15 @@ mod animation_tests {
#[test] #[test]
fn properties_test_animation_triggered_by_binding() { fn properties_test_animation_triggered_by_binding() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 { fn g(prop: &Property<i32>) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) } unsafe { Pin::new_unchecked(prop).get() }
} }
let dummy_eval_context = EvaluationContext {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
},
parent_context: None,
};
let compo = Rc::new(Component::default()); let compo = Rc::new(Component::default());
let w = Rc::downgrade(&compo); let w = Rc::downgrade(&compo);
compo.width_times_two.set_binding(move |context| { compo.width_times_two.set_binding(move || {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
g(&compo.width, context) * 2 g(&compo.width) * 2
}); });
let start_time = let start_time =
@ -1055,32 +965,32 @@ mod animation_tests {
let w = Rc::downgrade(&compo); let w = Rc::downgrade(&compo);
compo.width.set_animated_binding( compo.width.set_animated_binding(
move |context| { move || {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
g(&compo.feed_property, context) g(&compo.feed_property)
}, },
&animation_details, &animation_details,
); );
compo.feed_property.set(100); compo.feed_property.set(100);
assert_eq!(g(&compo.width, &dummy_eval_context), 100); assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two), 200);
compo.feed_property.set(200); compo.feed_property.set(200);
assert_eq!(g(&compo.width, &dummy_eval_context), 100); assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two), 200);
crate::animations::CURRENT_ANIMATION_DRIVER crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION / 2)); .with(|driver| driver.update_animations(start_time + DURATION / 2));
assert_eq!(g(&compo.width, &dummy_eval_context), 150); assert_eq!(g(&compo.width), 150);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300); assert_eq!(g(&compo.width_times_two), 300);
crate::animations::CURRENT_ANIMATION_DRIVER crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION)); .with(|driver| driver.update_animations(start_time + DURATION));
assert_eq!(g(&compo.width, &dummy_eval_context), 200); assert_eq!(g(&compo.width), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400); assert_eq!(g(&compo.width_times_two), 400);
} }
} }
@ -1094,7 +1004,7 @@ impl Default for PropertyListenerScope {
fn default() -> Self { fn default() -> Self {
static VT: &'static BindingVTable = &BindingVTable { static VT: &'static BindingVTable = &BindingVTable {
drop: |_| (), drop: |_| (),
evaluate: |_, _, _| BindingResult::KeepBinding, evaluate: |_, _| BindingResult::KeepBinding,
mark_dirty: |_| (), mark_dirty: |_| (),
}; };
@ -1134,21 +1044,13 @@ fn test_property_listener_scope() {
let scope = Box::pin(PropertyListenerScope::default()); let scope = Box::pin(PropertyListenerScope::default());
let prop1 = Box::pin(Property::new(42)); let prop1 = Box::pin(Property::new(42));
assert!(scope.is_dirty()); // It is dirty at the beginning assert!(scope.is_dirty()); // It is dirty at the beginning
let dummy_eval_context = EvaluationContext {
component: unsafe { let r = scope.as_ref().evaluate(|| prop1.as_ref().get());
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
},
parent_context: None,
};
let r = scope.as_ref().evaluate(|| prop1.as_ref().get(&dummy_eval_context));
assert_eq!(r, 42); assert_eq!(r, 42);
assert!(!scope.is_dirty()); // It is no longer dirty assert!(!scope.is_dirty()); // It is no longer dirty
prop1.as_ref().set(88); prop1.as_ref().set(88);
assert!(scope.is_dirty()); // now dirty for prop1 changed. assert!(scope.is_dirty()); // now dirty for prop1 changed.
let r = scope.as_ref().evaluate(|| prop1.as_ref().get(&dummy_eval_context) + 1); let r = scope.as_ref().evaluate(|| prop1.as_ref().get() + 1);
assert_eq!(r, 89); assert_eq!(r, 89);
assert!(!scope.is_dirty()); assert!(!scope.is_dirty());
let r = scope.as_ref().evaluate(|| 12); let r = scope.as_ref().evaluate(|| 12);

View file

@ -5,7 +5,6 @@ TODO: reconsider if we should rename that to `Event`
but then it should also be renamed everywhere, including in the language grammar but then it should also be renamed everywhere, including in the language grammar
*/ */
use super::properties::EvaluationContext;
use core::cell::Cell; use core::cell::Cell;
/// A Signal that can be connected to a handler. /// A Signal that can be connected to a handler.
@ -16,16 +15,14 @@ use core::cell::Cell;
#[repr(C)] #[repr(C)]
pub struct Signal<Arg> { pub struct Signal<Arg> {
/// 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(&EvaluationContext, Arg)>>>, handler: Cell<Option<Box<dyn Fn(Arg)>>>,
} }
impl<Arg> Signal<Arg> { impl<Arg> Signal<Arg> {
/// Emit the signal with the given argument. /// Emit the signal with the given argument.
/// pub fn emit(&self, a: Arg) {
/// The constext must be a context corresponding to the component in which the signal is contained.
pub fn emit(&self, context: &EvaluationContext, a: Arg) {
if let Some(h) = self.handler.take() { if let Some(h) = self.handler.take() {
h(context, a); 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))
} }
@ -34,44 +31,23 @@ impl<Arg> Signal<Arg> {
/// 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(&EvaluationContext, Arg) + 'static) { pub fn set_handler(&self, f: impl Fn(Arg) + 'static) {
self.handler.set(Some(Box::new(f))); self.handler.set(Some(Box::new(f)));
} }
} }
#[test] #[test]
fn signal_simple_test() { fn signal_simple_test() {
use std::pin::Pin; use std::rc::Rc;
#[derive(Default)] #[derive(Default)]
struct Component { struct Component {
pressed: core::cell::Cell<bool>, pressed: core::cell::Cell<bool>,
clicked: Signal<()>, clicked: Signal<()>,
} }
impl crate::abi::datastructures::Component for Component { let c = Rc::new(Component::default());
fn visit_children_item( let weak = Rc::downgrade(&c);
self: Pin<&Self>, c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true));
_: isize, c.clicked.emit(());
_: crate::abi::datastructures::ItemVisitorRefMut,
) {
}
fn layout_info(self: Pin<&Self>) -> crate::abi::datastructures::LayoutInfo {
unimplemented!()
}
fn compute_layout(self: Pin<&Self>, _: &crate::EvaluationContext) {}
}
use crate::abi::datastructures::ComponentVTable;
let c = Component::default();
c.clicked.set_handler(|c, ()| unsafe {
(*(c.component.as_ptr() as *const Component)).pressed.set(true)
});
let vtable = ComponentVTable::new::<Component>();
let ctx = super::properties::EvaluationContext::for_root_component(unsafe {
Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::from(&vtable),
core::ptr::NonNull::from(&c).cast(),
))
});
c.clicked.emit(&ctx, ());
assert_eq!(c.pressed.get(), true); assert_eq!(c.pressed.get(), true);
} }
@ -94,21 +70,18 @@ pub unsafe extern "C" fn sixtyfps_signal_init(out: *mut SignalOpaque) {
/// Emit the signal /// Emit the signal
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_emit( pub unsafe extern "C" fn sixtyfps_signal_emit(sig: *const SignalOpaque) {
sig: *const SignalOpaque,
component: &EvaluationContext,
) {
let sig = &*(sig as *const Signal<()>); let sig = &*(sig as *const Signal<()>);
sig.emit(component, ()); sig.emit(());
} }
/// Set signal handler. /// Set signal handler.
/// ///
/// The binding has signature fn(user_data, context) /// The binding has signature fn(user_data)
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_set_handler( pub unsafe extern "C" fn sixtyfps_signal_set_handler(
sig: *mut SignalOpaque, sig: *mut SignalOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext), binding: extern "C" fn(user_data: *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)>,
) { ) {
@ -128,8 +101,8 @@ pub unsafe extern "C" fn sixtyfps_signal_set_handler(
} }
let ud = UserData { user_data, drop_user_data }; let ud = UserData { user_data, drop_user_data };
let real_binding = move |compo: &EvaluationContext, ()| { let real_binding = move |()| {
binding(ud.user_data, compo); binding(ud.user_data);
}; };
sig.set_handler(real_binding); sig.set_handler(real_binding);
} }

View file

@ -39,15 +39,7 @@ impl AnimationDriver {
/// The current instant that is to be used for animation /// The current instant that is to be used for animation
/// using this function register the current binding as a dependency /// using this function register the current binding as a dependency
pub fn current_tick(&self) -> instant::Instant { pub fn current_tick(&self) -> instant::Instant {
// FIXME! we need to get rid of the contect there self.global_instant.as_ref().get()
#[allow(unsafe_code)]
let dummy_eval_context = crate::EvaluationContext::for_root_component(unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
});
self.global_instant.as_ref().get(&dummy_eval_context)
} }
} }

View file

@ -145,7 +145,7 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow
{ {
fn draw(&self, component: crate::ComponentRefPin) { fn draw(&self, component: crate::ComponentRefPin) {
// FIXME: we should do that only if some property change // FIXME: we should do that only if some property change
component.as_ref().compute_layout(&crate::EvaluationContext::for_root_component(component)); component.as_ref().compute_layout();
let mut this = self.borrow_mut(); let mut this = self.borrow_mut();
@ -156,9 +156,8 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow
// Generate cached rendering data once // Generate cached rendering data once
crate::item_tree::visit_items( crate::item_tree::visit_items(
component, component,
|ctx, item, _| { |_, item, _| {
crate::item_rendering::update_item_rendering_data( crate::item_rendering::update_item_rendering_data(
ctx,
item, item,
&mut this.rendering_cache, &mut this.rendering_cache,
&mut rendering_primitives_builder, &mut rendering_primitives_builder,

View file

@ -12,14 +12,14 @@ pub fn process_mouse_event(component: ComponentRefPin, event: MouseEvent) {
crate::item_tree::visit_items( crate::item_tree::visit_items(
component, component,
|context, item, offset| { |_, item, offset| {
let geom = item.as_ref().geometry(context); let geom = item.as_ref().geometry();
let geom = geom.translate(*offset); let geom = geom.translate(*offset);
if geom.contains(event.pos) { if geom.contains(event.pos) {
let mut event2 = event.clone(); let mut event2 = event.clone();
event2.pos -= geom.origin.to_vector(); event2.pos -= geom.origin.to_vector();
item.as_ref().input_event(event2, context); item.as_ref().input_event(event2);
} }
geom.origin.to_vector() geom.origin.to_vector()

View file

@ -2,16 +2,14 @@ use super::abi::datastructures::ItemRef;
use super::graphics::{ use super::graphics::{
Frame, GraphicsBackend, HasRenderingPrimitive, RenderingCache, RenderingPrimitivesBuilder, Frame, GraphicsBackend, HasRenderingPrimitive, RenderingCache, RenderingPrimitivesBuilder,
}; };
use super::EvaluationContext;
use cgmath::{Matrix4, SquareMatrix, Vector3}; use cgmath::{Matrix4, SquareMatrix, Vector3};
pub(crate) fn update_item_rendering_data<Backend: GraphicsBackend>( pub(crate) fn update_item_rendering_data<Backend: GraphicsBackend>(
context: &EvaluationContext,
item: core::pin::Pin<ItemRef>, item: core::pin::Pin<ItemRef>,
rendering_cache: &mut RenderingCache<Backend>, rendering_cache: &mut RenderingCache<Backend>,
rendering_primitives_builder: &mut Backend::RenderingPrimitivesBuilder, rendering_primitives_builder: &mut Backend::RenderingPrimitivesBuilder,
) { ) {
let item_rendering_primitive = item.as_ref().rendering_primitive(context); let item_rendering_primitive = item.as_ref().rendering_primitive();
let rendering_data = item.cached_rendering_data_offset(); let rendering_data = item.cached_rendering_data_offset();
@ -41,8 +39,8 @@ pub(crate) fn render_component_items<Backend: GraphicsBackend>(
crate::item_tree::visit_items( crate::item_tree::visit_items(
component, component,
|context, item, transform| { |_, item, transform| {
let origin = item.as_ref().geometry(context).origin; let origin = item.as_ref().geometry().origin;
let transform = let transform =
transform * Matrix4::from_translation(Vector3::new(origin.x, origin.y, 0.)); transform * Matrix4::from_translation(Vector3::new(origin.x, origin.y, 0.));

View file

@ -1,6 +1,5 @@
use crate::abi::datastructures::{ItemRef, ItemTreeNode, ItemVisitor, ItemVisitorVTable}; use crate::abi::datastructures::{ItemRef, ItemTreeNode, ItemVisitor, ItemVisitorVTable};
use crate::ComponentRefPin; use crate::ComponentRefPin;
use crate::EvaluationContext;
use core::pin::Pin; use core::pin::Pin;
/// Visit each items recursively /// Visit each items recursively
@ -8,31 +7,24 @@ use core::pin::Pin;
/// The state parametter returned by the visitor is passed to each children. /// The state parametter returned by the visitor is passed to each children.
pub fn visit_items<State>( pub fn visit_items<State>(
component: ComponentRefPin, component: ComponentRefPin,
mut visitor: impl FnMut(&EvaluationContext, Pin<ItemRef>, &State) -> State, mut visitor: impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> State,
state: State, state: State,
) { ) {
let context = EvaluationContext::for_root_component(component); visit_internal(component, &mut visitor, -1, &state)
visit_internal(&context, &mut visitor, -1, &state)
} }
fn visit_internal<State>( fn visit_internal<State>(
context: &EvaluationContext, component: ComponentRefPin,
visitor: &mut impl FnMut(&EvaluationContext, Pin<ItemRef>, &State) -> State, visitor: &mut impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> State,
index: isize, index: isize,
state: &State, state: &State,
) { ) {
let mut actual_visitor = |component: ComponentRefPin, index: isize, item: Pin<ItemRef>| { let mut actual_visitor = |component: ComponentRefPin, index: isize, item: Pin<ItemRef>| {
if component.as_ptr() == context.component.as_ptr() { let s = visitor(component, item, state);
let s = visitor(context, item, state); visit_internal(component, visitor, index, &s);
visit_internal(context, visitor, index, &s);
} else {
let context = context.child_context(component);
let s = visitor(&context, item, state);
visit_internal(&context, visitor, index, &s);
}
}; };
vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor); vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
context.component.as_ref().visit_children_item(index, actual_visitor); component.as_ref().visit_children_item(index, actual_visitor);
} }
/// Visit the children within an array of ItemTreeNode /// Visit the children within an array of ItemTreeNode

View file

@ -44,7 +44,7 @@ pub use abi::sharedarray::SharedArray;
pub use abi::datastructures::Resource; pub use abi::datastructures::Resource;
#[doc(inline)] #[doc(inline)]
pub use abi::properties::{EvaluationContext, Property}; pub use abi::properties::Property;
#[doc(inline)] #[doc(inline)]
pub use abi::signals::Signal; pub use abi::signals::Signal;

View file

@ -27,7 +27,7 @@ declare_ValueType![
]; ];
pub trait PropertyInfo<Item, Value> { pub trait PropertyInfo<Item, Value> {
fn get(&self, item: Pin<&Item>, context: &crate::EvaluationContext) -> Result<Value, ()>; fn get(&self, item: Pin<&Item>) -> Result<Value, ()>;
fn set( fn set(
&self, &self,
item: Pin<&Item>, item: Pin<&Item>,
@ -37,7 +37,7 @@ pub trait PropertyInfo<Item, Value> {
fn set_binding( fn set_binding(
&self, &self,
item: Pin<&Item>, item: Pin<&Item>,
binding: Box<dyn Fn(&crate::EvaluationContext) -> Value>, binding: Box<dyn Fn() -> Value>,
animation: Option<crate::abi::primitives::PropertyAnimation>, animation: Option<crate::abi::primitives::PropertyAnimation>,
) -> Result<(), ()>; ) -> Result<(), ()>;
@ -61,8 +61,8 @@ where
Value: TryInto<T>, Value: TryInto<T>,
T: TryInto<Value>, T: TryInto<Value>,
{ {
fn get(&self, item: Pin<&Item>, context: &crate::EvaluationContext) -> Result<Value, ()> { fn get(&self, item: Pin<&Item>) -> Result<Value, ()> {
self.apply_pin(item).get(context).try_into().map_err(|_| ()) self.apply_pin(item).get().try_into().map_err(|_| ())
} }
fn set( fn set(
&self, &self,
@ -80,14 +80,14 @@ where
fn set_binding( fn set_binding(
&self, &self,
item: Pin<&Item>, item: Pin<&Item>,
binding: Box<dyn Fn(&crate::EvaluationContext) -> Value>, binding: Box<dyn Fn() -> Value>,
animation: Option<crate::abi::primitives::PropertyAnimation>, animation: Option<crate::abi::primitives::PropertyAnimation>,
) -> Result<(), ()> { ) -> Result<(), ()> {
if animation.is_some() { if animation.is_some() {
Err(()) Err(())
} else { } else {
self.apply_pin(item).set_binding(move |context| { self.apply_pin(item).set_binding(move || {
binding(context).try_into().map_err(|_| ()).expect("binding was of the wrong type") binding().try_into().map_err(|_| ()).expect("binding was of the wrong type")
}); });
Ok(()) Ok(())
} }
@ -109,8 +109,8 @@ where
T: TryInto<Value>, T: TryInto<Value>,
T: crate::abi::properties::InterpolatedPropertyValue, T: crate::abi::properties::InterpolatedPropertyValue,
{ {
fn get(&self, item: Pin<&Item>, context: &crate::EvaluationContext) -> Result<Value, ()> { fn get(&self, item: Pin<&Item>) -> Result<Value, ()> {
self.0.get(item, context) self.0.get(item)
} }
fn set( fn set(
&self, &self,
@ -128,16 +128,13 @@ where
fn set_binding( fn set_binding(
&self, &self,
item: Pin<&Item>, item: Pin<&Item>,
binding: Box<dyn Fn(&crate::EvaluationContext) -> Value>, binding: Box<dyn Fn() -> Value>,
animation: Option<crate::abi::primitives::PropertyAnimation>, animation: Option<crate::abi::primitives::PropertyAnimation>,
) -> Result<(), ()> { ) -> Result<(), ()> {
if let Some(animation) = &animation { if let Some(animation) = &animation {
self.apply_pin(item).set_animated_binding( self.apply_pin(item).set_animated_binding(
move |context| { move || {
binding(context) binding().try_into().map_err(|_| ()).expect("binding was of the wrong type")
.try_into()
.map_err(|_| ())
.expect("binding was of the wrong type")
}, },
animation, animation,
); );

View file

@ -13,7 +13,7 @@ use sixtyfps_corelib::abi::primitives::PropertyAnimation;
use sixtyfps_corelib::abi::{properties::PropertyListenerScope, slice::Slice}; use sixtyfps_corelib::abi::{properties::PropertyListenerScope, slice::Slice};
use sixtyfps_corelib::rtti::PropertyInfo; use sixtyfps_corelib::rtti::PropertyInfo;
use sixtyfps_corelib::ComponentRefPin; use sixtyfps_corelib::ComponentRefPin;
use sixtyfps_corelib::{rtti, Color, EvaluationContext, Property, SharedString, Signal}; use sixtyfps_corelib::{rtti, Color, Property, SharedString, Signal};
use std::collections::HashMap; use std::collections::HashMap;
use std::{pin::Pin, rc::Rc}; use std::{pin::Pin, rc::Rc};
@ -129,28 +129,24 @@ unsafe extern "C" fn visit_children_item(
&*(component.as_ptr().add(listener_offset) as *const PropertyListenerScope), &*(component.as_ptr().add(listener_offset) as *const PropertyListenerScope),
); );
if listener.is_dirty() { if listener.is_dirty() {
let eval_context = EvaluationContext::for_root_component(component);
listener.evaluate(|| { listener.evaluate(|| {
match eval::eval_expression( match eval::eval_expression(&rep_in_comp.model, &*component_type, component)
&rep_in_comp.model, {
&*component_type,
&eval_context,
) {
crate::Value::Number(count) => populate_model( crate::Value::Number(count) => populate_model(
vec, vec,
rep_in_comp, rep_in_comp,
&eval_context, component,
(0..count as i32) (0..count as i32)
.into_iter() .into_iter()
.map(|v| crate::Value::Number(v as f64)), .map(|v| crate::Value::Number(v as f64)),
), ),
crate::Value::Array(a) => { crate::Value::Array(a) => {
populate_model(vec, rep_in_comp, &eval_context, a.into_iter()) populate_model(vec, rep_in_comp, component, a.into_iter())
} }
crate::Value::Bool(b) => populate_model( crate::Value::Bool(b) => populate_model(
vec, vec,
rep_in_comp, rep_in_comp,
&eval_context, component,
(if b { Some(crate::Value::Void) } else { None }).into_iter(), (if b { Some(crate::Value::Void) } else { None }).into_iter(),
), ),
_ => panic!("Unsupported model"), _ => panic!("Unsupported model"),
@ -370,7 +366,7 @@ fn generate_component(
fn animation_for_property( fn animation_for_property(
component_type: Rc<ComponentDescription>, component_type: Rc<ComponentDescription>,
eval_context: &EvaluationContext, eval_context: ComponentRefPin,
all_animations: &HashMap<String, ElementRc>, all_animations: &HashMap<String, ElementRc>,
property_name: &String, property_name: &String,
) -> Option<PropertyAnimation> { ) -> Option<PropertyAnimation> {
@ -386,7 +382,7 @@ fn animation_for_property(
fn animation_for_element_property( fn animation_for_element_property(
component_type: Rc<ComponentDescription>, component_type: Rc<ComponentDescription>,
eval_context: &EvaluationContext, eval_context: ComponentRefPin,
element: &Element, element: &Element,
property_name: &String, property_name: &String,
) -> Option<PropertyAnimation> { ) -> Option<PropertyAnimation> {
@ -401,11 +397,11 @@ fn animation_for_element_property(
fn populate_model( fn populate_model(
vec: &mut Vec<ComponentBox>, vec: &mut Vec<ComponentBox>,
rep_in_comp: &RepeaterWithinComponent, rep_in_comp: &RepeaterWithinComponent,
eval_context: &EvaluationContext, component: ComponentRefPin,
model: impl Iterator<Item = eval::Value> + ExactSizeIterator, model: impl Iterator<Item = eval::Value> + ExactSizeIterator,
) { ) {
vec.resize_with(model.size_hint().1.unwrap(), || { vec.resize_with(model.size_hint().1.unwrap(), || {
instantiate(rep_in_comp.component_to_repeat.clone(), Some(eval_context)) instantiate(rep_in_comp.component_to_repeat.clone(), Some(component))
}); });
for (i, (x, val)) in vec.iter().zip(model).enumerate() { for (i, (x, val)) in vec.iter().zip(model).enumerate() {
rep_in_comp rep_in_comp
@ -418,20 +414,17 @@ fn populate_model(
pub fn instantiate( pub fn instantiate(
component_type: Rc<ComponentDescription>, component_type: Rc<ComponentDescription>,
parent_ctx: Option<&EvaluationContext>, parent_ctx: Option<ComponentRefPin>,
) -> ComponentBox { ) -> ComponentBox {
let instance = component_type.dynamic_type.clone().create_instance(); let instance = component_type.dynamic_type.clone().create_instance();
let mem = instance.as_ptr().as_ptr() as *mut u8; let mem = instance.as_ptr().as_ptr() as *mut u8;
let component_box = ComponentBox { instance, component_type: component_type.clone() }; let component_box = ComponentBox { instance, component_type: component_type.clone() };
let eval_context = if let Some(parent) = parent_ctx { if let Some(parent) = parent_ctx {
unsafe { unsafe {
*(mem.add(component_type.parent_component_offset.unwrap()) *(mem.add(component_type.parent_component_offset.unwrap())
as *mut Option<ComponentRefPin>) = Some(parent.component); as *mut Option<ComponentRefPin>) = Some(parent);
} }
parent.child_context(component_box.borrow())
} else {
EvaluationContext::for_root_component(component_box.borrow())
}; };
for item_within_component in component_type.items.values() { for item_within_component in component_type.items.values() {
@ -453,8 +446,13 @@ pub fn instantiate(
as *mut Signal<()>); as *mut Signal<()>);
let expr = expr.clone(); let expr = expr.clone();
let component_type = component_type.clone(); let component_type = component_type.clone();
signal.set_handler(move |eval_context, _| { let instance = component_box.instance.as_ptr();
eval::eval_expression(&expr, &*component_type, &eval_context); signal.set_handler(move |_| {
let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(),
instance.cast(),
));
eval::eval_expression(&expr, &*component_type, c);
}) })
} else { } else {
if let Some(prop_rtti) = if let Some(prop_rtti) =
@ -462,7 +460,7 @@ pub fn instantiate(
{ {
let maybe_animation = animation_for_element_property( let maybe_animation = animation_for_element_property(
component_type.clone(), component_type.clone(),
&eval_context, component_box.borrow(),
&elem, &elem,
prop, prop,
); );
@ -470,17 +468,26 @@ pub fn instantiate(
if expr.is_constant() { if expr.is_constant() {
prop_rtti.set( prop_rtti.set(
item, item,
eval::eval_expression(expr, &*component_type, &eval_context), eval::eval_expression(
expr,
&*component_type,
component_box.borrow(),
),
maybe_animation, maybe_animation,
); );
} else { } else {
let expr = expr.clone(); let expr = expr.clone();
let component_type = component_type.clone(); let component_type = component_type.clone();
let instance = component_box.instance.as_ptr();
prop_rtti.set_binding( prop_rtti.set_binding(
item, item,
Box::new(move |eval_context| { Box::new(move || {
eval::eval_expression(&expr, &*component_type, eval_context) let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(),
instance.cast(),
));
eval::eval_expression(&expr, &*component_type, c)
}), }),
maybe_animation, maybe_animation,
); );
@ -491,24 +498,33 @@ pub fn instantiate(
{ {
let maybe_animation = animation_for_property( let maybe_animation = animation_for_property(
component_type.clone(), component_type.clone(),
&eval_context, component_box.borrow(),
&component_type.original.root_element.borrow().property_animations, &component_type.original.root_element.borrow().property_animations,
prop, prop,
); );
if expr.is_constant() { if expr.is_constant() {
let v = eval::eval_expression(expr, &*component_type, &eval_context); let v = eval::eval_expression(
expr,
&*component_type,
component_box.borrow(),
);
prop_info prop_info
.set(Pin::new_unchecked(&*mem.add(*offset)), v, maybe_animation) .set(Pin::new_unchecked(&*mem.add(*offset)), v, maybe_animation)
.unwrap(); .unwrap();
} else { } else {
let expr = expr.clone(); let expr = expr.clone();
let component_type = component_type.clone(); let component_type = component_type.clone();
let instance = component_box.instance.as_ptr();
prop_info prop_info
.set_binding( .set_binding(
Pin::new_unchecked(&*mem.add(*offset)), Pin::new_unchecked(&*mem.add(*offset)),
Box::new(move |eval_context| { Box::new(move || {
eval::eval_expression(&expr, &*component_type, eval_context) let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(),
instance.cast(),
));
eval::eval_expression(&expr, &*component_type, c)
}), }),
maybe_animation, maybe_animation,
) )
@ -527,20 +543,20 @@ pub fn instantiate(
continue; continue;
} }
let vec = unsafe { &mut *(mem.add(rep_in_comp.offset) as *mut RepeaterVec) }; let vec = unsafe { &mut *(mem.add(rep_in_comp.offset) as *mut RepeaterVec) };
match eval::eval_expression(&rep_in_comp.model, &*component_type, &eval_context) { match eval::eval_expression(&rep_in_comp.model, &*component_type, component_box.borrow()) {
crate::Value::Number(count) => populate_model( crate::Value::Number(count) => populate_model(
vec, vec,
rep_in_comp, rep_in_comp,
&eval_context, component_box.borrow(),
(0..count as i32).into_iter().map(|v| crate::Value::Number(v as f64)), (0..count as i32).into_iter().map(|v| crate::Value::Number(v as f64)),
), ),
crate::Value::Array(a) => { crate::Value::Array(a) => {
populate_model(vec, rep_in_comp, &eval_context, a.into_iter()) populate_model(vec, rep_in_comp, component_box.borrow(), a.into_iter())
} }
crate::Value::Bool(b) => populate_model( crate::Value::Bool(b) => populate_model(
vec, vec,
rep_in_comp, rep_in_comp,
&eval_context, component_box.borrow(),
(if b { Some(crate::Value::Void) } else { None }).into_iter(), (if b { Some(crate::Value::Void) } else { None }).into_iter(),
), ),
_ => panic!("Unsupported model"), _ => panic!("Unsupported model"),
@ -550,17 +566,13 @@ pub fn instantiate(
component_box component_box
} }
unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &EvaluationContext) { unsafe extern "C" fn compute_layout(component: ComponentRefPin) {
debug_assert!(component.as_ptr() == eval_context.component.as_ptr());
// This is fine since we can only be called with a component that with our vtable which is a ComponentDescription // This is fine since we can only be called with a component that with our vtable which is a ComponentDescription
let component_type = let component_type =
&*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription); &*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription);
let resolve_prop_ref = |prop_ref: &expression_tree::Expression| { let resolve_prop_ref = |prop_ref: &expression_tree::Expression| {
eval::eval_expression(&prop_ref, &component_type, eval_context) eval::eval_expression(&prop_ref, &component_type, component).try_into().unwrap_or_default()
.try_into()
.unwrap_or_default()
}; };
for it in &component_type.original.layout_constraints.borrow().grids { for it in &component_type.original.layout_constraints.borrow().grids {
@ -605,7 +617,7 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
let within_info = &component_type.items[it.within.borrow().id.as_str()]; let within_info = &component_type.items[it.within.borrow().id.as_str()];
let within_prop = |name| { let within_prop = |name| {
within_info.rtti.properties[name] within_info.rtti.properties[name]
.get(within_info.item_from_component(component.as_ptr()), &eval_context) .get(within_info.item_from_component(component.as_ptr()))
.try_into() .try_into()
.unwrap() .unwrap()
}; };
@ -639,7 +651,7 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let path_elements = eval::convert_path(&it.path, component_type, eval_context); let path_elements = eval::convert_path(&it.path, component_type, component);
solve_path_layout(&PathLayoutData { solve_path_layout(&PathLayoutData {
items: Slice::from(items.as_slice()), items: Slice::from(items.as_slice()),

View file

@ -7,18 +7,18 @@ use sixtyfps_compilerlib::{object_tree::ElementRc, typeregister::Type};
use sixtyfps_corelib as corelib; use sixtyfps_corelib as corelib;
use sixtyfps_corelib::{ use sixtyfps_corelib::{
abi::datastructures::ItemRef, abi::datastructures::PathElement, abi::datastructures::ItemRef, abi::datastructures::PathElement,
abi::primitives::PropertyAnimation, Color, EvaluationContext, PathData, Resource, SharedArray, abi::primitives::PropertyAnimation, Color, ComponentRefPin, PathData, Resource, SharedArray,
SharedString, SharedString,
}; };
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
pub trait ErasedPropertyInfo { pub trait ErasedPropertyInfo {
fn get(&self, item: Pin<ItemRef>, context: &EvaluationContext) -> Value; fn get(&self, item: Pin<ItemRef>) -> Value;
fn set(&self, item: Pin<ItemRef>, value: Value, animation: Option<PropertyAnimation>); fn set(&self, item: Pin<ItemRef>, value: Value, animation: Option<PropertyAnimation>);
fn set_binding( fn set_binding(
&self, &self,
item: Pin<ItemRef>, item: Pin<ItemRef>,
binding: Box<dyn Fn(&EvaluationContext) -> Value>, binding: Box<dyn Fn() -> Value>,
animation: Option<PropertyAnimation>, animation: Option<PropertyAnimation>,
); );
fn offset(&self) -> usize; fn offset(&self) -> usize;
@ -27,8 +27,8 @@ pub trait ErasedPropertyInfo {
impl<Item: vtable::HasStaticVTable<corelib::abi::datastructures::ItemVTable>> ErasedPropertyInfo impl<Item: vtable::HasStaticVTable<corelib::abi::datastructures::ItemVTable>> ErasedPropertyInfo
for &'static dyn corelib::rtti::PropertyInfo<Item, Value> for &'static dyn corelib::rtti::PropertyInfo<Item, Value>
{ {
fn get(&self, item: Pin<ItemRef>, context: &EvaluationContext) -> Value { fn get(&self, item: Pin<ItemRef>) -> Value {
(*self).get(ItemRef::downcast_pin(item).unwrap(), context).unwrap() (*self).get(ItemRef::downcast_pin(item).unwrap()).unwrap()
} }
fn set(&self, item: Pin<ItemRef>, value: Value, animation: Option<PropertyAnimation>) { fn set(&self, item: Pin<ItemRef>, value: Value, animation: Option<PropertyAnimation>) {
(*self).set(ItemRef::downcast_pin(item).unwrap(), value, animation).unwrap() (*self).set(ItemRef::downcast_pin(item).unwrap(), value, animation).unwrap()
@ -36,7 +36,7 @@ impl<Item: vtable::HasStaticVTable<corelib::abi::datastructures::ItemVTable>> Er
fn set_binding( fn set_binding(
&self, &self,
item: Pin<ItemRef>, item: Pin<ItemRef>,
binding: Box<dyn Fn(&EvaluationContext) -> Value>, binding: Box<dyn Fn() -> Value>,
animation: Option<PropertyAnimation>, animation: Option<PropertyAnimation>,
) { ) {
(*self).set_binding(ItemRef::downcast_pin(item).unwrap(), binding, animation).unwrap(); (*self).set_binding(ItemRef::downcast_pin(item).unwrap(), binding, animation).unwrap();
@ -119,7 +119,7 @@ declare_value_conversion!(PathElements => [PathData]);
pub fn eval_expression( pub fn eval_expression(
e: &Expression, e: &Expression,
component_type: &crate::ComponentDescription, component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext, component_ref: ComponentRefPin,
) -> Value { ) -> Value {
match e { match e {
Expression::Invalid => panic!("invalid expression while evaluating"), Expression::Invalid => panic!("invalid expression while evaluating"),
@ -130,23 +130,21 @@ pub fn eval_expression(
Expression::SignalReference { .. } => panic!("signal in expression"), Expression::SignalReference { .. } => panic!("signal in expression"),
Expression::PropertyReference(NamedReference { element, name }) => { Expression::PropertyReference(NamedReference { element, name }) => {
let element = element.upgrade().unwrap(); let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) = let (component_mem, component_type, _) =
enclosing_component_for_element(&element, component_type, eval_context); enclosing_component_for_element(&element, component_type, component_ref);
let element = element.borrow(); let element = element.borrow();
if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
{ {
if let Some(x) = component_type.custom_properties.get(name) { if let Some(x) = component_type.custom_properties.get(name) {
return unsafe { return unsafe {
x.prop x.prop.get(Pin::new_unchecked(&*component_mem.add(x.offset))).unwrap()
.get(Pin::new_unchecked(&*component_mem.add(x.offset)), &eval_context)
.unwrap()
}; };
} }
}; };
let item_info = &component_type.items[element.id.as_str()]; let item_info = &component_type.items[element.id.as_str()];
core::mem::drop(element); core::mem::drop(element);
let item = unsafe { item_info.item_from_component(component_mem) }; let item = unsafe { item_info.item_from_component(component_mem) };
item_info.rtti.properties[name.as_str()].get(item, &eval_context) item_info.rtti.properties[name.as_str()].get(item)
} }
Expression::RepeaterIndexReference { element } => { Expression::RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type if element.upgrade().unwrap().borrow().base_type
@ -154,12 +152,7 @@ pub fn eval_expression(
{ {
let x = &component_type.custom_properties["index"]; let x = &component_type.custom_properties["index"];
unsafe { unsafe {
x.prop x.prop.get(Pin::new_unchecked(&*component_ref.as_ptr().add(x.offset))).unwrap()
.get(
Pin::new_unchecked(&*eval_context.component.as_ptr().add(x.offset)),
&eval_context,
)
.unwrap()
} }
} else { } else {
todo!(); todo!();
@ -171,26 +164,21 @@ pub fn eval_expression(
{ {
let x = &component_type.custom_properties["model_data"]; let x = &component_type.custom_properties["model_data"];
unsafe { unsafe {
x.prop x.prop.get(Pin::new_unchecked(&*component_ref.as_ptr().add(x.offset))).unwrap()
.get(
Pin::new_unchecked(&*eval_context.component.as_ptr().add(x.offset)),
&eval_context,
)
.unwrap()
} }
} else { } else {
todo!(); todo!();
} }
} }
Expression::ObjectAccess { base, name } => { Expression::ObjectAccess { base, name } => {
if let Value::Object(mut o) = eval_expression(base, component_type, eval_context) { if let Value::Object(mut o) = eval_expression(base, component_type, component_ref) {
o.remove(name).unwrap_or(Value::Void) o.remove(name).unwrap_or(Value::Void)
} else { } else {
Value::Void Value::Void
} }
} }
Expression::Cast { from, to } => { Expression::Cast { from, to } => {
let v = eval_expression(&*from, component_type, eval_context); let v = eval_expression(&*from, component_type, component_ref);
match (v, to) { match (v, to) {
(Value::Number(n), Type::Int32) => Value::Number(n.round()), (Value::Number(n), Type::Int32) => Value::Number(n.round()),
(Value::Number(n), Type::String) => { (Value::Number(n), Type::String) => {
@ -203,15 +191,15 @@ pub fn eval_expression(
Expression::CodeBlock(sub) => { Expression::CodeBlock(sub) => {
let mut v = Value::Void; let mut v = Value::Void;
for e in sub { for e in sub {
v = eval_expression(e, component_type, eval_context); v = eval_expression(e, component_type, component_ref);
} }
v v
} }
Expression::FunctionCall { function, .. } => { Expression::FunctionCall { function, .. } => {
if let Expression::SignalReference(NamedReference { element, name }) = &**function { if let Expression::SignalReference(NamedReference { element, name }) = &**function {
let element = element.upgrade().unwrap(); let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) = let (component_mem, component_type, _) =
enclosing_component_for_element(&element, component_type, eval_context); enclosing_component_for_element(&element, component_type, component_ref);
let item_info = &component_type.items[element.borrow().id.as_str()]; let item_info = &component_type.items[element.borrow().id.as_str()];
let item = unsafe { item_info.item_from_component(component_mem) }; let item = unsafe { item_info.item_from_component(component_mem) };
@ -230,7 +218,7 @@ pub fn eval_expression(
.unwrap_or_else(|| panic!("unkown signal {}", name)) .unwrap_or_else(|| panic!("unkown signal {}", name))
as *mut corelib::Signal<()>) as *mut corelib::Signal<()>)
}; };
signal.emit(&eval_context, ()); signal.emit(());
Value::Void Value::Void
} else { } else {
panic!("call of something not a signal") panic!("call of something not a signal")
@ -239,7 +227,7 @@ pub fn eval_expression(
Expression::SelfAssignment { lhs, rhs, op } => match &**lhs { Expression::SelfAssignment { lhs, rhs, op } => match &**lhs {
Expression::PropertyReference(NamedReference { element, name }) => { Expression::PropertyReference(NamedReference { element, name }) => {
let eval = |lhs| { let eval = |lhs| {
let rhs = eval_expression(&**rhs, component_type, eval_context); let rhs = eval_expression(&**rhs, component_type, component_ref);
match (lhs, rhs, op) { match (lhs, rhs, op) {
(Value::Number(a), Value::Number(b), '+') => Value::Number(a + b), (Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
(Value::Number(a), Value::Number(b), '-') => Value::Number(a - b), (Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
@ -250,17 +238,15 @@ pub fn eval_expression(
}; };
let element = element.upgrade().unwrap(); let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) = let (component_mem, component_type, _) =
enclosing_component_for_element(&element, component_type, eval_context); enclosing_component_for_element(&element, component_type, component_ref);
let component = element.borrow().enclosing_component.upgrade().unwrap(); let component = element.borrow().enclosing_component.upgrade().unwrap();
if element.borrow().id == component.root_element.borrow().id { if element.borrow().id == component.root_element.borrow().id {
if let Some(x) = component_type.custom_properties.get(name) { if let Some(x) = component_type.custom_properties.get(name) {
unsafe { unsafe {
let p = Pin::new_unchecked(&*component_mem.add(x.offset)); let p = Pin::new_unchecked(&*component_mem.add(x.offset));
x.prop x.prop.set(p, eval(x.prop.get(p).unwrap()), None).unwrap();
.set(p, eval(x.prop.get(p, &eval_context).unwrap()), None)
.unwrap();
} }
return Value::Void; return Value::Void;
} }
@ -268,14 +254,14 @@ pub fn eval_expression(
let item_info = &component_type.items[element.borrow().id.as_str()]; let item_info = &component_type.items[element.borrow().id.as_str()];
let item = unsafe { item_info.item_from_component(component_mem) }; let item = unsafe { item_info.item_from_component(component_mem) };
let p = &item_info.rtti.properties[name.as_str()]; let p = &item_info.rtti.properties[name.as_str()];
p.set(item, eval(p.get(item, &eval_context)), None); p.set(item, eval(p.get(item)), None);
Value::Void Value::Void
} }
_ => panic!("typechecking should make sure this was a PropertyReference"), _ => panic!("typechecking should make sure this was a PropertyReference"),
}, },
Expression::BinaryExpression { lhs, rhs, op } => { Expression::BinaryExpression { lhs, rhs, op } => {
let lhs = eval_expression(&**lhs, component_type, eval_context); let lhs = eval_expression(&**lhs, component_type, component_ref);
let rhs = eval_expression(&**rhs, component_type, eval_context); let rhs = eval_expression(&**rhs, component_type, component_ref);
match (op, lhs, rhs) { match (op, lhs, rhs) {
('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b), ('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b),
@ -294,7 +280,7 @@ pub fn eval_expression(
} }
} }
Expression::UnaryOp { sub, op } => { Expression::UnaryOp { sub, op } => {
let sub = eval_expression(&**sub, component_type, eval_context); let sub = eval_expression(&**sub, component_type, component_ref);
match (sub, op) { match (sub, op) {
(Value::Number(a), '+') => Value::Number(a), (Value::Number(a), '+') => Value::Number(a),
(Value::Number(a), '-') => Value::Number(-a), (Value::Number(a), '-') => Value::Number(-a),
@ -306,25 +292,25 @@ pub fn eval_expression(
Value::Resource(Resource::AbsoluteFilePath(absolute_source_path.as_str().into())) Value::Resource(Resource::AbsoluteFilePath(absolute_source_path.as_str().into()))
} }
Expression::Condition { condition, true_expr, false_expr } => { Expression::Condition { condition, true_expr, false_expr } => {
match eval_expression(&**condition, component_type, eval_context).try_into() match eval_expression(&**condition, component_type, component_ref).try_into()
as Result<bool, _> as Result<bool, _>
{ {
Ok(true) => eval_expression(&**true_expr, component_type, eval_context), Ok(true) => eval_expression(&**true_expr, component_type, component_ref),
Ok(false) => eval_expression(&**false_expr, component_type, eval_context), Ok(false) => eval_expression(&**false_expr, component_type, component_ref),
_ => panic!("conditional expression did not evaluate to boolean"), _ => panic!("conditional expression did not evaluate to boolean"),
} }
} }
Expression::Array { values, .. } => Value::Array( Expression::Array { values, .. } => Value::Array(
values.iter().map(|e| eval_expression(e, component_type, eval_context)).collect(), values.iter().map(|e| eval_expression(e, component_type, component_ref)).collect(),
), ),
Expression::Object { values, .. } => Value::Object( Expression::Object { values, .. } => Value::Object(
values values
.iter() .iter()
.map(|(k, v)| (k.clone(), eval_expression(v, component_type, eval_context))) .map(|(k, v)| (k.clone(), eval_expression(v, component_type, component_ref)))
.collect(), .collect(),
), ),
Expression::PathElements { elements } => { Expression::PathElements { elements } => {
Value::PathElements(convert_path(elements, component_type, eval_context)) Value::PathElements(convert_path(elements, component_type, component_ref))
} }
} }
} }
@ -332,16 +318,16 @@ pub fn eval_expression(
fn enclosing_component_for_element<'a>( fn enclosing_component_for_element<'a>(
element: &ElementRc, element: &ElementRc,
component_type: &'a crate::ComponentDescription, component_type: &'a crate::ComponentDescription,
eval_context: &corelib::EvaluationContext<'a>, component_ref: ComponentRefPin<'a>,
) -> (*const u8, &'a crate::ComponentDescription, corelib::EvaluationContext<'a>) { ) -> (*const u8, &'a crate::ComponentDescription, ComponentRefPin<'a>) {
if Rc::ptr_eq( if Rc::ptr_eq(
&element.borrow().enclosing_component.upgrade().unwrap(), &element.borrow().enclosing_component.upgrade().unwrap(),
&component_type.original, &component_type.original,
) { ) {
let mem = eval_context.component.as_ptr(); let mem = component_ref.as_ptr();
(mem, component_type, EvaluationContext::for_root_component(eval_context.component)) (mem, component_type, component_ref)
} else { } else {
let mem = eval_context.component.as_ptr(); let mem = component_ref.as_ptr();
let parent_component = unsafe { let parent_component = unsafe {
*(mem.add(component_type.parent_component_offset.unwrap()) *(mem.add(component_type.parent_component_offset.unwrap())
as *const Option<corelib::ComponentRefPin>) as *const Option<corelib::ComponentRefPin>)
@ -349,11 +335,7 @@ fn enclosing_component_for_element<'a>(
.unwrap(); .unwrap();
let parent_component_type = let parent_component_type =
unsafe { crate::dynamic_component::get_component_type(parent_component) }; unsafe { crate::dynamic_component::get_component_type(parent_component) };
enclosing_component_for_element( enclosing_component_for_element(element, parent_component_type, parent_component)
element,
parent_component_type,
&EvaluationContext::for_root_component(parent_component),
)
} }
} }
@ -362,12 +344,12 @@ pub fn new_struct_with_bindings<
>( >(
bindings: &HashMap<String, Expression>, bindings: &HashMap<String, Expression>,
component_type: &crate::ComponentDescription, component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext, component_ref: ComponentRefPin,
) -> ElementType { ) -> ElementType {
let mut element = ElementType::default(); let mut element = ElementType::default();
for (prop, info) in ElementType::fields::<Value>().into_iter() { for (prop, info) in ElementType::fields::<Value>().into_iter() {
if let Some(binding) = &bindings.get(prop) { if let Some(binding) = &bindings.get(prop) {
let value = eval_expression(&binding, &*component_type, &eval_context); let value = eval_expression(&binding, &*component_type, component_ref);
info.set_field(&mut element, value).unwrap(); info.set_field(&mut element, value).unwrap();
} }
} }
@ -428,7 +410,7 @@ fn convert_from_lyon_path<'a>(
pub fn convert_path( pub fn convert_path(
path: &ExprPath, path: &ExprPath,
component_type: &crate::ComponentDescription, component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext, eval_context: ComponentRefPin,
) -> PathData { ) -> PathData {
match path { match path {
ExprPath::Elements(elements) => PathData::Elements(SharedArray::<PathElement>::from_iter( ExprPath::Elements(elements) => PathData::Elements(SharedArray::<PathElement>::from_iter(
@ -443,7 +425,7 @@ pub fn convert_path(
fn convert_path_element( fn convert_path_element(
expr_element: &ExprPathElement, expr_element: &ExprPathElement,
component_type: &crate::ComponentDescription, component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext, eval_context: ComponentRefPin,
) -> PathElement { ) -> PathElement {
match expr_element.element_type.class_name.as_str() { match expr_element.element_type.class_name.as_str() {
"LineTo" => PathElement::LineTo(new_struct_with_bindings( "LineTo" => PathElement::LineTo(new_struct_with_bindings(

View file

@ -16,7 +16,7 @@ pub use eval::Value;
pub use dynamic_component::ComponentBox; pub use dynamic_component::ComponentBox;
use sixtyfps_corelib::abi::datastructures::{ComponentRef, ComponentRefMut}; use sixtyfps_corelib::abi::datastructures::{ComponentRef, ComponentRefMut};
use sixtyfps_corelib::{ComponentRefPin, EvaluationContext, Signal}; use sixtyfps_corelib::{ComponentRefPin, Signal};
use std::{collections::HashMap, pin::Pin, rc::Rc}; use std::{collections::HashMap, pin::Pin, rc::Rc};
impl ComponentDescription { impl ComponentDescription {
@ -66,7 +66,7 @@ impl ComponentDescription {
&self, &self,
component: ComponentRef, component: ComponentRef,
name: &str, name: &str,
binding: Box<dyn Fn(&EvaluationContext) -> Value>, binding: Box<dyn Fn() -> 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(());
@ -84,17 +84,12 @@ impl ComponentDescription {
/// ///
/// Returns an error if the component is not an instance corresponding to this ComponentDescription, /// Returns an error if the component is not an instance corresponding to this ComponentDescription,
/// or if a signal with this name does not exist in this component /// or if a signal with this name does not exist in this component
pub fn get_property(&self, eval_context: &EvaluationContext, name: &str) -> Result<Value, ()> { pub fn get_property(&self, component: ComponentRefPin, name: &str) -> Result<Value, ()> {
if !core::ptr::eq((&self.ct) as *const _, eval_context.component.get_vtable() as *const _) { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
return Err(()); return Err(());
} }
let x = self.custom_properties.get(name).ok_or(())?; let x = self.custom_properties.get(name).ok_or(())?;
unsafe { unsafe { x.prop.get(Pin::new_unchecked(&*component.as_ptr().add(x.offset))) }
x.prop.get(
Pin::new_unchecked(&*eval_context.component.as_ptr().add(x.offset)),
eval_context,
)
}
} }
/// Sets an handler for a signal /// Sets an handler for a signal
@ -105,7 +100,7 @@ impl ComponentDescription {
&self, &self,
component: Pin<ComponentRefMut>, component: Pin<ComponentRefMut>,
name: &str, name: &str,
handler: Box<dyn Fn(&EvaluationContext, ())>, handler: Box<dyn Fn(())>,
) -> 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(());
@ -120,14 +115,13 @@ impl ComponentDescription {
/// ///
/// Returns an error if the component is not an instance corresponding to this ComponentDescription, /// Returns an error if the component is not an instance corresponding to this ComponentDescription,
/// or if the signal with this name does not exist in this component /// or if the signal with this name does not exist in this component
pub fn emit_signal(&self, eval_context: &EvaluationContext, name: &str) -> Result<(), ()> { pub fn emit_signal(&self, component: ComponentRefPin, name: &str) -> Result<(), ()> {
let component = eval_context.component;
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(());
} }
let x = self.custom_signals.get(name).ok_or(())?; let x = self.custom_signals.get(name).ok_or(())?;
let sig = unsafe { &mut *(component.as_ptr().add(*x) as *mut Signal<()>) }; let sig = unsafe { &mut *(component.as_ptr().add(*x) as *mut Signal<()>) };
sig.emit(eval_context, ()); sig.emit(());
Ok(()) Ok(())
} }
} }