Some refactoring of the rust generated code

Always use a Pin<Rc> for the component. (This is required to support repeater
within repeater as well anyway)
Do not use the context within the binding. We can get along by simply capturing
a weak pointer to the component
This commit is contained in:
Olivier Goffart 2020-07-13 15:34:09 +02:00
parent 1a0d053889
commit ab7ae9f3e2
15 changed files with 74 additions and 73 deletions

View file

@ -20,7 +20,7 @@ sixtyfps::sixtyfps!{
} }
fn main() { fn main() {
# return; // Don't run a window in an example # return; // Don't run a window in an example
HelloWorld::default().run() HelloWorld::new().run()
} }
``` ```
@ -56,7 +56,7 @@ Then in your main file
```ignore ```ignore
sixtyfps::include_modules!(); sixtyfps::include_modules!();
fn main() { fn main() {
HelloWorld::default().run() HelloWorld::new().run()
} }
``` ```
*/ */

View file

@ -1,4 +1,6 @@
use core::cell::RefCell; use core::cell::RefCell;
use core::pin::Pin;
use std::rc::Rc;
/// Component that can be instantiated by a repeater. /// Component that can be instantiated by a repeater.
pub trait RepeatedComponent: sixtyfps_corelib::abi::datastructures::Component { pub trait RepeatedComponent: sixtyfps_corelib::abi::datastructures::Component {
@ -11,9 +13,14 @@ pub trait RepeatedComponent: sixtyfps_corelib::abi::datastructures::Component {
/// This field is put in a component when using the `for` syntax /// This field is put in a component when using the `for` syntax
/// It helps instantiating the components `C` /// It helps instantiating the components `C`
#[derive(Default)]
pub struct Repeater<C> { pub struct Repeater<C> {
components: RefCell<Vec<core::pin::Pin<Box<C>>>>, components: RefCell<Vec<Pin<Rc<C>>>>,
}
impl<C> Default for Repeater<C> {
fn default() -> Self {
Repeater { components: Default::default() }
}
} }
impl<Data, C> Repeater<C> impl<Data, C> Repeater<C>
@ -21,7 +28,7 @@ where
C: RepeatedComponent<Data = Data>, C: RepeatedComponent<Data = Data>,
{ {
/// Called when the model is changed /// Called when the model is changed
pub fn update_model<'a>(&self, data: impl Iterator<Item = Data>, init: impl Fn() -> C) pub fn update_model<'a>(&self, data: impl Iterator<Item = Data>, init: impl Fn() -> Pin<Rc<C>>)
where where
Data: 'a, Data: 'a,
{ {
@ -29,7 +36,7 @@ where
for (i, d) in data.enumerate() { for (i, d) in data.enumerate() {
let c = init(); let c = init();
c.update(i, d); c.update(i, d);
self.components.borrow_mut().push(Box::pin(c)); self.components.borrow_mut().push(c);
} }
} }

View file

@ -127,7 +127,7 @@ Hello := Rectangle {
} }
fn main() { fn main() {
let mut app = Hello::default(); let app = Hello::new();
app.plus_clicked.set_handler(|context, ()| { app.plus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap(); let app = context.get_component::<Hello>().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app); let counter = Hello::field_offsets().counter.apply_pin(app);

View file

@ -3,7 +3,7 @@
sixtyfps::include_modules!(); sixtyfps::include_modules!();
fn main() { fn main() {
let mut app = Hello::default(); let app = Hello::new();
app.plus_clicked.set_handler(|context, ()| { app.plus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap(); let app = context.get_component::<Hello>().unwrap();

View file

@ -10,5 +10,5 @@ pub fn wasm_main() {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
Hello::default().run(); Hello::new().run();
} }

View file

@ -115,7 +115,6 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
let mut repeated_dynmodel_names = Vec::new(); let mut repeated_dynmodel_names = Vec::new();
let mut repeated_visit_branch = Vec::new(); let mut repeated_visit_branch = Vec::new();
let mut init = Vec::new(); let mut init = Vec::new();
let mut init_repeaters = Vec::new();
super::build_array_helper(component, |item_rc, children_index| { super::build_array_helper(component, |item_rc, children_index| {
let item = item_rc.borrow(); let item = item_rc.borrow();
if let Some(repeated) = &item.repeated { if let Some(repeated) = &item.repeated {
@ -166,11 +165,9 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
} }
if repeated.model.is_constant() { if repeated.model.is_constant() {
init_repeaters.push(quote! { init.push(quote! {
self_pinned.#repeater_id.update_model(#model, || { self_pinned.#repeater_id.update_model(#model, || {
let mut new_comp = #rep_component_id::default(); #rep_component_id::new(self_pinned.self_weak.get().unwrap().clone())
new_comp.parent = self_pinned.self_weak.get().unwrap().clone();
new_comp
}); });
}); });
repeated_visit_branch.push(quote!( repeated_visit_branch.push(quote!(
@ -189,9 +186,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
); );
let context = &context; let context = &context;
self_pinned.#repeater_id.update_model(#model, || { self_pinned.#repeater_id.update_model(#model, || {
let mut new_comp = #rep_component_id::default(); #rep_component_id::new(self_pinned.self_weak.get().unwrap().clone())
new_comp.parent = self_pinned.self_weak.get().unwrap().clone();
new_comp
}); });
}); });
} }
@ -231,40 +226,39 @@ 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_.#rust_property.set_handler(|context, ()| { self_pinned.#rust_property.set_handler(|context, ()| {
let _self = context.get_component::<#component_id>().unwrap(); let _self = context.get_component::<#component_id>().unwrap();
#tokens_for_expression; #tokens_for_expression;
}); });
)); ));
} else { } else {
if binding_expression.is_constant() { let setter = if binding_expression.is_constant() {
let setter = property_set_value_tokens( property_set_value_tokens(
component, component,
&item_rc, &item_rc,
k, k,
quote!((#tokens_for_expression) as _), quote!((#tokens_for_expression) as _),
); )
init.push(quote!(
self_.#rust_property.#setter;
));
} else { } else {
let setter = property_set_binding_tokens( property_set_binding_tokens(
component, component,
&item_rc, &item_rc,
k, k,
quote!( quote!({
|context| { let self_weak = sixtyfps::re_exports::WeakPin::downgrade(self_pinned.clone());
let _self = context.get_component::<#component_id>().unwrap(); move |context| {
let self_pinned = self_weak.upgrade().unwrap();
let _self = self_pinned.as_ref();
(#tokens_for_expression) as _ (#tokens_for_expression) as _
} }
), }),
); )
};
init.push(quote!( init.push(quote!(
self_.#rust_property.#setter; self_pinned.#rust_property.#setter;
)); ));
} }
} }
}
item_names.push(field_name); item_names.push(field_name);
item_types.push(quote::format_ident!("{}", item.base_type.as_builtin().class_name)); item_types.push(quote::format_ident!("{}", item.base_type.as_builtin().class_name));
} }
@ -312,15 +306,10 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
)); ));
} else { } else {
property_and_signal_accessors.push(quote! { property_and_signal_accessors.push(quote! {
fn run(self) { fn run(self : core::pin::Pin<std::rc::Rc<Self>>) {
use sixtyfps::re_exports::*; use sixtyfps::re_exports::*;
let window = sixtyfps::create_window(); let window = sixtyfps::create_window();
let self_pinned = Rc::pin(self); window.run(VRef::new_pin(self.as_ref()));
self_pinned.self_weak.set(WeakPin::downgrade(self_pinned.clone()))
.map_err(|_|())
.expect("Can only be pinned once");
#(#init_repeaters)*
window.run(VRef::new_pin(self_pinned.as_ref()));
} }
}); });
}; };
@ -349,25 +338,6 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
#(parent : sixtyfps::re_exports::WeakPin<#parent_component_type>,)* #(parent : sixtyfps::re_exports::WeakPin<#parent_component_type>,)*
} }
impl ::core::default::Default for #component_id {
fn default() -> Self {
#![allow(unused)]
use sixtyfps::re_exports::*;
ComponentVTable_static!(static VT for #component_id);
let mut self_ = Self {
#(#item_names : ::core::default::Default::default(),)*
#(#declared_property_vars : ::core::default::Default::default(),)*
#(#declared_signals : ::core::default::Default::default(),)*
#(#repeated_element_names : ::core::default::Default::default(),)*
#(#repeated_dynmodel_names : ::core::default::Default::default(),)*
self_weak : ::core::default::Default::default(),
#(parent : sixtyfps::re_exports::WeakPin::<#parent_component_type>::default(),)*
};
#(#init)*
self_
}
}
impl sixtyfps::re_exports::Component for #component_id { impl sixtyfps::re_exports::Component for #component_id {
fn visit_children_item(self: ::core::pin::Pin<&Self>, index: isize, visitor: sixtyfps::re_exports::ItemVisitorRefMut) { fn visit_children_item(self: ::core::pin::Pin<&Self>, index: isize, visitor: sixtyfps::re_exports::ItemVisitorRefMut) {
use sixtyfps::re_exports::*; use sixtyfps::re_exports::*;
@ -386,6 +356,27 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
} }
impl #component_id{ impl #component_id{
fn new(#(parent: sixtyfps::re_exports::WeakPin::<#parent_component_type>)*)
-> core::pin::Pin<std::rc::Rc<Self>>
{
#![allow(unused)]
use sixtyfps::re_exports::*;
ComponentVTable_static!(static VT for #component_id);
let mut self_ = Self {
#(#item_names : ::core::default::Default::default(),)*
#(#declared_property_vars : ::core::default::Default::default(),)*
#(#declared_signals : ::core::default::Default::default(),)*
#(#repeated_element_names : ::core::default::Default::default(),)*
#(#repeated_dynmodel_names : ::core::default::Default::default(),)*
self_weak : ::core::default::Default::default(),
#(parent : parent as sixtyfps::re_exports::WeakPin::<#parent_component_type>,)*
};
let self_pinned = std::rc::Rc::pin(self_);
self_pinned.self_weak.set(WeakPin::downgrade(self_pinned.clone())).map_err(|_|())
.expect("Can only be pinned once");
#(#init)*
self_pinned
}
#(#property_and_signal_accessors)* #(#property_and_signal_accessors)*
} }

