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() {
let mut app = Hello::default();
app.plus_clicked.set_handler(|context, ()| {
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, ()| {
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();
}

View file

@ -7,11 +7,13 @@ fn main() {
app.plus_clicked.set_handler(|context, ()| {
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, ()| {
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();
}

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

View file

@ -42,25 +42,27 @@ pub struct Rectangle {
impl Item for Rectangle {
fn geometry(self: Pin<&Self>, context: &EvaluationContext) -> Rect {
euclid::rect(
self.x.get(context),
self.y.get(context),
self.width.get(context),
self.height.get(context),
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().width.apply_pin(self).get(context),
Self::field_offsets().height.apply_pin(self).get(context),
)
}
fn rendering_primitive(
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
let width = self.width.get(context);
let height = self.height.get(context);
let width = Self::field_offsets().x.apply_pin(self).get(context);
let height = Self::field_offsets().height.apply_pin(self).get(context);
if width > 0. && height > 0. {
RenderingPrimitive::Rectangle {
x: self.x.get(context),
y: self.y.get(context),
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
width,
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 {
RenderingPrimitive::NoContents
@ -104,10 +106,10 @@ pub struct Image {
impl Item for Image {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect {
euclid::rect(
self.x.get(context),
self.y.get(context),
self.width.get(context),
self.height.get(context),
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().width.apply_pin(self).get(context),
Self::field_offsets().height.apply_pin(self).get(context),
)
}
fn rendering_primitive(
@ -115,9 +117,9 @@ impl Item for Image {
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
RenderingPrimitive::Image {
x: self.x.get(context),
y: self.y.get(context),
source: self.source.get(context),
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
source: Self::field_offsets().source.apply_pin(self).get(context),
}
}
@ -160,19 +162,26 @@ pub struct Text {
impl Item for Text {
// FIXME: width / height. or maybe it doesn't matter? (
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(
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
RenderingPrimitive::Text {
x: self.x.get(context),
y: self.y.get(context),
text: self.text.get(context),
font_family: self.font_family.get(context),
font_pixel_size: self.font_pixel_size.get(context),
color: Color::from_argb_encoded(self.color.get(context)),
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
text: Self::field_offsets().text.apply_pin(self).get(context),
font_family: Self::field_offsets().font_family.apply_pin(self).get(context),
font_pixel_size: Self::field_offsets().font_pixel_size.apply_pin(self).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 {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect {
euclid::rect(
self.x.get(context),
self.y.get(context),
self.width.get(context),
self.height.get(context),
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().width.apply_pin(self).get(context),
Self::field_offsets().height.apply_pin(self).get(context),
)
}
fn rendering_primitive(
@ -237,13 +246,13 @@ impl Item for TouchArea {
context: &crate::EvaluationContext,
) {
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::MouseReleased => false,
super::datastructures::MouseEventType::MouseMoved => return,
});
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 core::cell::*;
use core::ops::DerefMut;
use core::pin::Pin;
use std::rc::{Rc, Weak};
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
/// property
pub fn get(&self, context: &EvaluationContext) -> T {
pub fn get(self: Pin<&Self>, context: &EvaluationContext) -> T {
self.update(context);
self.inner.clone().register_current_binding_as_dependency();
self.try_borrow().expect("Binding loop detected").1.clone()
@ -303,6 +304,10 @@ impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
#[test]
fn properties_simple_test() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
}
#[derive(Default)]
struct Component {
width: Property<i32>,
@ -319,22 +324,22 @@ fn properties_simple_test() {
let w = Rc::downgrade(&compo);
compo.area.set_binding(move |ctx| {
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.height.set(8);
assert_eq!(compo.width.get(&dummy_eval_context), 4);
assert_eq!(compo.height.get(&dummy_eval_context), 8);
assert_eq!(compo.area.get(&dummy_eval_context), 4 * 8);
assert_eq!(g(&compo.width, &dummy_eval_context), 4);
assert_eq!(g(&compo.height, &dummy_eval_context), 8);
assert_eq!(g(&compo.area, &dummy_eval_context), 4 * 8);
let w = Rc::downgrade(&compo);
compo.width.set_binding(move |ctx| {
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!(compo.height.get(&dummy_eval_context), 8);
assert_eq!(compo.area.get(&dummy_eval_context), 8 * 8 * 2);
assert_eq!(g(&compo.width, &dummy_eval_context), 8 * 2);
assert_eq!(g(&compo.height, &dummy_eval_context), 8);
assert_eq!(g(&compo.area, &dummy_eval_context), 8 * 8 * 2);
}
#[allow(non_camel_case_types)]
@ -724,6 +729,9 @@ mod test {
#[test]
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 {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
@ -738,38 +746,41 @@ mod test {
let w = Rc::downgrade(&compo);
compo.width_times_two.set_binding(move |context| {
let compo = w.upgrade().unwrap();
compo.width.get(context) * 2
g(&compo.width, context) * 2
});
let animation_details = PropertyAnimation { duration: 10000 };
compo.width.set(100);
assert_eq!(compo.width.get(&dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
let animation = compo.width.set_animated_value(200, &animation_details);
assert_eq!(compo.width.get(&dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
assert_eq!(animation.borrow().from_value, 100);
assert_eq!(animation.borrow().to_value, 200);
animation.clone().update_animation_state(AnimationState::Running { progress: 0.5 });
assert_eq!(compo.width.get(&dummy_eval_context), 150);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 300);
assert_eq!(g(&compo.width, &dummy_eval_context), 150);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300);
animation.clone().update_animation_state(AnimationState::Running { progress: 1.0 });
assert_eq!(compo.width.get(&dummy_eval_context), 200);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 400);
assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
animation.clone().update_animation_state(AnimationState::Stopped);
assert_eq!(compo.width.get(&dummy_eval_context), 200);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 400);
assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
assert_eq!(Rc::strong_count(&animation), 1);
}
#[test]
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 {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
@ -784,7 +795,7 @@ mod test {
let w = Rc::downgrade(&compo);
compo.width_times_two.set_binding(move |context| {
let compo = w.upgrade().unwrap();
compo.width.get(context) * 2
g(&compo.width, context) * 2
});
let w = Rc::downgrade(&compo);
@ -794,29 +805,29 @@ mod test {
let animation = compo.width.set_animated_binding(
move |context| {
let compo = w.upgrade().unwrap();
compo.feed_property.get(context)
g(&compo.feed_property, context)
},
&animation_details,
);
compo.feed_property.set(100);
assert_eq!(compo.width.get(&dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
compo.feed_property.set(200);
assert_eq!(compo.width.get(&dummy_eval_context), 100);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 200);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
animation.clone().update_animation_state(AnimationState::Running { progress: 0.5 });
assert_eq!(compo.width.get(&dummy_eval_context), 150);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 300);
assert_eq!(g(&compo.width, &dummy_eval_context), 150);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300);
assert_eq!(animation.borrow().from_value, 100);
assert_eq!(animation.borrow().to_value, 200);
animation.clone().update_animation_state(AnimationState::Running { progress: 1.0 });
assert_eq!(compo.width.get(&dummy_eval_context), 200);
assert_eq!(compo.width_times_two.get(&dummy_eval_context), 400);
assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
}
}