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