WIP: Layout refactoring

Instead of using a solve_layout function in the component, use property to hold
a layout cache.

This commit only implement the GridLayout and only the interpreter part
This commit is contained in:
Olivier Goffart 2021-05-03 16:09:27 +02:00 committed by Olivier Goffart
parent 304699e6bc
commit 21a80f4562
18 changed files with 584 additions and 853 deletions

View file

@ -9,34 +9,34 @@ members = [
'sixtyfps_runtime/rendering_backends/testing', 'sixtyfps_runtime/rendering_backends/testing',
'sixtyfps_compiler', 'sixtyfps_compiler',
'sixtyfps_compiler/parser_test_macro', 'sixtyfps_compiler/parser_test_macro',
'api/sixtyfps-rs', # 'api/sixtyfps-rs',
'api/sixtyfps-rs/sixtyfps-macros', # 'api/sixtyfps-rs/sixtyfps-macros',
'api/sixtyfps-rs/sixtyfps-build', # 'api/sixtyfps-rs/sixtyfps-build',
'api/sixtyfps-cpp', # 'api/sixtyfps-cpp',
'api/sixtyfps-node/native', # 'api/sixtyfps-node/native',
'api/sixtyfps-wasm-interpreter', 'api/sixtyfps-wasm-interpreter',
'tools/compiler', # 'tools/compiler',
'tools/fmt', 'tools/fmt',
'tools/lsp', 'tools/lsp',
'tools/syntax_updater', 'tools/syntax_updater',
'tools/viewer', 'tools/viewer',
'examples/gallery', # 'examples/gallery',
'examples/printerdemo/rust', # 'examples/printerdemo/rust',
'examples/printerdemo_old/rust', # 'examples/printerdemo_old/rust',
'examples/todo/rust', # 'examples/todo/rust',
'examples/slide_puzzle', # 'examples/slide_puzzle',
'examples/7gui', # 'examples/7gui',
'examples/memory', # 'examples/memory',
'helper_crates/const-field-offset', 'helper_crates/const-field-offset',
'helper_crates/vtable', 'helper_crates/vtable',
'helper_crates/vtable/macro', 'helper_crates/vtable/macro',
'xtask', 'xtask',
'tests/doctests', # 'tests/doctests',
'tests/driver/driverlib', # 'tests/driver/driverlib',
'tests/driver/rust', # 'tests/driver/rust',
'tests/driver/cpp', # 'tests/driver/cpp',
'tests/driver/nodejs', # 'tests/driver/nodejs',
'tests/driver/interpreter', # 'tests/driver/interpreter',
] ]
default-members = [ default-members = [
@ -45,25 +45,25 @@ default-members = [
'sixtyfps_runtime/rendering_backends/gl', 'sixtyfps_runtime/rendering_backends/gl',
'sixtyfps_runtime/rendering_backends/qt', 'sixtyfps_runtime/rendering_backends/qt',
'sixtyfps_runtime/rendering_backends/default', 'sixtyfps_runtime/rendering_backends/default',
'sixtyfps_compiler', # 'sixtyfps_compiler',
'api/sixtyfps-rs', # 'api/sixtyfps-rs',
'api/sixtyfps-rs/sixtyfps-build', # 'api/sixtyfps-rs/sixtyfps-build',
'api/sixtyfps-node/native', # 'api/sixtyfps-node/native',
'tools/compiler', # 'tools/compiler',
'tools/fmt', 'tools/fmt',
'tools/lsp', 'tools/lsp',
'tools/syntax_updater', 'tools/syntax_updater',
'tools/viewer', 'tools/viewer',
'examples/gallery', # 'examples/gallery',
'examples/printerdemo/rust', # 'examples/printerdemo/rust',
'examples/printerdemo_old/rust', # 'examples/printerdemo_old/rust',
'examples/todo/rust', # 'examples/todo/rust',
'examples/slide_puzzle', # 'examples/slide_puzzle',
'examples/memory', # 'examples/memory',
'tests/doctests', # 'tests/doctests',
'tests/driver/rust', # 'tests/driver/rust',
'tests/driver/nodejs', # 'tests/driver/nodejs',
'tests/driver/interpreter', # 'tests/driver/interpreter',
] ]
resolver = "2" resolver = "2"

View file

@ -398,6 +398,13 @@ pub enum Expression {
EnumerationValue(EnumerationValue), EnumerationValue(EnumerationValue),
ReturnStatement(Option<Box<Expression>>), ReturnStatement(Option<Box<Expression>>),
LayoutCacheAccess {
layout_cache_prop: NamedReference,
index: usize,
},
ComputeLayoutInfo(crate::layout::Layout),
SolveLayout(crate::layout::Layout),
} }
impl Default for Expression { impl Default for Expression {
@ -530,6 +537,9 @@ impl Expression {
Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()), Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
// invalid because the expression is unreachable // invalid because the expression is unreachable
Expression::ReturnStatement(_) => Type::Invalid, Expression::ReturnStatement(_) => Type::Invalid,
Expression::LayoutCacheAccess { .. } => Type::LogicalLength,
Expression::ComputeLayoutInfo(_) => crate::layout::layout_info_type(),
Expression::SolveLayout(_) => Type::LayoutCache,
} }
} }
@ -613,6 +623,9 @@ impl Expression {
Expression::ReturnStatement(expr) => { Expression::ReturnStatement(expr) => {
expr.as_deref().map(|expr| visitor(expr)); expr.as_deref().map(|expr| visitor(expr));
} }
Expression::LayoutCacheAccess { .. } => {}
Expression::ComputeLayoutInfo(_) => {}
Expression::SolveLayout(_) => {}
} }
} }
@ -695,6 +708,9 @@ impl Expression {
Expression::ReturnStatement(expr) => { Expression::ReturnStatement(expr) => {
expr.as_deref_mut().map(|expr| visitor(expr)); expr.as_deref_mut().map(|expr| visitor(expr));
} }
Expression::LayoutCacheAccess { .. } => {}
Expression::ComputeLayoutInfo(_) => {}
Expression::SolveLayout(_) => {}
} }
} }
@ -751,6 +767,10 @@ impl Expression {
Expression::ReturnStatement(expr) => { Expression::ReturnStatement(expr) => {
expr.as_ref().map_or(true, |expr| expr.is_constant()) expr.as_ref().map_or(true, |expr| expr.is_constant())
} }
Expression::LayoutCacheAccess { .. } => false,
Expression::ComputeLayoutInfo(_) => false,
Expression::SolveLayout(_) => false,
} }
} }
@ -915,7 +935,8 @@ impl Expression {
| Type::Callback { .. } | Type::Callback { .. }
| Type::Function { .. } | Type::Function { .. }
| Type::Void | Type::Void
| Type::ElementReference => Expression::Invalid, | Type::ElementReference
| Type::LayoutCache => Expression::Invalid,
Type::Float32 => Expression::NumberLiteral(0., Unit::None), Type::Float32 => Expression::NumberLiteral(0., Unit::None),
Type::Int32 => Expression::NumberLiteral(0., Unit::None), Type::Int32 => Expression::NumberLiteral(0., Unit::None),
Type::String => Expression::StringLiteral(String::new()), Type::String => Expression::StringLiteral(String::new()),
@ -970,7 +991,7 @@ impl Expression {
} }
/// The expression in the Element::binding hash table /// The expression in the Element::binding hash table
#[derive(Default, Debug, Clone, derive_more::Deref, derive_more::DerefMut)] #[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut)]
pub struct BindingExpression { pub struct BindingExpression {
#[deref] #[deref]
#[deref_mut] #[deref_mut]
@ -979,6 +1000,8 @@ pub struct BindingExpression {
pub span: Option<SourceLocation>, pub span: Option<SourceLocation>,
/// How deep is this binding declared in the hierarchy. When two binding are conflicting /// How deep is this binding declared in the hierarchy. When two binding are conflicting
/// for the same priority (because of two way binding), the lower priority wins. /// for the same priority (because of two way binding), the lower priority wins.
/// The priority starts at 1, and each level of inlining adds one to the priority.
/// 0 means the expression was added by some passes and it is not explicit in the source code
pub priority: i32, pub priority: i32,
} }
@ -993,7 +1016,7 @@ impl BindingExpression {
Self { Self {
expression: Expression::Uncompiled(node.clone()), expression: Expression::Uncompiled(node.clone()),
span: Some(node.to_source_location()), span: Some(node.to_source_location()),
priority: 0, priority: 1,
} }
} }
} }
@ -1176,6 +1199,11 @@ pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std
write!(f, "return ")?; write!(f, "return ")?;
e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(())) e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
} }
Expression::LayoutCacheAccess { layout_cache_prop, index } => {
write!(f, "{:?}[{}]", layout_cache_prop, index)
}
Expression::ComputeLayoutInfo(l) => write!(f, "info({:?})", l),
Expression::SolveLayout(l) => write!(f, "solve({:?})", l),
} }
} }

View file