View file

@ -6,6 +6,7 @@ but then it should also be renamed everywhere, including in the language grammar
*/ */
use super::properties::EvaluationContext; use super::properties::EvaluationContext;
use core::cell::Cell;
/// A Signal that can be connected to a handler. /// A Signal that can be connected to a handler.
/// ///
@ -15,7 +16,7 @@ use super::properties::EvaluationContext;
#[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: Option<Box<dyn Fn(&EvaluationContext, Arg)>>, handler: Cell<Option<Box<dyn Fn(&EvaluationContext, Arg)>>>,
} }
impl<Arg> Signal<Arg> { impl<Arg> Signal<Arg> {
@ -23,16 +24,18 @@ impl<Arg> Signal<Arg> {
/// ///
/// The constext must be a context corresponding to the component in which the signal is contained. /// The constext must be a context corresponding to the component in which the signal is contained.
pub fn emit(&self, context: &EvaluationContext, a: Arg) { pub fn emit(&self, context: &EvaluationContext, a: Arg) {
if let Some(h) = &self.handler { if let Some(h) = self.handler.take() {
h(context, a); h(context, a);
assert!(self.handler.take().is_none(), "Signal Handler set while emitted");
self.handler.set(Some(h))
} }
} }
/// 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(&mut self, f: impl Fn(&EvaluationContext, Arg) + 'static) { pub fn set_handler(&self, f: impl Fn(&EvaluationContext, Arg) + 'static) {
self.handler = Some(Box::new(f)); self.handler.set(Some(Box::new(f)));
} }
} }
@ -57,7 +60,7 @@ fn signal_simple_test() {
fn compute_layout(self: Pin<&Self>, _: &crate::EvaluationContext) {} fn compute_layout(self: Pin<&Self>, _: &crate::EvaluationContext) {}
} }
use crate::abi::datastructures::ComponentVTable; use crate::abi::datastructures::ComponentVTable;
let mut c = Component::default(); let c = Component::default();
c.clicked.set_handler(|c, ()| unsafe { c.clicked.set_handler(|c, ()| unsafe {
(*(c.component.as_ptr() as *const Component)).pressed.set(true) (*(c.component.as_ptr() as *const Component)).pressed.set(true)
}); });

