More work on LLR

generate the actual tree and support a bunch more of expressions
This commit is contained in:
Olivier Goffart 2021-12-15 14:30:05 +01:00 committed by Olivier Goffart
parent 53c3e6a279
commit d395e92f3f
6 changed files with 907 additions and 155 deletions

View file

@ -14,7 +14,7 @@ use std::rc::{Rc, Weak};
// FIXME remove the pub
pub use crate::namedreference::NamedReference;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
/// A function built into the run-time
pub enum BuiltinFunction {
GetWindowScaleFactor,

View file

@ -1,11 +1,11 @@
// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
use std::collections::HashMap;
use crate::langtype::Type;
use super::PropertyReference;
use crate::expression_tree::{BuiltinFunction, OperatorClass};
use crate::langtype::Type;
use itertools::Either;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum Expression {
@ -64,17 +64,32 @@ pub enum Expression {
CodeBlock(Vec<Expression>),
/// A function call
FunctionCall {
function: Box<Expression>,
BuiltinFunctionCall {
function: BuiltinFunction,
arguments: Vec<Expression>,
},
CallBackCall {
callback: PropertyReference,
arguments: Vec<Expression>,
},
/// A SelfAssignment or an Assignment. When op is '=' this is a signal assignment.
SelfAssignment {
lhs: Box<Expression>,
rhs: Box<Expression>,
/// '+', '-', '/', '*', or '='
op: char,
/// A BuiltinFunctionCall, but the function is not yet in the `BuiltinFunction` enum
/// TODO: merge in BuiltinFunctionCall
ExtraBuiltinFunctionCall {
function: String,
arguments: Vec<Expression>,
},
/// An assignment of a value to a property
PropertyAssignment {
property: PropertyReference,
value: Box<Expression>,
},
/// an assignment of a value to the model data
ModelDataAssignment {
// how deep in the parent hierarchy we go
level: usize,
value: Box<Expression>,
},
BinaryExpression {
@ -109,9 +124,7 @@ pub enum Expression {
values: HashMap<String, Expression>,
},
PathElements {
elements: crate::expression_tree::PathEvents,
},
PathEvents(crate::expression_tree::PathEvents),
EasingCurve(crate::expression_tree::EasingCurve),
@ -132,55 +145,15 @@ pub enum Expression {
/// So this looks like `layout_cache_prop[layout_cache_prop[index] + repeater_index]`
repeater_index: Option<Box<Expression>>,
},
//-/ Compute the LayoutInfo for the given layout.
//TODO
//ComputeLayoutInfo(Layout),
//SolveLayout(Layout),
}
/*
#[derive(Debug, Default, Clone)]
pub struct LayoutConstraints {
pub min: Option<PropertyReference>,
pub max: Option<PropertyReference>,
pub preferred: Option<PropertyReference>,
pub stretch: Option<PropertyReference>,
pub fixed: bool,
}
#[derive(Debug, Clone)]
pub struct LayoutItem {
pos: Option<PropertyReference>,
size: Option<PropertyReference>,
}
pub struct LayoutGeometry {}
/// An element in a GridLayout
#[derive(Debug, Clone)]
pub struct GridLayoutElement {
pub col: u16,
pub row: u16,
pub colspan: u16,
pub rowspan: u16,
pub item_horiz: LayoutItem,
pub item_vert: LayoutItem,
}
#[derive(Debug, Clone)]
pub enum Layout {
Grid {
/// All the elements will be layout within that element.
elems: Vec<GridLayoutElement>,
geometry: LayoutGeometry,
/// When this GridLyout is actually the layout of a Dialog, then the cells start with all the buttons,
/// and this variable contains their roles. The string is actually one of the values from the sixtyfps_corelib::layout::DialogButtonRole
dialog_button_roles: Option<Vec<String>>,
/// Generate the array of BoxLayoutCellData form elements
BoxLayoutCellDataArray {
/// Either an expression of type BoxLayoutCellData, or an index to the repeater
elements: Vec<Either<Expression, usize>>,
/// The name for the local variable that stores the repeater indices
/// In other word, this expression has side effect and change that
repeater_indices: Option<String>,
},
}
*/
impl Expression {
pub fn default_value_for_type(ty: &Type) -> Option<Self> {
@ -213,7 +186,7 @@ impl Expression {
},
Type::Bool => Expression::BoolLiteral(false),
Type::Model => return None,
Type::PathElements => Expression::PathElements { elements: Default::default() },
Type::PathElements => Expression::PathEvents(Default::default()),
Type::Array(element_ty) => {
Expression::Array { element_ty: (**element_ty).clone(), values: vec![] }
}
@ -234,4 +207,51 @@ impl Expression {
}
})
}
pub fn ty(&self) -> Type {
match self {
Self::StringLiteral(_) => Type::String,
Self::NumberLiteral(_) => Type::Float32,
Self::BoolLiteral(_) => Type::Bool,
Self::PropertyReference(_) => todo!(),
Self::FunctionParameterReference { .. } => todo!(),
Self::StoreLocalVariable { .. } => Type::Void,
Self::ReadLocalVariable { ty, .. } => ty.clone(),
Self::StructFieldAccess { base, name } => match base.ty() {
Type::Struct { fields, .. } => fields[name].clone(),
_ => unreachable!(),
},
Self::Cast { to, .. } => to.clone(),
Self::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty()),
Self::BuiltinFunctionCall { function, .. } => match function.ty() {
Type::Function { return_type, .. } => *return_type,
_ => unreachable!(),
},
Self::CallBackCall { .. } => todo!(),
Self::ExtraBuiltinFunctionCall { .. } => todo!(),
Self::PropertyAssignment { .. } => Type::Void,
Self::ModelDataAssignment { .. } => Type::Void,
Self::BinaryExpression { lhs, rhs: _, op } => {
if crate::expression_tree::operator_class(*op) != OperatorClass::ArithmeticOp {
Type::Bool
} else {
lhs.ty()
}
}
Self::UnaryOp { sub, .. } => sub.ty(),
Self::ImageReference { .. } => Type::Image,
Self::Condition { true_expr, .. } => true_expr.ty(),
Self::Array { element_ty, .. } => Type::Array(element_ty.clone().into()),
Self::Struct { ty, .. } => ty.clone(),
Self::PathEvents { .. } => todo!(),
Self::EasingCurve(_) => Type::Easing,
Self::LinearGradient { .. } => Type::Brush,
Self::EnumerationValue(e) => Type::Enumeration(e.enumeration.clone()),
Self::ReturnStatement(_) => Type::Invalid,
Self::LayoutCacheAccess { .. } => crate::layout::layout_info_type(),
Self::BoxLayoutCellDataArray { .. } => {
Type::Array(Box::new(crate::layout::layout_info_type()))
}
}
}
}

View file

@ -1,31 +1,34 @@
// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
use std::collections::HashMap;
use super::Expression;
use crate::langtype::{NativeClass, Type};
use std::collections::BTreeMap;
use std::num::NonZeroUsize;
use std::rc::Rc;
use crate::langtype::Type;
use super::Expression;
// Index in the `SubComponent::properties`
pub type PropertyIndex = usize;
#[derive(Debug)]
pub enum Animation {
/// The expression is a Struct with the animation fields
Static(Expression),
Transition(Expression),
}
#[derive(Debug)]
pub struct BindingExpression {
pub expression: Expression,
pub animation: Option<Animation>,
}
#[derive(Debug)]
pub struct GlobalComponent {
pub name: String,
pub properties: Vec<Property>,
pub init_values: Vec<Option<Expression>>,
pub const_properties: Vec<bool>,
}
/// a Reference to a property, in the context of a SubComponent
@ -41,12 +44,14 @@ pub enum PropertyReference {
Global { global_index: usize, property_index: usize },
}
#[derive(Debug)]
pub struct Property {
pub name: String,
pub ty: Type,
//pub binding: Option<BindingExpression>,
}
#[derive(Debug)]
pub struct RepeatedElement {
pub model: Expression,
/// Within the sub_tree's root component
@ -56,41 +61,52 @@ pub struct RepeatedElement {
pub sub_tree: ItemTree,
}
pub struct ItemType {
// cpp_name: String,
// rust_name: String,
// cpp_init_function: String,
// mouse_function: String,
// extra_data_type: String,
}
#[derive(Debug)]
pub struct Item {
pub ty: Rc<ItemType>,
pub ty: Rc<NativeClass>,
pub name: String,
/// When this is true, this item does not need to be created because it is
/// already in the flickable.
/// The Item::name is the same as the flickable, and ty is Rectangle
pub is_flickable_viewport: bool,
}
#[derive(Debug)]
pub struct TreeNode {
pub sub_component_path: Vec<usize>,
pub item_index: usize,
pub repeated: bool,
pub children: Vec<TreeNode>,
}
#[derive(Debug)]
pub struct SubComponent {
pub name: String,
pub properties: Vec<Property>,
pub items: Vec<Item>,
pub repeated: Vec<RepeatedElement>,
pub popup_windows: Vec<ItemTree>,
pub sub_components: Vec<SubComponentInstance>,
pub property_init: Vec<(PropertyReference, BindingExpression)>,
pub two_way_bindings: Vec<(PropertyReference, PropertyReference)>,
pub const_properties: Vec<PropertyReference>,
}
pub struct SubComponentInstance {
pub ty: Rc<SubComponent>,
pub name: String,
//pub property_values: Vec<(PropertyReference, BindingExpression)>,
}
impl std::fmt::Debug for SubComponentInstance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ty.name)
}
}
#[derive(Debug)]
pub struct ItemTree {
pub root: SubComponentInstance,
pub root: SubComponent,
pub tree: TreeNode,
/// This tree has a parent. e.g: it is a Repeater or a PopupMenu whose property can access
/// the parent ItemTree.
@ -98,8 +114,10 @@ pub struct ItemTree {
pub parent_context: Option<String>,
}
#[derive(Debug)]
pub struct PublicComponent {
pub public_properties: BTreeMap<String, (Type, PropertyReference)>,
pub item_tree: ItemTree,
pub sub_components: HashMap<String, Rc<SubComponent>>,
pub sub_components: BTreeMap<String, Rc<SubComponent>>,
pub globals: Vec<GlobalComponent>,
}

View file

@ -2,13 +2,17 @@
// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::num::NonZeroUsize;
use std::rc::{Rc, Weak};
use super::lower_to_item_tree::{LoweredSubComponentMapping, LoweringState};
use itertools::Either;
use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
use super::{Animation, PropertyReference};
use crate::expression_tree::Expression as tree_Expression;
use crate::langtype::Type;
use crate::expression_tree::{BuiltinFunction, Expression as tree_Expression};
use crate::langtype::{EnumerationValue, Type};
use crate::layout::Orientation;
use crate::llr::Expression as llr_Expression;
use crate::namedreference::NamedReference;
use crate::object_tree::{Element, ElementRc, PropertyAnimation};
@ -21,11 +25,7 @@ pub struct ExpressionContext<'a> {
}
impl ExpressionContext<'_> {
pub fn map_property_reference(
&self,
from: &NamedReference,
state: &LoweringState,
) -> Option<PropertyReference> {
pub fn map_property_reference(&self, from: &NamedReference) -> Option<PropertyReference> {
let element = from.element();
let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
if !enclosing.is_global() {
@ -36,13 +36,15 @@ impl ExpressionContext<'_> {
level += 1;
}
if let Some(level) = NonZeroUsize::new(level) {
PropertyReference::InParent {
return Some(PropertyReference::InParent {
level,
parent_reference: Box::new(map.mapping.map_property_reference(from, state)?),
};
parent_reference: Box::new(
map.mapping.map_property_reference(from, self.state)?,
),
});
}
}
self.mapping.map_property_reference(from, state)
self.mapping.map_property_reference(from, self.state)
}
}
@ -52,20 +54,34 @@ pub fn lower_expression(
) -> Option<llr_Expression> {
match expression {
tree_Expression::Invalid => None,
tree_Expression::Uncompiled(_) => None,
tree_Expression::Uncompiled(_) => panic!(),
tree_Expression::StringLiteral(s) => Some(llr_Expression::StringLiteral(s.clone())),
tree_Expression::NumberLiteral(n, _) => Some(llr_Expression::NumberLiteral(*n)),
tree_Expression::NumberLiteral(n, unit) => {
Some(llr_Expression::NumberLiteral(unit.normalize(*n)))
}
tree_Expression::BoolLiteral(b) => Some(llr_Expression::BoolLiteral(*b)),
tree_Expression::CallbackReference(nr) => Some(llr_Expression::PropertyReference(
ctx.mapping.map_property_reference(nr, ctx.state)?,
)),
tree_Expression::PropertyReference(nr) => Some(llr_Expression::PropertyReference(
ctx.mapping.map_property_reference(nr, ctx.state)?,
)),
tree_Expression::BuiltinFunctionReference(_, _) => todo!(),
tree_Expression::MemberFunction { .. } => None,
tree_Expression::BuiltinMacroReference(_, _) => None,
tree_Expression::ElementReference(_) => todo!(),
tree_Expression::CallbackReference(nr) => {
Some(llr_Expression::PropertyReference(ctx.map_property_reference(nr)?))
}
tree_Expression::PropertyReference(nr) => {
Some(llr_Expression::PropertyReference(ctx.map_property_reference(nr)?))
}
tree_Expression::BuiltinFunctionReference(_, _) => panic!(),
tree_Expression::MemberFunction { .. } => panic!(),
tree_Expression::BuiltinMacroReference(_, _) => panic!(),
tree_Expression::ElementReference(e) => {
// We map an element reference to a reference to the property "" inside that native item
Some(llr_Expression::PropertyReference(
ctx.mapping
.map_property_reference(
&NamedReference::new(&e.upgrade().unwrap(), ""),
ctx.state,
)
.expect(
"this should be a reference to a native item and it should always exist",
),
))
}
tree_Expression::RepeaterIndexReference { element } => {
repeater_special_property(element, ctx.component, 1)
}
@ -97,20 +113,26 @@ pub fn lower_expression(
tree_Expression::CodeBlock(expr) => Some(llr_Expression::CodeBlock(
expr.iter().map(|e| lower_expression(e, ctx)).collect::<Option<_>>()?,
)),
tree_Expression::FunctionCall { function, arguments, .. } => {
Some(llr_Expression::FunctionCall {
function: Box::new(lower_expression(function, ctx)?),
arguments: arguments
.iter()
.map(|e| lower_expression(e, ctx))
.collect::<Option<_>>()?,
})
}
tree_Expression::SelfAssignment { lhs, rhs, op } => Some(llr_Expression::SelfAssignment {
lhs: Box::new(lower_expression(lhs, ctx)?),
rhs: Box::new(lower_expression(rhs, ctx)?),
op: *op,
}),
tree_Expression::FunctionCall { function, arguments, .. } => match &**function {
tree_Expression::BuiltinFunctionReference(BuiltinFunction::ShowPopupWindow, _) => {
lower_show_popup(arguments, ctx)
}
tree_Expression::BuiltinFunctionReference(f, _) => {
let arguments =
arguments.iter().map(|e| lower_expression(e, ctx)).collect::<Option<_>>()?;
Some(llr_Expression::BuiltinFunctionCall { function: *f, arguments })
}
tree_Expression::CallbackReference(nr) => {
let arguments =
arguments.iter().map(|e| lower_expression(e, ctx)).collect::<Option<_>>()?;
Some(llr_Expression::CallBackCall {
callback: ctx.map_property_reference(nr)?,
arguments,
})
}
_ => panic!("not calling a function"),
},
tree_Expression::SelfAssignment { lhs, rhs, op } => lower_assignment(lhs, rhs, *op, ctx),
tree_Expression::BinaryExpression { lhs, rhs, op } => {
Some(llr_Expression::BinaryExpression {
lhs: Box::new(lower_expression(lhs, ctx)?),
@ -142,10 +164,7 @@ pub fn lower_expression(
.map(|(s, e)| Some((s.clone(), lower_expression(e, ctx)?)))
.collect::<Option<_>>()?,
}),
tree_Expression::PathElements { elements } => match elements {
crate::expression_tree::Path::Elements(_) => todo!(),
crate::expression_tree::Path::Events(_) => todo!(),
},
tree_Expression::PathElements { elements } => compile_path(elements, ctx),
tree_Expression::EasingCurve(x) => Some(llr_Expression::EasingCurve(x.clone())),
tree_Expression::LinearGradient { angle, stops } => Some(llr_Expression::LinearGradient {
angle: Box::new(lower_expression(angle, ctx)?),
@ -170,8 +189,98 @@ pub fn lower_expression(
.map(Box::new),
})
}
tree_Expression::ComputeLayoutInfo(_, _) => todo!(),
tree_Expression::SolveLayout(_, _) => todo!(),
tree_Expression::ComputeLayoutInfo(l, o) => compute_layout_info(l, *o, ctx),
tree_Expression::SolveLayout(l, o) => solve_layout(l, *o, ctx),
}
}
fn lower_assignment(
lhs: &tree_Expression,
rhs: &tree_Expression,
op: char,
ctx: &ExpressionContext,
) -> Option<llr_Expression> {
match lhs {
tree_Expression::PropertyReference(nr) => {
let rhs = lower_expression(rhs, ctx)?;
let property = ctx.map_property_reference(nr)?;
let value = if op == '=' {
rhs
} else {
llr_Expression::BinaryExpression {
lhs: llr_Expression::PropertyReference(property.clone()).into(),
rhs: rhs.into(),
op,
}
}
.into();
Some(llr_Expression::PropertyAssignment { property, value })
}
tree_Expression::StructFieldAccess { base, name } => {
let ty = base.ty();
static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
let unique_name = format!(
"struct_assignment{}",
COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
);
let s = tree_Expression::StoreLocalVariable {
name: unique_name.clone(),
value: base.clone(),
};
let lower_base =
tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
let mut values = HashMap::new();
match &ty {
Type::Struct { fields, .. } => {
for (field, _) in fields {
let e = if field != name {
tree_Expression::StructFieldAccess {
base: lower_base.clone().into(),
name: field.clone(),
}
} else if op == '=' {
rhs.clone()
} else {
tree_Expression::BinaryExpression {
lhs: tree_Expression::StructFieldAccess {
base: lower_base.clone().into(),
name: field.clone(),
}
.into(),
rhs: Box::new(rhs.clone()),
op,
}
};
values.insert(field.clone(), e);
}
}
_ => unreachable!(),
}
let new_value =
tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
lower_assignment(base, &new_value, '=', ctx)
}
tree_Expression::RepeaterModelReference { element } => {
let rhs = lower_expression(rhs, ctx)?;
let prop = repeater_special_property(element, ctx.component, 0)?;
let level = match &prop {
llr_Expression::PropertyReference(PropertyReference::InParent {
level, ..
}) => (*level).into(),
_ => 0,
};
let value = Box::new(if op == '=' {
rhs
} else {
llr_Expression::BinaryExpression { lhs: prop.into(), rhs: rhs.into(), op }
});
Some(llr_Expression::ModelDataAssignment { level, value })
}
_ => panic!("not a rvalue"),
}
}
@ -201,6 +310,39 @@ fn repeater_special_property(
Some(llr_Expression::PropertyReference(r))
}
fn lower_show_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> Option<llr_Expression> {
if let [tree_Expression::ElementReference(e)] = args {
let popup_window = e.upgrade().unwrap();
let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
let parent_component = pop_comp
.parent_element
.upgrade()
.unwrap()
.borrow()
.enclosing_component
.upgrade()
.unwrap();
let popup_list = parent_component.popup_windows.borrow();
let (popup_index, popup) = popup_list
.iter()
.enumerate()
.find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
.unwrap();
let x = llr_Expression::PropertyReference(ctx.map_property_reference(&popup.x)?);
let y = llr_Expression::PropertyReference(ctx.map_property_reference(&popup.y)?);
let item_ref = lower_expression(
&tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
ctx,
)?;
Some(llr_Expression::BuiltinFunctionCall {
function: BuiltinFunction::ShowPopupWindow,
arguments: vec![llr_Expression::NumberLiteral(popup_index as _), x, y, item_ref],
})
} else {
panic!("invalid arguments to ShowPopupWindow");
}
}
pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> Option<Animation> {
fn lower_animation_element(
a: &ElementRc,
@ -256,3 +398,458 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> Op
}
}
}
fn compute_layout_info(
l: &crate::layout::Layout,
o: Orientation,
ctx: &ExpressionContext,
) -> Option<llr_Expression> {
match l {
crate::layout::Layout::GridLayout(layout) => {
let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx)?;
let cells = grid_layout_cell_data(layout, o, ctx)?;
Some(llr_Expression::ExtraBuiltinFunctionCall {
function: "grid_layout_info".into(),
arguments: vec![cells, spacing, padding],
})
}
crate::layout::Layout::BoxLayout(layout) => {
let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx)?;
let (cells, alignment) = box_layout_data(layout, o, ctx, None)?;
if o == layout.orientation {
Some(llr_Expression::ExtraBuiltinFunctionCall {
function: "box_layout_info".into(),
arguments: vec![cells, spacing, padding, alignment],
})
} else {
Some(llr_Expression::ExtraBuiltinFunctionCall {
function: "box_layout_info_ortho".into(),
arguments: vec![cells, padding],
})
}
}
crate::layout::Layout::PathLayout(_) => unimplemented!(),
}
}
fn solve_layout(
l: &crate::layout::Layout,
o: Orientation,
ctx: &ExpressionContext,
) -> Option<llr_Expression> {
match l {
crate::layout::Layout::GridLayout(layout) => {
let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx)?;
let cells = grid_layout_cell_data(layout, o, ctx)?;
let size = layout_geometry_size(&layout.geometry.rect, o, ctx)?;
if let (Some(button_roles), Orientation::Horizontal) = (&layout.dialog_button_roles, o)
{
let cells_ty = cells.ty();
let e = crate::typeregister::DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone());
let roles = button_roles
.iter()
.map(|r| {
llr_Expression::EnumerationValue(EnumerationValue {
value: e.values.iter().position(|x| x == r).unwrap() as _,
enumeration: e.clone(),
})
})
.collect();
Some(llr_Expression::CodeBlock(vec![
llr_Expression::StoreLocalVariable {
name: "cells".into(),
value: Box::new(cells),
},
llr_Expression::ExtraBuiltinFunctionCall {
function: "reorder_dialog_button_layout".into(),
arguments: vec![
llr_Expression::ReadLocalVariable {
name: "cells".into(),
ty: cells_ty.clone(),
},
llr_Expression::Array {
element_ty: Type::Enumeration(e),
values: roles,
},
],
},
llr_Expression::ExtraBuiltinFunctionCall {
function: "solve_grid_layout".into(),
arguments: vec![make_struct(
"GridLayoutData".into(),
[
("size", Type::Float32, size),
("spacing", Type::Float32, spacing),
("padding", padding.ty(), padding),
(
"cells",
cells_ty.clone(),
llr_Expression::ReadLocalVariable {
name: "cells".into(),
ty: cells_ty,
},
),
],
)],
},
]))
} else {
Some(llr_Expression::ExtraBuiltinFunctionCall {
function: "solve_grid_layout".into(),
arguments: vec![make_struct(
"GridLayoutData".into(),
[
("size", Type::Float32, size),
("spacing", Type::Float32, spacing),
("padding", padding.ty(), padding),
("cells", cells.ty(), cells),
],
)],
})
}
}
crate::layout::Layout::BoxLayout(layout) => {
let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx)?;
let mut repeated_indices = String::new();
let (cells, alignment) = box_layout_data(layout, o, ctx, Some(&mut repeated_indices))?;
let size = layout_geometry_size(&layout.geometry.rect, o, ctx)?;
let data = make_struct(
"BoxLayoutData".into(),
[
("size", Type::Float32, size),
("spacing", Type::Float32, spacing),
("padding", padding.ty(), padding),
(
"alignment",
crate::typeregister::LAYOUT_ALIGNMENT_ENUM
.with(|e| Type::Enumeration(e.clone())),
alignment,
),
("cells", cells.ty(), cells),
],
);
if repeated_indices.is_empty() {
Some(llr_Expression::ExtraBuiltinFunctionCall {
function: "solve_grid_layout".into(),
arguments: vec![
data,
llr_Expression::Array { element_ty: Type::Int32, values: vec![] },
],
})
} else {
Some(llr_Expression::CodeBlock(vec![
llr_Expression::StoreLocalVariable {
name: repeated_indices.clone(),
value: llr_Expression::Array { element_ty: Type::Int32, values: vec![] }
.into(),
},
llr_Expression::ExtraBuiltinFunctionCall {
function: "solve_grid_layout".into(),
arguments: vec![
data,
llr_Expression::ReadLocalVariable {
name: repeated_indices,
ty: Type::Array(Type::Int32.into()),
},
],
},
]))
}
}
crate::layout::Layout::PathLayout(layout) => {
let width = layout_geometry_size(&layout.rect, Orientation::Horizontal, ctx)?;
let height = layout_geometry_size(&layout.rect, Orientation::Vertical, ctx)?;
let elements = compile_path(&layout.path, ctx)?;
let offset = if let Some(expr) = &layout.offset_reference {
llr_Expression::PropertyReference(ctx.map_property_reference(expr)?)
} else {
llr_Expression::NumberLiteral(0.)
};
let count = layout.elements.len(); // FIXME! repeater
Some(llr_Expression::ExtraBuiltinFunctionCall {
function: "solve_path_layout".into(),
arguments: vec![make_struct(
"PathLayoutData".into(),
[
("width", Type::Float32, width),
("height", Type::Float32, height),
("x", Type::Float32, llr_Expression::NumberLiteral(0.)),
("y", Type::Float32, llr_Expression::NumberLiteral(0.)),
("elements", elements.ty(), elements),
("offset", Type::Int32, offset),
("item_count", Type::Int32, llr_Expression::NumberLiteral(count as _)),
],
)],
})
}
}
}
fn box_layout_data(
layout: &crate::layout::BoxLayout,
orientation: Orientation,
ctx: &ExpressionContext,
repeater_indices: Option<&mut String>,
) -> Option<(llr_Expression, llr_Expression)> {
let alignment = if let Some(expr) = &layout.geometry.alignment {
llr_Expression::PropertyReference(ctx.map_property_reference(expr)?)
} else {
let e = crate::typeregister::LAYOUT_ALIGNMENT_ENUM.with(|e| e.clone());
llr_Expression::EnumerationValue(EnumerationValue {
value: e.default_value,
enumeration: e,
})
};
let repeater_count =
layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
let element_ty = Type::Struct {
fields: IntoIterator::into_iter([(
"constraint".to_string(),
crate::layout::layout_info_type(),
)])
.collect(),
name: Some("BoxLayoutCellData".into()),
node: None,
};
if repeater_count == 0 {
let cells = llr_Expression::Array {
values: layout
.elems
.iter()
.map(|li| {
let layout_info =
get_layout_info(&li.element, ctx, &li.constraints, orientation).unwrap();
make_struct(
"BoxLayoutCellData".into(),
[("constraint", crate::layout::layout_info_type(), layout_info)],
)
})
.collect(),
element_ty,
};
Some((cells, alignment))
} else {
let mut elements = vec![];
for item in &layout.elems {
if item.element.borrow().repeated.is_some() {
let repeater_index =
match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
LoweredElement::Repeated { repeated_index } => *repeated_index,
_ => panic!(),
};
elements.push(Either::Right(repeater_index))
} else {
let layout_info =
get_layout_info(&item.element, ctx, &item.constraints, orientation).unwrap();
elements.push(Either::Left(make_struct(
"BoxLayoutCellData".into(),
[("constraint", crate::layout::layout_info_type(), layout_info)],
)));
}
}
Some((
llr_Expression::BoxLayoutCellDataArray {
elements,
repeater_indices: repeater_indices.map(|ri| {
*ri = "repeater_indices".into();
(*ri).clone()
}),
},
alignment,
))
}
}
fn grid_layout_cell_data(
layout: &crate::layout::GridLayout,
orientation: Orientation,
ctx: &ExpressionContext,
) -> Option<llr_Expression> {
Some(llr_Expression::Array {
element_ty: Type::Struct {
fields: IntoIterator::into_iter([
("col_or_row".to_string(), Type::Int32),
("span".to_string(), Type::Int32),
("constraint".to_string(), crate::layout::layout_info_type()),
])
.collect(),
name: Some("GridLayoutCellData".into()),
node: None,
},
values: layout
.elems
.iter()
.map(|c| {
let (col_or_row, span) = c.col_or_row_and_span(orientation);
let layout_info =
get_layout_info(&c.item.element, ctx, &c.item.constraints, orientation)
.unwrap();
make_struct(
"BoxLayoutCellData".into(),
[
("constraint", crate::layout::layout_info_type(), layout_info),
("col_or_row", Type::Int32, llr_Expression::NumberLiteral(col_or_row as _)),
("span", Type::Int32, llr_Expression::NumberLiteral(span as _)),
],
)
})
.collect(),
})
}
fn generate_layout_padding_and_spacing(
layout_geometry: &crate::layout::LayoutGeometry,
orientation: Orientation,
ctx: &ExpressionContext,
) -> Option<(llr_Expression, llr_Expression)> {
let padding_prop = |expr| {
Some(if let Some(expr) = expr {
llr_Expression::PropertyReference(ctx.map_property_reference(expr)?)
} else {
llr_Expression::NumberLiteral(0.)
})
};
let spacing = padding_prop(layout_geometry.spacing.as_ref())?;
let (begin, end) = layout_geometry.padding.begin_end(orientation);
let padding = make_struct(
"Padding".into(),
[
("begin", Type::Float32, padding_prop(begin)?),
("end", Type::Float32, padding_prop(end)?),
],
);
Some((padding, spacing))
}
fn layout_geometry_size(
rect: &crate::layout::LayoutRect,
orientation: Orientation,
ctx: &ExpressionContext,
) -> Option<llr_Expression> {
match rect.size_reference(orientation) {
Some(nr) => Some(llr_Expression::PropertyReference(ctx.map_property_reference(nr)?)),
None => Some(llr_Expression::NumberLiteral(0.)),
}
}
fn get_layout_info(
elem: &ElementRc,
ctx: &ExpressionContext,
constraints: &crate::layout::LayoutConstraints,
orientation: Orientation,
) -> Option<llr_Expression> {
let layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop(orientation) {
llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop)?)
} else {
lower_expression(&crate::layout::implicit_layout_info_call(elem, orientation), ctx)?
};
if constraints.has_explicit_restrictions() {
llr_Expression::StoreLocalVariable {
name: "layout_info".into(),
value: layout_info.into(),
};
let ty = crate::layout::layout_info_type();
let fields = match &ty {
Type::Struct { fields, .. } => fields,
_ => panic!(),
};
let mut values = fields
.keys()
.map(|p| {
(
p.clone(),
llr_Expression::StructFieldAccess {
base: llr_Expression::ReadLocalVariable {
name: "layout_info".into(),
ty: ty.clone(),
}
.into(),
name: p.clone(),
},
)
})
.collect::<HashMap<_, _>>();
for (nr, s) in constraints.for_each_restrictions(orientation) {
values.insert(
s.into(),
llr_Expression::PropertyReference(ctx.map_property_reference(nr)?),
);
}
Some(llr_Expression::Struct { ty, values })
} else {
Some(layout_info)
}
}
fn compile_path(
path: &crate::expression_tree::Path,
ctx: &ExpressionContext,
) -> Option<llr_Expression> {
match path {
crate::expression_tree::Path::Elements(elements) => {
let converted_elements = elements
.iter()
.map(|element| {
let ty = Type::Struct {
fields: element
.element_type
.properties
.iter()
.map(|(k, v)| (k.clone(), v.ty.clone()))
.collect(),
name: element.element_type.native_class.cpp_type.clone(),
node: None,
};
llr_Expression::Struct {
ty,
values: element
.bindings
.iter()
.map(|(property, expr)| {
(
property.clone(),
lower_expression(&expr.borrow().expression, ctx).unwrap(),
)
})
.collect(),
}
})
.collect();
Some(llr_Expression::Cast {
from: llr_Expression::Array {
element_ty: Type::PathElements,
values: converted_elements,
}
.into(),
to: Type::PathElements,
})
}
crate::expression_tree::Path::Events(events) => {
Some(llr_Expression::PathEvents(events.clone()))
}
}
}
fn make_struct(
name: String,
it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
) -> llr_Expression {
let mut fields = BTreeMap::<String, Type>::new();
let mut values = HashMap::<String, llr_Expression>::new();
for (name, ty, expr) in it {
fields.insert(name.to_string(), ty);
values.insert(name.to_string(), expr);
}
llr_Expression::Struct { ty: Type::Struct { fields, name: Some(name), node: None }, values }
}

