Use Pin<&Self> for Property::get

This commit is contained in:
Olivier Goffart 2020-06-26 13:18:04 +02:00
parent 0029921f1a
commit 2d22bac451
5 changed files with 108 additions and 74 deletions

View file

@ -112,15 +112,15 @@ Hello := Rectangle {
fn main() { fn main() {
let mut app = Hello::default(); let mut app = Hello::default();
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();
app.counter.set(app.counter.get(context) + 1); let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) + 1);
}); });
app.minus_clicked.set_handler(|context, ()| { app.minus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap(); let app = context.get_component::<Hello>().unwrap();
app.counter.set(app.counter.get(context) - 1); let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) - 1);
}); });
app.run(); app.run();
} }

View file

@ -7,11 +7,13 @@ fn main() {
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();
app.counter.set(app.counter.get(context) + 1); let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) + 1);
}); });
app.minus_clicked.set_handler(|context, ()| { app.minus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap(); let app = context.get_component::<Hello>().unwrap();
app.counter.set(app.counter.get(context) - 1); let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) - 1);
}); });
app.run(); app.run();
} }

View file

@ -55,7 +55,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component( let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component(
sixtyfps::re_exports::ComponentRef::new_pin(self) sixtyfps::re_exports::ComponentRef::new_pin(self)
); );
self.#prop_ident.emit(&eval_context, ()) Self::field_offsets().#prop_ident.apply_pin(self).emit(&eval_context, ())
} }
) )
.into(), .into(),
@ -82,7 +82,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component( let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component(
sixtyfps::re_exports::ComponentRef::new_pin(self) sixtyfps::re_exports::ComponentRef::new_pin(self)
); );
self.#prop_ident.get(&eval_context) Self::field_offsets().#prop_ident.apply_pin(self).get(&eval_context)
} }
) )
.into(), .into(),
@ -92,7 +92,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
quote!( quote!(
#[allow(dead_code)] #[allow(dead_code)]
fn #setter_ident(&self, value: #rust_property_type) { fn #setter_ident(&self, value: #rust_property_type) {
self.#prop_ident.set(value) Self::field_offsets().#prop_ident.apply(self).set(value)
} }
) )
.into(), .into(),
@ -402,10 +402,16 @@ fn access_member(
let name_ident = quote::format_ident!("{}", name); let name_ident = quote::format_ident!("{}", name);
let comp = quote!(#context.get_component::<#component_id>().unwrap()); let comp = quote!(#context.get_component::<#component_id>().unwrap());
if e.property_declarations.contains_key(name) { if e.property_declarations.contains_key(name) {
(quote!(#comp.#name_ident), context) (quote!(#component_id::field_offsets().#name_ident.apply_pin(#comp)), context)
} else { } else {
let elem_ident = quote::format_ident!("{}", e.id); let elem_ident = quote::format_ident!("{}", e.id);
(quote!(#comp.#elem_ident.#name_ident), context) 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)
.apply_pin(#comp)
),
context,
)
} }
} else { } else {
access_member(element, name, &enclosing_component, quote!(#context.parent_context.unwrap())) access_member(element, name, &enclosing_component, quote!(#context.parent_context.unwrap()))
@ -438,14 +444,16 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
} }
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()) {
quote!({ _self.index.get(context) }) let component_id = component_id(&component);
quote!({ #component_id::field_offsets().index.apply_pin(_self).get(context) })
} else { } else {
todo!(); todo!();
} }
} }
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()) {
quote!({ _self.model_data.get(context) }) let component_id = component_id(&component);
quote!({ #component_id::field_offsets().model_data.apply_pin(_self).get(context) })
} else { } else {
todo!(); todo!();
} }
@ -554,6 +562,8 @@ fn compute_layout(component: &Component) -> TokenStream {
let mut layouts = vec![]; let mut layouts = vec![];
for x in component.layout_constraints.borrow().0.iter() { for x in component.layout_constraints.borrow().0.iter() {
let within = quote::format_ident!("{}", x.within.borrow().id); let within = quote::format_ident!("{}", x.within.borrow().id);
let within_ty =
quote::format_ident!("{}", x.within.borrow().base_type.as_builtin().class_name);
let row_constraint = vec![quote!(Constraint::default()); x.row_count()]; let row_constraint = vec![quote!(Constraint::default()); x.row_count()];
let col_constraint = vec![quote!(Constraint::default()); x.col_count()]; let col_constraint = vec![quote!(Constraint::default()); x.col_count()];
let cells = x let cells = x
@ -594,8 +604,10 @@ fn compute_layout(component: &Component) -> TokenStream {
solve_grid_layout(&GridLayoutData { solve_grid_layout(&GridLayoutData {
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.#within.width.get(eval_context), width: (Self::field_offsets().#within + #within_ty::field_offsets().width)
height: self.#within.height.get(eval_context), .apply_pin(self).get(eval_context),
height: (Self::field_offsets().#within + #within_ty::field_offsets().height)
.apply_pin(self).get(eval_context),
x: 0., x: 0.,
y: 0., y: 0.,
cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]), cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]),

View file

@ -42,25 +42,27 @@ pub struct Rectangle {
impl Item for Rectangle { impl Item for Rectangle {
fn geometry(self: Pin<&Self>, context: &EvaluationContext) -> Rect { fn geometry(self: Pin<&Self>, context: &EvaluationContext) -> Rect {
euclid::rect( euclid::rect(
self.x.get(context), Self::field_offsets().x.apply_pin(self).get(context),
self.y.get(context), Self::field_offsets().y.apply_pin(self).get(context),
self.width.get(context), Self::field_offsets().width.apply_pin(self).get(context),
self.height.get(context), Self::field_offsets().height.apply_pin(self).get(context),
) )
} }
fn rendering_primitive( fn rendering_primitive(
self: Pin<&Self>, self: Pin<&Self>,
context: &crate::EvaluationContext, context: &crate::EvaluationContext,
) -> RenderingPrimitive { ) -> RenderingPrimitive {
let width = self.width.get(context); let width = Self::field_offsets().x.apply_pin(self).get(context);
let height = self.height.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.x.get(context), x: Self::field_offsets().x.apply_pin(self).get(context),
y: self.y.get(context), y: Self::field_offsets().y.apply_pin(self).get(context),
width, width,
height, height,
color: Color::from_argb_encoded(self.color.get(context)), color: Color::from_argb_encoded(
Self::field_offsets().color.apply_pin(self).get(context),
),
} }
} else { } else {
RenderingPrimitive::NoContents RenderingPrimitive::NoContents
@ -104,10 +106,10 @@ 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>, context: &crate::EvaluationContext) -> Rect {
euclid::rect( euclid::rect(
self.x.get(context), Self::field_offsets().x.apply_pin(self).get(context),
self.y.get(context), Self::field_offsets().y.apply_pin(self).get(context),
self.width.get(context), Self::field_offsets().width.apply_pin(self).get(context),
self.height.get(context), Self::field_offsets().height.apply_pin(self).get(context),
) )
} }
fn rendering_primitive( fn rendering_primitive(
@ -115,9 +117,9 @@ impl Item for Image {
context: &crate::EvaluationContext, context: &crate::EvaluationContext,
) -> RenderingPrimitive { ) -> RenderingPrimitive {
RenderingPrimitive::Image { RenderingPrimitive::Image {
x: self.x.get(context), x: Self::field_offsets().x.apply_pin(self).get(context),
y: self.y.get(context), y: Self::field_offsets().y.apply_pin(self).get(context),
source: self.source.get(context), source: Self::field_offsets().source.apply_pin(self).get(context),
} }
} }
@ -160,19 +162,26 @@ 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>, context: &crate::EvaluationContext) -> Rect {
euclid::rect(self.x.get(context), self.y.get(context), 0., 0.) euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
0.,
0.,
)
} }
fn rendering_primitive( fn rendering_primitive(
self: Pin<&Self>, self: Pin<&Self>,
context: &crate::EvaluationContext, context: &crate::EvaluationContext,
) -> RenderingPrimitive { ) -> RenderingPrimitive {
RenderingPrimitive::Text { RenderingPrimitive::Text {
x: self.x.get(context), x: Self::field_offsets().x.apply_pin(self).get(context),
y: self.y.get(context), y: Self::field_offsets().y.apply_pin(self).get(context),
text: self.text.get(context), text: Self::field_offsets().text.apply_pin(self).get(context),
font_family: self.font_family.get(context), font_family: Self::field_offsets().font_family.apply_pin(self).get(context),
font_pixel_size: self.font_pixel_size.get(context), font_pixel_size: Self::field_offsets().font_pixel_size.apply_pin(self).get(context),
color: Color::from_argb_encoded(self.color.get(context)), color: Color::from_argb_encoded(
Self::field_offsets().color.apply_pin(self).get(context),
),
} }
} }
@ -214,10 +223,10 @@ 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>, context: &crate::EvaluationContext) -> Rect {
euclid::rect( euclid::rect(
self.x.get(context), Self::field_offsets().x.apply_pin(self).get(context),
self.y.get(context), Self::field_offsets().y.apply_pin(self).get(context),
self.width.get(context), Self::field_offsets().width.apply_pin(self).get(context),
self.height.get(context), Self::field_offsets().height.apply_pin(self).get(context),
) )
} }
fn rendering_primitive( fn rendering_primitive(
@ -237,13 +246,13 @@ impl Item for TouchArea {
context: &crate::EvaluationContext, context: &crate::EvaluationContext,
) { ) {
println!("Touch Area Event {:?}", event); println!("Touch Area Event {:?}", event);
self.pressed.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,
super::datastructures::MouseEventType::MouseReleased => false, super::datastructures::MouseEventType::MouseReleased => false,
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.clicked.emit(context, ()) Self::field_offsets().clicked.apply_pin(self).emit(context, ())
} }
} }
} }

View file

@ -9,6 +9,7 @@ use crate::abi::primitives::PropertyAnimation;
use crate::ComponentRefPin; use crate::ComponentRefPin;
use core::cell::*; use core::cell::*;
use core::ops::DerefMut; use core::ops::DerefMut;
use core::pin::Pin;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
thread_local!(static CURRENT_BINDING : RefCell<Option<Rc<dyn PropertyNotify>>> = Default::default()); thread_local!(static CURRENT_BINDING : RefCell<Option<Rc<dyn PropertyNotify>>> = Default::default());
@ -178,7 +179,7 @@ impl<T: Clone + 'static> Property<T> {
/// ///
/// The context must be the constext matching the Component which contains this /// The context must be the constext matching the Component which contains this
/// property /// property
pub fn get(&self, context: &EvaluationContext) -> T { pub fn get(self: Pin<&Self>, context: &EvaluationContext) -> T {
self.update(context); self.update(context);
self.inner.clone().register_current_binding_as_dependency(); self.inner.clone().register_current_binding_as_dependency();
self.try_borrow().expect("Binding loop detected").1.clone() self.try_borrow().expect("Binding loop detected").1.clone()
@ -303,6 +304,10 @@ impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
#[test] #[test]
fn properties_simple_test() { fn properties_simple_test() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
}
#[derive(Default)] #[derive(Default)]
struct Component { struct Component {
width: Property<i32>, width: Property<i32>,
@ -319,22 +324,22 @@ fn properties_simple_test() {
let w = Rc::downgrade(&compo); let w = Rc::downgrade(&compo);
compo.area.set_binding(move |ctx| { compo.area.set_binding(move |ctx| {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
compo.width.get(ctx) * compo.height.get(ctx) g(&compo.width, ctx) * g(&compo.height, ctx)
}); });
compo.width.set(4); compo.width.set(4);
compo.height.set(8); compo.height.set(8);
assert_eq!(compo.width.get(&dummy_eval_context), 4); assert_eq!(g(&compo.width, &dummy_eval_context), 4);
assert_eq!(compo.height.get(&dummy_eval_context), 8); assert_eq!(g(&compo.height, &dummy_eval_context), 8);
assert_eq!(compo.area.get(&dummy_eval_context), 4 * 8); assert_eq!(g(&compo.area, &dummy_eval_context), 4 * 8);
let w = Rc::downgrade(&compo); let w = Rc::downgrade(&compo);
compo.width.set_binding(move |ctx| { compo.width.set_binding(move |ctx| {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
compo.height.get(ctx) * 2 g(&compo.height, ctx) * 2
}); });
assert_eq!(compo.width.get(&dummy_eval_context), 8 * 2); assert_eq!(g(&compo.width, &dummy_eval_context), 8 * 2);
assert_eq!(compo.height.get(&dummy_eval_context), 8); assert_eq!(g(&compo.height, &dummy_eval_context), 8);
assert_eq!(compo.area.get(&dummy_eval_context), 8 * 8 * 2); assert_eq!(g(&compo.area, &dummy_eval_context), 8 * 8 * 2);
} }
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -724,6 +729,9 @@ mod test {
#[test] #[test]
fn properties_test_animation_triggered_by_set() { fn properties_test_animation_triggered_by_set() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
}
let dummy_eval_context = EvaluationContext { let dummy_eval_context = EvaluationContext {
component: unsafe { component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw( core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
@ -738,38 +746,41 @@ mod test {
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 |context| {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
compo.width.get(context) * 2 g(&compo.width, context) * 2
}); });
let animation_details = PropertyAnimation { duration: 10000 }; let animation_details = PropertyAnimation { duration: 10000 };
compo.width.set(100); compo.width.set(100);
assert_eq!(compo.width.get(&dummy_eval_context), 100); assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
let animation = compo.width.set_animated_value(200, &animation_details); let animation = compo.width.set_animated_value(200, &animation_details);
assert_eq!(compo.width.get(&dummy_eval_context), 100); assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
assert_eq!(animation.borrow().from_value, 100); assert_eq!(animation.borrow().from_value, 100);
assert_eq!(animation.borrow().to_value, 200); assert_eq!(animation.borrow().to_value, 200);
animation.clone().update_animation_state(AnimationState::Running { progress: 0.5 }); animation.clone().update_animation_state(AnimationState::Running { progress: 0.5 });
assert_eq!(compo.width.get(&dummy_eval_context), 150); assert_eq!(g(&compo.width, &dummy_eval_context), 150);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 300); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300);
animation.clone().update_animation_state(AnimationState::Running { progress: 1.0 }); animation.clone().update_animation_state(AnimationState::Running { progress: 1.0 });
assert_eq!(compo.width.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 400); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
animation.clone().update_animation_state(AnimationState::Stopped); animation.clone().update_animation_state(AnimationState::Stopped);
assert_eq!(compo.width.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 400); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
assert_eq!(Rc::strong_count(&animation), 1); assert_eq!(Rc::strong_count(&animation), 1);
} }
#[test] #[test]
fn properties_test_animation_triggered_by_binding() { fn properties_test_animation_triggered_by_binding() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
}
let dummy_eval_context = EvaluationContext { let dummy_eval_context = EvaluationContext {
component: unsafe { component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw( core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
@ -784,7 +795,7 @@ mod test {
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 |context| {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
compo.width.get(context) * 2 g(&compo.width, context) * 2
}); });
let w = Rc::downgrade(&compo); let w = Rc::downgrade(&compo);
@ -794,29 +805,29 @@ mod test {
let animation = compo.width.set_animated_binding( let animation = compo.width.set_animated_binding(
move |context| { move |context| {
let compo = w.upgrade().unwrap(); let compo = w.upgrade().unwrap();
compo.feed_property.get(context) g(&compo.feed_property, context)
}, },
&animation_details, &animation_details,
); );
compo.feed_property.set(100); compo.feed_property.set(100);
assert_eq!(compo.width.get(&dummy_eval_context), 100); assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
compo.feed_property.set(200); compo.feed_property.set(200);
assert_eq!(compo.width.get(&dummy_eval_context), 100); assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
animation.clone().update_animation_state(AnimationState::Running { progress: 0.5 }); animation.clone().update_animation_state(AnimationState::Running { progress: 0.5 });
assert_eq!(compo.width.get(&dummy_eval_context), 150); assert_eq!(g(&compo.width, &dummy_eval_context), 150);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 300); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300);
assert_eq!(animation.borrow().from_value, 100); assert_eq!(animation.borrow().from_value, 100);
assert_eq!(animation.borrow().to_value, 200); assert_eq!(animation.borrow().to_value, 200);
animation.clone().update_animation_state(AnimationState::Running { progress: 1.0 }); animation.clone().update_animation_state(AnimationState::Running { progress: 1.0 });
assert_eq!(compo.width.get(&dummy_eval_context), 200); assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 400); assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
} }
} }