View file

@ -18,7 +18,7 @@ assert(instance.get_t4() == 3 + - 5 - 8 - -9 * - - - 120);
```rust ```rust
let instance = Box::pin(TestCase::default()); let instance = TestCase::new();
let instance = instance.as_ref(); let instance = instance.as_ref();
assert_eq!(instance.get_t1(), 4 + 3 * 2 + 2 - 50 - 2); assert_eq!(instance.get_t1(), 4 + 3 * 2 + 2 - 50 - 2);
assert_eq!(instance.get_t2(), 500 / 2 * 30 - 1); assert_eq!(instance.get_t2(), 500 / 2 * 30 - 1);

View file

@ -11,7 +11,7 @@ assert(!instance.get_falsevar());
```rust ```rust
let instance = Box::pin(TestCase::default()); let instance = TestCase::new();
let instance = instance.as_ref(); let instance = instance.as_ref();
assert!(instance.get_truevar(), 1); assert!(instance.get_truevar(), 1);
assert!(!instance.get_falsevar(), 1); assert!(!instance.get_falsevar(), 1);

View file

@ -21,7 +21,7 @@ assert(instance.get_s1() == "123");
```rust ```rust
let instance = Box::pin(TestCase::default()); let instance = TestCase::new();
let instance = instance.as_ref(); let instance = instance.as_ref();
instance.set_condition(true); instance.set_condition(true);
assert_eq!(instance.get_s1(), "abc"); assert_eq!(instance.get_s1(), "abc");