View file

@ -26,9 +26,23 @@ pub fn lower_to_item_tree(component: &Rc<Component>) -> PublicComponent {
}
let sc = lower_sub_component(component, &state, None);
let public_properties = component
.root_element
.borrow()
.property_declarations
.iter()
.filter(|(_, c)| c.expose_in_public_api)
.map(|(p, c)| {
let property_reference = sc
.mapping
.map_property_reference(&NamedReference::new(&component.root_element, p), &state)
.unwrap();
(p.clone(), (c.property_type.clone(), property_reference))
})
.collect();
let item_tree = ItemTree {
root: SubComponentInstance { ty: sc.sub_component.clone() },
tree: make_tree(&state, component),
tree: make_tree(&state, &component.root_element, &sc, &[]),
root: Rc::try_unwrap(sc.sub_component).unwrap(),
parent_context: None,
};
PublicComponent {
@ -39,6 +53,7 @@ pub fn lower_to_item_tree(component: &Rc<Component>) -> PublicComponent {
.into_iter()
.map(|(c, sc)| (c.id.clone(), sc.sub_component))
.collect(),
public_properties,
}
}
@ -51,13 +66,14 @@ pub struct LoweringState {
#[derive(Debug, Clone)]
pub enum LoweredElement {
SubComponent { sub_component_index: usize },
NativeItem { item_index: usize }, //property_mapping: HashMap<String, (Vec<usize>, PropertyIndex)>,
NativeItem { item_index: usize },
Repeated { repeated_index: usize },
}
#[derive(Default, Debug, Clone)]
pub struct LoweredSubComponentMapping {
element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
property_mapping: HashMap<NamedReference, PropertyReference>,
pub element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
pub property_mapping: HashMap<NamedReference, PropertyReference>,
}
impl LoweredSubComponentMapping {
@ -72,11 +88,20 @@ impl LoweredSubComponentMapping {
if let Some(x) = state.global_properties.get(&from) {
return Some(x.clone());
}
match self.element_mapping.get(&from.element().into())? {
let element = from.element();
if let Some(alias) = element
.borrow()
.property_declarations
.get(from.name())
.and_then(|x| x.is_alias.as_ref())
{
return state.map_property_reference(alias);
}
match self.element_mapping.get(&element.clone().into())? {
LoweredElement::SubComponent { sub_component_index } => {
if let Type::Component(base) = &from.element().borrow().base_type {
if let Type::Component(base) = &element.borrow().base_type {
return Some(property_reference_within_sub_component(
state.map_property_reference(NamedReference::new(
state.map_property_reference(&NamedReference::new(
&base.root_element,
from.name(),
))?,
@ -92,6 +117,7 @@ impl LoweredSubComponentMapping {
prop_name: from.name().into(),
});
}
LoweredElement::Repeated { .. } => unreachable!(),
}
}
}
@ -102,7 +128,7 @@ pub struct LoweredSubComponent {
}
impl LoweringState {
pub fn map_property_reference(&self, from: NamedReference) -> Option<PropertyReference> {
pub fn map_property_reference(&self, from: &NamedReference) -> Option<PropertyReference> {
if let Some(x) = self.global_properties.get(&from) {
return Some(x.clone());
}
@ -112,7 +138,7 @@ impl LoweringState {
.sub_components
.get(&element.borrow().enclosing_component.upgrade().unwrap().into())?;
enclosing.mapping.map_property_reference(&from, self)
enclosing.mapping.map_property_reference(from, self)
}
}
@ -160,9 +186,11 @@ fn lower_sub_component(
properties: Default::default(),
items: Default::default(),
repeated: Default::default(),
popup_windows: Default::default(),
sub_components: Default::default(),
property_init: Default::default(),
two_way_bindings: Default::default(),
const_properties: Default::default(),
};
let mut mapping = LoweredSubComponentMapping::default();
let mut property_bindings = vec![];
@ -201,6 +229,10 @@ fn lower_sub_component(
}
}
if elem.repeated.is_some() {
mapping.element_mapping.insert(
element.clone().into(),
LoweredElement::Repeated { repeated_index: repeated.len() },
);
repeated.push(element.clone());
return;
}
@ -218,14 +250,16 @@ fn lower_sub_component(
continue;
}
let prop_ref = state
.map_property_reference(NamedReference::new(&comp.root_element, p))
.map_property_reference(&NamedReference::new(&comp.root_element, p))
.map(|x| property_reference_within_sub_component(x, sub_component_index));
property_bindings.push((prop_ref.unwrap(), b.borrow().clone()));
}
sub_component.sub_components.push(SubComponentInstance { ty });
sub_component
.sub_components
.push(SubComponentInstance { ty, name: elem.id.clone() });
}
Type::Native(_) => {
Type::Native(n) => {
let item_index = sub_component.items.len();
mapping
.element_mapping
@ -243,6 +277,11 @@ fn lower_sub_component(
b.borrow().clone(),
));
}
sub_component.items.push(Item {
ty: n.clone(),
name: elem.id.clone(),
is_flickable_viewport: elem.is_flickable_viewport,
})
}
_ => unreachable!(),
};
@ -266,9 +305,21 @@ fn lower_sub_component(
.push((prop.clone(), BindingExpression { expression, animation }))
}
}
for elem in repeated {
sub_component.repeated.push(lower_repeated_component(&elem, &ctx))
}
sub_component.repeated =
repeated.into_iter().map(|elem| lower_repeated_component(&elem, &ctx)).collect();
sub_component.popup_windows = component
.popup_windows
.borrow()
.iter()
.map(|popup| lower_popup_component(&popup.component, &ctx))
.collect();
crate::generator::for_each_const_properties(component, |elem, n| {
if let Some(x) = ctx.map_property_reference(&NamedReference::new(elem, n)) {
sub_component.const_properties.push(x);
}
});
LoweredSubComponent { sub_component: Rc::new(sub_component), mapping }
}
@ -277,12 +328,12 @@ fn lower_repeated_component(elem: &ElementRc, ctx: &ExpressionContext) -> Repeat
let component = e.base_type.as_component().clone();
let repeated = e.repeated.as_ref().unwrap();
let sc = ctx.state.sub_component(&component);
let sc = lower_sub_component(&component, &ctx.state, Some(ctx));
RepeatedElement {
model: super::lower_expression::lower_expression(&repeated.model, ctx).unwrap(),
sub_tree: ItemTree {
root: SubComponentInstance { ty: sc.sub_component.clone() },
tree: make_tree(ctx.state, &component),
tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
root: Rc::try_unwrap(sc.sub_component).unwrap(),
parent_context: Some(e.enclosing_component.upgrade().unwrap().id.clone()),
},
index_prop: 1,
@ -290,6 +341,26 @@ fn lower_repeated_component(elem: &ElementRc, ctx: &ExpressionContext) -> Repeat
}
}
fn lower_popup_component(component: &Rc<Component>, ctx: &ExpressionContext) -> ItemTree {
let sc = lower_sub_component(component, &ctx.state, Some(ctx));
ItemTree {
tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
root: Rc::try_unwrap(sc.sub_component).unwrap(),
parent_context: Some(
component
.parent_element
.upgrade()
.unwrap()
.borrow()
.enclosing_component
.upgrade()
.unwrap()
.id
.clone(),
),
}
}
fn lower_global(
global: &Rc<Component>,
global_index: usize,
@ -297,6 +368,7 @@ fn lower_global(
) -> GlobalComponent {
let mut mapping = LoweredSubComponentMapping::default();
let mut properties = vec![];
let mut const_properties = vec![];
for (p, x) in &global.root_element.borrow().property_declarations {
let property_index = properties.len();
@ -307,6 +379,7 @@ fn lower_global(
);
properties.push(Property { name: p.clone(), ty: x.property_type.clone() });
const_properties.push(nr.is_constant());
state
.global_properties
.insert(nr.clone(), PropertyReference::Global { global_index, property_index });
@ -330,9 +403,45 @@ fn lower_global(
}
}
GlobalComponent { name: global.id.clone(), properties, init_values }
GlobalComponent { name: global.id.clone(), properties, init_values, const_properties }
}
fn make_tree(state: &LoweringState, component: &Rc<Component>) -> TreeNode {
todo!()
fn make_tree(
state: &LoweringState,
element: &ElementRc,
component: &LoweredSubComponent,
sub_component_path: &[usize],
) -> TreeNode {
let e = element.borrow();
let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
LoweredElement::SubComponent { sub_component_index } => {
let sub_component = e.sub_component().unwrap();
let new_sub_component_path = sub_component_path
.iter()
.copied()
.chain(std::iter::once(*sub_component_index))
.collect::<Vec<_>>();
let mut tree_node = make_tree(
state,
&sub_component.root_element,
state.sub_component(sub_component),
&new_sub_component_path,
);
tree_node.children.extend(children);
tree_node
}
LoweredElement::NativeItem { item_index } => TreeNode {
sub_component_path: sub_component_path.into(),
item_index: *item_index,
children: children.collect(),
repeated: false,
},
LoweredElement::Repeated { repeated_index } => TreeNode {
sub_component_path: sub_component_path.into(),
item_index: *repeated_index,
children: vec![],
repeated: true,
},
}
}

View file

@ -38,8 +38,8 @@ const RESERVED_LAYOUT_PROPERTIES: &[(&str, Type)] = &[
];
thread_local! {
pub static DIALOG_BUTTON_ROLE_ENUM: Type =
Type::Enumeration(Rc::new(Enumeration {
pub static DIALOG_BUTTON_ROLE_ENUM: Rc<Enumeration> =
Rc::new(Enumeration {
name: "DialogButtonRole".into(),
values: IntoIterator::into_iter([
"none".to_owned(),
@ -52,7 +52,16 @@ thread_local! {
])
.collect(),
default_value: 0,
}));
});
pub static LAYOUT_ALIGNMENT_ENUM: Rc<Enumeration> =
Rc::new(Enumeration {
name: "LayoutAlignment".into(),
values: IntoIterator::into_iter(
["stretch", "center", "start", "end", "space-between", "space-around"]
).map(String::from).collect(),
default_value: 0,
});
}
const RESERVED_OTHER_PROPERTIES: &[(&str, Type)] = &[
@ -79,7 +88,7 @@ pub fn reserved_properties() -> impl Iterator<Item = (&'static str, Type)> {
.chain(IntoIterator::into_iter([
("forward-focus", Type::ElementReference),
("focus", BuiltinFunction::SetFocusItem.ty()),
("dialog-button-role", DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone())),
("dialog-button-role", Type::Enumeration(DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone()))),
]))
}
@ -179,10 +188,6 @@ impl TypeRegister {
declare_enum("TextVerticalAlignment", &["top", "center", "bottom"]);
declare_enum("TextWrap", &["no-wrap", "word-wrap"]);
declare_enum("TextOverflow", &["clip", "elide"]);
declare_enum(
"LayoutAlignment",
&["stretch", "center", "start", "end", "space-between", "space-around"],
);
declare_enum("ImageFit", &["fill", "contain", "cover"]);
declare_enum("ImageRendering", &["smooth", "pixelated"]);
declare_enum("EventResult", &["reject", "accept"]);
@ -229,7 +234,10 @@ impl TypeRegister {
);
declare_enum("PointerEventKind", &["cancel", "down", "up"]);
declare_enum("PointerEventButton", &["none", "left", "right", "middle"]);
register.insert_type(DIALOG_BUTTON_ROLE_ENUM.with(|x| x.clone()));
DIALOG_BUTTON_ROLE_ENUM
.with(|e| register.insert_type_with_name(Type::Enumeration(e.clone()), e.name.clone()));
LAYOUT_ALIGNMENT_ENUM
.with(|e| register.insert_type_with_name(Type::Enumeration(e.clone()), e.name.clone()));
register.supported_property_animation_types.insert(Type::Float32.to_string());
register.supported_property_animation_types.insert(Type::Int32.to_string());