@ -54,7 +54,7 @@ pub enum Type {
PathElements, PathElements,
Easing, Easing,
Brush, Brush,
/// This is usually a model
Array(Box<Type>), Array(Box<Type>),
Struct { Struct {
fields: BTreeMap<String, Type>, fields: BTreeMap<String, Type>,
@ -69,6 +69,9 @@ pub enum Type {
UnitProduct(Vec<(Unit, i8)>), UnitProduct(Vec<(Unit, i8)>),
ElementReference, ElementReference,
/// This is a `SharedArray<f32>`
LayoutCache,
} }
impl core::cmp::PartialEq for Type { impl core::cmp::PartialEq for Type {
@ -107,6 +110,7 @@ impl core::cmp::PartialEq for Type {
Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs), Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b), Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
Type::ElementReference => matches!(other, Type::ElementReference), Type::ElementReference => matches!(other, Type::ElementReference),
Type::LayoutCache => matches!(other, Type::LayoutCache),
} }
} }
} }
@ -189,6 +193,7 @@ impl Display for Type {
write!(f, "({})", x.join("×")) write!(f, "({})", x.join("×"))
} }
Type::ElementReference => write!(f, "element ref"), Type::ElementReference => write!(f, "element ref"),
Type::LayoutCache => write!(f, "layout cache"),
} }
} }
} }
@ -469,6 +474,7 @@ impl Type {
Type::Enumeration(_) => None, Type::Enumeration(_) => None,
Type::UnitProduct(_) => None, Type::UnitProduct(_) => None,
Type::ElementReference => None, Type::ElementReference => None,
Type::LayoutCache => None,
} }
} }
@ -682,6 +688,12 @@ pub struct PropertyLookupResult<'a> {
pub property_type: Type, pub property_type: Type,
} }
impl<'a> PropertyLookupResult<'a> {
pub fn is_valid(&self) -> bool {
self.property_type != Type::Invalid
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Enumeration { pub struct Enumeration {
pub name: String, pub name: String,

View file

@ -9,16 +9,14 @@
LICENSE END */ LICENSE END */
//! Datastructures used to represent layouts in the compiler //! Datastructures used to represent layouts in the compiler
use crate::langtype::Type; use crate::diagnostics::BuildDiagnostics;
use crate::object_tree::{ElementRc, PropertyDeclaration}; use crate::expression_tree::{Expression, NamedReference, Path};
use crate::{diagnostics::BuildDiagnostics, langtype::PropertyLookupResult}; use crate::langtype::{PropertyLookupResult, Type};
use crate::{ use crate::object_tree::{Component, ElementRc};
expression_tree::{Expression, NamedReference, Path},
object_tree::Component,
};
use std::{borrow::Cow, rc::Rc};
#[derive(Debug, derive_more::From)] use std::rc::Rc;
#[derive(Clone, Debug, derive_more::From)]
pub enum Layout { pub enum Layout {
GridLayout(GridLayout), GridLayout(GridLayout),
PathLayout(PathLayout), PathLayout(PathLayout),
@ -37,7 +35,7 @@ impl Layout {
impl Layout { impl Layout {
/// Call the visitor for each NamedReference stored in the layout /// Call the visitor for each NamedReference stored in the layout
fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) { pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
match self { match self {
Layout::GridLayout(grid) => grid.visit_named_references(visitor), Layout::GridLayout(grid) => grid.visit_named_references(visitor),
Layout::BoxLayout(l) => l.visit_named_references(visitor), Layout::BoxLayout(l) => l.visit_named_references(visitor),
@ -46,65 +44,29 @@ impl Layout {
} }
} }
/// Holds a list of all layout in the component
#[derive(derive_more::Deref, derive_more::DerefMut, Default, Debug)]
pub struct LayoutVec {
#[deref]
#[deref_mut]
pub layouts: Vec<Layout>,
/// The index within the vector of the layout which applies to the root item, if any
pub main_layout: Option<usize>,
/// The constraints that applies to the root item
pub root_constraints: LayoutConstraints,
}
impl LayoutVec {
/// Call the visitor for each NamedReference stored in the layout
pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
self.root_constraints.visit_named_references(visitor);
for sub in &mut self.layouts {
sub.visit_named_references(visitor);
}
}
}
/// An Item in the layout tree /// An Item in the layout tree
#[derive(Debug, Default)] #[derive(Debug, Default, Clone)]
pub struct LayoutItem { pub struct LayoutItem {
pub element: Option<ElementRc>, pub element: ElementRc,
pub layout: Option<Layout>,
pub constraints: LayoutConstraints, pub constraints: LayoutConstraints,
} }
impl LayoutItem { impl LayoutItem {
pub fn rect(&self) -> Cow<LayoutRect> { pub fn rect(&self) -> LayoutRect {
if let Some(e) = &self.element {
let p = |unresolved_name: &str| { let p = |unresolved_name: &str| {
let PropertyLookupResult { resolved_name, property_type } = let PropertyLookupResult { resolved_name, property_type } =
e.borrow().lookup_property(unresolved_name); self.element.borrow().lookup_property(unresolved_name);
if property_type == Type::LogicalLength { if property_type == Type::LogicalLength {
Some(NamedReference::new(e, resolved_name.as_ref())) Some(NamedReference::new(&self.element, resolved_name.as_ref()))
} else { } else {
None None
} }
}; };
Cow::Owned(LayoutRect { LayoutRect {
x_reference: p("x"), x_reference: p("x"),
y_reference: p("y"), y_reference: p("y"),
width_reference: if !self.constraints.fixed_width { p("width") } else { None }, width_reference: if !self.constraints.fixed_width { p("width") } else { None },
height_reference: if !self.constraints.fixed_height { p("height") } else { None }, height_reference: if !self.constraints.fixed_height { p("height") } else { None },
})
} else if let Some(l) = &self.layout {
let mut r = Cow::Borrowed(l.rect());
if r.width_reference.is_some() && self.constraints.fixed_width {
r.to_mut().width_reference = None;
}
if r.height_reference.is_some() && self.constraints.fixed_height {
r.to_mut().height_reference = None;
}
r
} else {
Cow::Owned(LayoutRect::default())
} }
} }
} }
@ -119,17 +81,7 @@ pub struct LayoutRect {
impl LayoutRect { impl LayoutRect {
pub fn install_on_element(element: &ElementRc) -> Self { pub fn install_on_element(element: &ElementRc) -> Self {
let install_prop = |name: &str| { let install_prop = |name: &str| Some(NamedReference::new(element, name));
element.borrow_mut().property_declarations.insert(
name.to_string(),
PropertyDeclaration {
property_type: Type::LogicalLength,
node: None,
..Default::default()
},
);
Some(NamedReference::new(element, name))
};
Self { Self {
x_reference: install_prop("x"), x_reference: install_prop("x"),
@ -147,7 +99,7 @@ impl LayoutRect {
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default, Clone)]
pub struct LayoutConstraints { pub struct LayoutConstraints {
pub minimum_width: Option<NamedReference>, pub minimum_width: Option<NamedReference>,
pub maximum_width: Option<NamedReference>, pub maximum_width: Option<NamedReference>,
@ -251,7 +203,7 @@ impl LayoutConstraints {
.chain(self.vertical_stretch.as_ref().map(|x| (x, "vertical_stretch"))) .chain(self.vertical_stretch.as_ref().map(|x| (x, "vertical_stretch")))
} }
fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) { pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
self.maximum_width.as_mut().map(|e| visitor(&mut *e)); self.maximum_width.as_mut().map(|e| visitor(&mut *e));
self.minimum_width.as_mut().map(|e| visitor(&mut *e)); self.minimum_width.as_mut().map(|e| visitor(&mut *e));
self.maximum_height.as_mut().map(|e| visitor(&mut *e)); self.maximum_height.as_mut().map(|e| visitor(&mut *e));
@ -264,7 +216,7 @@ impl LayoutConstraints {
} }
/// An element in a GridLayout /// An element in a GridLayout
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct GridLayoutElement { pub struct GridLayoutElement {
pub col: u16, pub col: u16,
pub row: u16, pub row: u16,
@ -273,7 +225,7 @@ pub struct GridLayoutElement {
pub item: LayoutItem, pub item: LayoutItem,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Padding { pub struct Padding {
pub left: Option<NamedReference>, pub left: Option<NamedReference>,
pub right: Option<NamedReference>, pub right: Option<NamedReference>,
@ -290,15 +242,12 @@ impl Padding {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct LayoutGeometry { pub struct LayoutGeometry {
pub rect: LayoutRect, pub rect: LayoutRect,
pub spacing: Option<NamedReference>, pub spacing: Option<NamedReference>,
pub alignment: Option<NamedReference>, pub alignment: Option<NamedReference>,
pub padding: Padding, pub padding: Padding,
/// This contains the reference of properties of the materialized properties who
/// don't have explicit binding and must therefore need to be set
pub materialized_constraints: LayoutConstraints,
} }
impl LayoutGeometry { impl LayoutGeometry {
@ -307,10 +256,10 @@ impl LayoutGeometry {
self.spacing.as_mut().map(|e| visitor(&mut *e)); self.spacing.as_mut().map(|e| visitor(&mut *e));
self.alignment.as_mut().map(|e| visitor(&mut *e)); self.alignment.as_mut().map(|e| visitor(&mut *e));
self.padding.visit_named_references(visitor); self.padding.visit_named_references(visitor);
self.materialized_constraints.visit_named_references(visitor);
} }
} }
/// Return a named reference to a property if a binding is set on that property
fn binding_reference(element: &ElementRc, name: &str) -> Option<NamedReference> { fn binding_reference(element: &ElementRc, name: &str) -> Option<NamedReference> {
element.borrow().bindings.contains_key(name).then(|| NamedReference::new(element, name)) element.borrow().bindings.contains_key(name).then(|| NamedReference::new(element, name))
} }
@ -337,11 +286,7 @@ fn init_fake_property(
} }
impl LayoutGeometry { impl LayoutGeometry {
pub fn new( pub fn new(layout_element: &ElementRc, style_metrics: &Option<Rc<Component>>) -> Self {
rect: LayoutRect,
layout_element: &ElementRc,
style_metrics: &Option<Rc<Component>>,
) -> Self {
let style_metrics_element = style_metrics.as_ref().map(|comp| comp.root_element.clone()); let style_metrics_element = style_metrics.as_ref().map(|comp| comp.root_element.clone());
let padding = || { let padding = || {
@ -358,10 +303,6 @@ impl LayoutGeometry {
}); });
let alignment = binding_reference(layout_element, "alignment"); let alignment = binding_reference(layout_element, "alignment");
init_fake_property(layout_element, "width", || rect.width_reference.clone());
init_fake_property(layout_element, "height", || rect.height_reference.clone());
init_fake_property(layout_element, "x", || rect.x_reference.clone());
init_fake_property(layout_element, "y", || rect.y_reference.clone());
init_fake_property(layout_element, "padding_left", padding); init_fake_property(layout_element, "padding_left", padding);
init_fake_property(layout_element, "padding_right", padding); init_fake_property(layout_element, "padding_right", padding);
init_fake_property(layout_element, "padding_top", padding); init_fake_property(layout_element, "padding_top", padding);
@ -374,31 +315,14 @@ impl LayoutGeometry {
bottom: binding_reference(layout_element, "padding_bottom").or_else(padding), bottom: binding_reference(layout_element, "padding_bottom").or_else(padding),
}; };
// that's kind of the opposite of binding reference let rect = LayoutRect::install_on_element(layout_element);
let fake_property_ref = |name| {
(layout_element.borrow().property_declarations.contains_key(name) Self { rect, spacing, padding, alignment }
&& !layout_element.borrow().bindings.contains_key(name))
.then(|| NamedReference::new(layout_element, name))
};
let materialized_constraints = LayoutConstraints {
minimum_width: fake_property_ref("minimum_width"),
maximum_width: fake_property_ref("maximum_width"),
minimum_height: fake_property_ref("minimum_height"),
maximum_height: fake_property_ref("maximum_height"),
preferred_width: fake_property_ref("preferred_width"),
preferred_height: fake_property_ref("preferred_height"),
horizontal_stretch: fake_property_ref("horizontal_stretch"),
vertical_stretch: fake_property_ref("vertical_stretch"),
// these two have booleans have no meeing in this case
fixed_width: false,
fixed_height: false,
};
Self { rect, spacing, padding, alignment, materialized_constraints }
} }
} }
/// Internal representation of a grid layout /// Internal representation of a grid layout
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct GridLayout { pub struct GridLayout {
/// All the elements will be layout within that element. /// All the elements will be layout within that element.
pub elems: Vec<GridLayoutElement>, pub elems: Vec<GridLayoutElement>,
@ -409,7 +333,6 @@ pub struct GridLayout {
impl GridLayout { impl GridLayout {
fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) { fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
for cell in &mut self.elems { for cell in &mut self.elems {
cell.item.layout.as_mut().map(|x| x.visit_named_references(visitor));
cell.item.constraints.visit_named_references(visitor); cell.item.constraints.visit_named_references(visitor);
} }
self.geometry.visit_named_references(visitor); self.geometry.visit_named_references(visitor);
@ -417,7 +340,7 @@ impl GridLayout {
} }
/// Internal representation of a BoxLayout /// Internal representation of a BoxLayout
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct BoxLayout { pub struct BoxLayout {
/// When true, this is a HorizonalLayout, otherwise a VerticalLayout /// When true, this is a HorizonalLayout, otherwise a VerticalLayout
pub is_horizontal: bool, pub is_horizontal: bool,
@ -428,7 +351,6 @@ pub struct BoxLayout {
impl BoxLayout { impl BoxLayout {
fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) { fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
for cell in &mut self.elems { for cell in &mut self.elems {
cell.layout.as_mut().map(|x| x.visit_named_references(visitor));
cell.constraints.visit_named_references(visitor); cell.constraints.visit_named_references(visitor);
} }
self.geometry.visit_named_references(visitor); self.geometry.visit_named_references(visitor);
@ -436,7 +358,7 @@ impl BoxLayout {
} }
/// Internal representation of a path layout /// Internal representation of a path layout
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct PathLayout { pub struct PathLayout {
pub path: Path, pub path: Path,
pub elements: Vec<ElementRc>, pub elements: Vec<ElementRc>,
@ -555,3 +477,34 @@ pub mod gen {
layout_tree.last().unwrap() layout_tree.last().unwrap()
} }
} }
/// The [`Type`] for a runtime LayoutInfo structure
pub fn layout_info_type() -> Type {
Type::Struct {
fields: [
"min_width",
"min_height",
"max_width",
"max_height",
"preferred_width",
"preferred_height",
]
.iter()
.map(|s| (s.to_string(), Type::LogicalLength))
.chain(
[
"min_width_percent",
"min_height_percent",
"max_width_percent",
"max_height_percent",
"horizontal_stretch",
"vertical_stretch",
]
.iter()
.map(|s| (s.to_string(), Type::Float32)),
)
.collect(),
name: Some("LayoutInfo".into()),
node: None,
}
}

View file

@ -17,6 +17,7 @@ use crate::diagnostics::{BuildDiagnostics, Spanned};
use crate::expression_tree::{self, BindingExpression, Expression, Unit}; use crate::expression_tree::{self, BindingExpression, Expression, Unit};
use crate::langtype::PropertyLookupResult; use crate::langtype::PropertyLookupResult;
use crate::langtype::{BuiltinElement, NativeClass, Type}; use crate::langtype::{BuiltinElement, NativeClass, Type};
use crate::layout::LayoutConstraints;
use crate::namedreference::NamedReference; use crate::namedreference::NamedReference;
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNode}; use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNode};
use crate::typeloader::ImportedTypes; use crate::typeloader::ImportedTypes;
@ -188,8 +189,8 @@ pub struct Component {
/// generator for symbol generation. /// generator for symbol generation.
pub embedded_file_resources: RefCell<HashMap<String, usize>>, pub embedded_file_resources: RefCell<HashMap<String, usize>>,
/// All layouts in this component /// The layout constraints of the root item
pub layouts: RefCell<crate::layout::LayoutVec>, pub root_constraints: RefCell<LayoutConstraints>,
/// When creating this component and inserting "children", append them to the children of /// When creating this component and inserting "children", append them to the children of
/// the element pointer to by this field. /// the element pointer to by this field.
@ -332,6 +333,8 @@ pub struct Element {
/// true when this item's geometry is handled by a layout /// true when this item's geometry is handled by a layout
pub child_of_layout: bool, pub child_of_layout: bool,
/// The property pointing to the layout info
pub layout_info_prop: Option<NamedReference>,
/// true if this Element is the fake Flickable viewport /// true if this Element is the fake Flickable viewport
pub is_flickable_viewport: bool, pub is_flickable_viewport: bool,
@ -1233,6 +1236,9 @@ pub fn visit_all_named_references_in_element(
match expr { match expr {
Expression::PropertyReference(r) | Expression::CallbackReference(r) => vis(r), Expression::PropertyReference(r) | Expression::CallbackReference(r) => vis(r),
Expression::TwoWayBinding(r, _) => vis(r), Expression::TwoWayBinding(r, _) => vis(r),
Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop),
Expression::SolveLayout(l) => l.visit_named_references(vis),
Expression::ComputeLayoutInfo(l) => l.visit_named_references(vis),
// This is not really a named reference, but the result is the same, it need to be updated // This is not really a named reference, but the result is the same, it need to be updated
// FIXME: this should probably be lowered into a PropertyReference // FIXME: this should probably be lowered into a PropertyReference
Expression::RepeaterModelReference { element } Expression::RepeaterModelReference { element }
@ -1272,6 +1278,9 @@ pub fn visit_all_named_references_in_element(
} }
} }
elem.borrow_mut().repeated = repeated; elem.borrow_mut().repeated = repeated;
let mut layout_info_prop = std::mem::take(&mut elem.borrow_mut().layout_info_prop);
layout_info_prop.as_mut().map(vis);
elem.borrow_mut().layout_info_prop = layout_info_prop;
} }
/// Visit all named reference in this component and sub component /// Visit all named reference in this component and sub component
@ -1287,7 +1296,7 @@ pub fn visit_all_named_references(
let compo = elem.borrow().enclosing_component.clone(); let compo = elem.borrow().enclosing_component.clone();
if !Weak::ptr_eq(parent_compo, &compo) { if !Weak::ptr_eq(parent_compo, &compo) {
let compo = compo.upgrade().unwrap(); let compo = compo.upgrade().unwrap();
compo.layouts.borrow_mut().visit_named_references(vis); compo.root_constraints.borrow_mut().visit_named_references(vis);
compo.popup_windows.borrow_mut().iter_mut().for_each(|p| { compo.popup_windows.borrow_mut().iter_mut().for_each(|p| {
vis(&mut p.x); vis(&mut p.x);
vis(&mut p.y); vis(&mut p.y);

View file

@ -182,6 +182,7 @@ fn duplicate_element_with_mapping(
.map(|t| duplicate_transition(t, mapping, root_component)) .map(|t| duplicate_transition(t, mapping, root_component))
.collect(), .collect(),
child_of_layout: elem.child_of_layout, child_of_layout: elem.child_of_layout,
layout_info_prop: elem.layout_info_prop.clone(),
named_references: Default::default(), named_references: Default::default(),
item_index: Default::default(), // Not determined yet item_index: Default::default(), // Not determined yet
is_flickable_viewport: elem.is_flickable_viewport, is_flickable_viewport: elem.is_flickable_viewport,

View file

@ -15,30 +15,139 @@ use crate::layout::*;
use crate::object_tree::*; use crate::object_tree::*;
use crate::typeregister::TypeRegister; use crate::typeregister::TypeRegister;
use crate::{diagnostics::BuildDiagnostics, typeloader::TypeLoader}; use crate::{diagnostics::BuildDiagnostics, typeloader::TypeLoader};
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
fn lower_grid_layout( pub async fn lower_layouts<'a>(
component: &Rc<Component>,
type_loader: &mut TypeLoader<'a>,
diag: &mut BuildDiagnostics,
) {
// Ignore import errors
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
let style_metrics = type_loader
.import_type("sixtyfps_widgets.60", "StyleMetrics", &mut build_diags_to_ignore)
.await;
let style_metrics =
style_metrics.and_then(|sm| if let Type::Component(c) = sm { Some(c) } else { None });
lower_layouts_impl(component, &type_loader.global_type_registry.borrow(), &style_metrics, diag);
}
fn lower_layouts_impl(
component: &Rc<Component>, component: &Rc<Component>,
rect: LayoutRect,
grid_layout_element: &ElementRc,
collected_children: &mut Vec<ElementRc>,
type_register: &TypeRegister, type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>, style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics, diag: &mut BuildDiagnostics,
) -> Option<Layout> { ) {
// FIXME: one should enable minimum_width and minimum_height on the window, but not height and width
//component.layouts.borrow_mut().root_constraints =
// LayoutConstraints::new(&component.root_element, diag);
recurse_elem_including_sub_components(&component, &(), &mut |elem, _| {
let component = elem.borrow().enclosing_component.upgrade().unwrap();
lower_element_layout(&component, elem, type_register, style_metrics, diag);
check_no_layout_properties(elem, diag);
});
}
fn lower_element_layout(
component: &Rc<Component>,
elem: &ElementRc,
type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) {
//let base_type =
// if let Type::Builtin(be) = &elem.borrow().base_type { be.clone() } else { return };
let base_type = if let Type::Builtin(base_type) = &elem.borrow().base_type {
base_type.clone()
} else {
return;
};
match base_type.name.as_str() {
"Row" => panic!("Error caught at element lookup time"),
"GridLayout" => lower_grid_layout(component, elem, style_metrics, diag),
"HorizontalLayout" => lower_grid_layout(component, elem, style_metrics, diag),
"VerticalLayout" => lower_grid_layout(component, elem, style_metrics, diag),
/*"HorizontalLayout" => &lower_box_layout,
"VerticalLayout" => &lower_box_layout,
"PathLayout" => &lower_path_layout,*/
_ => return,
};
{
let mut elem = elem.borrow_mut();
let elem = &mut *elem;
let prev_base = std::mem::replace(&mut elem.base_type, type_register.lookup("Rectangle"));
// Create fake properties for the layout properties
for p in elem.bindings.keys() {
if !elem.base_type.lookup_property(p).is_valid()
&& !elem.property_declarations.contains_key(p)
{
let ty = prev_base.lookup_property(p).property_type;
if ty != Type::Invalid {
elem.property_declarations.insert(p.into(), ty.into());
}
}
}
}
/*
let merge_min_max = |prop, op| {
let mut elem = elem.borrow_mut();
if let Some(old) = elem.bindings.get_mut(prop) {
if old.priority == 0 {
old.expression = expression_tree::min_max_expression(
std::mem::take(&mut old.expression),
Expression::PropertyReference(NamedReference::new(&child, prop)),
op,
);
}
} else {
elem.bindings.insert(
prop.to_owned(),
Expression::PropertyReference(NamedReference::new(&child, prop)).into(),
);
}
};
// Instead of forwarding each property like that, one could imagine having only one LayoutInfo property that we merge
// instead of merging all the component separately
merge_min_max("minimum_width", '>');
merge_min_max("minimum_height", '>');
merge_min_max("maximum_width", '<');
merge_min_max("maximum_height", '<');
// Ideally this should be the same as "merge"
merge_min_max("preferred_width", '>');
merge_min_max("preferred_height", '>');
// TODO: handle the implicit constraints
*/
}
fn lower_grid_layout(
component: &Rc<Component>,
grid_layout_element: &ElementRc,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) {
let mut grid = GridLayout { let mut grid = GridLayout {
elems: Default::default(), elems: Default::default(),
geometry: LayoutGeometry::new(rect, &grid_layout_element, style_metrics), geometry: LayoutGeometry::new(&grid_layout_element, style_metrics),
}; };
// FIXME: remove
let is_vertical = grid_layout_element.borrow().base_type.to_string() == "VerticalLayout";
let layout_cache_prop = create_new_prop(grid_layout_element, "layout_cache", Type::LayoutCache);
let layout_info_prop = create_new_prop(grid_layout_element, "layout_info", layout_info_type());
let mut row = 0; let mut row = 0;
let mut col = 0; let mut col = 0;
let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children); let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children);
let mut collected_children = Vec::new();
for layout_child in layout_children { for layout_child in layout_children {
let is_row = if let Type::Builtin(be) = &layout_child.borrow().base_type { let is_row = if let Type::Builtin(be) = &layout_child.borrow().base_type {
be.native_class.class_name == "Row" be.name == "Row"
} else { } else {
false false
}; };
@ -49,49 +158,39 @@ fn lower_grid_layout(
} }
let row_children = std::mem::take(&mut layout_child.borrow_mut().children); let row_children = std::mem::take(&mut layout_child.borrow_mut().children);
for x in row_children { for x in row_children {
grid.add_element( grid.add_element(&x, (&mut row, &mut col), diag, &layout_cache_prop);
x,
(&mut row, &mut col),
diag,
type_register,
style_metrics,
&component,
collected_children,
);
col += 1; col += 1;
collected_children.push(x);
} }
if col > 0 { if col > 0 {
row += 1; row += 1;
col = 0; col = 0;
} }
component.optimized_elements.borrow_mut().push(layout_child.clone()); component.optimized_elements.borrow_mut().push(layout_child);
} else { } else {
grid.add_element( grid.add_element(&layout_child, (&mut row, &mut col), diag, &layout_cache_prop);
layout_child, if is_vertical {
(&mut row, &mut col), row += 1
diag, } else {
type_register, col += 1
style_metrics, };
&component, collected_children.push(layout_child);
collected_children, }
}
grid_layout_element.borrow_mut().children = collected_children;
layout_cache_prop.element().borrow_mut().bindings.insert(
layout_cache_prop.name().into(),
Expression::SolveLayout(Layout::GridLayout(grid.clone())).into(),
);
layout_info_prop.element().borrow_mut().bindings.insert(
layout_info_prop.name().into(),
Expression::ComputeLayoutInfo(Layout::GridLayout(grid)).into(),
); );
col += 1;
}
}
component.optimized_elements.borrow_mut().push(grid_layout_element.clone());
if !grid.elems.is_empty() {
Some(grid.into())
} else {
None
}
} }
fn lower_box_layout( fn lower_box_layout(
component: &Rc<Component>, component: &Rc<Component>,
rect: LayoutRect,
layout_element: &ElementRc, layout_element: &ElementRc,
collected_children: &mut Vec<ElementRc>,
type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>, style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics, diag: &mut BuildDiagnostics,
) -> Option<Layout> { ) -> Option<Layout> {
@ -99,18 +198,11 @@ fn lower_box_layout(
let mut layout = BoxLayout { let mut layout = BoxLayout {
is_horizontal, is_horizontal,
elems: Default::default(), elems: Default::default(),
geometry: LayoutGeometry::new(rect, &layout_element, style_metrics), geometry: LayoutGeometry::new(&layout_element, style_metrics),
}; };
let layout_children = std::mem::take(&mut layout_element.borrow_mut().children); let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
for layout_child in layout_children { for layout_child in layout_children {
if let Some(item) = create_layout_item( if let Some(item) = create_layout_item(&layout_child, diag) {
&layout_child,
component,
collected_children,
type_register,
style_metrics,
diag,
) {
layout.elems.push(item) layout.elems.push(item)
} }
} }
@ -166,131 +258,8 @@ fn lower_path_layout(
) )
} }
type LayoutParseFunction = dyn Fn(
&Rc<Component>,
LayoutRect,
&ElementRc,
&mut Vec<ElementRc>,
&TypeRegister,
&Option<Rc<Component>>,
&mut BuildDiagnostics,
) -> Option<Layout>;
fn layout_parse_function(
layout_element_candidate: &ElementRc,
) -> Option<&'static LayoutParseFunction> {
if let Type::Builtin(be) = &layout_element_candidate.borrow().base_type {
match be.native_class.class_name.as_str() {
"Row" => panic!("Error caught at element lookup time"),
"GridLayout" => Some(&lower_grid_layout),
"HorizontalLayout" => Some(&lower_box_layout),
"VerticalLayout" => Some(&lower_box_layout),
"PathLayout" => Some(&lower_path_layout),
_ => None,
}
} else {
None
}
}
fn lower_element_layout(
component: &Rc<Component>,
elem: &ElementRc,
type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) -> LayoutVec {
let old_children = {
let mut elem = elem.borrow_mut();
let new_children = Vec::with_capacity(elem.children.len());
std::mem::replace(&mut elem.children, new_children)
};
// lay out within the current element's boundaries.
let rect_to_layout = LayoutRect {
x_reference: None,
y_reference: None,
width_reference: Some(NamedReference::new(elem, "width")),
height_reference: Some(NamedReference::new(elem, "height")),
};
let mut found_layouts = LayoutVec::default();
for child in old_children {
if let Some(layout_parser) = layout_parse_function(&child) {
let mut children = std::mem::take(&mut elem.borrow_mut().children);
if let Some(layout) = layout_parser(
component,
rect_to_layout.clone(),
&child,
&mut children,
type_register,
style_metrics,
diag,
) {
if Rc::ptr_eq(elem, &component.root_element) {
found_layouts.main_layout = Some(found_layouts.len());
}
found_layouts.push(layout);
}
elem.borrow_mut().children = children;
continue;
} else {
elem.borrow_mut().children.push(child);
}
}
found_layouts
}
/// Currently this just removes the layout from the tree
pub async fn lower_layouts<'a>(
component: &Rc<Component>,
type_loader: &mut TypeLoader<'a>,
diag: &mut BuildDiagnostics,
) {
// Ignore import errors
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
let style_metrics = type_loader
.import_type("sixtyfps_widgets.60", "StyleMetrics", &mut build_diags_to_ignore)
.await;
let style_metrics =
style_metrics.and_then(|sm| if let Type::Component(c) = sm { Some(c) } else { None });
lower_layouts_impl(component, &type_loader.global_type_registry.borrow(), &style_metrics, diag);
}
fn lower_layouts_impl(
component: &Rc<Component>,
type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) {
// FIXME: one should enable minimum_width and minimum_height on the window, but not height and width
//component.layouts.borrow_mut().root_constraints =
// LayoutConstraints::new(&component.root_element, diag);
recurse_elem_including_sub_components(&component, &(), &mut |elem, _| {
let component = elem.borrow().enclosing_component.upgrade().unwrap();
let mut layouts =
lower_element_layout(&component, elem, type_register, style_metrics, diag);
let mut component_layouts = component.layouts.borrow_mut();
component_layouts.main_layout = component_layouts
.main_layout
.or_else(|| layouts.main_layout.map(|x| x + component_layouts.len()));
component_layouts.append(&mut layouts);
check_no_layout_properties(elem, diag);
});
}
/// Create a LayoutItem for the given `item_element` returns None is the layout is empty /// Create a LayoutItem for the given `item_element` returns None is the layout is empty
fn create_layout_item( fn create_layout_item(item_element: &ElementRc, diag: &mut BuildDiagnostics) -> Option<LayoutItem> {
item_element: &ElementRc,
component: &Rc<Component>,
collected_children: &mut Vec<ElementRc>,
type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) -> Option<LayoutItem> {
let fix_explicit_percent = |prop: &str, item: &ElementRc| { let fix_explicit_percent = |prop: &str, item: &ElementRc| {
if !item.borrow().bindings.get(prop).map_or(false, |b| b.ty() == Type::Percent) { if !item.borrow().bindings.get(prop).map_or(false, |b| b.ty() == Type::Percent) {
return; return;
@ -315,68 +284,25 @@ fn create_layout_item(
let constraints = LayoutConstraints::new(item_element, diag); let constraints = LayoutConstraints::new(item_element, diag);
item_element.borrow_mut().child_of_layout = true; item_element.borrow_mut().child_of_layout = true;
if item_element.borrow().repeated.is_some() { if item_element.borrow().repeated.is_some() {
let rep_comp = item_element.borrow().base_type.as_component().clone(); let rep_comp = item_element.borrow().base_type.as_component().clone();
fix_explicit_percent("width", &rep_comp.root_element); fix_explicit_percent("width", &rep_comp.root_element);
fix_explicit_percent("height", &rep_comp.root_element); fix_explicit_percent("height", &rep_comp.root_element);
rep_comp.layouts.borrow_mut().root_constraints = *rep_comp.root_constraints.borrow_mut() =
LayoutConstraints::new(&rep_comp.root_element, diag); LayoutConstraints::new(&rep_comp.root_element, diag);
rep_comp.root_element.borrow_mut().child_of_layout = true; rep_comp.root_element.borrow_mut().child_of_layout = true;
collected_children.push(item_element.clone());
if layout_parse_function(&rep_comp.root_element).is_some() {
let new_root = ElementRc::new(RefCell::new(Element {
id: format!("{}_rootrect", item_element.borrow().id),
base_type: type_register.lookup_element("Rectangle").unwrap(),
enclosing_component: Rc::downgrade(&rep_comp),
..Default::default()
}));
drop(rep_comp);
crate::object_tree::inject_element_as_repeated_element(item_element, new_root);
}
Some(LayoutItem { element: Some(item_element.clone()), layout: None, constraints })
} else if let Some(nested_layout_parser) = layout_parse_function(item_element) {
let layout_rect = LayoutRect::install_on_element(&item_element);
nested_layout_parser(
component,
layout_rect,
&item_element,
collected_children,
type_register,
style_metrics,
diag,
)
.map(|x| LayoutItem { layout: Some(x), constraints, element: None })
} else {
collected_children.push(item_element.clone());
let element = item_element.clone();
let layout = {
let mut layouts =
lower_element_layout(component, &element, type_register, style_metrics, diag);
if layouts.is_empty() {
None
} else {
Some(layouts.remove(0))
}
}; };
Some(LayoutItem { element: Some(element), layout, constraints }) Some(LayoutItem { element: item_element.clone(), constraints })
}
} }
impl GridLayout { impl GridLayout {
fn add_element( fn add_element(
&mut self, &mut self,
item_element: ElementRc, item_element: &ElementRc,
(row, col): (&mut u16, &mut u16), (row, col): (&mut u16, &mut u16),
diag: &mut BuildDiagnostics, diag: &mut BuildDiagnostics,
type_register: &TypeRegister, layout_cache_prop: &NamedReference,
style_metrics: &Option<Rc<Component>>,
component: &Rc<Component>,
collected_children: &mut Vec<ElementRc>,
) { ) {
let mut get_const_value = |name: &str| { let mut get_const_value = |name: &str| {
item_element item_element
@ -395,14 +321,28 @@ impl GridLayout {
*col = c; *col = c;
} }
if let Some(layout_item) = create_layout_item( if let Some(layout_item) = create_layout_item(&item_element, diag) {
&item_element, let index = self.elems.len();
component, //FIXME: report errors if there is already bindings on x or y
collected_children, let set_prop_from_cache = |prop: &str, offset: usize| {
type_register, item_element.borrow_mut().bindings.insert(
style_metrics, prop.into(),
diag, Expression::LayoutCacheAccess {
) { layout_cache_prop: layout_cache_prop.clone(),
index: index * 4 + offset,
}
.into(),
);
};
set_prop_from_cache("x", 0);
set_prop_from_cache("y", 1);
if !layout_item.constraints.fixed_width {
set_prop_from_cache("width", 2);
}
if !layout_item.constraints.fixed_height {
set_prop_from_cache("height", 3);
}
self.elems.push(GridLayoutElement { self.elems.push(GridLayoutElement {
col: *col, col: *col,
row: *row, row: *row,
@ -438,6 +378,28 @@ fn eval_const_expr(
} }
} }
/// Create a new property based on the name. (it might get a different name if that property exist)
fn create_new_prop(elem: &ElementRc, tentative_name: &str, ty: Type) -> NamedReference {
let mut e = elem.borrow_mut();
if !e.lookup_property(tentative_name).is_valid() {
e.property_declarations.insert(tentative_name.into(), ty.into());
drop(e);
NamedReference::new(elem, tentative_name)
} else {
let mut counter = 0;
loop {
counter += 1;
let name = format!("{}{}", tentative_name, counter);
if !e.lookup_property(&name).is_valid() {
e.property_declarations.insert(name.clone(), ty.into());
drop(e);
return NamedReference::new(elem, &name);
}
}
}
}
/// Checks that there is grid-layout specific properties left
fn check_no_layout_properties(item: &ElementRc, diag: &mut BuildDiagnostics) { fn check_no_layout_properties(item: &ElementRc, diag: &mut BuildDiagnostics) {
for (prop, expr) in item.borrow().bindings.iter() { for (prop, expr) in item.borrow().bindings.iter() {
if matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan") { if matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan") {

View file

@ -60,8 +60,6 @@ fn lower_popup_window(
}; };
let parent_component = parent_element.borrow().enclosing_component.upgrade().unwrap(); let parent_component = parent_element.borrow().enclosing_component.upgrade().unwrap();
// Because layout constraint which are supposed to be in the popup will not be lowered, the layout lowering should be done after
debug_assert!(parent_component.layouts.borrow().is_empty());
// Remove the popup_window_element from its parent // Remove the popup_window_element from its parent
parent_element.borrow_mut().children.retain(|child| !Rc::ptr_eq(child, popup_window_element)); parent_element.borrow_mut().children.retain(|child| !Rc::ptr_eq(child, popup_window_element));

View file

@ -93,7 +93,7 @@ pub fn move_declarations(component: &Rc<Component>, diag: &mut BuildDiagnostics)
component.optimized_elements.borrow().iter().for_each(|e| move_bindings_and_animations(e)); component.optimized_elements.borrow().iter().for_each(|e| move_bindings_and_animations(e));
component.layouts.borrow_mut().visit_named_references(&mut |e| fixup_reference(e)); component.root_constraints.borrow_mut().visit_named_references(&mut |e| fixup_reference(e));
component.popup_windows.borrow_mut().iter_mut().for_each(|p| { component.popup_windows.borrow_mut().iter_mut().for_each(|p| {
fixup_reference(&mut p.x); fixup_reference(&mut p.x);
fixup_reference(&mut p.y); fixup_reference(&mut p.y);

View file

@ -23,9 +23,6 @@ pub fn process_repeater_components(component: &Rc<Component>) {
} }
fn create_repeater_components(component: &Rc<Component>) { fn create_repeater_components(component: &Rc<Component>) {
// Because layout constraint which are supposed to be in the repeater will not be lowered
debug_assert!(component.layouts.borrow().is_empty());
recurse_elem(&component.root_element, &(), &mut |elem, _| { recurse_elem(&component.root_element, &(), &mut |elem, _| {
if elem.borrow().repeated.is_none() { if elem.borrow().repeated.is_none() {
return; return;
@ -48,6 +45,7 @@ fn create_repeater_components(component: &Rc<Component>) {
states: std::mem::take(&mut elem.states), states: std::mem::take(&mut elem.states),
transitions: std::mem::take(&mut elem.transitions), transitions: std::mem::take(&mut elem.transitions),
child_of_layout: elem.child_of_layout, child_of_layout: elem.child_of_layout,
layout_info_prop: elem.layout_info_prop.take(),
is_flickable_viewport: elem.is_flickable_viewport, is_flickable_viewport: elem.is_flickable_viewport,
item_index: Default::default(), // Not determined yet item_index: Default::default(), // Not determined yet
})), })),

View file

@ -11,7 +11,7 @@ LICENSE END */
//! //!
//! Currently this is a very basic implementation //! Currently this is a very basic implementation
use crate::{slice::Slice, Property}; use crate::{slice::Slice, Property, SharedVector};
type Coord = f32; type Coord = f32;
@ -257,24 +257,21 @@ pub struct GridLayoutData<'a> {
pub y: Coord, pub y: Coord,
pub spacing: Coord, pub spacing: Coord,
pub padding: &'a Padding, pub padding: &'a Padding,
pub cells: Slice<'a, GridLayoutCellData<'a>>, pub cells: Slice<'a, GridLayoutCellData>,
} }
#[repr(C)] #[repr(C)]
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct GridLayoutCellData<'a> { pub struct GridLayoutCellData {
pub col: u16, pub col: u16,
pub row: u16, pub row: u16,
pub colspan: u16, pub colspan: u16,
pub rowspan: u16, pub rowspan: u16,
pub constraint: LayoutInfo, pub constraint: LayoutInfo,
pub x: Option<&'a Property<Coord>>,
pub y: Option<&'a Property<Coord>>,
pub width: Option<&'a Property<Coord>>,
pub height: Option<&'a Property<Coord>>,
} }
pub fn solve_grid_layout(data: &GridLayoutData) { /// return, an array which is of siz `data.cells.len() * 4` which for each cell we give the x, y, width, height
pub fn solve_grid_layout(data: &GridLayoutData) -> SharedVector<Coord> {
let (mut num_col, mut num_row) = (0, 0); let (mut num_col, mut num_row) = (0, 0);
for cell in data.cells.iter() { for cell in data.cells.iter() {
num_row = num_row.max(cell.row + cell.rowspan); num_row = num_row.max(cell.row + cell.rowspan);
@ -282,7 +279,7 @@ pub fn solve_grid_layout(data: &GridLayoutData) {
} }
if num_col < 1 || num_row < 1 { if num_col < 1 || num_row < 1 {
return; return Default::default();
} }
let mut row_layout_data = vec![grid_internal::LayoutData::default(); num_row as usize]; let mut row_layout_data = vec![grid_internal::LayoutData::default(); num_row as usize];
@ -345,30 +342,28 @@ pub fn solve_grid_layout(data: &GridLayoutData) {
data.width - (data.padding.left + data.padding.right), data.width - (data.padding.left + data.padding.right),
data.spacing, data.spacing,
); );
let mut result = SharedVector::with_capacity(4 * data.cells.len());
for cell in data.cells.iter() { for cell in data.cells.iter() {
let rdata = &row_layout_data[cell.row as usize]; let rdata = &row_layout_data[cell.row as usize];
let cdata = &col_layout_data[cell.col as usize]; let cdata = &col_layout_data[cell.col as usize];
cell.x.map(|p| p.set(cdata.pos)); result.push(cdata.pos);
cell.width.map(|p| { result.push(rdata.pos);
p.set({ result.push({
let first_cell = &col_layout_data[cell.col as usize]; let first_cell = &col_layout_data[cell.col as usize];
let last_cell = &col_layout_data[cell.col as usize + cell.colspan as usize - 1]; let last_cell = &col_layout_data[cell.col as usize + cell.colspan as usize - 1];
last_cell.pos + last_cell.size - first_cell.pos last_cell.pos + last_cell.size - first_cell.pos
})
}); });
cell.y.map(|p| p.set(rdata.pos)); result.push({
cell.height.map(|p| {
p.set({
let first_cell = &row_layout_data[cell.row as usize]; let first_cell = &row_layout_data[cell.row as usize];
let last_cell = &row_layout_data[cell.row as usize + cell.rowspan as usize - 1]; let last_cell = &row_layout_data[cell.row as usize + cell.rowspan as usize - 1];
last_cell.pos + last_cell.size - first_cell.pos last_cell.pos + last_cell.size - first_cell.pos
})
}); });
} }
result
} }
pub fn grid_layout_info<'a>( pub fn grid_layout_info<'a>(
cells: &Slice<'a, GridLayoutCellData<'a>>, cells: &Slice<'a, GridLayoutCellData>,
spacing: Coord, spacing: Coord,
padding: &Padding, padding: &Padding,
) -> LayoutInfo { ) -> LayoutInfo {

View file

@ -111,6 +111,8 @@ pub enum Value {
/// An enumation, like `TextHorizontalAlignment::align_center`, represented by `("TextHorizontalAlignment", "align_center")`. /// An enumation, like `TextHorizontalAlignment::align_center`, represented by `("TextHorizontalAlignment", "align_center")`.
/// FIXME: consider representing that with a number? /// FIXME: consider representing that with a number?
EnumerationValue(String, String), EnumerationValue(String, String),
#[doc(hidden)]
LayoutCache(SharedVector<f32>),
} }
impl Value { impl Value {
@ -153,6 +155,7 @@ impl PartialEq for Value {
Value::EnumerationValue(lhs_name, lhs_value) => { Value::EnumerationValue(lhs_name, lhs_value) => {
matches!(other, Value::EnumerationValue(rhs_name, rhs_value) if lhs_name == rhs_name && lhs_value == rhs_value) matches!(other, Value::EnumerationValue(rhs_name, rhs_value) if lhs_name == rhs_name && lhs_value == rhs_value)
} }
Value::LayoutCache(lhs) => matches!(other, Value::LayoutCache(rhs) if lhs == rhs),
} }
} }
} }
@ -172,6 +175,7 @@ impl std::fmt::Debug for Value {
Value::PathElements(e) => write!(f, "Value::PathElements({:?})", e), Value::PathElements(e) => write!(f, "Value::PathElements({:?})", e),
Value::EasingCurve(c) => write!(f, "Value::EasingCurve({:?})", c), Value::EasingCurve(c) => write!(f, "Value::EasingCurve({:?})", c),
Value::EnumerationValue(n, v) => write!(f, "Value::EnumerationValue({:?}, {:?})", n, v), Value::EnumerationValue(n, v) => write!(f, "Value::EnumerationValue({:?}, {:?})", n, v),
Value::LayoutCache(v) => write!(f, "Value::LayoutCache({:?})", v),
} }
} }
} }
@ -213,6 +217,7 @@ declare_value_conversion!(Struct => [Struct] );
declare_value_conversion!(Brush => [Brush] ); declare_value_conversion!(Brush => [Brush] );
declare_value_conversion!(PathElements => [PathData]); declare_value_conversion!(PathElements => [PathData]);
declare_value_conversion!(EasingCurve => [sixtyfps_corelib::animations::EasingCurve]); declare_value_conversion!(EasingCurve => [sixtyfps_corelib::animations::EasingCurve]);
declare_value_conversion!(LayoutCache => [SharedVector<f32>] );
/// Implement From / TryInto for Value that convert a `struct` to/from `Value::Object` /// Implement From / TryInto for Value that convert a `struct` to/from `Value::Object`
macro_rules! declare_value_struct_conversion { macro_rules! declare_value_struct_conversion {
@ -245,6 +250,11 @@ declare_value_struct_conversion!(struct sixtyfps_corelib::model::StandardListVie
declare_value_struct_conversion!(struct sixtyfps_corelib::properties::StateInfo { current_state, previous_state, change_time }); declare_value_struct_conversion!(struct sixtyfps_corelib::properties::StateInfo { current_state, previous_state, change_time });
declare_value_struct_conversion!(struct sixtyfps_corelib::input::KeyboardModifiers { control, alt, shift, meta }); declare_value_struct_conversion!(struct sixtyfps_corelib::input::KeyboardModifiers { control, alt, shift, meta });
declare_value_struct_conversion!(struct sixtyfps_corelib::input::KeyEvent { event_type, text, modifiers }); declare_value_struct_conversion!(struct sixtyfps_corelib::input::KeyEvent { event_type, text, modifiers });
declare_value_struct_conversion!(struct sixtyfps_corelib::layout::LayoutInfo {
min_width, max_width, min_height, max_height,
min_width_percent, max_width_percent, min_height_percent, max_height_percent,
preferred_width, preferred_height, horizontal_stretch, vertical_stretch,
});
/// Implement From / TryInto for Value that convert an `enum` to/from `Value::EnumerationValue` /// Implement From / TryInto for Value that convert an `enum` to/from `Value::EnumerationValue`
/// ///

View file

@ -15,9 +15,6 @@ use dynamic_type::{Instance, InstanceBox};
use expression_tree::NamedReference; use expression_tree::NamedReference;
use object_tree::{Element, ElementRc}; use object_tree::{Element, ElementRc};
use sixtyfps_compilerlib::langtype::Type; use sixtyfps_compilerlib::langtype::Type;
use sixtyfps_compilerlib::layout::{
Layout, LayoutConstraints, LayoutGeometry, LayoutItem, PathLayout,
};
use sixtyfps_compilerlib::*; use sixtyfps_compilerlib::*;
use sixtyfps_compilerlib::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration}; use sixtyfps_compilerlib::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
use sixtyfps_compilerlib::{expression_tree::Expression, langtype::PropertyLookupResult}; use sixtyfps_compilerlib::{expression_tree::Expression, langtype::PropertyLookupResult};
@ -29,14 +26,14 @@ use sixtyfps_corelib::item_tree::{
use sixtyfps_corelib::items::{ use sixtyfps_corelib::items::{
Flickable, ItemRc, ItemRef, ItemVTable, ItemWeak, PropertyAnimation, Flickable, ItemRc, ItemRef, ItemVTable, ItemWeak, PropertyAnimation,
}; };
use sixtyfps_corelib::layout::{LayoutInfo, Padding}; use sixtyfps_corelib::layout::{BoxLayoutCellData, LayoutInfo};
use sixtyfps_corelib::model::RepeatedComponent; use sixtyfps_corelib::model::RepeatedComponent;
use sixtyfps_corelib::model::Repeater; use sixtyfps_corelib::model::Repeater;
use sixtyfps_corelib::properties::InterpolatedPropertyValue; use sixtyfps_corelib::properties::InterpolatedPropertyValue;
use sixtyfps_corelib::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo}; use sixtyfps_corelib::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
use sixtyfps_corelib::slice::Slice; use sixtyfps_corelib::slice::Slice;
use sixtyfps_corelib::window::ComponentWindow; use sixtyfps_corelib::window::ComponentWindow;
use sixtyfps_corelib::{Brush, Color, Property, SharedString}; use sixtyfps_corelib::{Brush, Color, Property, SharedString, SharedVector};
use std::collections::HashMap; use std::collections::HashMap;
use std::{pin::Pin, rc::Rc}; use std::{pin::Pin, rc::Rc};
@ -184,7 +181,7 @@ impl RepeatedComponent for ErasedComponentBox {
} }
}; };
let root_c = &s.component_type.original.layouts.borrow().root_constraints; let root_c = &s.component_type.original.root_constraints.borrow();
BoxLayoutCellData { BoxLayoutCellData {
constraint: self.borrow().as_ref().layout_info(), constraint: self.borrow().as_ref().layout_info(),
x: get_prop("x"), x: get_prop("x"),
@ -774,7 +771,8 @@ fn generate_component<'id>(
"FillRule" => property_info::<sixtyfps_corelib::items::FillRule>(), "FillRule" => property_info::<sixtyfps_corelib::items::FillRule>(),
_ => panic!("unkown enum"), _ => panic!("unkown enum"),
}, },
_ => panic!("bad type"), Type::LayoutCache => property_info::<SharedVector<f32>>(),
_ => panic!("bad type {:?}", &decl.property_type),
}; };
custom_properties.insert( custom_properties.insert(
name.clone(), name.clone(),
@ -1147,7 +1145,7 @@ pub fn instantiate<'id>(
comp_rc comp_rc
} }
fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () { pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () {
let element = nr.element(); let element = nr.element();
generativity::make_guard!(guard); generativity::make_guard!(guard);
let enclosing_component = eval::enclosing_component_for_element(&element, instance, guard); let enclosing_component = eval::enclosing_component_for_element(&element, instance, guard);
@ -1167,15 +1165,6 @@ fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () {
unsafe { item.as_ptr().add(item_info.rtti.properties.get(nr.name()).unwrap().offset()).cast() } unsafe { item.as_ptr().add(item_info.rtti.properties.get(nr.name()).unwrap().offset()).cast() }
} }
use sixtyfps_corelib::layout::*;
struct LayoutWithCells<'a, C> {
geometry: &'a sixtyfps_compilerlib::layout::LayoutGeometry,
cells: Vec<C>,
spacing: f32,
padding: Padding,
}
pub struct ErasedComponentBox(ComponentBox<'static>); pub struct ErasedComponentBox(ComponentBox<'static>);
impl ErasedComponentBox { impl ErasedComponentBox {
pub fn unerase<'a, 'id>( pub fn unerase<'a, 'id>(
@ -1221,399 +1210,6 @@ impl<'id> From<ComponentBox<'id>> for ErasedComponentBox {
} }
} }
type RepeatedComponentRc = vtable::VRc<ComponentVTable, ErasedComponentBox>;
enum BoxLayoutCellTmpData<'a> {
Item(BoxLayoutCellData<'a>),
Repeater(Vec<RepeatedComponentRc>),
}
impl<'a> BoxLayoutCellTmpData<'a> {
fn into_cells(cells: &'a [Self]) -> Vec<BoxLayoutCellData<'a>> {
let mut c = Vec::with_capacity(cells.len());
for x in cells.iter() {
match x {
BoxLayoutCellTmpData::Item(cell) => {
c.push((*cell).clone());
}
BoxLayoutCellTmpData::Repeater(vec) => {
c.extend(vec.iter().map(|x| x.as_pin_ref().box_layout_data()))
}
}
}
c
}
}
#[derive(derive_more::From)]
enum LayoutTreeItem<'a> {
GridLayout(LayoutWithCells<'a, GridLayoutCellData<'a>>),
BoxLayout(
LayoutWithCells<'a, BoxLayoutCellTmpData<'a>>,
bool,
sixtyfps_corelib::layout::LayoutAlignment,
),
PathLayout(&'a PathLayout),
}
impl<'a> LayoutTreeItem<'a> {
fn layout_info(&self) -> LayoutInfo {
match self {
LayoutTreeItem::GridLayout(grid_layout) => grid_layout_info(
&Slice::from(grid_layout.cells.as_slice()),
grid_layout.spacing,
&grid_layout.padding,
),
LayoutTreeItem::BoxLayout(box_layout, is_horizontal, alignment) => {
let cells = BoxLayoutCellTmpData::into_cells(&box_layout.cells);
box_layout_info(
&Slice::from(cells.as_slice()),
box_layout.spacing,
&box_layout.padding,
*alignment,
*is_horizontal,
)
}
LayoutTreeItem::PathLayout(_) => todo!(),
}
}
fn geometry(&self) -> Option<&LayoutGeometry> {
match self {
Self::GridLayout(LayoutWithCells { geometry, .. })
| Self::BoxLayout(LayoutWithCells { geometry, .. }, _, _) => Some(geometry),
_ => None,
}
}
}
fn get_layout_info<'a, 'b>(
item: &'a LayoutItem,
component: InstanceRef<'a, '_>,
layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
window: &ComponentWindow,
) -> LayoutInfo {
let layout_info = item.layout.as_ref().map(|l| {
let layout_tree_item = collect_layouts_recursively(layout_tree, l, component, window);
layout_tree_item.layout_info()
});
let elem_info = item.element.as_ref().map(|elem| {
let item = &component
.component_type
.items
.get(elem.borrow().id.as_str())
.unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.borrow().id));
unsafe { item.item_from_component(component.as_ptr()).as_ref().layouting_info(window) }
});
match (layout_info, elem_info) {
(None, None) => Default::default(),
(None, Some(x)) => x,
(Some(x), None) => x,
(Some(layout_info), Some(elem_info)) => layout_info.merge(&elem_info),
}
}
fn fill_layout_info_constraints(
layout_info: &mut LayoutInfo,
constraints: &LayoutConstraints,
expr_eval: &impl Fn(&NamedReference) -> f32,
) {
let is_percent =
|nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
constraints.minimum_width.as_ref().map(|e| {
if !is_percent(e) {
layout_info.min_width = expr_eval(e)
} else {
layout_info.min_width_percent = expr_eval(e)
}
});
constraints.maximum_width.as_ref().map(|e| {
if !is_percent(e) {
layout_info.max_width = expr_eval(e)
} else {
layout_info.max_width_percent = expr_eval(e)
}
});
constraints.minimum_height.as_ref().map(|e| {
if !is_percent(e) {
layout_info.min_height = expr_eval(e)
} else {
layout_info.min_height_percent = expr_eval(e)
}
});
constraints.maximum_height.as_ref().map(|e| {
if !is_percent(e) {
layout_info.max_height = expr_eval(e)
} else {
layout_info.max_height_percent = expr_eval(e)
}
});
constraints.preferred_width.as_ref().map(|e| {
layout_info.preferred_width = expr_eval(e);
});
constraints.preferred_height.as_ref().map(|e| {
layout_info.preferred_height = expr_eval(e);
});
constraints.horizontal_stretch.as_ref().map(|e| layout_info.horizontal_stretch = expr_eval(e));
constraints.vertical_stretch.as_ref().map(|e| layout_info.vertical_stretch = expr_eval(e));
}
fn collect_layouts_recursively<'a, 'b>(
layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
layout: &'a Layout,
component: InstanceRef<'a, '_>,
window: &ComponentWindow,
) -> &'b LayoutTreeItem<'a> {
let assume_property_f32 = |nr: &Option<NamedReference>| {
nr.as_ref().map(|nr| {
let p = get_property_ptr(nr, component);
unsafe { &*(p as *const Property<f32>) }
})
};
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
match layout {
Layout::GridLayout(grid_layout) => {
let cells = grid_layout
.elems
.iter()
.map(|cell| {
let mut layout_info =
get_layout_info(&cell.item, component, layout_tree, window);
fill_layout_info_constraints(
&mut layout_info,
&cell.item.constraints,
&expr_eval,
);
let rect = cell.item.rect();
GridLayoutCellData {
x: assume_property_f32(&rect.x_reference),
y: assume_property_f32(&rect.y_reference),
width: assume_property_f32(&rect.width_reference),
height: assume_property_f32(&rect.height_reference),
col: cell.col,
row: cell.row,
colspan: cell.colspan,
rowspan: cell.rowspan,
constraint: layout_info,
}
})
.collect();
let spacing = grid_layout.geometry.spacing.as_ref().map_or(0., expr_eval);
let padding = Padding {
left: grid_layout.geometry.padding.left.as_ref().map_or(0., expr_eval),
right: grid_layout.geometry.padding.right.as_ref().map_or(0., expr_eval),
top: grid_layout.geometry.padding.top.as_ref().map_or(0., expr_eval),
bottom: grid_layout.geometry.padding.bottom.as_ref().map_or(0., expr_eval),
};
layout_tree.push(
LayoutWithCells { geometry: &grid_layout.geometry, cells, spacing, padding }.into(),
);
}
Layout::BoxLayout(box_layout) => {
let mut make_box_layout_cell_data = |cell| {
let mut layout_info = get_layout_info(cell, component, layout_tree, window);
fill_layout_info_constraints(&mut layout_info, &cell.constraints, &expr_eval);
let rect = cell.rect();
BoxLayoutCellData {
x: assume_property_f32(&rect.x_reference),
y: assume_property_f32(&rect.y_reference),
width: assume_property_f32(&rect.width_reference),
height: assume_property_f32(&rect.height_reference),
constraint: layout_info,
}
};
let cells = box_layout
.elems
.iter()
.map(|item| match &item.element {
Some(elem) if elem.borrow().repeated.is_some() => {
generativity::make_guard!(guard);
let rep = get_repeater_by_name(component, elem.borrow().id.as_str(), guard);
rep.0.as_ref().ensure_updated(|| {
let window = component
.component_type
.window_offset
.apply(component.as_ref())
.as_ref()
.unwrap()
.clone();
let instance =
instantiate(rep.1.clone(), Some(component.borrow()), window);
instance.run_setup_code();
instance
});
BoxLayoutCellTmpData::Repeater(
rep.0.as_ref().components_vec().into_iter().collect(),
)
}
_ => BoxLayoutCellTmpData::Item(make_box_layout_cell_data(item)),
})
.collect::<Vec<_>>();
let spacing = box_layout.geometry.spacing.as_ref().map_or(0., expr_eval);
let padding = Padding {
left: box_layout.geometry.padding.left.as_ref().map_or(0., expr_eval),
right: box_layout.geometry.padding.right.as_ref().map_or(0., expr_eval),
top: box_layout.geometry.padding.top.as_ref().map_or(0., expr_eval),
bottom: box_layout.geometry.padding.bottom.as_ref().map_or(0., expr_eval),
};
let alignment = box_layout
.geometry
.alignment
.as_ref()
.map(|nr| {
eval::load_property(component, &nr.element(), nr.name())
.unwrap()
.try_into()
.unwrap_or_default()
})
.unwrap_or_default();
layout_tree.push(LayoutTreeItem::BoxLayout(
LayoutWithCells { geometry: &box_layout.geometry, cells, spacing, padding },
box_layout.is_horizontal,
alignment,
));
}
Layout::PathLayout(layout) => layout_tree.push(layout.into()),
}
layout_tree.last().unwrap()
}
impl<'a> LayoutTreeItem<'a> {
fn solve(&self, instance_ref: InstanceRef) {
let resolve_prop_ref = |prop_ref: &Option<NamedReference>| {
prop_ref.as_ref().map_or(0., |nr| {
eval::load_property(instance_ref, &nr.element(), nr.name())
.unwrap()
.try_into()
.unwrap_or(0.)
})
};
if let Some(geometry) = self.geometry() {
// Set the properties that depends on the constraints
if geometry.materialized_constraints.has_explicit_restrictions() {
let info = self.layout_info();
let apply_materialized_constraint = |nr: &Option<NamedReference>, c: f32| {
if let Some(nr) = nr {
let p = get_property_ptr(nr, instance_ref);
let p_ref = unsafe { &*(p as *const Property<f32>) };
p_ref.set(c);
};
};
let c = &geometry.materialized_constraints;
apply_materialized_constraint(&c.minimum_width, info.min_width);
apply_materialized_constraint(&c.minimum_height, info.min_height);
apply_materialized_constraint(&c.maximum_width, info.max_width);
apply_materialized_constraint(&c.maximum_height, info.max_height);
apply_materialized_constraint(&c.vertical_stretch, info.vertical_stretch);
apply_materialized_constraint(&c.horizontal_stretch, info.horizontal_stretch);
}
}
match self {
Self::GridLayout(grid_layout) => {
solve_grid_layout(&GridLayoutData {
width: resolve_prop_ref(&grid_layout.geometry.rect.width_reference),
height: resolve_prop_ref(&grid_layout.geometry.rect.height_reference),
x: resolve_prop_ref(&grid_layout.geometry.rect.x_reference),
y: resolve_prop_ref(&grid_layout.geometry.rect.y_reference),
spacing: grid_layout.spacing,
padding: &grid_layout.padding,
cells: Slice::from(grid_layout.cells.as_slice()),
});
}
Self::BoxLayout(box_layout, is_horizontal, alignment) => {
let cells = BoxLayoutCellTmpData::into_cells(&box_layout.cells);
solve_box_layout(
&BoxLayoutData {
width: resolve_prop_ref(&box_layout.geometry.rect.width_reference),
height: resolve_prop_ref(&box_layout.geometry.rect.height_reference),
x: resolve_prop_ref(&box_layout.geometry.rect.x_reference),
y: resolve_prop_ref(&box_layout.geometry.rect.y_reference),
spacing: box_layout.spacing,
padding: &box_layout.padding,
cells: Slice::from(cells.as_slice()),
alignment: *alignment,
},
*is_horizontal,
);
}
Self::PathLayout(path_layout) => {
use sixtyfps_corelib::layout::*;
let mut items = vec![];
for elem in &path_layout.elements {
let mut push_layout_data = |elem: &ElementRc, instance_ref: InstanceRef| {
let item_info =
&instance_ref.component_type.items[elem.borrow().id.as_str()];
let get_prop = |name| {
item_info.rtti.properties.get(name).map(|p| unsafe {
&*(instance_ref.as_ptr().add(item_info.offset).add(p.offset())
as *const Property<f32>)
})
};
let item = unsafe { item_info.item_from_component(instance_ref.as_ptr()) };
let get_prop_value = |name| {
item_info
.rtti
.properties
.get(name)
.map(|p| p.get(item))
.unwrap_or_default()
};
items.push(PathLayoutItemData {
x: get_prop("x"),
y: get_prop("y"),
width: get_prop_value("width").try_into().unwrap_or_default(),
height: get_prop_value("height").try_into().unwrap_or_default(),
});
};
if elem.borrow().repeated.is_none() {
push_layout_data(elem, instance_ref)
} else {
generativity::make_guard!(guard);
let repeater =
get_repeater_by_name(instance_ref, elem.borrow().id.as_str(), guard);
let vec = repeater.0.components_vec();
for sub_comp in vec.iter() {
generativity::make_guard!(guard);
push_layout_data(
&elem.borrow().base_type.as_component().root_element,
sub_comp.unerase(guard).borrow_instance(),
)
}
}
}
let path_elements = eval::convert_path(
&path_layout.path,
&mut eval::EvalLocalContext::from_component_instance(instance_ref),
);
solve_path_layout(&PathLayoutData {
items: Slice::from(items.as_slice()),
elements: &path_elements,
x: resolve_prop_ref(&path_layout.rect.x_reference),
y: resolve_prop_ref(&path_layout.rect.y_reference),
width: resolve_prop_ref(&path_layout.rect.width_reference),
height: resolve_prop_ref(&path_layout.rect.height_reference),
offset: resolve_prop_ref(&Some(path_layout.offset_reference.clone())),
});
}
}
}
}
pub fn get_repeater_by_name<'a, 'id>( pub fn get_repeater_by_name<'a, 'id>(
instance_ref: InstanceRef<'a, '_>, instance_ref: InstanceRef<'a, '_>,
name: &str, name: &str,
@ -1629,24 +1225,16 @@ extern "C" fn layout_info(component: ComponentRefPin) -> LayoutInfo {
// 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 instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
let component_layouts = instance_ref.component_type.original.layouts.borrow(); let constraints = instance_ref.component_type.original.root_constraints.borrow();
let result = if let Some(idx) = component_layouts.main_layout {
let mut inverse_layout_tree = Default::default(); let result =
collect_layouts_recursively( instance_ref.root_item().as_ref().layouting_info(&eval::window_ref(instance_ref).unwrap());
&mut inverse_layout_tree,
&component_layouts[idx], if constraints.has_explicit_restrictions() {
instance_ref,
&eval::window_ref(instance_ref).unwrap(),
)
.layout_info()
} else {
instance_ref.root_item().as_ref().layouting_info(&eval::window_ref(instance_ref).unwrap())
};
if component_layouts.root_constraints.has_explicit_restrictions() {
let mut info = LayoutInfo::default(); let mut info = LayoutInfo::default();
fill_layout_info_constraints( crate::eval_layout::fill_layout_info_constraints(
&mut info, &mut info,
&component_layouts.root_constraints, &constraints,
&|nr: &NamedReference| { &|nr: &NamedReference| {
eval::load_property(instance_ref, &nr.element(), nr.name()) eval::load_property(instance_ref, &nr.element(), nr.name())
.unwrap() .unwrap()
@ -1664,17 +1252,6 @@ extern "C" fn apply_layout(component: ComponentRefPin, _r: sixtyfps_corelib::gra
generativity::make_guard!(guard); generativity::make_guard!(guard);
// 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 instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
let window = eval::window_ref(instance_ref).unwrap();
instance_ref.component_type.original.layouts.borrow().iter().for_each(|layout| {
let mut inverse_layout_tree = Vec::new();
collect_layouts_recursively(&mut inverse_layout_tree, &layout, instance_ref, &window);
inverse_layout_tree.iter().rev().for_each(|layout| {
layout.solve(instance_ref);
});
});
for rep_in_comp in &instance_ref.component_type.repeater { for rep_in_comp in &instance_ref.component_type.repeater {
generativity::make_guard!(g); generativity::make_guard!(g);

View file

@ -89,7 +89,7 @@ impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackIn
impl corelib::rtti::ValueType for Value {} impl corelib::rtti::ValueType for Value {}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum ComponentInstance<'a, 'id> { pub(crate) enum ComponentInstance<'a, 'id> {
InstanceRef(InstanceRef<'a, 'id>), InstanceRef(InstanceRef<'a, 'id>),
GlobalComponent(&'a Pin<Rc<dyn crate::global_component::GlobalComponent>>), GlobalComponent(&'a Pin<Rc<dyn crate::global_component::GlobalComponent>>),
} }
@ -98,7 +98,7 @@ enum ComponentInstance<'a, 'id> {
pub struct EvalLocalContext<'a, 'id> { pub struct EvalLocalContext<'a, 'id> {
local_variables: HashMap<String, Value>, local_variables: HashMap<String, Value>,
function_arguments: Vec<Value>, function_arguments: Vec<Value>,
component_instance: ComponentInstance<'a, 'id>, pub(crate) component_instance: ComponentInstance<'a, 'id>,
/// When Some, a return statement was executed and one must stop evaluating /// When Some, a return statement was executed and one must stop evaluating
return_value: Option<Value>, return_value: Option<Value>,
} }
@ -539,6 +539,17 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
} }
local_context.return_value.clone().unwrap() local_context.return_value.clone().unwrap()
} }
Expression::LayoutCacheAccess { layout_cache_prop, index } => {
let cache = load_property_helper(local_context.component_instance, &layout_cache_prop.element(), layout_cache_prop.name()).unwrap();
if let Value::LayoutCache(cache) = cache {
Value::Number(cache[*index].into())
} else {
panic!("invalid layout cache")
}
}
Expression::ComputeLayoutInfo(lay) => crate::eval_layout::compute_layout_info(lay, local_context),
Expression::SolveLayout(lay) => crate::eval_layout::solve_layout(lay, local_context),
} }
} }

View file

@ -0,0 +1,175 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
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 */
use crate::dynamic_component::InstanceRef;
use crate::eval::{self, ComponentInstance, EvalLocalContext};
use crate::Value;
use sixtyfps_compilerlib::expression_tree::Expression;
use sixtyfps_compilerlib::langtype::Type;
use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutItem};
use sixtyfps_compilerlib::namedreference::NamedReference;
use sixtyfps_corelib::layout as core_layout;
use sixtyfps_corelib::slice::Slice;
use sixtyfps_corelib::window::ComponentWindow;
use std::convert::TryInto;
pub(crate) fn compute_layout_info(lay: &Layout, local_context: &mut EvalLocalContext) -> Value {
let component = match local_context.component_instance {
ComponentInstance::InstanceRef(c) => c,
ComponentInstance::GlobalComponent(_) => panic!("Cannot compute layout from a Global"),
};
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
match lay {
Layout::GridLayout(grid_layout) => {
let (cells, padding, spacing) = grid_layout_data(grid_layout, component, &expr_eval);
core_layout::grid_layout_info(&Slice::from(cells.as_slice()), spacing, &padding).into()
}
_ => todo!(),
}
}
pub(crate) fn solve_layout(lay: &Layout, local_context: &mut EvalLocalContext) -> Value {
let component = match local_context.component_instance {
ComponentInstance::InstanceRef(c) => c,
ComponentInstance::GlobalComponent(_) => panic!("Cannot compute layout from a Global"),
};
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
match lay {
Layout::GridLayout(grid_layout) => {
let (cells, padding, spacing) = grid_layout_data(grid_layout, component, &expr_eval);
Value::LayoutCache(core_layout::solve_grid_layout(&core_layout::GridLayoutData {
width: grid_layout
.geometry
.rect
.width_reference
.as_ref()
.map(expr_eval)
.unwrap_or(0.),
height: grid_layout
.geometry
.rect
.height_reference
.as_ref()
.map(expr_eval)
.unwrap_or(0.),
x: 0.,
y: 0.,
spacing,
padding: &padding,
cells: Slice::from(cells.as_slice()),
}))
}
_ => todo!(),
}
}
/// return the celldata, the padding, and the spacing of a grid layout
fn grid_layout_data(
grid_layout: &sixtyfps_compilerlib::layout::GridLayout,
component: InstanceRef,
expr_eval: &impl Fn(&NamedReference) -> f32,
) -> (Vec<core_layout::GridLayoutCellData>, core_layout::Padding, f32) {
let cells = grid_layout
.elems
.iter()
.map(|cell| {
let mut layout_info =
get_layout_info(&cell.item, component, &eval::window_ref(component).unwrap());
fill_layout_info_constraints(&mut layout_info, &cell.item.constraints, &expr_eval);
core_layout::GridLayoutCellData {
col: cell.col,
row: cell.row,
colspan: cell.colspan,
rowspan: cell.rowspan,
constraint: layout_info,
}
})
.collect::<Vec<_>>();
let spacing = grid_layout.geometry.spacing.as_ref().map_or(0., expr_eval);
let padding = core_layout::Padding {
left: grid_layout.geometry.padding.left.as_ref().map_or(0., expr_eval),
right: grid_layout.geometry.padding.right.as_ref().map_or(0., expr_eval),
top: grid_layout.geometry.padding.top.as_ref().map_or(0., expr_eval),
bottom: grid_layout.geometry.padding.bottom.as_ref().map_or(0., expr_eval),
};
(cells, padding, spacing)
}
pub(crate) fn fill_layout_info_constraints(
layout_info: &mut core_layout::LayoutInfo,
constraints: &LayoutConstraints,
expr_eval: &impl Fn(&NamedReference) -> f32,
) {
let is_percent =
|nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
constraints.minimum_width.as_ref().map(|e| {
if !is_percent(e) {
layout_info.min_width = expr_eval(e)
} else {
layout_info.min_width_percent = expr_eval(e)
}
});
constraints.maximum_width.as_ref().map(|e| {
if !is_percent(e) {
layout_info.max_width = expr_eval(e)
} else {
layout_info.max_width_percent = expr_eval(e)
}
});
constraints.minimum_height.as_ref().map(|e| {
if !is_percent(e) {
layout_info.min_height = expr_eval(e)
} else {
layout_info.min_height_percent = expr_eval(e)
}
});
constraints.maximum_height.as_ref().map(|e| {
if !is_percent(e) {
layout_info.max_height = expr_eval(e)
} else {
layout_info.max_height_percent = expr_eval(e)
}
});
constraints.preferred_width.as_ref().map(|e| {
layout_info.preferred_width = expr_eval(e);
});
constraints.preferred_height.as_ref().map(|e| {
layout_info.preferred_height = expr_eval(e);
});
constraints.horizontal_stretch.as_ref().map(|e| layout_info.horizontal_stretch = expr_eval(e));
constraints.vertical_stretch.as_ref().map(|e| layout_info.vertical_stretch = expr_eval(e));
}
fn get_layout_info<'a, 'b>(
item: &'a LayoutItem,
component: InstanceRef<'a, '_>,
window: &ComponentWindow,
) -> core_layout::LayoutInfo {
let elem = item.element.borrow();
let item = &component
.component_type
.items
.get(elem.id.as_str())
.unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
let r = unsafe { item.item_from_component(component.as_ptr()).as_ref().layouting_info(window) };
if let Some(nr) = &elem.layout_info_prop {
r.merge(
&eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap(),
)
} else {
r
}
}

View file

@ -154,6 +154,7 @@ fn default_value_for_type(ty: &Type) -> Value {
Type::Model => Value::Void, Type::Model => Value::Void,
Type::UnitProduct(_) => Value::Number(0.), Type::UnitProduct(_) => Value::Number(0.),
Type::PathElements => Value::PathElements(Default::default()), Type::PathElements => Value::PathElements(Default::default()),
Type::LayoutCache => Value::LayoutCache(Default::default()),
Type::ElementReference Type::ElementReference
| Type::Builtin(_) | Type::Builtin(_)
| Type::Component(_) | Type::Component(_)

View file

@ -78,6 +78,7 @@ mod api;
mod dynamic_component; mod dynamic_component;
mod dynamic_type; mod dynamic_type;
mod eval; mod eval;
mod eval_layout;
mod global_component; mod global_component;
mod value_model; mod value_model;

View file

@ -20,7 +20,7 @@ TestCase := Rectangle {
rowspan: 1; rowspan: 1;
} }
rect2 := Rectangle { rect2 := Rectangle {
background: red; background: blue;
} }
} }