View file

@ -27,7 +27,7 @@ assert(t.get_b1() != t.get_r5());
```rust ```rust
let t = Box::pin(Test::default()); let t = Test::new();
let t = t.as_ref(); let t = t.as_ref();
assert_eq!(t.get_r1(), t.get_r2()); assert_eq!(t.get_r1(), t.get_r2());
assert_eq!(t.get_r1(), t.get_r3()); assert_eq!(t.get_r1(), t.get_r3());

View file

@ -58,7 +58,7 @@ assert(instance.get_t6() == true);
```rust ```rust
let instance = Box::pin(TestCase::default()); let instance = TestCase::new();
let instance = instance.as_ref(); let instance = instance.as_ref();
assert_eq!(instance.get_t1(), true); assert_eq!(instance.get_t1(), true);

View file

@ -20,7 +20,7 @@ assert(instance.get_test_value2() == 3);
```rust ```rust
let instance = Box::pin(TestCase::default()); let instance = TestCase::new();
let instance = instance.as_ref(); let instance = instance.as_ref();
instance.set_condition(true); instance.set_condition(true);
assert_eq!(instance.get_test_value(), 1); assert_eq!(instance.get_test_value(), 1);

View file

@ -14,7 +14,7 @@ assert(instance.get_signal_emission_count() == 1);
```rust ```rust
let instance = Box::pin(TestCase::default()); let instance = TestCase::new();
let instance = instance.as_ref(); let instance = instance.as_ref();
instance.set_signal_emission_count(0); instance.set_signal_emission_count(0);
assert_eq!(instance.get_signal_emission_count(), 0); assert_eq!(instance.get_signal_emission_count(), 0);

View file

@ -8,7 +8,7 @@ TestCase instance;
``` ```
```rust ```rust
TestCase::default(); TestCase::new();
``` ```
```js ```js