1use std::cell::RefCell;
5use std::collections::{BTreeMap, HashMap};
6use std::num::NonZeroUsize;
7use std::rc::{Rc, Weak};
8
9use itertools::Either;
10
11use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
12use super::{Animation, PropertyReference};
13use crate::expression_tree::{BuiltinFunction, Expression as tree_Expression};
14use crate::langtype::{EnumerationValue, Type};
15use crate::layout::Orientation;
16use crate::llr::Expression as llr_Expression;
17use crate::namedreference::NamedReference;
18use crate::object_tree::{Element, ElementRc, PropertyAnimation};
19
20pub struct ExpressionContext<'a> {
21 pub component: &'a Rc<crate::object_tree::Component>,
22 pub mapping: &'a LoweredSubComponentMapping,
23 pub state: &'a LoweringState,
24 pub parent: Option<&'a ExpressionContext<'a>>,
25}
26
27impl ExpressionContext<'_> {
28 pub fn map_property_reference(&self, from: &NamedReference) -> PropertyReference {
29 let element = from.element();
30 let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
31 if !enclosing.is_global() {
32 let mut map = self;
33 let mut level = 0;
34 while !Rc::ptr_eq(&enclosing, map.component) {
35 map = map.parent.unwrap();
36 level += 1;
37 }
38 if let Some(level) = NonZeroUsize::new(level) {
39 return PropertyReference::InParent {
40 level,
41 parent_reference: Box::new(
42 map.mapping.map_property_reference(from, self.state),
43 ),
44 };
45 }
46 }
47 self.mapping.map_property_reference(from, self.state)
48 }
49}
50
51impl super::TypeResolutionContext for ExpressionContext<'_> {
52 fn property_ty(&self, _: &PropertyReference) -> &Type {
53 todo!()
54 }
55}
56
57pub fn lower_expression(
58 expression: &tree_Expression,
59 ctx: &ExpressionContext<'_>,
60) -> llr_Expression {
61 match expression {
62 tree_Expression::Invalid => {
63 panic!("internal error, encountered invalid expression at code generation time")
64 }
65 tree_Expression::Uncompiled(_) => panic!(),
66 tree_Expression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
67 tree_Expression::NumberLiteral(n, unit) => {
68 llr_Expression::NumberLiteral(unit.normalize(*n))
69 }
70 tree_Expression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
71 tree_Expression::CallbackReference(nr) => {
72 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
73 }
74 tree_Expression::PropertyReference(nr) => {
75 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
76 }
77 tree_Expression::BuiltinFunctionReference(_, _) => panic!(),
78 tree_Expression::MemberFunction { .. } => panic!(),
79 tree_Expression::BuiltinMacroReference(_, _) => panic!(),
80 tree_Expression::ElementReference(e) => {
81 llr_Expression::PropertyReference(
83 ctx.map_property_reference(&NamedReference::new(&e.upgrade().unwrap(), "")),
84 )
85 }
86 tree_Expression::RepeaterIndexReference { element } => {
87 repeater_special_property(element, ctx.component, 1)
88 }
89 tree_Expression::RepeaterModelReference { element } => {
90 repeater_special_property(element, ctx.component, 0)
91 }
92 tree_Expression::FunctionParameterReference { index, .. } => {
93 llr_Expression::FunctionParameterReference { index: *index }
94 }
95 tree_Expression::StoreLocalVariable { name, value } => llr_Expression::StoreLocalVariable {
96 name: name.clone(),
97 value: Box::new(lower_expression(value, ctx)),
98 },
99 tree_Expression::ReadLocalVariable { name, ty } => {
100 llr_Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }
101 }
102 tree_Expression::StructFieldAccess { base, name } => llr_Expression::StructFieldAccess {
103 base: Box::new(lower_expression(base, ctx)),
104 name: name.clone(),
105 },
106 tree_Expression::ArrayIndex { array, index } => llr_Expression::ArrayIndex {
107 array: Box::new(lower_expression(array, ctx)),
108 index: Box::new(lower_expression(index, ctx)),
109 },
110 tree_Expression::Cast { from, to } => {
111 llr_Expression::Cast { from: Box::new(lower_expression(from, ctx)), to: to.clone() }
112 }
113 tree_Expression::CodeBlock(expr) => {
114 llr_Expression::CodeBlock(expr.iter().map(|e| lower_expression(e, ctx)).collect::<_>())
115 }
116 tree_Expression::FunctionCall { function, arguments, .. } => match &**function {
117 tree_Expression::BuiltinFunctionReference(BuiltinFunction::ShowPopupWindow, _) => {
118 lower_show_popup(arguments, ctx)
119 }
120 tree_Expression::BuiltinFunctionReference(f, _) => {
121 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
122 llr_Expression::BuiltinFunctionCall { function: *f, arguments }
123 }
124 tree_Expression::CallbackReference(nr) => {
125 let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
126 llr_Expression::CallBackCall { callback: ctx.map_property_reference(nr), arguments }
127 }
128 _ => panic!("not calling a function"),
129 },
130 tree_Expression::SelfAssignment { lhs, rhs, op } => lower_assignment(lhs, rhs, *op, ctx),
131 tree_Expression::BinaryExpression { lhs, rhs, op } => llr_Expression::BinaryExpression {
132 lhs: Box::new(lower_expression(lhs, ctx)),
133 rhs: Box::new(lower_expression(rhs, ctx)),
134 op: *op,
135 },
136 tree_Expression::UnaryOp { sub, op } => {
137 llr_Expression::UnaryOp { sub: Box::new(lower_expression(sub, ctx)), op: *op }
138 }
139 tree_Expression::ImageReference { resource_ref, .. } => {
140 llr_Expression::ImageReference { resource_ref: resource_ref.clone() }
141 }
142 tree_Expression::Condition { condition, true_expr, false_expr } => {
143 llr_Expression::Condition {
144 condition: Box::new(lower_expression(condition, ctx)),
145 true_expr: Box::new(lower_expression(true_expr, ctx)),
146 false_expr: lower_expression(false_expr, ctx).into(),
147 }
148 }
149 tree_Expression::Array { element_ty, values } => llr_Expression::Array {
150 element_ty: element_ty.clone(),
151 values: values.iter().map(|e| lower_expression(e, ctx)).collect::<_>(),
152 as_model: true,
153 },
154 tree_Expression::Struct { ty, values } => llr_Expression::Struct {
155 ty: ty.clone(),
156 values: values
157 .iter()
158 .map(|(s, e)| (s.clone(), lower_expression(e, ctx)))
159 .collect::<_>(),
160 },
161 tree_Expression::PathData(data) => compile_path(data, ctx),
162 tree_Expression::EasingCurve(x) => llr_Expression::EasingCurve(x.clone()),
163 tree_Expression::LinearGradient { angle, stops } => llr_Expression::LinearGradient {
164 angle: Box::new(lower_expression(angle, ctx)),
165 stops: stops
166 .iter()
167 .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
168 .collect::<_>(),
169 },
170 tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
171 tree_Expression::ReturnStatement(x) => {
172 llr_Expression::ReturnStatement(x.as_ref().map(|e| lower_expression(e, ctx).into()))
173 }
174 tree_Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
175 llr_Expression::LayoutCacheAccess {
176 layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
177 index: *index,
178 repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
179 }
180 }
181 tree_Expression::ComputeLayoutInfo(l, o) => compute_layout_info(l, *o, ctx),
182 tree_Expression::SolveLayout(l, o) => solve_layout(l, *o, ctx),
183 }
184}
185
186fn lower_assignment(
187 lhs: &tree_Expression,
188 rhs: &tree_Expression,
189 op: char,
190 ctx: &ExpressionContext,
191) -> llr_Expression {
192 match lhs {
193 tree_Expression::PropertyReference(nr) => {
194 let rhs = lower_expression(rhs, ctx);
195 let property = ctx.map_property_reference(nr);
196 let value = if op == '=' {
197 rhs
198 } else {
199 llr_Expression::BinaryExpression {
200 lhs: llr_Expression::PropertyReference(property.clone()).into(),
201 rhs: rhs.into(),
202 op,
203 }
204 }
205 .into();
206 llr_Expression::PropertyAssignment { property, value }
207 }
208 tree_Expression::StructFieldAccess { base, name } => {
209 let ty = base.ty();
210
211 static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
212 let unique_name = format!(
213 "struct_assignment{}",
214 COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
215 );
216 let s = tree_Expression::StoreLocalVariable {
217 name: unique_name.clone(),
218 value: base.clone(),
219 };
220 let lower_base =
221 tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
222 let mut values = HashMap::new();
223 match &ty {
224 Type::Struct { fields, .. } => {
225 for (field, _) in fields {
226 let e = if field != name {
227 tree_Expression::StructFieldAccess {
228 base: lower_base.clone().into(),
229 name: field.clone(),
230 }
231 } else if op == '=' {
232 rhs.clone()
233 } else {
234 tree_Expression::BinaryExpression {
235 lhs: tree_Expression::StructFieldAccess {
236 base: lower_base.clone().into(),
237 name: field.clone(),
238 }
239 .into(),
240 rhs: Box::new(rhs.clone()),
241 op,
242 }
243 };
244 values.insert(field.clone(), e);
245 }
246 }
247 _ => unreachable!(),
248 }
249 let new_value =
250 tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
251 lower_assignment(base, &new_value, '=', ctx)
252 }
253 tree_Expression::RepeaterModelReference { element } => {
254 let rhs = lower_expression(rhs, ctx);
255 let prop = repeater_special_property(element, ctx.component, 0);
256
257 let level = match &prop {
258 llr_Expression::PropertyReference(PropertyReference::InParent {
259 level, ..
260 }) => (*level).into(),
261 _ => 0,
262 };
263
264 let value = Box::new(if op == '=' {
265 rhs
266 } else {
267 llr_Expression::BinaryExpression { lhs: prop.into(), rhs: rhs.into(), op }
268 });
269
270 llr_Expression::ModelDataAssignment { level, value }
271 }
272 tree_Expression::ArrayIndex { array, index } => {
273 let rhs = lower_expression(rhs, ctx);
274 let array = Box::new(lower_expression(array, ctx));
275 let index = Box::new(lower_expression(index, ctx));
276 let value = Box::new(if op == '=' {
277 rhs
278 } else {
279 llr_Expression::BinaryExpression {
282 lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
283 .into(),
284 rhs: rhs.into(),
285 op,
286 }
287 });
288
289 llr_Expression::ArrayIndexAssignment { array, index, value }
290 }
291 _ => panic!("not a rvalue"),
292 }
293}
294
295fn repeater_special_property(
296 element: &Weak<RefCell<Element>>,
297 component: &Rc<crate::object_tree::Component>,
298 property_index: usize,
299) -> llr_Expression {
300 let mut r = PropertyReference::Local { sub_component_path: vec![], property_index };
301 let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
302 let mut level = 0;
303 let mut component = component.clone();
304 while !Rc::ptr_eq(&enclosing, &component) {
305 component = component
306 .parent_element
307 .upgrade()
308 .unwrap()
309 .borrow()
310 .enclosing_component
311 .upgrade()
312 .unwrap();
313 level += 1;
314 }
315 if let Some(level) = NonZeroUsize::new(level - 1) {
316 r = PropertyReference::InParent { level, parent_reference: Box::new(r) };
317 }
318 llr_Expression::PropertyReference(r)
319}
320
321fn lower_show_popup(args: &[tree_Expression], ctx: &ExpressionContext) -> llr_Expression {
322 if let [tree_Expression::ElementReference(e)] = args {
323 let popup_window = e.upgrade().unwrap();
324 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
325 let parent_component = pop_comp
326 .parent_element
327 .upgrade()
328 .unwrap()
329 .borrow()
330 .enclosing_component
331 .upgrade()
332 .unwrap();
333 let popup_list = parent_component.popup_windows.borrow();
334 let (popup_index, popup) = popup_list
335 .iter()
336 .enumerate()
337 .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
338 .unwrap();
339 let x = llr_Expression::PropertyReference(ctx.map_property_reference(&popup.x));
340 let y = llr_Expression::PropertyReference(ctx.map_property_reference(&popup.y));
341 let item_ref = lower_expression(
342 &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
343 ctx,
344 );
345 llr_Expression::BuiltinFunctionCall {
346 function: BuiltinFunction::ShowPopupWindow,
347 arguments: vec![llr_Expression::NumberLiteral(popup_index as _), x, y, item_ref],
348 }
349 } else {
350 panic!("invalid arguments to ShowPopupWindow");
351 }
352}
353
354pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> Animation {
355 fn lower_animation_element(a: &ElementRc, ctx: &ExpressionContext<'_>) -> llr_Expression {
356 llr_Expression::Struct {
357 values: animation_fields()
358 .map(|(k, ty)| {
359 let e = a.borrow().bindings.get(&k).map_or_else(
360 || llr_Expression::default_value_for_type(&ty).unwrap(),
361 |v| lower_expression(&v.borrow().expression, ctx),
362 );
363 (k, e)
364 })
365 .collect::<_>(),
366 ty: animation_ty(),
367 }
368 }
369
370 fn animation_fields() -> impl Iterator<Item = (String, Type)> {
371 IntoIterator::into_iter([
372 ("duration".to_string(), Type::Int32),
373 ("loop-count".to_string(), Type::Int32),
374 ("easing".to_string(), Type::Easing),
375 ("delay".to_string(), Type::Int32),
376 ])
377 }
378
379 fn animation_ty() -> Type {
380 Type::Struct {
381 fields: animation_fields().collect(),
382 name: Some("PropertyAnimation".into()),
383 node: None,
384 }
385 }
386
387 match a {
388 PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
389 PropertyAnimation::Transition { state_ref, animations } => {
390 let set_state = llr_Expression::StoreLocalVariable {
391 name: "state".into(),
392 value: Box::new(lower_expression(state_ref, ctx)),
393 };
394 let animation_ty = animation_ty();
395 let mut get_anim = llr_Expression::default_value_for_type(&animation_ty).unwrap();
396 for tr in animations.iter().rev() {
397 let condition = lower_expression(
398 &tr.condition(tree_Expression::ReadLocalVariable {
399 name: "state".into(),
400 ty: state_ref.ty(),
401 }),
402 ctx,
403 );
404 get_anim = llr_Expression::Condition {
405 condition: Box::new(condition),
406 true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
407 false_expr: Box::new(get_anim),
408 }
409 }
410 let result = llr_Expression::Struct {
411 ty: Type::Struct {
413 fields: IntoIterator::into_iter([
414 ("0".to_string(), animation_ty),
415 ("1".to_string(), Type::Invalid),
417 ])
418 .collect(),
419 name: None,
420 node: None,
421 },
422 values: IntoIterator::into_iter([
423 ("0".to_string(), get_anim),
424 (
425 "1".to_string(),
426 llr_Expression::StructFieldAccess {
427 base: llr_Expression::ReadLocalVariable {
428 name: "state".into(),
429 ty: state_ref.ty(),
430 }
431 .into(),
432 name: "change_time".into(),
433 },
434 ),
435 ])
436 .collect(),
437 };
438 Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
439 }
440 }
441}
442
443fn compute_layout_info(
444 l: &crate::layout::Layout,
445 o: Orientation,
446 ctx: &ExpressionContext,
447) -> llr_Expression {
448 match l {
449 crate::layout::Layout::GridLayout(layout) => {
450 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
451 let cells = grid_layout_cell_data(layout, o, ctx);
452 llr_Expression::ExtraBuiltinFunctionCall {
453 function: "grid_layout_info".into(),
454 arguments: vec![cells, spacing, padding],
455 return_ty: crate::layout::layout_info_type(),
456 }
457 }
458 crate::layout::Layout::BoxLayout(layout) => {
459 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
460 let bld = box_layout_data(layout, o, ctx);
461 let sub_expression = if o == layout.orientation {
462 llr_Expression::ExtraBuiltinFunctionCall {
463 function: "box_layout_info".into(),
464 arguments: vec![bld.cells, spacing, padding, bld.alignment],
465 return_ty: crate::layout::layout_info_type(),
466 }
467 } else {
468 llr_Expression::ExtraBuiltinFunctionCall {
469 function: "box_layout_info_ortho".into(),
470 arguments: vec![bld.cells, padding],
471 return_ty: crate::layout::layout_info_type(),
472 }
473 };
474 match bld.compute_cells {
475 Some((cells_variable, elements)) => llr_Expression::BoxLayoutFunction {
476 cells_variable,
477 repeater_indices: None,
478 elements,
479 orientation: o,
480 sub_expression: Box::new(sub_expression),
481 },
482 None => sub_expression,
483 }
484 }
485 crate::layout::Layout::PathLayout(_) => unimplemented!(),
486 }
487}
488
489fn solve_layout(
490 l: &crate::layout::Layout,
491 o: Orientation,
492 ctx: &ExpressionContext,
493) -> llr_Expression {
494 match l {
495 crate::layout::Layout::GridLayout(layout) => {
496 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
497 let cells = grid_layout_cell_data(layout, o, ctx);
498 let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
499 if let (Some(button_roles), Orientation::Horizontal) = (&layout.dialog_button_roles, o)
500 {
501 let cells_ty = cells.ty(ctx);
502 let e = crate::typeregister::DIALOG_BUTTON_ROLE_ENUM.with(|e| e.clone());
503 let roles = button_roles
504 .iter()
505 .map(|r| {
506 llr_Expression::EnumerationValue(EnumerationValue {
507 value: e.values.iter().position(|x| x == r).unwrap() as _,
508 enumeration: e.clone(),
509 })
510 })
511 .collect();
512 llr_Expression::CodeBlock(vec![
513 llr_Expression::ComputeDialogLayoutCells {
514 cells_variable: "cells".into(),
515 roles: llr_Expression::Array {
516 element_ty: Type::Enumeration(e),
517 values: roles,
518 as_model: false,
519 }
520 .into(),
521 unsorted_cells: Box::new(cells),
522 },
523 llr_Expression::ExtraBuiltinFunctionCall {
524 function: "solve_grid_layout".into(),
525 arguments: vec![make_struct(
526 "GridLayoutData".into(),
527 [
528 ("size", Type::Float32, size),
529 ("spacing", Type::Float32, spacing),
530 ("padding", padding.ty(ctx), padding),
531 (
532 "cells",
533 cells_ty.clone(),
534 llr_Expression::ReadLocalVariable {
535 name: "cells".into(),
536 ty: cells_ty,
537 },
538 ),
539 ],
540 )],
541 return_ty: Type::LayoutCache,
542 },
543 ])
544 } else {
545 llr_Expression::ExtraBuiltinFunctionCall {
546 function: "solve_grid_layout".into(),
547 arguments: vec![make_struct(
548 "GridLayoutData".into(),
549 [
550 ("size", Type::Float32, size),
551 ("spacing", Type::Float32, spacing),
552 ("padding", padding.ty(ctx), padding),
553 ("cells", cells.ty(ctx), cells),
554 ],
555 )],
556 return_ty: Type::LayoutCache,
557 }
558 }
559 }
560 crate::layout::Layout::BoxLayout(layout) => {
561 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
562 let bld = box_layout_data(layout, o, ctx);
563 let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
564 let data = make_struct(
565 "BoxLayoutData".into(),
566 [
567 ("size", Type::Float32, size),
568 ("spacing", Type::Float32, spacing),
569 ("padding", padding.ty(ctx), padding),
570 (
571 "alignment",
572 crate::typeregister::LAYOUT_ALIGNMENT_ENUM
573 .with(|e| Type::Enumeration(e.clone())),
574 bld.alignment,
575 ),
576 ("cells", bld.cells.ty(ctx), bld.cells),
577 ],
578 );
579 match bld.compute_cells {
580 Some((cells_variable, elements)) => llr_Expression::BoxLayoutFunction {
581 cells_variable,
582 repeater_indices: Some("repeated_indices".into()),
583 elements,
584 orientation: o,
585 sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
586 function: "solve_box_layout".into(),
587 arguments: vec![
588 data,
589 llr_Expression::ReadLocalVariable {
590 name: "repeated_indices".into(),
591 ty: Type::Array(Type::Int32.into()),
592 },
593 ],
594 return_ty: Type::LayoutCache,
595 }),
596 },
597 None => llr_Expression::ExtraBuiltinFunctionCall {
598 function: "solve_box_layout".into(),
599 arguments: vec![
600 data,
601 llr_Expression::Array {
602 element_ty: Type::Int32,
603 values: vec![],
604 as_model: false,
605 },
606 ],
607 return_ty: Type::LayoutCache,
608 },
609 }
610 }
611 crate::layout::Layout::PathLayout(layout) => {
612 let width = layout_geometry_size(&layout.rect, Orientation::Horizontal, ctx);
613 let height = layout_geometry_size(&layout.rect, Orientation::Vertical, ctx);
614 let elements = compile_path(&layout.path, ctx);
615 let offset = if let Some(expr) = &layout.offset_reference {
616 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
617 } else {
618 llr_Expression::NumberLiteral(0.)
619 };
620
621 let repeater_indices =
623 llr_Expression::Array { element_ty: Type::Int32, values: vec![], as_model: false };
624 let count = layout.elements.len();
625 llr_Expression::ExtraBuiltinFunctionCall {
626 function: "solve_path_layout".into(),
627 arguments: vec![
628 make_struct(
629 "PathLayoutData".into(),
630 [
631 ("width", Type::Float32, width),
632 ("height", Type::Float32, height),
633 ("x", Type::Float32, llr_Expression::NumberLiteral(0.)),
634 ("y", Type::Float32, llr_Expression::NumberLiteral(0.)),
635 ("elements", elements.ty(ctx), elements),
636 ("offset", Type::Int32, offset),
637 ("item_count", Type::Int32, llr_Expression::NumberLiteral(count as _)),
638 ],
639 ),
640 repeater_indices,
641 ],
642 return_ty: Type::LayoutCache,
643 }
644 }
645 }
646}
647
648struct BoxLayoutDataResult {
649 alignment: llr_Expression,
650 cells: llr_Expression,
651 compute_cells: Option<(String, Vec<Either<llr_Expression, usize>>)>,
654}
655
656fn box_layout_data(
657 layout: &crate::layout::BoxLayout,
658 orientation: Orientation,
659 ctx: &ExpressionContext,
660) -> BoxLayoutDataResult {
661 let alignment = if let Some(expr) = &layout.geometry.alignment {
662 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
663 } else {
664 let e = crate::typeregister::LAYOUT_ALIGNMENT_ENUM.with(|e| e.clone());
665 llr_Expression::EnumerationValue(EnumerationValue {
666 value: e.default_value,
667 enumeration: e,
668 })
669 };
670
671 let repeater_count =
672 layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
673
674 let element_ty = Type::Struct {
675 fields: IntoIterator::into_iter([(
676 "constraint".to_string(),
677 crate::layout::layout_info_type(),
678 )])
679 .collect(),
680 name: Some("BoxLayoutCellData".into()),
681 node: None,
682 };
683
684 if repeater_count == 0 {
685 let cells = llr_Expression::Array {
686 values: layout
687 .elems
688 .iter()
689 .map(|li| {
690 let layout_info =
691 get_layout_info(&li.element, ctx, &li.constraints, orientation);
692 make_struct(
693 "BoxLayoutCellData".into(),
694 [("constraint", crate::layout::layout_info_type(), layout_info)],
695 )
696 })
697 .collect(),
698 element_ty,
699 as_model: false,
700 };
701 BoxLayoutDataResult { alignment, cells, compute_cells: None }
702 } else {
703 let mut elements = vec![];
704 for item in &layout.elems {
705 if item.element.borrow().repeated.is_some() {
706 let repeater_index =
707 match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
708 LoweredElement::Repeated { repeated_index } => *repeated_index,
709 _ => panic!(),
710 };
711 elements.push(Either::Right(repeater_index))
712 } else {
713 let layout_info =
714 get_layout_info(&item.element, ctx, &item.constraints, orientation);
715 elements.push(Either::Left(make_struct(
716 "BoxLayoutCellData".into(),
717 [("constraint", crate::layout::layout_info_type(), layout_info)],
718 )));
719 }
720 }
721 let cells = llr_Expression::ReadLocalVariable {
722 name: "cells".into(),
723 ty: Type::Array(Box::new(crate::layout::layout_info_type())),
724 };
725 BoxLayoutDataResult { alignment, cells, compute_cells: Some(("cells".into(), elements)) }
726 }
727}
728
729fn grid_layout_cell_data(
730 layout: &crate::layout::GridLayout,
731 orientation: Orientation,
732 ctx: &ExpressionContext,
733) -> llr_Expression {
734 llr_Expression::Array {
735 element_ty: grid_layout_cell_data_ty(),
736 values: layout
737 .elems
738 .iter()
739 .map(|c| {
740 let (col_or_row, span) = c.col_or_row_and_span(orientation);
741 let layout_info =
742 get_layout_info(&c.item.element, ctx, &c.item.constraints, orientation);
743
744 make_struct(
745 "GridLayoutCellData".into(),
746 [
747 ("constraint", crate::layout::layout_info_type(), layout_info),
748 ("col_or_row", Type::Int32, llr_Expression::NumberLiteral(col_or_row as _)),
749 ("span", Type::Int32, llr_Expression::NumberLiteral(span as _)),
750 ],
751 )
752 })
753 .collect(),
754 as_model: false,
755 }
756}
757
758pub(super) fn grid_layout_cell_data_ty() -> Type {
759 Type::Struct {
760 fields: IntoIterator::into_iter([
761 ("col_or_row".to_string(), Type::Int32),
762 ("span".to_string(), Type::Int32),
763 ("constraint".to_string(), crate::layout::layout_info_type()),
764 ])
765 .collect(),
766 name: Some("GridLayoutCellData".into()),
767 node: None,
768 }
769}
770
771fn generate_layout_padding_and_spacing(
772 layout_geometry: &crate::layout::LayoutGeometry,
773 orientation: Orientation,
774 ctx: &ExpressionContext,
775) -> (llr_Expression, llr_Expression) {
776 let padding_prop = |expr| {
777 if let Some(expr) = expr {
778 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
779 } else {
780 llr_Expression::NumberLiteral(0.)
781 }
782 };
783 let spacing = padding_prop(layout_geometry.spacing.as_ref());
784 let (begin, end) = layout_geometry.padding.begin_end(orientation);
785
786 let padding = make_struct(
787 "Padding".into(),
788 [("begin", Type::Float32, padding_prop(begin)), ("end", Type::Float32, padding_prop(end))],
789 );
790
791 (padding, spacing)
792}
793
794fn layout_geometry_size(
795 rect: &crate::layout::LayoutRect,
796 orientation: Orientation,
797 ctx: &ExpressionContext,
798) -> llr_Expression {
799 match rect.size_reference(orientation) {
800 Some(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
801 None => llr_Expression::NumberLiteral(0.),
802 }
803}
804
805pub fn get_layout_info(
806 elem: &ElementRc,
807 ctx: &ExpressionContext,
808 constraints: &crate::layout::LayoutConstraints,
809 orientation: Orientation,
810) -> llr_Expression {
811 let layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop(orientation) {
812 llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop))
813 } else {
814 lower_expression(&crate::layout::implicit_layout_info_call(elem, orientation), ctx)
815 };
816
817 if constraints.has_explicit_restrictions() {
818 let store = llr_Expression::StoreLocalVariable {
819 name: "layout_info".into(),
820 value: layout_info.into(),
821 };
822 let ty = crate::layout::layout_info_type();
823 let fields = match &ty {
824 Type::Struct { fields, .. } => fields,
825 _ => panic!(),
826 };
827 let mut values = fields
828 .keys()
829 .map(|p| {
830 (
831 p.clone(),
832 llr_Expression::StructFieldAccess {
833 base: llr_Expression::ReadLocalVariable {
834 name: "layout_info".into(),
835 ty: ty.clone(),
836 }
837 .into(),
838 name: p.clone(),
839 },
840 )
841 })
842 .collect::<HashMap<_, _>>();
843
844 for (nr, s) in constraints.for_each_restrictions(orientation) {
845 values.insert(
846 s.into(),
847 llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
848 );
849 }
850 llr_Expression::CodeBlock([store, llr_Expression::Struct { ty, values }].into())
851 } else {
852 layout_info
853 }
854}
855
856fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) -> llr_Expression {
857 fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
858 llr_Expression::Cast {
859 from: llr_Expression::Array {
860 element_ty: Type::Struct {
861 fields: Default::default(),
862 name: Some("PathElement".to_owned()),
863 node: None,
864 },
865 values: elements,
866 as_model: false,
867 }
868 .into(),
869 to: Type::PathData,
870 }
871 }
872
873 match path {
874 crate::expression_tree::Path::Elements(elements) => {
875 let converted_elements = elements
876 .iter()
877 .map(|element| {
878 let element_type = Type::Struct {
879 fields: element
880 .element_type
881 .properties
882 .iter()
883 .map(|(k, v)| (k.clone(), v.ty.clone()))
884 .collect(),
885 name: element.element_type.native_class.cpp_type.clone(),
886 node: None,
887 };
888
889 llr_Expression::Struct {
890 ty: element_type,
891 values: element
892 .element_type
893 .properties
894 .iter()
895 .map(|(element_field_name, element_property)| {
896 (
897 element_field_name.clone(),
898 element.bindings.get(element_field_name).map_or_else(
899 || {
900 llr_Expression::default_value_for_type(
901 &element_property.ty,
902 )
903 .unwrap()
904 },
905 |expr| lower_expression(&expr.borrow().expression, ctx),
906 ),
907 )
908 })
909 .collect(),
910 }
911 })
912 .collect();
913 llr_path_elements(converted_elements)
914 }
915 crate::expression_tree::Path::Events(events, points) => {
916 if events.is_empty() || points.is_empty() {
917 return llr_path_elements(vec![]);
918 }
919
920 let events: Vec<_> =
921 events.into_iter().map(|event| lower_expression(&event, &ctx)).collect();
922
923 let event_type = events.first().unwrap().ty(ctx).clone();
924
925 let points: Vec<_> =
926 points.into_iter().map(|point| lower_expression(&point, &ctx)).collect();
927
928 let point_type = points.first().unwrap().ty(ctx).clone();
929
930 llr_Expression::Cast {
931 from: llr_Expression::Struct {
932 ty: Type::Struct {
933 fields: IntoIterator::into_iter([
934 ("events".to_owned(), Type::Array(event_type.clone().into())),
935 ("points".to_owned(), Type::Array(point_type.clone().into())),
936 ])
937 .collect(),
938 name: None,
939 node: None,
940 },
941 values: IntoIterator::into_iter([
942 (
943 "events".to_owned(),
944 llr_Expression::Array {
945 element_ty: event_type,
946 values: events,
947 as_model: false,
948 },
949 ),
950 (
951 "points".to_owned(),
952 llr_Expression::Array {
953 element_ty: point_type.clone(),
954 values: points,
955 as_model: false,
956 },
957 ),
958 ])
959 .collect(),
960 }
961 .into(),
962 to: Type::PathData,
963 }
964 }
965 }
966}
967
968fn make_struct(
969 name: String,
970 it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
971) -> llr_Expression {
972 let mut fields = BTreeMap::<String, Type>::new();
973 let mut values = HashMap::<String, llr_Expression>::new();
974 for (name, ty, expr) in it {
975 fields.insert(name.to_string(), ty);
976 values.insert(name.to_string(), expr);
977 }
978
979 llr_Expression::Struct { ty: Type::Struct { fields, name: Some(name), node: None }, values }
980}