From a712f515fa84ca4feaf732847f762af537aa86df Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 9 Apr 2021 13:49:23 +0200 Subject: [PATCH] Make the viewport element of the flickable a real Element in the object_tree --- sixtyfps_compiler/generator.rs | 38 +- sixtyfps_compiler/generator/cpp.rs | 136 +++--- sixtyfps_compiler/generator/rust.rs | 418 +++++++++--------- sixtyfps_compiler/lib.rs | 5 + sixtyfps_compiler/object_tree.rs | 25 ++ sixtyfps_compiler/passes/flickable.rs | 72 +++ .../passes/generate_item_indices.rs | 6 +- sixtyfps_compiler/passes/inlining.rs | 1 + .../passes/repeater_component.rs | 1 + sixtyfps_compiler/passes/resolving.rs | 22 - .../interpreter/dynamic_component.rs | 143 ++---- 11 files changed, 430 insertions(+), 437 deletions(-) create mode 100644 sixtyfps_compiler/passes/flickable.rs diff --git a/sixtyfps_compiler/generator.rs b/sixtyfps_compiler/generator.rs index 7667cc149..e4a4f8847 100644 --- a/sixtyfps_compiler/generator.rs +++ b/sixtyfps_compiler/generator.rs @@ -92,13 +92,9 @@ pub fn generate( /// 1. the item /// 2. the first_children_offset, /// 3. the parent index -/// 4. wether this is the flickable rectangle #[allow(dead_code)] -pub fn build_array_helper( - component: &Component, - mut visit_item: impl FnMut(&ElementRc, u32, u32, bool), -) { - visit_item(&component.root_element, 1, 0, false); +pub fn build_array_helper(component: &Component, mut visit_item: impl FnMut(&ElementRc, u32, u32)) { + visit_item(&component.root_element, 1, 0); visit_children(&component.root_element, &mut 0, 1, &mut visit_item); fn sub_children_count(e: &ElementRc) -> usize { @@ -106,9 +102,6 @@ pub fn build_array_helper( for i in &e.borrow().children { count += sub_children_count(i); } - if is_flickable(e) { - count += 1; - } count } @@ -116,17 +109,12 @@ pub fn build_array_helper( item: &ElementRc, index: &mut u32, children_offset: u32, - visit_item: &mut impl FnMut(&ElementRc, u32, u32, bool), + visit_item: &mut impl FnMut(&ElementRc, u32, u32), ) { let mut offset = children_offset + item.borrow().children.len() as u32; - if is_flickable(item) { - visit_item(item, offset, *index, true); - offset += 1; - } - for i in &item.borrow().children { - visit_item(i, offset, *index, false); + visit_item(i, offset, *index); offset += sub_children_count(i) as u32; } @@ -134,27 +122,9 @@ pub fn build_array_helper( let mut offset = children_offset + item.borrow().children.len() as u32; - if is_flickable(item) { - offset += 1; - *index += 1; - } - for e in &item.borrow().children { visit_children(e, index, offset, visit_item); offset += sub_children_count(e) as u32; } } } - -pub fn is_flickable(e: &ElementRc) -> bool { - matches!(&e.borrow().base_type, crate::langtype::Type::Native(n) if n.class_name == "Flickable") -} - -/// If the element is a Flickable and the property is the property of the viewport, returns the property with the prefix stipped -pub fn as_flickable_viewport_property<'a>(e: &ElementRc, name: &'a str) -> Option<&'a str> { - if is_flickable(e) { - name.strip_prefix("viewport_") - } else { - None - } -} diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index f49ecc687..44128dd8b 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -332,25 +332,29 @@ fn handle_property_binding( ) { let item = elem.borrow(); let component = item.enclosing_component.upgrade().unwrap(); - let id = &item.id; + let accessor_prefix = if item.property_declarations.contains_key(prop_name) { + String::new() + } else if item.is_flickable_viewport { + format!( + "{id}.viewport.", + id = crate::object_tree::find_parent_element(elem).unwrap().borrow().id + ) + } else { + format!("{id}.", id = item.id) + }; let prop_type = item.lookup_property(prop_name).property_type; if let Type::Callback { args, .. } = &prop_type { - let callback_accessor_prefix = if item.property_declarations.contains_key(prop_name) { - String::new() - } else { - format!("{id}.", id = id.clone()) - }; let mut params = args.iter().enumerate().map(|(i, ty)| { format!("[[maybe_unused]] {} arg_{}", ty.cpp_type().unwrap_or_default(), i) }); init.push(format!( - "{callback_accessor_prefix}{prop}.set_handler( + "{accessor_prefix}{prop}.set_handler( [this]({params}) {{ [[maybe_unused]] auto self = this; return {code}; }});", - callback_accessor_prefix = callback_accessor_prefix, + accessor_prefix = accessor_prefix, prop = prop_name, params = params.join(", "), code = compile_expression_wrap_return(binding_expression, &component) @@ -366,20 +370,10 @@ fn handle_property_binding( handle_property_binding(elem, prop_name, next, init) } } else { - let accessor_prefix = if item.property_declarations.contains_key(prop_name) { - String::new() - } else { - format!("{id}.", id = id.clone()) - }; - let component = &item.enclosing_component.upgrade().unwrap(); let init_expr = compile_expression_wrap_return(binding_expression, component); - let cpp_prop = if let Some(vp) = super::as_flickable_viewport_property(elem, prop_name) { - format!("{}viewport.{}", accessor_prefix, vp,) - } else { - format!("{}{}", accessor_prefix, prop_name,) - }; + let cpp_prop = format!("{}{}", accessor_prefix, prop_name); init.push(if binding_expression.is_constant() { format!("{}.set({});", cpp_prop, init_expr) @@ -1005,67 +999,66 @@ fn generate_component( let mut tree_array = vec![]; let mut item_names_and_vt_symbols = vec![]; let mut repeater_count = 0; - super::build_array_helper( - component, - |item_rc, children_offset, parent_index, is_flickable_rect| { - let item = item_rc.borrow(); - if is_flickable_rect { - tree_array.push(format!( - "sixtyfps::private_api::make_item_node(offsetof({}, {}) + offsetof(sixtyfps::Flickable, viewport), &sixtyfps::private_api::RectangleVTable, {}, {}, {})", - &component_id, - item.id, - item.children.len(), - tree_array.len() + 1, - parent_index, + super::build_array_helper(component, |item_rc, children_offset, parent_index| { + let item = item_rc.borrow(); + if item.base_type == Type::Void { + assert!(component.is_global()); + for (prop_name, binding_expression) in &item.bindings { + handle_property_binding(item_rc, prop_name, binding_expression, &mut init); + } + } else if let Some(repeated) = &item.repeated { + tree_array.push(format!( + "sixtyfps::private_api::make_dyn_node({}, {})", + repeater_count, parent_index )); - } else if item.base_type == Type::Void { - assert!(component.is_global()); - for (prop_name, binding_expression) in &item.bindings { - handle_property_binding(item_rc, prop_name, binding_expression, &mut init); - } - } else if let Some(repeated) = &item.repeated { + let base_component = item.base_type.as_component(); + let mut friends = Vec::new(); + generate_component(file, base_component, diag, Some(&mut friends)); + if let Some(sub_components) = sub_components.as_mut() { + sub_components.extend_from_slice(friends.as_slice()); + sub_components.push(self::component_id(base_component)) + } + component_struct.friends.append(&mut friends); + component_struct.friends.push(self::component_id(base_component)); + handle_repeater( + repeated, + base_component, + component, + repeater_count, + &mut component_struct, + &mut init, + &mut children_visitor_cases, + &mut repeated_input_branch, + &mut repeater_layout_code, + diag, + ); + repeater_count += 1; + } else { + if item.is_flickable_viewport { tree_array.push(format!( - "sixtyfps::private_api::make_dyn_node({}, {})", - repeater_count, parent_index + "sixtyfps::private_api::make_item_node(offsetof({}, {}) + offsetof(sixtyfps::Flickable, viewport), &sixtyfps::private_api::RectangleVTable, {}, {}, {})", + &component_id, + crate::object_tree::find_parent_element(item_rc).unwrap().borrow().id, + item.children.len(), + children_offset, + parent_index, )); - let base_component = item.base_type.as_component(); - let mut friends = Vec::new(); - generate_component(file, base_component, diag, Some(&mut friends)); - if let Some(sub_components) = sub_components.as_mut() { - sub_components.extend_from_slice(friends.as_slice()); - sub_components.push(self::component_id(base_component)) - } - component_struct.friends.append(&mut friends); - component_struct.friends.push(self::component_id(base_component)); - handle_repeater( - repeated, - base_component, - component, - repeater_count, - &mut component_struct, - &mut init, - &mut children_visitor_cases, - &mut repeated_input_branch, - &mut repeater_layout_code, - diag, - ); - repeater_count += 1; } else { tree_array.push(format!( "sixtyfps::private_api::make_item_node(offsetof({}, {}), &sixtyfps::private_api::{}, {}, {}, {})", component_id, item.id, item.base_type.as_native().vtable_symbol, - if super::is_flickable(item_rc) { 1 } else { item.children.len() }, + item.children.len(), children_offset, parent_index, )); - handle_item(item_rc, &mut component_struct, &mut init); - item_names_and_vt_symbols - .push((item.id.clone(), item.base_type.as_native().vtable_symbol.clone())); } - }, - ); + handle_item(item_rc, &mut component_struct, &mut init); + item_names_and_vt_symbols + .push((item.id.clone(), item.base_type.as_native().vtable_symbol.clone())); + } + }); if !component.is_global() { component_struct @@ -1327,8 +1320,13 @@ fn access_member( if Rc::ptr_eq(component, &enclosing_component) { if e.property_declarations.contains_key(name) || name == "" || component.is_global() { format!("{}->{}", component_cpp, name) - } else if let Some(vp) = super::as_flickable_viewport_property(element, name) { - format!("{}->{}.viewport.{}", component_cpp, e.id.as_str(), vp) + } else if e.is_flickable_viewport { + format!( + "{}->{}.viewport.{}", + component_cpp, + crate::object_tree::find_parent_element(element).unwrap().borrow().id, + name + ) } else { format!("{}->{}.{}", component_cpp, e.id.as_str(), name) } diff --git a/sixtyfps_compiler/generator/rust.rs b/sixtyfps_compiler/generator/rust.rs index 1ccbe1f89..949b178b0 100644 --- a/sixtyfps_compiler/generator/rust.rs +++ b/sixtyfps_compiler/generator/rust.rs @@ -377,228 +377,228 @@ fn generate_component( let mut init = Vec::new(); let mut window_field_init = None; let mut window_parent_param = None; - super::build_array_helper( - component, - |item_rc, children_index, parent_index, is_flickable_rect| { - let parent_index = parent_index as u32; - let item = item_rc.borrow(); - if is_flickable_rect { - let field_name = format_ident!("{}", item.id); - let children_count = item.children.len() as u32; - let children_index = item_tree_array.len() as u32 + 1; + super::build_array_helper(component, |item_rc, children_index, parent_index| { + let parent_index = parent_index as u32; + let item = item_rc.borrow(); + if item.base_type == Type::Void { + assert!(component.is_global()); + for (k, binding_expression) in &item.bindings { + handle_property_binding(component, item_rc, k, binding_expression, &mut init); + } + } else if let Some(repeated) = &item.repeated { + let base_component = item.base_type.as_component(); + let repeater_index = repeated_element_names.len(); + let repeater_id = format_ident!("repeater_{}", item.id); + let rep_inner_component_id = self::inner_component_id(&*base_component); - item_tree_array.push(quote!( - sixtyfps::re_exports::ItemTreeNode::Item{ - item: VOffset::new(#inner_component_id::FIELD_OFFSETS.#field_name + sixtyfps::re_exports::Flickable::FIELD_OFFSETS.viewport), - chilren_count: #children_count, - children_index: #children_index, - parent_index: #parent_index + extra_components.push(generate_component(&*base_component, diag).unwrap_or_else( + || { + assert!(diag.has_error()); + Default::default() + }, + )); + + let extra_fn = if repeated.is_listview.is_some() { + let am = |prop| { + access_member( + &base_component.root_element, + prop, + base_component, + quote!(self), + false, + ) + }; + let p_y = am("y"); + let p_height = am("height"); + let p_width = am("width"); + quote! { + fn listview_layout( + self: core::pin::Pin<&Self>, + offset_y: &mut f32, + viewport_width: core::pin::Pin<&sixtyfps::re_exports::Property>, + ) { + use sixtyfps::re_exports::*; + let vp_w = viewport_width.get(); + self.apply_layout(Rect::new(Point::new(0., *offset_y), Size::new(vp_w, 0.))); + #p_y.set(*offset_y); + *offset_y += #p_height.get(); + let w = #p_width.get(); + if vp_w < w { + viewport_width.set(w); + } } - )); - } else if item.base_type == Type::Void { - assert!(component.is_global()); - for (k, binding_expression) in &item.bindings { - handle_property_binding(component, item_rc, k, binding_expression, &mut init); } - } else if let Some(repeated) = &item.repeated { - let base_component = item.base_type.as_component(); - let repeater_index = repeated_element_names.len(); - let repeater_id = format_ident!("repeater_{}", item.id); - let rep_inner_component_id = self::inner_component_id(&*base_component); - - extra_components.push(generate_component(&*base_component, diag).unwrap_or_else( - || { - assert!(diag.has_error()); - Default::default() - }, - )); - - let extra_fn = if repeated.is_listview.is_some() { - let am = |prop| { - access_member( - &base_component.root_element, - prop, - base_component, - quote!(self), - false, - ) - }; - let p_y = am("y"); - let p_height = am("height"); - let p_width = am("width"); - quote! { - fn listview_layout( - self: core::pin::Pin<&Self>, - offset_y: &mut f32, - viewport_width: core::pin::Pin<&sixtyfps::re_exports::Property>, - ) { - use sixtyfps::re_exports::*; - let vp_w = viewport_width.get(); - self.apply_layout(Rect::new(Point::new(0., *offset_y), Size::new(vp_w, 0.))); - #p_y.set(*offset_y); - *offset_y += #p_height.get(); - let w = #p_width.get(); - if vp_w < w { - viewport_width.set(w); - } - } - } + } else { + // TODO: we could generate this code only if we know that this component is in a box layout + let root_id = format_ident!("{}", base_component.root_element.borrow().id); + let root_c = &base_component.layouts.borrow().root_constraints; + let width = if root_c.fixed_width { + quote!(None) } else { - // TODO: we could generate this code only if we know that this component is in a box layout - let root_id = format_ident!("{}", base_component.root_element.borrow().id); - let root_c = &base_component.layouts.borrow().root_constraints; - let width = if root_c.fixed_width { - quote!(None) - } else { - quote!(Some(&self.get_ref().#root_id.width)) - }; - let height = if root_c.fixed_height { - quote!(None) - } else { - quote!(Some(&self.get_ref().#root_id.height)) - }; - quote! { - fn box_layout_data<'a>(self: ::core::pin::Pin<&'a Self>) -> sixtyfps::re_exports::BoxLayoutCellData<'a> { - use sixtyfps::re_exports::*; - BoxLayoutCellData { - constraint: self.layout_info(), - x: Some(&self.get_ref().#root_id.x), - y: Some(&self.get_ref().#root_id.y), - width: #width, - height: #height, - } + quote!(Some(&self.get_ref().#root_id.width)) + }; + let height = if root_c.fixed_height { + quote!(None) + } else { + quote!(Some(&self.get_ref().#root_id.height)) + }; + quote! { + fn box_layout_data<'a>(self: ::core::pin::Pin<&'a Self>) -> sixtyfps::re_exports::BoxLayoutCellData<'a> { + use sixtyfps::re_exports::*; + BoxLayoutCellData { + constraint: self.layout_info(), + x: Some(&self.get_ref().#root_id.x), + y: Some(&self.get_ref().#root_id.y), + width: #width, + height: #height, } } + } + }; + + extra_components.push(if repeated.is_conditional_element { + quote! { + impl sixtyfps::re_exports::RepeatedComponent for #rep_inner_component_id { + type Data = (); + fn update(&self, _: usize, _: Self::Data) { } + #extra_fn + } + } + } else { + let data_type = get_rust_type( + &Expression::RepeaterModelReference { element: Rc::downgrade(item_rc) }.ty(), + &item.node.as_ref().map(|x| x.to_source_location()), + diag, + ); + + quote! { + impl sixtyfps::re_exports::RepeatedComponent for #rep_inner_component_id { + type Data = #data_type; + fn update(&self, index: usize, data: Self::Data) { + self.index.set(index); + self.model_data.set(data); + } + #extra_fn + } + } + }); + + let mut model = compile_expression(&repeated.model, component); + if repeated.is_conditional_element { + model = + quote!(sixtyfps::re_exports::ModelHandle::new(std::rc::Rc::::new(#model))) + } + + // FIXME: there could be an optimization if `repeated.model.is_constant()`, we don't need a binding + init.push(quote! { + _self.#repeater_id.set_model_binding({ + let self_weak = sixtyfps::re_exports::VRc::downgrade(&self_rc); + move || { + let self_rc = self_weak.upgrade().unwrap(); + let _self = self_rc.as_pin_ref(); + (#model) as _ + } + }); + }); + + if let Some(listview) = &repeated.is_listview { + let vp_y = access_named_reference(&listview.viewport_y, component, quote!(_self)); + let vp_h = + access_named_reference(&listview.viewport_height, component, quote!(_self)); + let lv_h = + access_named_reference(&listview.listview_height, component, quote!(_self)); + let vp_w = + access_named_reference(&listview.viewport_width, component, quote!(_self)); + let lv_w = + access_named_reference(&listview.listview_width, component, quote!(_self)); + + let ensure_updated = quote! { + #inner_component_id::FIELD_OFFSETS.#repeater_id.apply_pin(_self).ensure_updated_listview( + || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone(), &_self.window).into() }, + #vp_w, #vp_h, #vp_y, #lv_w.get(), #lv_h + ); }; - extra_components.push(if repeated.is_conditional_element { - quote! { - impl sixtyfps::re_exports::RepeatedComponent for #rep_inner_component_id { - type Data = (); - fn update(&self, _: usize, _: Self::Data) { } - #extra_fn - } - } - } else { - let data_type = get_rust_type( - &Expression::RepeaterModelReference { element: Rc::downgrade(item_rc) } - .ty(), - &item.node.as_ref().map(|x| x.to_source_location()), - diag, - ); - - quote! { - impl sixtyfps::re_exports::RepeatedComponent for #rep_inner_component_id { - type Data = #data_type; - fn update(&self, index: usize, data: Self::Data) { - self.index.set(index); - self.model_data.set(data); - } - #extra_fn - } - } - }); - - let mut model = compile_expression(&repeated.model, component); - if repeated.is_conditional_element { - model = quote!(sixtyfps::re_exports::ModelHandle::new(std::rc::Rc::::new(#model))) - } - - // FIXME: there could be an optimization if `repeated.model.is_constant()`, we don't need a binding - init.push(quote! { - _self.#repeater_id.set_model_binding({ - let self_weak = sixtyfps::re_exports::VRc::downgrade(&self_rc); - move || { - let self_rc = self_weak.upgrade().unwrap(); - let _self = self_rc.as_pin_ref(); - (#model) as _ - } - }); - }); - - if let Some(listview) = &repeated.is_listview { - let vp_y = - access_named_reference(&listview.viewport_y, component, quote!(_self)); - let vp_h = - access_named_reference(&listview.viewport_height, component, quote!(_self)); - let lv_h = - access_named_reference(&listview.listview_height, component, quote!(_self)); - let vp_w = - access_named_reference(&listview.viewport_width, component, quote!(_self)); - let lv_w = - access_named_reference(&listview.listview_width, component, quote!(_self)); - - let ensure_updated = quote! { - #inner_component_id::FIELD_OFFSETS.#repeater_id.apply_pin(_self).ensure_updated_listview( - || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone(), &_self.window).into() }, - #vp_w, #vp_h, #vp_y, #lv_w.get(), #lv_h - ); - }; - - repeated_visit_branch.push(quote!( - #repeater_index => { - #ensure_updated - _self.#repeater_id.visit(order, visitor) - } - )); - - repeated_element_layouts.push(quote!( + repeated_visit_branch.push(quote!( + #repeater_index => { #ensure_updated - )); - } else { - let ensure_updated = quote! { - #inner_component_id::FIELD_OFFSETS.#repeater_id.apply_pin(_self).ensure_updated( - || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone(), &_self.window).into() } - ); - }; - - repeated_visit_branch.push(quote!( - #repeater_index => { - #ensure_updated - _self.#repeater_id.visit(order, visitor) - } - )); - - repeated_element_layouts.push(quote!( - #ensure_updated - _self.#repeater_id.compute_layout(); - )); - } - - repeated_input_branch.push(quote!( - #repeater_index => self.#repeater_id.input_event(rep_index, event, window), - )); - - item_tree_array.push(quote!( - sixtyfps::re_exports::ItemTreeNode::DynamicTree { - index: #repeater_index, - parent_index: #parent_index, + _self.#repeater_id.visit(order, visitor) } )); - repeated_element_names.push(repeater_id); - repeated_element_components.push(rep_inner_component_id); + repeated_element_layouts.push(quote!( + #ensure_updated + )); } else { - let field_name = format_ident!("{}", item.id); - let children_count = - if super::is_flickable(item_rc) { 1 } else { item.children.len() as u32 }; + let ensure_updated = quote! { + #inner_component_id::FIELD_OFFSETS.#repeater_id.apply_pin(_self).ensure_updated( + || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone(), &_self.window).into() } + ); + }; - item_tree_array.push(quote!( - sixtyfps::re_exports::ItemTreeNode::Item{ - item: VOffset::new(#inner_component_id::FIELD_OFFSETS.#field_name), - chilren_count: #children_count, - children_index: #children_index, - parent_index: #parent_index, + repeated_visit_branch.push(quote!( + #repeater_index => { + #ensure_updated + _self.#repeater_id.visit(order, visitor) } )); - for (k, binding_expression) in &item.bindings { - handle_property_binding(component, item_rc, k, binding_expression, &mut init); - } - item_names.push(field_name); - item_types.push(format_ident!("{}", item.base_type.as_native().class_name)); + + repeated_element_layouts.push(quote!( + #ensure_updated + _self.#repeater_id.compute_layout(); + )); } - }, - ); + + repeated_input_branch.push(quote!( + #repeater_index => self.#repeater_id.input_event(rep_index, event, window), + )); + + item_tree_array.push(quote!( + sixtyfps::re_exports::ItemTreeNode::DynamicTree { + index: #repeater_index, + parent_index: #parent_index, + } + )); + + repeated_element_names.push(repeater_id); + repeated_element_components.push(rep_inner_component_id); + } else if item.is_flickable_viewport { + let field_name = format_ident!( + "{}", + crate::object_tree::find_parent_element(item_rc).unwrap().borrow().id + ); + let children_count = item.children.len() as u32; + + item_tree_array.push(quote!( + sixtyfps::re_exports::ItemTreeNode::Item{ + item: VOffset::new(#inner_component_id::FIELD_OFFSETS.#field_name + sixtyfps::re_exports::Flickable::FIELD_OFFSETS.viewport), + chilren_count: #children_count, + children_index: #children_index, + parent_index: #parent_index + } + )); + for (k, binding_expression) in &item.bindings { + handle_property_binding(component, item_rc, k, binding_expression, &mut init); + } + } else { + let field_name = format_ident!("{}", item.id); + let children_count = item.children.len() as u32; + + item_tree_array.push(quote!( + sixtyfps::re_exports::ItemTreeNode::Item{ + item: VOffset::new(#inner_component_id::FIELD_OFFSETS.#field_name), + chilren_count: #children_count, + children_index: #children_index, + parent_index: #parent_index, + } + )); + for (k, binding_expression) in &item.bindings { + handle_property_binding(component, item_rc, k, binding_expression, &mut init); + } + item_names.push(field_name); + item_types.push(format_ident!("{}", item.base_type.as_native().class_name)); + } + }); let resource_symbols: Vec = component .embedded_file_resources @@ -976,9 +976,11 @@ fn access_member( let name_ident = format_ident!("{}", name); if e.property_declarations.contains_key(name) || is_special || component.is_global() { quote!(#inner_component_id::FIELD_OFFSETS.#name_ident.apply_pin(#component_rust)) - } else if let Some(vp) = super::as_flickable_viewport_property(element, name) { - let name_ident = format_ident!("{}", vp); - let elem_ident = format_ident!("{}", e.id); + } else if e.is_flickable_viewport { + let elem_ident = format_ident!( + "{}", + crate::object_tree::find_parent_element(element).unwrap().borrow().id + ); quote!((#inner_component_id::FIELD_OFFSETS.#elem_ident + sixtyfps::re_exports::Flickable::FIELD_OFFSETS.viewport diff --git a/sixtyfps_compiler/lib.rs b/sixtyfps_compiler/lib.rs index 0f150b229..623fbb43e 100644 --- a/sixtyfps_compiler/lib.rs +++ b/sixtyfps_compiler/lib.rs @@ -50,6 +50,7 @@ mod passes { pub mod deduplicate_property_read; pub mod default_geometry; pub mod embed_resources; + pub mod flickable; pub mod focus_item; pub mod generate_item_indices; pub mod inlining; @@ -160,6 +161,10 @@ pub async fn run_passes( passes::focus_item::resolve_element_reference_in_set_focus_calls(&doc.root_component, diag); passes::focus_item::determine_initial_focus_item(&doc.root_component, diag); passes::focus_item::erase_forward_focus_properties(&doc.root_component); + passes::flickable::handle_flickable( + &doc.root_component, + &type_loader.global_type_registry.borrow(), + ); passes::materialize_fake_properties::materialize_fake_properties(&doc.root_component); if compiler_config.embed_resources { passes::embed_resources::embed_resources(&doc.root_component); diff --git a/sixtyfps_compiler/object_tree.rs b/sixtyfps_compiler/object_tree.rs index 622ebbb89..467e3a0d6 100644 --- a/sixtyfps_compiler/object_tree.rs +++ b/sixtyfps_compiler/object_tree.rs @@ -287,6 +287,9 @@ pub struct Element { /// true when this item's geometry is handled by a layout pub child_of_layout: bool, + /// true if this Element is the fake Flickable viewport + pub is_flickable_viewport: bool, + /// This is the component-local index of this item in the item tree array. /// It is generated after the last pass and before the generators run. pub item_index: once_cell::unsync::OnceCell, @@ -1017,6 +1020,28 @@ fn find_element_by_id(e: &ElementRc, name: &str) -> Option { None } +/// Find the parent element to a given element. +/// (since there is no parent mapping we need to fo an exhaustive search) +pub fn find_parent_element(e: &ElementRc) -> Option { + fn recurse(base: &ElementRc, e: &ElementRc) -> Option { + for child in &base.borrow().children { + if Rc::ptr_eq(child, e) { + return Some(base.clone()); + } + if let Some(x) = recurse(child, e) { + return Some(x); + } + } + None + } + + let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone(); + if Rc::ptr_eq(&root, e) { + return None; + } + recurse(&root, e) +} + /// Call the visitor for each children of the element recursively, starting with the element itself /// /// The state returned by the visitor is passed to the children diff --git a/sixtyfps_compiler/passes/flickable.rs b/sixtyfps_compiler/passes/flickable.rs new file mode 100644 index 000000000..d8aee302b --- /dev/null +++ b/sixtyfps_compiler/passes/flickable.rs @@ -0,0 +1,72 @@ +/* LICENSE BEGIN + This file is part of the SixtyFPS Project -- https://sixtyfps.io + Copyright (c) 2020 Olivier Goffart + Copyright (c) 2020 Simon Hausmann + + SPDX-License-Identifier: GPL-3.0-only + This file is also available under commercial licensing terms. + Please contact info@sixtyfps.io for more information. +LICENSE END */ + +//! Flickable pass +//! +//! The Flickable element is special in the sense that it has a viewport +//! which is not exposed. This passes create the viewport and fixes all property access + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::expression_tree::{Expression, NamedReference}; +use crate::langtype::Type; +use crate::object_tree::{Component, Element, ElementRc}; +use crate::typeregister::TypeRegister; + +pub fn handle_flickable(root_component: &Rc, tr: &TypeRegister) -> () { + let mut native_rect = tr.lookup("Rectangle").as_builtin().native_class.clone(); + while let Some(p) = native_rect.parent.clone() { + native_rect = p; + } + crate::object_tree::recurse_elem_including_sub_components( + &root_component, + &(), + &mut |elem: &ElementRc, _| { + if !matches!(elem.borrow().native_class(), Some(n) if n.class_name == "Flickable") { + return; + } + + let mut flickable = elem.borrow_mut(); + let flickable = &mut *flickable; + + let viewport = Rc::new(RefCell::new(Element { + id: format!("{}_viewport", flickable.id), + base_type: Type::Native(native_rect.clone()), + children: std::mem::take(&mut flickable.children), + enclosing_component: flickable.enclosing_component.clone(), + is_flickable_viewport: true, + ..Element::default() + })); + + // Create aliases. All these aliases should be removed by the alias optimisation pass + for (prop, ty) in &flickable.base_type.as_builtin().properties { + if let Some(vp_prop) = prop.strip_prefix("viewport_") { + let nr = NamedReference::new(&viewport, vp_prop); + flickable.property_declarations.insert(prop.to_owned(), ty.clone().into()); + match flickable.bindings.entry(prop.to_owned()) { + std::collections::hash_map::Entry::Occupied(entry) => { + let entry = entry.into_mut(); + entry.expression = Expression::TwoWayBinding( + nr, + Some(Box::new(std::mem::take(&mut entry.expression))), + ) + } + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(Expression::TwoWayBinding(nr, None).into()); + } + } + } + } + + flickable.children.push(viewport); + }, + ) +} diff --git a/sixtyfps_compiler/passes/generate_item_indices.rs b/sixtyfps_compiler/passes/generate_item_indices.rs index 61c8668a0..8a7deb1a0 100644 --- a/sixtyfps_compiler/passes/generate_item_indices.rs +++ b/sixtyfps_compiler/passes/generate_item_indices.rs @@ -10,11 +10,9 @@ LICENSE END */ //! Assign the Element::item_index on each elements pub fn generate_item_indices(component: &std::rc::Rc) { let mut current_item_index: usize = 0; - crate::generator::build_array_helper(&component, move |item_rc, _, _, is_flickable_rect| { + crate::generator::build_array_helper(&component, move |item_rc, _, _| { let item = item_rc.borrow(); - if is_flickable_rect { - current_item_index += 1; - } else if item.base_type == crate::langtype::Type::Void { + if item.base_type == crate::langtype::Type::Void { } else { if let crate::langtype::Type::Component(c) = &item.base_type { generate_item_indices(c); diff --git a/sixtyfps_compiler/passes/inlining.rs b/sixtyfps_compiler/passes/inlining.rs index 6ce3980b1..1e3d00cfd 100644 --- a/sixtyfps_compiler/passes/inlining.rs +++ b/sixtyfps_compiler/passes/inlining.rs @@ -184,6 +184,7 @@ fn duplicate_element_with_mapping( child_of_layout: elem.child_of_layout, named_references: Default::default(), item_index: Default::default(), // Not determined yet + is_flickable_viewport: elem.is_flickable_viewport, })); mapping.insert(element_key(element.clone()), new.clone()); new diff --git a/sixtyfps_compiler/passes/repeater_component.rs b/sixtyfps_compiler/passes/repeater_component.rs index 510fd76f5..6ce3469c1 100644 --- a/sixtyfps_compiler/passes/repeater_component.rs +++ b/sixtyfps_compiler/passes/repeater_component.rs @@ -48,6 +48,7 @@ fn create_repeater_components(component: &Rc) { states: std::mem::take(&mut elem.states), transitions: std::mem::take(&mut elem.transitions), child_of_layout: elem.child_of_layout, + is_flickable_viewport: elem.is_flickable_viewport, item_index: Default::default(), // Not determined yet })), parent_element, diff --git a/sixtyfps_compiler/passes/resolving.rs b/sixtyfps_compiler/passes/resolving.rs index ab88365c2..a4e83eb0a 100644 --- a/sixtyfps_compiler/passes/resolving.rs +++ b/sixtyfps_compiler/passes/resolving.rs @@ -190,28 +190,6 @@ fn find_element_by_id(roots: &[ElementRc], name: &str) -> Option { None } -/// Find the parent element to a given element. -/// (since there is no parent mapping we need to fo an exhaustive search) -fn find_parent_element(e: &ElementRc) -> Option { - fn recurse(base: &ElementRc, e: &ElementRc) -> Option { - for child in &base.borrow().children { - if Rc::ptr_eq(child, e) { - return Some(base.clone()); - } - if let Some(x) = recurse(child, e) { - return Some(x); - } - } - None - } - - let root = e.borrow().enclosing_component.upgrade().unwrap().root_element.clone(); - if Rc::ptr_eq(&root, e) { - return None; - } - recurse(&root, e) -} - impl Expression { pub fn from_binding_expression_node( node: SyntaxNodeWithSourceFile, diff --git a/sixtyfps_runtime/interpreter/dynamic_component.rs b/sixtyfps_runtime/interpreter/dynamic_component.rs index e38c372b9..c9b41c9ec 100644 --- a/sixtyfps_runtime/interpreter/dynamic_component.rs +++ b/sixtyfps_runtime/interpreter/dynamic_component.rs @@ -27,7 +27,7 @@ use sixtyfps_corelib::item_tree::{ ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, TraversalOrder, VisitChildrenResult, }; use sixtyfps_corelib::items::{ - Flickable, ItemRc, ItemRef, ItemVTable, ItemWeak, PropertyAnimation, Rectangle, + Flickable, ItemRc, ItemRef, ItemVTable, ItemWeak, PropertyAnimation, }; use sixtyfps_corelib::layout::{LayoutInfo, Padding}; use sixtyfps_corelib::model::RepeatedComponent; @@ -569,57 +569,6 @@ fn rtti_for (&'static str, Rc) { - let (name, mut rtti) = rtti_for::(); - - use rtti::BuiltinItem; - let rect_prop = &["viewport_x", "viewport_y", "viewport_width", "viewport_height"]; - - struct FlickableViewPortPropertyInfo(&'static dyn rtti::PropertyInfo); - fn viewport(flick: Pin) -> Pin<&Rectangle> { - Flickable::FIELD_OFFSETS - .viewport - .apply_pin(ItemRef::downcast_pin::(flick).unwrap()) - } - - impl eval::ErasedPropertyInfo for FlickableViewPortPropertyInfo { - fn get(&self, item: Pin) -> Value { - (*self.0).get(viewport(item)).unwrap() - } - fn set(&self, item: Pin, value: Value, animation: Option) { - (*self.0).set(viewport(item), value, animation).unwrap() - } - fn set_binding( - &self, - item: Pin, - binding: Box Value>, - animation: AnimatedBindingKind, - ) { - (*self.0).set_binding(viewport(item), binding, animation).unwrap(); - } - fn offset(&self) -> usize { - (*self.0).offset() + Flickable::FIELD_OFFSETS.viewport.get_byte_offset() - } - - unsafe fn link_two_ways(&self, item: Pin, property2: *const ()) { - (*self.0).link_two_ways(viewport(item), property2) - } - } - - Rc::get_mut(&mut rtti).unwrap().properties.extend( - Rectangle::properties().into_iter().filter_map(|(k, v)| { - Some(( - *rect_prop.iter().find(|x| x.ends_with(k))?, - Box::new(FlickableViewPortPropertyInfo(v)) as Box, - )) - }), - ); - - (name, rtti) -} - /// Create a ComponentDescription from a source. /// The path corresponding to the source need to be passed as well (path is used for diagnostics /// and loading relative assets) @@ -668,7 +617,7 @@ fn generate_component<'id>( rtti_for::(), rtti_for::(), rtti_for::(), - rtti_for_flickable(), + rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), @@ -707,55 +656,49 @@ fn generate_component<'id>( let mut repeater = vec![]; let mut repeater_names = HashMap::new(); - generator::build_array_helper( - component, - |rc_item, child_offset, parent_index, is_flickable_rect| { - let item = rc_item.borrow(); - if is_flickable_rect { - use vtable::HasStaticVTable; - let offset = items_types[&item.id].offset - + Flickable::FIELD_OFFSETS.viewport.get_byte_offset(); - tree_array.push(ItemTreeNode::Item { - item: unsafe { vtable::VOffset::from_raw(Rectangle::static_vtable(), offset) }, - children_index: tree_array.len() as u32 + 1, - chilren_count: item.children.len() as _, - parent_index, - }); - } else if let Some(repeated) = &item.repeated { - tree_array.push(ItemTreeNode::DynamicTree { index: repeater.len(), parent_index }); - let base_component = item.base_type.as_component(); - repeater_names.insert(item.id.clone(), repeater.len()); - generativity::make_guard!(guard); - repeater.push( - RepeaterWithinComponent { - component_to_repeat: generate_component(base_component, guard), - offset: builder.add_field_type::>(), - model: repeated.model.clone(), - } - .into(), + generator::build_array_helper(component, |rc_item, child_offset, parent_index| { + let item = rc_item.borrow(); + if let Some(repeated) = &item.repeated { + tree_array.push(ItemTreeNode::DynamicTree { index: repeater.len(), parent_index }); + let base_component = item.base_type.as_component(); + repeater_names.insert(item.id.clone(), repeater.len()); + generativity::make_guard!(guard); + repeater.push( + RepeaterWithinComponent { + component_to_repeat: generate_component(base_component, guard), + offset: builder.add_field_type::>(), + model: repeated.model.clone(), + } + .into(), + ); + } else { + let rt = rtti.get(&*item.base_type.as_native().class_name).unwrap_or_else(|| { + panic!("Native type not registered: {}", item.base_type.as_native().class_name) + }); + + let offset = if item.is_flickable_viewport { + let parent = + &items_types[&object_tree::find_parent_element(rc_item).unwrap().borrow().id]; + assert_eq!( + parent.elem.borrow().base_type.as_native().class_name.as_str(), + "Flickable" ); + parent.offset + Flickable::FIELD_OFFSETS.viewport.get_byte_offset() } else { - let rt = rtti.get(&*item.base_type.as_native().class_name).unwrap_or_else(|| { - panic!("Native type not registered: {}", item.base_type.as_native().class_name) - }); - let offset = builder.add_field(rt.type_info); - tree_array.push(ItemTreeNode::Item { - item: unsafe { vtable::VOffset::from_raw(rt.vtable, offset) }, - children_index: child_offset, - chilren_count: if generator::is_flickable(rc_item) { - 1 - } else { - item.children.len() as _ - }, - parent_index, - }); - items_types.insert( - item.id.clone(), - ItemWithinComponent { offset, rtti: rt.clone(), elem: rc_item.clone() }, - ); - } - }, - ); + builder.add_field(rt.type_info) + }; + tree_array.push(ItemTreeNode::Item { + item: unsafe { vtable::VOffset::from_raw(rt.vtable, offset) }, + children_index: child_offset, + chilren_count: item.children.len() as u32, + parent_index, + }); + items_types.insert( + item.id.clone(), + ItemWithinComponent { offset, rtti: rt.clone(), elem: rc_item.clone() }, + ); + } + }); let mut custom_properties = HashMap::new(); let mut custom_callbacks = HashMap::new();