mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 22:54:36 +00:00
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:
parent
304699e6bc
commit
21a80f4562
18 changed files with 584 additions and 853 deletions
68
Cargo.toml
68
Cargo.toml
|
@ -9,34 +9,34 @@ members = [
|
|||
'sixtyfps_runtime/rendering_backends/testing',
|
||||
'sixtyfps_compiler',
|
||||
'sixtyfps_compiler/parser_test_macro',
|
||||
'api/sixtyfps-rs',
|
||||
'api/sixtyfps-rs/sixtyfps-macros',
|
||||
'api/sixtyfps-rs/sixtyfps-build',
|
||||
'api/sixtyfps-cpp',
|
||||
'api/sixtyfps-node/native',
|
||||
# 'api/sixtyfps-rs',
|
||||
# 'api/sixtyfps-rs/sixtyfps-macros',
|
||||
# 'api/sixtyfps-rs/sixtyfps-build',
|
||||
# 'api/sixtyfps-cpp',
|
||||
# 'api/sixtyfps-node/native',
|
||||
'api/sixtyfps-wasm-interpreter',
|
||||
'tools/compiler',
|
||||
# 'tools/compiler',
|
||||
'tools/fmt',
|
||||
'tools/lsp',
|
||||
'tools/syntax_updater',
|
||||
'tools/viewer',
|
||||
'examples/gallery',
|
||||
'examples/printerdemo/rust',
|
||||
'examples/printerdemo_old/rust',
|
||||
'examples/todo/rust',
|
||||
'examples/slide_puzzle',
|
||||
'examples/7gui',
|
||||
'examples/memory',
|
||||
# 'examples/gallery',
|
||||
# 'examples/printerdemo/rust',
|
||||
# 'examples/printerdemo_old/rust',
|
||||
# 'examples/todo/rust',
|
||||
# 'examples/slide_puzzle',
|
||||
# 'examples/7gui',
|
||||
# 'examples/memory',
|
||||
'helper_crates/const-field-offset',
|
||||
'helper_crates/vtable',
|
||||
'helper_crates/vtable/macro',
|
||||
'xtask',
|
||||
'tests/doctests',
|
||||
'tests/driver/driverlib',
|
||||
'tests/driver/rust',
|
||||
'tests/driver/cpp',
|
||||
'tests/driver/nodejs',
|
||||
'tests/driver/interpreter',
|
||||
# 'tests/doctests',
|
||||
# 'tests/driver/driverlib',
|
||||
# 'tests/driver/rust',
|
||||
# 'tests/driver/cpp',
|
||||
# 'tests/driver/nodejs',
|
||||
# 'tests/driver/interpreter',
|
||||
]
|
||||
|
||||
default-members = [
|
||||
|
@ -45,25 +45,25 @@ default-members = [
|
|||
'sixtyfps_runtime/rendering_backends/gl',
|
||||
'sixtyfps_runtime/rendering_backends/qt',
|
||||
'sixtyfps_runtime/rendering_backends/default',
|
||||
'sixtyfps_compiler',
|
||||
'api/sixtyfps-rs',
|
||||
'api/sixtyfps-rs/sixtyfps-build',
|
||||
'api/sixtyfps-node/native',
|
||||
'tools/compiler',
|
||||
# 'sixtyfps_compiler',
|
||||
# 'api/sixtyfps-rs',
|
||||
# 'api/sixtyfps-rs/sixtyfps-build',
|
||||
# 'api/sixtyfps-node/native',
|
||||
# 'tools/compiler',
|
||||
'tools/fmt',
|
||||
'tools/lsp',
|
||||
'tools/syntax_updater',
|
||||
'tools/viewer',
|
||||
'examples/gallery',
|
||||
'examples/printerdemo/rust',
|
||||
'examples/printerdemo_old/rust',
|
||||
'examples/todo/rust',
|
||||
'examples/slide_puzzle',
|
||||
'examples/memory',
|
||||
'tests/doctests',
|
||||
'tests/driver/rust',
|
||||
'tests/driver/nodejs',
|
||||
'tests/driver/interpreter',
|
||||
# 'examples/gallery',
|
||||
# 'examples/printerdemo/rust',
|
||||
# 'examples/printerdemo_old/rust',
|
||||
# 'examples/todo/rust',
|
||||
# 'examples/slide_puzzle',
|
||||
# 'examples/memory',
|
||||
# 'tests/doctests',
|
||||
# 'tests/driver/rust',
|
||||
# 'tests/driver/nodejs',
|
||||
# 'tests/driver/interpreter',
|
||||
]
|
||||
|
||||
resolver = "2"
|
||||
|
|
|
@ -398,6 +398,13 @@ pub enum Expression {
|
|||
EnumerationValue(EnumerationValue),
|
||||
|
||||
ReturnStatement(Option<Box<Expression>>),
|
||||
|
||||
LayoutCacheAccess {
|
||||
layout_cache_prop: NamedReference,
|
||||
index: usize,
|
||||
},
|
||||
ComputeLayoutInfo(crate::layout::Layout),
|
||||
SolveLayout(crate::layout::Layout),
|
||||
}
|
||||
|
||||
impl Default for Expression {
|
||||
|
@ -530,6 +537,9 @@ impl Expression {
|
|||
Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
|
||||
// invalid because the expression is unreachable
|
||||
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) => {
|
||||
expr.as_deref().map(|expr| visitor(expr));
|
||||
}
|
||||
Expression::LayoutCacheAccess { .. } => {}
|
||||
Expression::ComputeLayoutInfo(_) => {}
|
||||
Expression::SolveLayout(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -695,6 +708,9 @@ impl Expression {
|
|||
Expression::ReturnStatement(expr) => {
|
||||
expr.as_deref_mut().map(|expr| visitor(expr));
|
||||
}
|
||||
Expression::LayoutCacheAccess { .. } => {}
|
||||
Expression::ComputeLayoutInfo(_) => {}
|
||||
Expression::SolveLayout(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,6 +767,10 @@ impl Expression {
|
|||
Expression::ReturnStatement(expr) => {
|
||||
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::Function { .. }
|
||||
| Type::Void
|
||||
| Type::ElementReference => Expression::Invalid,
|
||||
| Type::ElementReference
|
||||
| Type::LayoutCache => Expression::Invalid,
|
||||
Type::Float32 => Expression::NumberLiteral(0., Unit::None),
|
||||
Type::Int32 => Expression::NumberLiteral(0., Unit::None),
|
||||
Type::String => Expression::StringLiteral(String::new()),
|
||||
|
@ -970,7 +991,7 @@ impl Expression {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
|
@ -979,6 +1000,8 @@ pub struct BindingExpression {
|
|||
pub span: Option<SourceLocation>,
|
||||
/// 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.
|
||||
/// 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,
|
||||
}
|
||||
|
||||
|
@ -993,7 +1016,7 @@ impl BindingExpression {
|
|||
Self {
|
||||
expression: Expression::Uncompiled(node.clone()),
|
||||
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 ")?;
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ pub enum Type {
|
|||
PathElements,
|
||||
Easing,
|
||||
Brush,
|
||||
|
||||
/// This is usually a model
|
||||
Array(Box<Type>),
|
||||
Struct {
|
||||
fields: BTreeMap<String, Type>,
|
||||
|
@ -69,6 +69,9 @@ pub enum Type {
|
|||
UnitProduct(Vec<(Unit, i8)>),
|
||||
|
||||
ElementReference,
|
||||
|
||||
/// This is a `SharedArray<f32>`
|
||||
LayoutCache,
|
||||
}
|
||||
|
||||
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::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
|
||||
Type::ElementReference => matches!(other, Type::ElementReference),
|
||||
Type::LayoutCache => matches!(other, Type::LayoutCache),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +193,7 @@ impl Display for Type {
|
|||
write!(f, "({})", x.join("×"))
|
||||
}
|
||||
Type::ElementReference => write!(f, "element ref"),
|
||||
Type::LayoutCache => write!(f, "layout cache"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -469,6 +474,7 @@ impl Type {
|
|||
Type::Enumeration(_) => None,
|
||||
Type::UnitProduct(_) => None,
|
||||
Type::ElementReference => None,
|
||||
Type::LayoutCache => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -682,6 +688,12 @@ pub struct PropertyLookupResult<'a> {
|
|||
pub property_type: Type,
|
||||
}
|
||||
|
||||
impl<'a> PropertyLookupResult<'a> {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.property_type != Type::Invalid
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Enumeration {
|
||||
pub name: String,
|
||||
|
|
|
@ -9,16 +9,14 @@
|
|||
LICENSE END */
|
||||
//! Datastructures used to represent layouts in the compiler
|
||||
|
||||
use crate::langtype::Type;
|
||||
use crate::object_tree::{ElementRc, PropertyDeclaration};
|
||||
use crate::{diagnostics::BuildDiagnostics, langtype::PropertyLookupResult};
|
||||
use crate::{
|
||||
expression_tree::{Expression, NamedReference, Path},
|
||||
object_tree::Component,
|
||||
};
|
||||
use std::{borrow::Cow, rc::Rc};
|
||||
use crate::diagnostics::BuildDiagnostics;
|
||||
use crate::expression_tree::{Expression, NamedReference, Path};
|
||||
use crate::langtype::{PropertyLookupResult, Type};
|
||||
use crate::object_tree::{Component, ElementRc};
|
||||
|
||||
#[derive(Debug, derive_more::From)]
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone, Debug, derive_more::From)]
|
||||
pub enum Layout {
|
||||
GridLayout(GridLayout),
|
||||
PathLayout(PathLayout),
|
||||
|
@ -37,7 +35,7 @@ impl Layout {
|
|||
|
||||
impl 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 {
|
||||
Layout::GridLayout(grid) => grid.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
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LayoutItem {
|
||||
pub element: Option<ElementRc>,
|
||||
pub layout: Option<Layout>,
|
||||
pub element: ElementRc,
|
||||
pub constraints: LayoutConstraints,
|
||||
}
|
||||
|
||||
impl LayoutItem {
|
||||
pub fn rect(&self) -> Cow<LayoutRect> {
|
||||
if let Some(e) = &self.element {
|
||||
let p = |unresolved_name: &str| {
|
||||
let PropertyLookupResult { resolved_name, property_type } =
|
||||
e.borrow().lookup_property(unresolved_name);
|
||||
if property_type == Type::LogicalLength {
|
||||
Some(NamedReference::new(e, resolved_name.as_ref()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
Cow::Owned(LayoutRect {
|
||||
x_reference: p("x"),
|
||||
y_reference: p("y"),
|
||||
width_reference: if !self.constraints.fixed_width { p("width") } 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;
|
||||
pub fn rect(&self) -> LayoutRect {
|
||||
let p = |unresolved_name: &str| {
|
||||
let PropertyLookupResult { resolved_name, property_type } =
|
||||
self.element.borrow().lookup_property(unresolved_name);
|
||||
if property_type == Type::LogicalLength {
|
||||
Some(NamedReference::new(&self.element, resolved_name.as_ref()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if r.height_reference.is_some() && self.constraints.fixed_height {
|
||||
r.to_mut().height_reference = None;
|
||||
}
|
||||
r
|
||||
} else {
|
||||
Cow::Owned(LayoutRect::default())
|
||||
};
|
||||
LayoutRect {
|
||||
x_reference: p("x"),
|
||||
y_reference: p("y"),
|
||||
width_reference: if !self.constraints.fixed_width { p("width") } else { None },
|
||||
height_reference: if !self.constraints.fixed_height { p("height") } else { None },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,17 +81,7 @@ pub struct LayoutRect {
|
|||
|
||||
impl LayoutRect {
|
||||
pub fn install_on_element(element: &ElementRc) -> Self {
|
||||
let install_prop = |name: &str| {
|
||||
element.borrow_mut().property_declarations.insert(
|
||||
name.to_string(),
|
||||
PropertyDeclaration {
|
||||
property_type: Type::LogicalLength,
|
||||
node: None,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
Some(NamedReference::new(element, name))
|
||||
};
|
||||
let install_prop = |name: &str| Some(NamedReference::new(element, name));
|
||||
|
||||
Self {
|
||||
x_reference: install_prop("x"),
|
||||
|
@ -147,7 +99,7 @@ impl LayoutRect {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LayoutConstraints {
|
||||
pub minimum_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")))
|
||||
}
|
||||
|
||||
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.minimum_width.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
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GridLayoutElement {
|
||||
pub col: u16,
|
||||
pub row: u16,
|
||||
|
@ -273,7 +225,7 @@ pub struct GridLayoutElement {
|
|||
pub item: LayoutItem,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Padding {
|
||||
pub left: Option<NamedReference>,
|
||||
pub right: Option<NamedReference>,
|
||||
|
@ -290,15 +242,12 @@ impl Padding {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LayoutGeometry {
|
||||
pub rect: LayoutRect,
|
||||
pub spacing: Option<NamedReference>,
|
||||
pub alignment: Option<NamedReference>,
|
||||
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 {
|
||||
|
@ -307,10 +256,10 @@ impl LayoutGeometry {
|
|||
self.spacing.as_mut().map(|e| visitor(&mut *e));
|
||||
self.alignment.as_mut().map(|e| visitor(&mut *e));
|
||||
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> {
|
||||
element.borrow().bindings.contains_key(name).then(|| NamedReference::new(element, name))
|
||||
}
|
||||
|
@ -337,11 +286,7 @@ fn init_fake_property(
|
|||
}
|
||||
|
||||
impl LayoutGeometry {
|
||||
pub fn new(
|
||||
rect: LayoutRect,
|
||||
layout_element: &ElementRc,
|
||||
style_metrics: &Option<Rc<Component>>,
|
||||
) -> Self {
|
||||
pub fn new(layout_element: &ElementRc, style_metrics: &Option<Rc<Component>>) -> Self {
|
||||
let style_metrics_element = style_metrics.as_ref().map(|comp| comp.root_element.clone());
|
||||
|
||||
let padding = || {
|
||||
|
@ -358,10 +303,6 @@ impl LayoutGeometry {
|
|||
});
|
||||
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_right", 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),
|
||||
};
|
||||
|
||||
// that's kind of the opposite of binding reference
|
||||
let fake_property_ref = |name| {
|
||||
(layout_element.borrow().property_declarations.contains_key(name)
|
||||
&& !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 }
|
||||
let rect = LayoutRect::install_on_element(layout_element);
|
||||
|
||||
Self { rect, spacing, padding, alignment }
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal representation of a grid layout
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GridLayout {
|
||||
/// All the elements will be layout within that element.
|
||||
pub elems: Vec<GridLayoutElement>,
|
||||
|
@ -409,7 +333,6 @@ pub struct GridLayout {
|
|||
impl GridLayout {
|
||||
fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
|
||||
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);
|
||||
}
|
||||
self.geometry.visit_named_references(visitor);
|
||||
|
@ -417,7 +340,7 @@ impl GridLayout {
|
|||
}
|
||||
|
||||
/// Internal representation of a BoxLayout
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BoxLayout {
|
||||
/// When true, this is a HorizonalLayout, otherwise a VerticalLayout
|
||||
pub is_horizontal: bool,
|
||||
|
@ -428,7 +351,6 @@ pub struct BoxLayout {
|
|||
impl BoxLayout {
|
||||
fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
|
||||
for cell in &mut self.elems {
|
||||
cell.layout.as_mut().map(|x| x.visit_named_references(visitor));
|
||||
cell.constraints.visit_named_references(visitor);
|
||||
}
|
||||
self.geometry.visit_named_references(visitor);
|
||||
|
@ -436,7 +358,7 @@ impl BoxLayout {
|
|||
}
|
||||
|
||||
/// Internal representation of a path layout
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PathLayout {
|
||||
pub path: Path,
|
||||
pub elements: Vec<ElementRc>,
|
||||
|
@ -555,3 +477,34 @@ pub mod gen {
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::diagnostics::{BuildDiagnostics, Spanned};
|
|||
use crate::expression_tree::{self, BindingExpression, Expression, Unit};
|
||||
use crate::langtype::PropertyLookupResult;
|
||||
use crate::langtype::{BuiltinElement, NativeClass, Type};
|
||||
use crate::layout::LayoutConstraints;
|
||||
use crate::namedreference::NamedReference;
|
||||
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNode};
|
||||
use crate::typeloader::ImportedTypes;
|
||||
|
@ -188,8 +189,8 @@ pub struct Component {
|
|||
/// generator for symbol generation.
|
||||
pub embedded_file_resources: RefCell<HashMap<String, usize>>,
|
||||
|
||||
/// All layouts in this component
|
||||
pub layouts: RefCell<crate::layout::LayoutVec>,
|
||||
/// The layout constraints of the root item
|
||||
pub root_constraints: RefCell<LayoutConstraints>,
|
||||
|
||||
/// When creating this component and inserting "children", append them to the children of
|
||||
/// 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
|
||||
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
|
||||
pub is_flickable_viewport: bool,
|
||||
|
@ -1233,6 +1236,9 @@ pub fn visit_all_named_references_in_element(
|
|||
match expr {
|
||||
Expression::PropertyReference(r) | Expression::CallbackReference(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
|
||||
// FIXME: this should probably be lowered into a PropertyReference
|
||||
Expression::RepeaterModelReference { element }
|
||||
|
@ -1272,6 +1278,9 @@ pub fn visit_all_named_references_in_element(
|
|||
}
|
||||
}
|
||||
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
|
||||
|
@ -1287,7 +1296,7 @@ pub fn visit_all_named_references(
|
|||
let compo = elem.borrow().enclosing_component.clone();
|
||||
if !Weak::ptr_eq(parent_compo, &compo) {
|
||||
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| {
|
||||
vis(&mut p.x);
|
||||
vis(&mut p.y);
|
||||
|
|
|
@ -182,6 +182,7 @@ fn duplicate_element_with_mapping(
|
|||
.map(|t| duplicate_transition(t, mapping, root_component))
|
||||
.collect(),
|
||||
child_of_layout: elem.child_of_layout,
|
||||
layout_info_prop: elem.layout_info_prop.clone(),
|
||||
named_references: Default::default(),
|
||||
item_index: Default::default(), // Not determined yet
|
||||
is_flickable_viewport: elem.is_flickable_viewport,
|
||||
|
|
|
@ -15,30 +15,139 @@ use crate::layout::*;
|
|||
use crate::object_tree::*;
|
||||
use crate::typeregister::TypeRegister;
|
||||
use crate::{diagnostics::BuildDiagnostics, typeloader::TypeLoader};
|
||||
use std::cell::RefCell;
|
||||
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>,
|
||||
rect: LayoutRect,
|
||||
grid_layout_element: &ElementRc,
|
||||
collected_children: &mut Vec<ElementRc>,
|
||||
type_register: &TypeRegister,
|
||||
style_metrics: &Option<Rc<Component>>,
|
||||
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 {
|
||||
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 col = 0;
|
||||
|
||||
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 {
|
||||
let is_row = if let Type::Builtin(be) = &layout_child.borrow().base_type {
|
||||
be.native_class.class_name == "Row"
|
||||
be.name == "Row"
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
@ -49,49 +158,39 @@ fn lower_grid_layout(
|
|||
}
|
||||
let row_children = std::mem::take(&mut layout_child.borrow_mut().children);
|
||||
for x in row_children {
|
||||
grid.add_element(
|
||||
x,
|
||||
(&mut row, &mut col),
|
||||
diag,
|
||||
type_register,
|
||||
style_metrics,
|
||||
&component,
|
||||
collected_children,
|
||||
);
|
||||
grid.add_element(&x, (&mut row, &mut col), diag, &layout_cache_prop);
|
||||
col += 1;
|
||||
collected_children.push(x);
|
||||
}
|
||||
if col > 0 {
|
||||
row += 1;
|
||||
col = 0;
|
||||
}
|
||||
component.optimized_elements.borrow_mut().push(layout_child.clone());
|
||||
component.optimized_elements.borrow_mut().push(layout_child);
|
||||
} else {
|
||||
grid.add_element(
|
||||
layout_child,
|
||||
(&mut row, &mut col),
|
||||
diag,
|
||||
type_register,
|
||||
style_metrics,
|
||||
&component,
|
||||
collected_children,
|
||||
);
|
||||
col += 1;
|
||||
grid.add_element(&layout_child, (&mut row, &mut col), diag, &layout_cache_prop);
|
||||
if is_vertical {
|
||||
row += 1
|
||||
} else {
|
||||
col += 1
|
||||
};
|
||||
collected_children.push(layout_child);
|
||||
}
|
||||
}
|
||||
component.optimized_elements.borrow_mut().push(grid_layout_element.clone());
|
||||
if !grid.elems.is_empty() {
|
||||
Some(grid.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
fn lower_box_layout(
|
||||
component: &Rc<Component>,
|
||||
rect: LayoutRect,
|
||||
layout_element: &ElementRc,
|
||||
collected_children: &mut Vec<ElementRc>,
|
||||
type_register: &TypeRegister,
|
||||
style_metrics: &Option<Rc<Component>>,
|
||||
diag: &mut BuildDiagnostics,
|
||||
) -> Option<Layout> {
|
||||
|
@ -99,18 +198,11 @@ fn lower_box_layout(
|
|||
let mut layout = BoxLayout {
|
||||
is_horizontal,
|
||||
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);
|
||||
for layout_child in layout_children {
|
||||
if let Some(item) = create_layout_item(
|
||||
&layout_child,
|
||||
component,
|
||||
collected_children,
|
||||
type_register,
|
||||
style_metrics,
|
||||
diag,
|
||||
) {
|
||||
if let Some(item) = create_layout_item(&layout_child, diag) {
|
||||
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
|
||||
fn create_layout_item(
|
||||
item_element: &ElementRc,
|
||||
component: &Rc<Component>,
|
||||
collected_children: &mut Vec<ElementRc>,
|
||||
type_register: &TypeRegister,
|
||||
style_metrics: &Option<Rc<Component>>,
|
||||
diag: &mut BuildDiagnostics,
|
||||
) -> Option<LayoutItem> {
|
||||
fn create_layout_item(item_element: &ElementRc, diag: &mut BuildDiagnostics) -> Option<LayoutItem> {
|
||||
let fix_explicit_percent = |prop: &str, item: &ElementRc| {
|
||||
if !item.borrow().bindings.get(prop).map_or(false, |b| b.ty() == Type::Percent) {
|
||||
return;
|
||||
|
@ -315,68 +284,25 @@ fn create_layout_item(
|
|||
let constraints = LayoutConstraints::new(item_element, diag);
|
||||
|
||||
item_element.borrow_mut().child_of_layout = true;
|
||||
|
||||
if item_element.borrow().repeated.is_some() {
|
||||
let rep_comp = item_element.borrow().base_type.as_component().clone();
|
||||
fix_explicit_percent("width", &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);
|
||||
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 {
|
||||
fn add_element(
|
||||
&mut self,
|
||||
item_element: ElementRc,
|
||||
item_element: &ElementRc,
|
||||
(row, col): (&mut u16, &mut u16),
|
||||
diag: &mut BuildDiagnostics,
|
||||
type_register: &TypeRegister,
|
||||
style_metrics: &Option<Rc<Component>>,
|
||||
component: &Rc<Component>,
|
||||
collected_children: &mut Vec<ElementRc>,
|
||||
layout_cache_prop: &NamedReference,
|
||||
) {
|
||||
let mut get_const_value = |name: &str| {
|
||||
item_element
|
||||
|
@ -395,14 +321,28 @@ impl GridLayout {
|
|||
*col = c;
|
||||
}
|
||||
|
||||
if let Some(layout_item) = create_layout_item(
|
||||
&item_element,
|
||||
component,
|
||||
collected_children,
|
||||
type_register,
|
||||
style_metrics,
|
||||
diag,
|
||||
) {
|
||||
if let Some(layout_item) = create_layout_item(&item_element, diag) {
|
||||
let index = self.elems.len();
|
||||
//FIXME: report errors if there is already bindings on x or y
|
||||
let set_prop_from_cache = |prop: &str, offset: usize| {
|
||||
item_element.borrow_mut().bindings.insert(
|
||||
prop.into(),
|
||||
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 {
|
||||
col: *col,
|
||||
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) {
|
||||
for (prop, expr) in item.borrow().bindings.iter() {
|
||||
if matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan") {
|
||||
|
|
|
@ -60,8 +60,6 @@ fn lower_popup_window(
|
|||
};
|
||||
|
||||
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
|
||||
parent_element.borrow_mut().children.retain(|child| !Rc::ptr_eq(child, popup_window_element));
|
||||
|
|
|
@ -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.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| {
|
||||
fixup_reference(&mut p.x);
|
||||
fixup_reference(&mut p.y);
|
||||
|
|
|
@ -23,9 +23,6 @@ pub fn process_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, _| {
|
||||
if elem.borrow().repeated.is_none() {
|
||||
return;
|
||||
|
@ -48,6 +45,7 @@ fn create_repeater_components(component: &Rc<Component>) {
|
|||
states: std::mem::take(&mut elem.states),
|
||||
transitions: std::mem::take(&mut elem.transitions),
|
||||
child_of_layout: elem.child_of_layout,
|
||||
layout_info_prop: elem.layout_info_prop.take(),
|
||||
is_flickable_viewport: elem.is_flickable_viewport,
|
||||
item_index: Default::default(), // Not determined yet
|
||||
})),
|
||||
|
|
|
@ -11,7 +11,7 @@ LICENSE END */
|
|||
//!
|
||||
//! Currently this is a very basic implementation
|
||||
|
||||
use crate::{slice::Slice, Property};
|
||||
use crate::{slice::Slice, Property, SharedVector};
|
||||
|
||||
type Coord = f32;
|
||||
|
||||
|
@ -257,24 +257,21 @@ pub struct GridLayoutData<'a> {
|
|||
pub y: Coord,
|
||||
pub spacing: Coord,
|
||||
pub padding: &'a Padding,
|
||||
pub cells: Slice<'a, GridLayoutCellData<'a>>,
|
||||
pub cells: Slice<'a, GridLayoutCellData>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct GridLayoutCellData<'a> {
|
||||
pub struct GridLayoutCellData {
|
||||
pub col: u16,
|
||||
pub row: u16,
|
||||
pub colspan: u16,
|
||||
pub rowspan: u16,
|
||||
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);
|
||||
for cell in data.cells.iter() {
|
||||
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 {
|
||||
return;
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
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.spacing,
|
||||
);
|
||||
let mut result = SharedVector::with_capacity(4 * data.cells.len());
|
||||
for cell in data.cells.iter() {
|
||||
let rdata = &row_layout_data[cell.row as usize];
|
||||
let cdata = &col_layout_data[cell.col as usize];
|
||||
cell.x.map(|p| p.set(cdata.pos));
|
||||
cell.width.map(|p| {
|
||||
p.set({
|
||||
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];
|
||||
last_cell.pos + last_cell.size - first_cell.pos
|
||||
})
|
||||
result.push(cdata.pos);
|
||||
result.push(rdata.pos);
|
||||
result.push({
|
||||
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];
|
||||
last_cell.pos + last_cell.size - first_cell.pos
|
||||
});
|
||||
cell.y.map(|p| p.set(rdata.pos));
|
||||
cell.height.map(|p| {
|
||||
p.set({
|
||||
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];
|
||||
last_cell.pos + last_cell.size - first_cell.pos
|
||||
})
|
||||
result.push({
|
||||
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];
|
||||
last_cell.pos + last_cell.size - first_cell.pos
|
||||
});
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn grid_layout_info<'a>(
|
||||
cells: &Slice<'a, GridLayoutCellData<'a>>,
|
||||
cells: &Slice<'a, GridLayoutCellData>,
|
||||
spacing: Coord,
|
||||
padding: &Padding,
|
||||
) -> LayoutInfo {
|
||||
|
|
|
@ -111,6 +111,8 @@ pub enum Value {
|
|||
/// An enumation, like `TextHorizontalAlignment::align_center`, represented by `("TextHorizontalAlignment", "align_center")`.
|
||||
/// FIXME: consider representing that with a number?
|
||||
EnumerationValue(String, String),
|
||||
#[doc(hidden)]
|
||||
LayoutCache(SharedVector<f32>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
@ -153,6 +155,7 @@ impl PartialEq for Value {
|
|||
Value::EnumerationValue(lhs_name, lhs_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::EasingCurve(c) => write!(f, "Value::EasingCurve({:?})", c),
|
||||
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!(PathElements => [PathData]);
|
||||
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`
|
||||
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::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::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`
|
||||
///
|
||||
|
|
|
@ -15,9 +15,6 @@ use dynamic_type::{Instance, InstanceBox};
|
|||
use expression_tree::NamedReference;
|
||||
use object_tree::{Element, ElementRc};
|
||||
use sixtyfps_compilerlib::langtype::Type;
|
||||
use sixtyfps_compilerlib::layout::{
|
||||
Layout, LayoutConstraints, LayoutGeometry, LayoutItem, PathLayout,
|
||||
};
|
||||
use sixtyfps_compilerlib::*;
|
||||
use sixtyfps_compilerlib::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
|
||||
use sixtyfps_compilerlib::{expression_tree::Expression, langtype::PropertyLookupResult};
|
||||
|
@ -29,14 +26,14 @@ use sixtyfps_corelib::item_tree::{
|
|||
use sixtyfps_corelib::items::{
|
||||
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::Repeater;
|
||||
use sixtyfps_corelib::properties::InterpolatedPropertyValue;
|
||||
use sixtyfps_corelib::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
|
||||
use sixtyfps_corelib::slice::Slice;
|
||||
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::{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 {
|
||||
constraint: self.borrow().as_ref().layout_info(),
|
||||
x: get_prop("x"),
|
||||
|
@ -774,7 +771,8 @@ fn generate_component<'id>(
|
|||
"FillRule" => property_info::<sixtyfps_corelib::items::FillRule>(),
|
||||
_ => panic!("unkown enum"),
|
||||
},
|
||||
_ => panic!("bad type"),
|
||||
Type::LayoutCache => property_info::<SharedVector<f32>>(),
|
||||
_ => panic!("bad type {:?}", &decl.property_type),
|
||||
};
|
||||
custom_properties.insert(
|
||||
name.clone(),
|
||||
|
@ -1147,7 +1145,7 @@ pub fn instantiate<'id>(
|
|||
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();
|
||||
generativity::make_guard!(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() }
|
||||
}
|
||||
|
||||
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>);
|
||||
impl ErasedComponentBox {
|
||||
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>(
|
||||
instance_ref: InstanceRef<'a, '_>,
|
||||
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
|
||||
let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
|
||||
|
||||
let component_layouts = instance_ref.component_type.original.layouts.borrow();
|
||||
let result = if let Some(idx) = component_layouts.main_layout {
|
||||
let mut inverse_layout_tree = Default::default();
|
||||
collect_layouts_recursively(
|
||||
&mut inverse_layout_tree,
|
||||
&component_layouts[idx],
|
||||
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 constraints = instance_ref.component_type.original.root_constraints.borrow();
|
||||
|
||||
let result =
|
||||
instance_ref.root_item().as_ref().layouting_info(&eval::window_ref(instance_ref).unwrap());
|
||||
|
||||
if constraints.has_explicit_restrictions() {
|
||||
let mut info = LayoutInfo::default();
|
||||
fill_layout_info_constraints(
|
||||
crate::eval_layout::fill_layout_info_constraints(
|
||||
&mut info,
|
||||
&component_layouts.root_constraints,
|
||||
&constraints,
|
||||
&|nr: &NamedReference| {
|
||||
eval::load_property(instance_ref, &nr.element(), nr.name())
|
||||
.unwrap()
|
||||
|
@ -1664,17 +1252,6 @@ extern "C" fn apply_layout(component: ComponentRefPin, _r: sixtyfps_corelib::gra
|
|||
generativity::make_guard!(guard);
|
||||
// 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 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 {
|
||||
generativity::make_guard!(g);
|
||||
|
|
|
@ -89,7 +89,7 @@ impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackIn
|
|||
impl corelib::rtti::ValueType for Value {}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum ComponentInstance<'a, 'id> {
|
||||
pub(crate) enum ComponentInstance<'a, 'id> {
|
||||
InstanceRef(InstanceRef<'a, 'id>),
|
||||
GlobalComponent(&'a Pin<Rc<dyn crate::global_component::GlobalComponent>>),
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ enum ComponentInstance<'a, 'id> {
|
|||
pub struct EvalLocalContext<'a, 'id> {
|
||||
local_variables: HashMap<String, 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
|
||||
return_value: Option<Value>,
|
||||
}
|
||||
|
@ -539,6 +539,17 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
|
|||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
175
sixtyfps_runtime/interpreter/eval_layout.rs
Normal file
175
sixtyfps_runtime/interpreter/eval_layout.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -154,6 +154,7 @@ fn default_value_for_type(ty: &Type) -> Value {
|
|||
Type::Model => Value::Void,
|
||||
Type::UnitProduct(_) => Value::Number(0.),
|
||||
Type::PathElements => Value::PathElements(Default::default()),
|
||||
Type::LayoutCache => Value::LayoutCache(Default::default()),
|
||||
Type::ElementReference
|
||||
| Type::Builtin(_)
|
||||
| Type::Component(_)
|
||||
|
|
|
@ -78,6 +78,7 @@ mod api;
|
|||
mod dynamic_component;
|
||||
mod dynamic_type;
|
||||
mod eval;
|
||||
mod eval_layout;
|
||||
mod global_component;
|
||||
mod value_model;
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ TestCase := Rectangle {
|
|||
rowspan: 1;
|
||||
}
|
||||
rect2 := Rectangle {
|
||||
background: red;
|
||||
background: blue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue