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