Skip to main content

i_slint_compiler/generator/
cpp.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4/*! module for the C++ code generator
5*/
6
7// cSpell:ignore cmath constexpr cstdlib decltype intptr itertools nullptr prepended struc subcomponent uintptr vals
8
9use crate::fileaccess;
10use std::collections::HashSet;
11use std::fmt::Write;
12use std::sync::OnceLock;
13
14use smol_str::{SmolStr, StrExt, format_smolstr};
15
16/// The configuration for the C++ code generator
17#[derive(Clone, Debug, Default, PartialEq)]
18pub struct Config {
19    pub namespace: Option<String>,
20    pub cpp_files: Vec<std::path::PathBuf>,
21    pub header_include: String,
22}
23
24// Check if word is one of C++ keywords
25fn is_cpp_keyword(word: &str) -> bool {
26    static CPP_KEYWORDS: OnceLock<HashSet<&'static str>> = OnceLock::new();
27    let keywords = CPP_KEYWORDS.get_or_init(|| {
28        #[rustfmt::skip]
29        let keywords: HashSet<&str> = HashSet::from([
30            "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit",
31            "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch",
32            "char", "char8_t", "char16_t", "char32_t", "class", "compl", "concept", "const",
33            "consteval", "constexpr", "constinit", "const_cast", "continue", "co_await",
34            "co_return", "co_yield", "decltype", "default", "delete", "do", "double",
35            "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float",
36            "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace",
37            "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private",
38            "protected", "public", "reflexpr", "register", "reinterpret_cast", "requires",
39            "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast",
40            "struct", "switch", "synchronized", "template", "this", "thread_local", "throw",
41            "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using",
42            "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq",
43        ]);
44        keywords
45    });
46    keywords.contains(word)
47}
48
49pub fn ident(ident: &str) -> SmolStr {
50    let mut new_ident = SmolStr::from(ident);
51    if ident.contains('-') {
52        new_ident = ident.replace_smolstr("-", "_");
53    }
54    if is_cpp_keyword(new_ident.as_str()) {
55        new_ident = format_smolstr!("{}_", new_ident);
56    }
57    new_ident
58}
59
60pub fn concatenate_ident(ident: &str) -> SmolStr {
61    if ident.contains('-') { ident.replace_smolstr("-", "_") } else { ident.into() }
62}
63
64/// Given a property reference to a native item (eg, the property name is empty)
65/// return tokens to the `ItemRc`
66fn access_item_rc(pr: &llr::MemberReference, ctx: &EvaluationContext) -> String {
67    let mut component_access = "self->".into();
68
69    let llr::MemberReference::Relative { parent_level, local_reference } = pr else {
70        unreachable!()
71    };
72    let llr::LocalMemberIndex::Native { item_index, prop_name: _ } = &local_reference.reference
73    else {
74        unreachable!()
75    };
76
77    for _ in 0..*parent_level {
78        component_access = format!("{component_access}parent.lock().value()->");
79    }
80
81    let (sub_compo_path, sub_component) = follow_sub_component_path(
82        ctx.compilation_unit,
83        ctx.parent_sub_component_idx(*parent_level).unwrap(),
84        &local_reference.sub_component_path,
85    );
86    if !local_reference.sub_component_path.is_empty() {
87        component_access += &sub_compo_path;
88    }
89    let component_rc = format!("{component_access}self_weak.lock()->into_dyn()");
90    let item_index_in_tree = sub_component.items[*item_index].index_in_tree;
91    let item_index = if item_index_in_tree == 0 {
92        format!("{component_access}tree_index")
93    } else {
94        format!("{component_access}tree_index_of_first_child + {item_index_in_tree} - 1")
95    };
96
97    format!("{}, {}", &component_rc, item_index)
98}
99
100/// This module contains some data structure that helps represent a C++ code.
101/// It is then rendered into an actual C++ text using the Display trait
102pub mod cpp_ast {
103
104    use std::cell::Cell;
105    use std::fmt::{Display, Error, Formatter};
106
107    use smol_str::{SmolStr, format_smolstr};
108
109    thread_local!(static INDENTATION : Cell<u32> = const { Cell::new(0) });
110    fn indent(f: &mut Formatter<'_>) -> Result<(), Error> {
111        INDENTATION.with(|i| {
112            for _ in 0..(i.get()) {
113                write!(f, "    ")?;
114            }
115            Ok(())
116        })
117    }
118
119    ///A full C++ file
120    #[derive(Default, Debug)]
121    pub struct File {
122        pub is_cpp_file: bool,
123        pub includes: Vec<SmolStr>,
124        pub after_includes: String,
125        pub namespace: Option<String>,
126        pub declarations: Vec<Declaration>,
127        pub resources: Vec<Declaration>,
128        pub definitions: Vec<Declaration>,
129    }
130
131    impl File {
132        #[allow(clippy::manual_checked_ops)]
133        pub fn split_off_cpp_files(&mut self, header_file_name: String, count: usize) -> Vec<File> {
134            let mut cpp_files = Vec::with_capacity(count);
135            if count > 0 {
136                let mut definitions = Vec::new();
137
138                let mut i = 0;
139                while i < self.definitions.len() {
140                    if matches!(
141                        &self.definitions[i],
142                        Declaration::Function(Function { template_parameters: Some(..), .. })
143                            | Declaration::TypeAlias(..)
144                    ) {
145                        i += 1;
146                        continue;
147                    }
148
149                    definitions.push(self.definitions.remove(i));
150                }
151
152                let mut cpp_resources = self
153                    .resources
154                    .iter_mut()
155                    .filter_map(|header_resource| match header_resource {
156                        Declaration::Var(var) => {
157                            var.is_extern = true;
158                            Some(Declaration::Var(Var {
159                                ty: var.ty.clone(),
160                                name: var.name.clone(),
161                                array_size: var.array_size,
162                                init: std::mem::take(&mut var.init),
163                                is_extern: false,
164                                ..Default::default()
165                            }))
166                        }
167                        _ => None,
168                    })
169                    .collect::<Vec<_>>();
170
171                let cpp_includes = vec![format_smolstr!("\"{header_file_name}\"")];
172
173                let def_chunk_size = definitions.len() / count;
174                let res_chunk_size = cpp_resources.len() / count;
175                cpp_files.extend((0..count - 1).map(|_| File {
176                    is_cpp_file: true,
177                    includes: cpp_includes.clone(),
178                    after_includes: String::new(),
179                    namespace: self.namespace.clone(),
180                    declarations: Default::default(),
181                    resources: cpp_resources.drain(0..res_chunk_size).collect(),
182                    definitions: definitions.drain(0..def_chunk_size).collect(),
183                }));
184
185                cpp_files.push(File {
186                    is_cpp_file: true,
187                    includes: cpp_includes,
188                    after_includes: String::new(),
189                    namespace: self.namespace.clone(),
190                    declarations: Default::default(),
191                    resources: cpp_resources,
192                    definitions,
193                });
194
195                cpp_files.resize_with(count, Default::default);
196            }
197
198            // Any definition in the header file is inline.
199            self.definitions.iter_mut().for_each(|def| match def {
200                Declaration::Function(f) => f.is_inline = true,
201                Declaration::Var(v) => v.is_inline = true,
202                _ => {}
203            });
204
205            cpp_files
206        }
207    }
208
209    impl Display for File {
210        fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
211            writeln!(f, "// This file is auto-generated")?;
212            if !self.is_cpp_file {
213                writeln!(f, "#pragma once")?;
214            }
215            for i in &self.includes {
216                writeln!(f, "#include {i}")?;
217            }
218            if let Some(namespace) = &self.namespace {
219                writeln!(f, "namespace {namespace} {{")?;
220                INDENTATION.with(|x| x.set(x.get() + 1));
221            }
222
223            write!(f, "{}", self.after_includes)?;
224            for d in self.declarations.iter().chain(self.resources.iter()) {
225                write!(f, "\n{d}")?;
226            }
227            for d in &self.definitions {
228                write!(f, "\n{d}")?;
229            }
230            if let Some(namespace) = &self.namespace {
231                writeln!(f, "}} // namespace {namespace}")?;
232                INDENTATION.with(|x| x.set(x.get() - 1));
233            }
234
235            Ok(())
236        }
237    }
238
239    /// Declarations  (top level, or within a struct)
240    #[derive(Debug, derive_more::Display)]
241    pub enum Declaration {
242        Struct(Struct),
243        Function(Function),
244        Var(Var),
245        TypeAlias(TypeAlias),
246        Enum(Enum),
247    }
248
249    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
250    pub enum Access {
251        Public,
252        Private,
253        /*Protected,*/
254    }
255
256    #[derive(Default, Debug)]
257    pub struct Struct {
258        pub name: SmolStr,
259        pub members: Vec<(Access, Declaration)>,
260        pub friends: Vec<SmolStr>,
261    }
262
263    impl Display for Struct {
264        fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
265            indent(f)?;
266            if self.members.is_empty() && self.friends.is_empty() {
267                writeln!(f, "class {};", self.name)
268            } else {
269                writeln!(f, "class {} {{", self.name)?;
270                INDENTATION.with(|x| x.set(x.get() + 1));
271                let mut access = Access::Private;
272                for m in &self.members {
273                    if m.0 != access {
274                        access = m.0;
275                        indent(f)?;
276                        match access {
277                            Access::Public => writeln!(f, "public:")?,
278                            Access::Private => writeln!(f, "private:")?,
279                        }
280                    }
281                    write!(f, "{}", m.1)?;
282                }
283                for friend in &self.friends {
284                    indent(f)?;
285                    writeln!(f, "friend class {friend};")?;
286                }
287                INDENTATION.with(|x| x.set(x.get() - 1));
288                indent(f)?;
289                writeln!(f, "}};")
290            }
291        }
292    }
293
294    impl Struct {
295        pub fn extract_definitions(&mut self) -> impl Iterator<Item = Declaration> + '_ {
296            let struct_name = self.name.clone();
297            self.members.iter_mut().filter_map(move |x| match &mut x.1 {
298                Declaration::Function(f) if f.statements.is_some() => {
299                    Some(Declaration::Function(Function {
300                        name: format_smolstr!("{}::{}", struct_name, f.name),
301                        signature: f.signature.clone(),
302                        is_constructor_or_destructor: f.is_constructor_or_destructor,
303                        is_static: false,
304                        is_friend: false,
305                        statements: f.statements.take(),
306                        template_parameters: f.template_parameters.clone(),
307                        constructor_member_initializers: f.constructor_member_initializers.clone(),
308                        ..Default::default()
309                    }))
310                }
311                _ => None,
312            })
313        }
314    }
315
316    #[derive(Default, Debug)]
317    pub struct Enum {
318        pub name: SmolStr,
319        pub values: Vec<SmolStr>,
320    }
321
322    impl Display for Enum {
323        fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
324            indent(f)?;
325            writeln!(f, "enum class {} {{", self.name)?;
326            INDENTATION.with(|x| x.set(x.get() + 1));
327            for value in &self.values {
328                write!(f, "{value},")?;
329            }
330            INDENTATION.with(|x| x.set(x.get() - 1));
331            indent(f)?;
332            writeln!(f, "}};")
333        }
334    }
335
336    /// Function or method
337    #[derive(Default, Debug)]
338    pub struct Function {
339        pub name: SmolStr,
340        /// "(...) -> ..."
341        pub signature: String,
342        /// The function does not have return type
343        pub is_constructor_or_destructor: bool,
344        pub is_static: bool,
345        pub is_friend: bool,
346        pub is_inline: bool,
347        /// The list of statement instead the function.  When None,  this is just a function
348        /// declaration without the definition
349        pub statements: Option<Vec<String>>,
350        /// What's inside template<...> if any
351        pub template_parameters: Option<String>,
352        /// Explicit initializers, such as FooClass::FooClass() : someMember(42) {}
353        pub constructor_member_initializers: Vec<String>,
354    }
355
356    impl Display for Function {
357        fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
358            indent(f)?;
359            if let Some(tpl) = &self.template_parameters {
360                write!(f, "template<{tpl}> ")?;
361            }
362            if self.is_static {
363                write!(f, "static ")?;
364            }
365            if self.is_friend {
366                write!(f, "friend ")?;
367            }
368            if self.is_inline {
369                write!(f, "inline ")?;
370            }
371            if !self.is_constructor_or_destructor {
372                write!(f, "auto ")?;
373            }
374            write!(f, "{} {}", self.name, self.signature)?;
375            if let Some(st) = &self.statements {
376                if !self.constructor_member_initializers.is_empty() {
377                    writeln!(f, "\n : {}", self.constructor_member_initializers.join(","))?;
378                }
379                writeln!(f, "{{")?;
380                for s in st {
381                    indent(f)?;
382                    writeln!(f, "    {s}")?;
383                }
384                indent(f)?;
385                writeln!(f, "}}")
386            } else {
387                writeln!(f, ";")
388            }
389        }
390    }
391
392    /// A variable or a member declaration.
393    #[derive(Default, Debug)]
394    pub struct Var {
395        pub is_inline: bool,
396        pub is_extern: bool,
397        pub ty: SmolStr,
398        pub name: SmolStr,
399        pub array_size: Option<usize>,
400        pub init: Option<String>,
401    }
402
403    impl Display for Var {
404        fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
405            indent(f)?;
406            if self.is_extern {
407                write!(f, "extern ")?;
408            }
409            if self.is_inline {
410                write!(f, "inline ")?;
411            }
412            write!(f, "{} {}", self.ty, self.name)?;
413            if let Some(size) = self.array_size {
414                write!(f, "[{size}]")?;
415            }
416            if let Some(i) = &self.init {
417                write!(f, " = {i}")?;
418            }
419            writeln!(f, ";")
420        }
421    }
422
423    #[derive(Default, Debug)]
424    pub struct TypeAlias {
425        pub new_name: SmolStr,
426        pub old_name: SmolStr,
427    }
428
429    impl Display for TypeAlias {
430        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
431            indent(f)?;
432            writeln!(f, "using {} = {};", self.new_name, self.old_name)
433        }
434    }
435
436    pub trait CppType {
437        fn cpp_type(&self) -> Option<SmolStr>;
438    }
439
440    pub fn escape_string(str: &str) -> String {
441        let mut result = String::with_capacity(str.len());
442        for x in str.chars() {
443            match x {
444                '\n' => result.push_str("\\n"),
445                '\\' => result.push_str("\\\\"),
446                '\"' => result.push_str("\\\""),
447                '\t' => result.push_str("\\t"),
448                '\r' => result.push_str("\\r"),
449                _ if !x.is_ascii() || (x as u32) < 32 => {
450                    use std::fmt::Write;
451                    write!(result, "\\U{:0>8x}", x as u32).unwrap();
452                }
453                _ => result.push(x),
454            }
455        }
456        result
457    }
458}
459
460use crate::CompilerConfiguration;
461use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp};
462use crate::langtype::{
463    BuiltinPrivateStruct, BuiltinPublicStruct, Enumeration, EnumerationValue, NativeClass,
464    StructName, Type,
465};
466use crate::layout::Orientation;
467use crate::llr::{
468    self, EvaluationContext as llr_EvaluationContext, EvaluationScope, ParentScope,
469    TypeResolutionContext as _,
470};
471use crate::object_tree::Document;
472use cpp_ast::*;
473use itertools::{Either, Itertools};
474use std::cell::Cell;
475use std::collections::{BTreeMap, BTreeSet};
476
477const SHARED_GLOBAL_CLASS: &str = "SharedGlobals";
478
479#[derive(Default)]
480struct ConditionalIncludes {
481    iostream: Cell<bool>,
482    cstdlib: Cell<bool>,
483    cmath: Cell<bool>,
484}
485
486#[derive(Clone)]
487struct CppGeneratorContext<'a> {
488    global_access: String,
489    conditional_includes: &'a ConditionalIncludes,
490}
491
492type EvaluationContext<'a> = llr_EvaluationContext<'a, CppGeneratorContext<'a>>;
493
494impl CppType for StructName {
495    fn cpp_type(&self) -> Option<SmolStr> {
496        match self {
497            StructName::None => None,
498            StructName::User { name, .. } => Some(ident(name)),
499            StructName::BuiltinPrivate(builtin_private) => builtin_private.cpp_type(),
500            StructName::BuiltinPublic(builtin_public) => builtin_public.cpp_type(),
501        }
502    }
503}
504
505impl CppType for BuiltinPrivateStruct {
506    fn cpp_type(&self) -> Option<SmolStr> {
507        let name: &'static str = self.into();
508        match self {
509            Self::PathMoveTo
510            | Self::PathLineTo
511            | Self::PathArcTo
512            | Self::PathCubicTo
513            | Self::PathQuadraticTo
514            | Self::PathClose => Some(format_smolstr!("slint::private_api::{}", name)),
515            _ => Some(format_smolstr!("slint::cbindgen_private::{}", name)),
516        }
517    }
518}
519
520impl CppType for BuiltinPublicStruct {
521    fn cpp_type(&self) -> Option<SmolStr> {
522        let name: &'static str = self.into();
523        match self {
524            Self::Color | Self::LogicalPosition | Self::LogicalSize => {
525                Some(format_smolstr!("slint::{}", name))
526            }
527            _ => Some(format_smolstr!("slint::language::{}", name)),
528        }
529    }
530}
531
532impl CppType for Type {
533    fn cpp_type(&self) -> Option<SmolStr> {
534        match self {
535            Type::Void => Some("void".into()),
536            Type::Float32 => Some("float".into()),
537            Type::Int32 => Some("int".into()),
538            Type::String => Some("slint::SharedString".into()),
539            Type::Keys => Some("slint::Keys".into()),
540            Type::Color => Some("slint::Color".into()),
541            Type::Duration => Some("std::int64_t".into()),
542            Type::Angle => Some("float".into()),
543            Type::PhysicalLength => Some("float".into()),
544            Type::LogicalLength => Some("float".into()),
545            Type::Rem => Some("float".into()),
546            Type::Percent => Some("float".into()),
547            Type::Bool => Some("bool".into()),
548            Type::Struct(s) => s.name.cpp_type().or_else(|| {
549                let elem = s.fields.values().map(|v| v.cpp_type()).collect::<Option<Vec<_>>>()?;
550
551                Some(format_smolstr!("std::tuple<{}>", elem.join(", ")))
552            }),
553            Type::Array(i) => {
554                Some(format_smolstr!("std::shared_ptr<slint::Model<{}>>", i.cpp_type()?))
555            }
556            Type::Image => Some("slint::Image".into()),
557            Type::Enumeration(enumeration) => {
558                if enumeration.node.is_some() {
559                    Some(ident(&enumeration.name))
560                } else {
561                    Some(format_smolstr!("slint::cbindgen_private::{}", ident(&enumeration.name)))
562                }
563            }
564            Type::Brush => Some("slint::Brush".into()),
565            Type::LayoutCache => Some("slint::SharedVector<float>".into()),
566            Type::ArrayOfU16 => Some("slint::SharedVector<uint16_t>".into()),
567            Type::Easing => Some("slint::cbindgen_private::EasingCurve".into()),
568            Type::StyledText => Some("slint::private_api::StyledText".into()),
569            _ => None,
570        }
571    }
572}
573
574fn to_cpp_orientation(o: Orientation) -> &'static str {
575    match o {
576        Orientation::Horizontal => "slint::cbindgen_private::Orientation::Horizontal",
577        Orientation::Vertical => "slint::cbindgen_private::Orientation::Vertical",
578    }
579}
580
581/// If the expression is surrounded with parentheses, remove these parentheses
582fn remove_parentheses(expr: &str) -> &str {
583    if expr.starts_with('(') && expr.ends_with(')') {
584        let mut level = 0;
585        // check that the opening and closing parentheses are on the same level
586        for byte in &expr.as_bytes()[1..expr.len() - 1] {
587            match byte {
588                b')' if level == 0 => return expr,
589                b')' => level -= 1,
590                b'(' => level += 1,
591                _ => (),
592            }
593        }
594        &expr[1..expr.len() - 1]
595    } else {
596        expr
597    }
598}
599
600#[test]
601fn remove_parentheses_test() {
602    assert_eq!(remove_parentheses("(foo(bar))"), "foo(bar)");
603    assert_eq!(remove_parentheses("(foo).bar"), "(foo).bar");
604    assert_eq!(remove_parentheses("(foo(bar))"), "foo(bar)");
605    assert_eq!(remove_parentheses("(foo)(bar)"), "(foo)(bar)");
606    assert_eq!(remove_parentheses("(foo).get()"), "(foo).get()");
607    assert_eq!(remove_parentheses("((foo).get())"), "(foo).get()");
608    assert_eq!(remove_parentheses("(((()())()))"), "((()())())");
609    assert_eq!(remove_parentheses("((()())())"), "(()())()");
610    assert_eq!(remove_parentheses("(()())()"), "(()())()");
611    assert_eq!(remove_parentheses("()())("), "()())(");
612}
613
614fn property_set_value_code(
615    property: &llr::MemberReference,
616    value_expr: &str,
617    ctx: &EvaluationContext,
618) -> String {
619    let prop = access_member(property, ctx);
620    if let Some((animation, map)) = &ctx.property_info(property).animation {
621        let mut animation = (*animation).clone();
622        map.map_expression(&mut animation);
623        let animation_code = compile_expression(&animation, ctx);
624        return prop
625            .then(|prop| format!("{prop}.set_animated_value({value_expr}, {animation_code})"));
626    }
627    prop.then(|prop| format!("{prop}.set({value_expr})"))
628}
629
630fn handle_property_init(
631    prop: &llr::MemberReference,
632    binding_expression: &llr::BindingExpression,
633    init: &mut Vec<String>,
634    ctx: &EvaluationContext,
635) {
636    let prop_access = access_member(prop, ctx).unwrap();
637    let prop_type = ctx.property_ty(prop);
638    if let Type::Callback(callback) = &prop_type {
639        let mut ctx2 = ctx.clone();
640        ctx2.argument_types = &callback.args;
641
642        let mut params = callback.args.iter().enumerate().map(|(i, ty)| {
643            format!("[[maybe_unused]] {} arg_{}", ty.cpp_type().unwrap_or_default(), i)
644        });
645
646        init.push(format!(
647            "{prop_access}.set_handler(
648                [this]({params}) {{
649                    [[maybe_unused]] auto self = this;
650                    {code};
651                }});",
652            prop_access = prop_access,
653            params = params.join(", "),
654            code = return_compile_expression(
655                &binding_expression.expression.borrow(),
656                &ctx2,
657                Some(&callback.return_type)
658            )
659        ));
660    } else {
661        let init_expr = compile_expression(&binding_expression.expression.borrow(), ctx);
662
663        init.push(if binding_expression.is_constant && !binding_expression.is_state_info {
664            format!("{prop_access}.set({init_expr});")
665        } else {
666            let binding_code = format!(
667                "[this]() {{
668                            [[maybe_unused]] auto self = this;
669                            return {init_expr};
670                        }}"
671            );
672
673            if binding_expression.is_state_info {
674                format!("slint::private_api::set_state_binding({prop_access}, {binding_code});")
675            } else {
676                match &binding_expression.animation {
677                    Some(llr::Animation::Static(anim)) => {
678                        let anim = compile_expression(anim, ctx);
679                        // Note: The start_time defaults to the current tick, so doesn't need to be
680                        // udpated here.
681                        format!("{prop_access}.set_animated_binding({binding_code},
682                                [this](uint64_t **start_time) -> slint::cbindgen_private::PropertyAnimation {{
683                                    [[maybe_unused]] auto self = this;
684                                    auto anim = {anim};
685                                    *start_time = nullptr;
686                                    return anim;
687                                }});",
688                                )
689                    }
690                    Some(llr::Animation::Transition(animation)) => {
691                        let animation = compile_expression(animation, ctx);
692                        format!(
693                            "{prop_access}.set_animated_binding({binding_code},
694                            [this](uint64_t **start_time) -> slint::cbindgen_private::PropertyAnimation {{
695                                [[maybe_unused]] auto self = this;
696                                auto [animation, change_time] = {animation};
697                                **start_time = change_time;
698                                return animation;
699                            }});",
700                        )
701                    }
702                    None => format!("{prop_access}.set_binding({binding_code});"),
703                }
704            }
705        });
706    }
707}
708
709/// Returns the text of the C++ code produced by the given root component
710pub fn generate(
711    doc: &Document,
712    config: Config,
713    compiler_config: &CompilerConfiguration,
714) -> std::io::Result<impl std::fmt::Display> {
715    if std::env::var("SLINT_LIVE_PREVIEW").is_ok() {
716        return super::cpp_live_preview::generate(doc, config, compiler_config);
717    }
718
719    let mut file = generate_types(&doc.used_types.borrow().structs_and_enums, &config);
720
721    for (resource_id, er) in doc.embedded_file_resources.borrow().iter_enumerated() {
722        embed_resource(er, resource_id, &mut file.resources);
723    }
724
725    let llr = llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config);
726
727    #[cfg(feature = "bundle-translations")]
728    if let Some(translations) = &llr.translations {
729        generate_translation(translations, &llr, &mut file.resources);
730    }
731
732    // Forward-declare the root so that sub-components can access singletons, the window, etc.
733    file.declarations.extend(
734        llr.public_components
735            .iter()
736            .map(|c| Declaration::Struct(Struct { name: ident(&c.name), ..Default::default() })),
737    );
738
739    // forward-declare the global struct
740    file.declarations.push(Declaration::Struct(Struct {
741        name: SmolStr::new_static(SHARED_GLOBAL_CLASS),
742        ..Default::default()
743    }));
744
745    // Forward-declare sub components.
746    file.declarations.extend(llr.used_sub_components.iter().map(|sub_compo| {
747        Declaration::Struct(Struct {
748            name: ident(&llr.sub_components[*sub_compo].name),
749            ..Default::default()
750        })
751    }));
752
753    let conditional_includes = ConditionalIncludes::default();
754
755    for sub_compo in &llr.used_sub_components {
756        let sub_compo_id = ident(&llr.sub_components[*sub_compo].name);
757        let mut sub_compo_struct = Struct { name: sub_compo_id.clone(), ..Default::default() };
758        generate_sub_component(
759            &mut sub_compo_struct,
760            *sub_compo,
761            &llr,
762            None,
763            Access::Public,
764            &mut file,
765            &conditional_includes,
766        );
767        file.definitions.extend(sub_compo_struct.extract_definitions().collect::<Vec<_>>());
768        file.declarations.push(Declaration::Struct(sub_compo_struct));
769    }
770
771    let mut globals_struct =
772        Struct { name: SmolStr::new_static(SHARED_GLOBAL_CLASS), ..Default::default() };
773
774    // The window need to be the first member so it is destroyed last
775    globals_struct.members.push((
776        // FIXME: many of the different component bindings need to access this
777        Access::Public,
778        Declaration::Var(Var {
779            ty: "std::optional<slint::Window>".into(),
780            name: "m_window".into(),
781            ..Default::default()
782        }),
783    ));
784
785    globals_struct.members.push((
786        Access::Public,
787        Declaration::Var(Var {
788            ty: "slint::cbindgen_private::ItemTreeWeak".into(),
789            name: "root_weak".into(),
790            ..Default::default()
791        }),
792    ));
793
794    let mut window_creation_code = vec![
795        format!("auto self = const_cast<{SHARED_GLOBAL_CLASS} *>(this);"),
796        "if (!self->m_window.has_value()) {".into(),
797        "   auto &window = self->m_window.emplace(slint::private_api::WindowAdapterRc());".into(),
798    ];
799
800    if let Some(scale_factor) = compiler_config.const_scale_factor {
801        window_creation_code
802            .push(format!("window.window_handle().set_const_scale_factor({scale_factor});"));
803    }
804
805    window_creation_code.extend([
806        "   window.window_handle().set_component(self->root_weak);".into(),
807        "}".into(),
808        "return *self->m_window;".into(),
809    ]);
810
811    globals_struct.members.push((
812        Access::Public,
813        Declaration::Function(Function {
814            name: "window".into(),
815            signature: "() const -> slint::Window&".into(),
816            statements: Some(window_creation_code),
817            ..Default::default()
818        }),
819    ));
820
821    let mut init_global = Vec::new();
822
823    for (idx, glob) in llr.globals.iter_enumerated() {
824        if !glob.must_generate() {
825            continue;
826        }
827        let name = format_smolstr!("global_{}", concatenate_ident(&glob.name));
828        let ty = if glob.is_builtin {
829            generate_global_builtin(&mut file, &conditional_includes, idx, glob, &llr);
830            format_smolstr!("slint::cbindgen_private::{}", glob.name)
831        } else {
832            init_global.push(format!("{name}->init();"));
833            generate_global(&mut file, &conditional_includes, idx, glob, &llr);
834            ident(&glob.name)
835        };
836
837        file.definitions.extend(glob.aliases.iter().map(|name| {
838            Declaration::TypeAlias(TypeAlias { old_name: ident(&glob.name), new_name: ident(name) })
839        }));
840
841        globals_struct.members.push((
842            Access::Public,
843            Declaration::Var(Var {
844                ty: format_smolstr!("std::shared_ptr<{}>", ty),
845                name,
846                init: Some(format!("std::make_shared<{ty}>(this)")),
847                ..Default::default()
848            }),
849        ));
850    }
851
852    globals_struct.members.push((
853        Access::Public,
854        Declaration::Function(Function {
855            name: globals_struct.name.clone(),
856            is_constructor_or_destructor: true,
857            signature: "()".into(),
858            statements: Some(init_global),
859            ..Default::default()
860        }),
861    ));
862
863    file.declarations.push(Declaration::Struct(globals_struct));
864
865    if let Some(popup_menu) = &llr.popup_menu {
866        let component_id = ident(&llr.sub_components[popup_menu.item_tree.root].name);
867        let mut popup_struct = Struct { name: component_id.clone(), ..Default::default() };
868        generate_item_tree(
869            &mut popup_struct,
870            &popup_menu.item_tree,
871            &llr,
872            None,
873            true,
874            component_id,
875            Access::Public,
876            &mut file,
877            &conditional_includes,
878        );
879        file.definitions.extend(popup_struct.extract_definitions().collect::<Vec<_>>());
880        file.declarations.push(Declaration::Struct(popup_struct));
881    };
882
883    for p in &llr.public_components {
884        generate_public_component(&mut file, &conditional_includes, p, &llr);
885    }
886
887    generate_type_aliases(&mut file, doc);
888
889    if conditional_includes.iostream.get() {
890        file.includes.push("<iostream>".into());
891    }
892
893    if conditional_includes.cstdlib.get() {
894        file.includes.push("<cstdlib>".into());
895    }
896
897    if conditional_includes.cmath.get() {
898        file.includes.push("<cmath>".into());
899    }
900
901    let cpp_files = file.split_off_cpp_files(config.header_include, config.cpp_files.len());
902
903    for (cpp_file_name, cpp_file) in config.cpp_files.iter().zip(cpp_files) {
904        // Important: Write without unnecessary mtime modification to avoid
905        // build systems to always detect the generated file as modified.
906        fileaccess::write_file_if_changed(cpp_file_name, cpp_file.to_string().as_bytes())?;
907    }
908
909    Ok(file)
910}
911
912pub fn generate_types(used_types: &[Type], config: &Config) -> File {
913    let mut file = File { namespace: config.namespace.clone(), ..Default::default() };
914
915    file.includes.push("<array>".into());
916    file.includes.push("<limits>".into());
917    file.includes.push("<slint.h>".into());
918
919    file.after_includes = format!(
920        "static_assert({x} == SLINT_VERSION_MAJOR && {y} == SLINT_VERSION_MINOR && {z} == SLINT_VERSION_PATCH, \
921        \"This file was generated with Slint compiler version {x}.{y}.{z}, but the Slint library used is \" \
922        SLINT_VERSION_STRING \". The version numbers must match exactly.\");",
923        x = env!("CARGO_PKG_VERSION_MAJOR"),
924        y = env!("CARGO_PKG_VERSION_MINOR"),
925        z = env!("CARGO_PKG_VERSION_PATCH")
926    );
927
928    for ty in used_types {
929        match ty {
930            Type::Struct(s) if s.node().is_some() => {
931                generate_struct(&mut file, &s.name, &s.fields);
932            }
933            Type::Enumeration(en) => {
934                generate_enum(&mut file, en);
935            }
936            _ => (),
937        }
938    }
939
940    file
941}
942
943fn expand_data_to_cpp_u8_array(data: &[u8]) -> String {
944    let mut init = "{ ".to_string();
945
946    for (index, byte) in data.iter().enumerate() {
947        if index > 0 {
948            init.push(',');
949        }
950        write!(&mut init, "0x{byte:x}").unwrap();
951        if index % 16 == 0 {
952            init.push('\n');
953        }
954    }
955
956    init.push('}');
957    init
958}
959
960fn embed_resource(
961    resource: &crate::embedded_resources::EmbeddedResources,
962    resource_id: crate::embedded_resources::EmbeddedResourcesIdx,
963    declarations: &mut Vec<Declaration>,
964) {
965    match &resource.kind {
966        crate::embedded_resources::EmbeddedResourcesKind::ListOnly => {}
967        crate::embedded_resources::EmbeddedResourcesKind::FileData => {
968            let resource_file = crate::fileaccess::load_file(std::path::Path::new(
969                resource.path.as_deref().unwrap(),
970            ))
971            .unwrap(); // embedding pass ensured that the file exists
972            let data = resource_file.read();
973
974            declarations.push(Declaration::Var(Var {
975                ty: "const uint8_t".into(),
976                name: format_smolstr!("slint_embedded_resource_{}", resource_id),
977                array_size: Some(data.len()),
978                init: Some(expand_data_to_cpp_u8_array(data.as_ref())),
979                ..Default::default()
980            }));
981        }
982        crate::embedded_resources::EmbeddedResourcesKind::DataUriPayload(data, _) => {
983            declarations.push(Declaration::Var(Var {
984                ty: "const uint8_t".into(),
985                name: format_smolstr!("slint_embedded_resource_{}", resource_id),
986                array_size: Some(data.len()),
987                init: Some(expand_data_to_cpp_u8_array(data)),
988                ..Default::default()
989            }));
990        }
991        #[cfg(feature = "software-renderer")]
992        crate::embedded_resources::EmbeddedResourcesKind::TextureData(
993            crate::embedded_resources::Texture {
994                data,
995                format,
996                rect,
997                total_size: crate::embedded_resources::Size { width, height },
998                original_size:
999                    crate::embedded_resources::Size { width: unscaled_width, height: unscaled_height },
1000            },
1001        ) => {
1002            let (r_x, r_y, r_w, r_h) = (rect.x(), rect.y(), rect.width(), rect.height());
1003            let color = if let crate::embedded_resources::PixelFormat::AlphaMap([r, g, b]) = format
1004            {
1005                format!("slint::Color::from_rgb_uint8({r}, {g}, {b})")
1006            } else {
1007                "slint::Color{}".to_string()
1008            };
1009            let count = data.len();
1010            let data = data.iter().map(ToString::to_string).join(", ");
1011            let data_name = format_smolstr!("slint_embedded_resource_{}_data", resource_id);
1012            declarations.push(Declaration::Var(Var {
1013                ty: "const uint8_t".into(),
1014                name: data_name.clone(),
1015                array_size: Some(count),
1016                init: Some(format!("{{ {data} }}")),
1017                ..Default::default()
1018            }));
1019            let texture_name = format_smolstr!("slint_embedded_resource_{}_texture", resource_id);
1020            declarations.push(Declaration::Var(Var {
1021                ty: "const slint::cbindgen_private::types::StaticTexture".into(),
1022                name: texture_name.clone(),
1023                array_size: None,
1024                init: Some(format!(
1025                    "{{
1026                            .rect = {{ {r_x}, {r_y}, {r_w}, {r_h} }},
1027                            .format = slint::cbindgen_private::types::TexturePixelFormat::{format},
1028                            .color = {color},
1029                            .index = 0,
1030                            }}"
1031                )),
1032                ..Default::default()
1033            }));
1034            let init = format!(
1035                "slint::cbindgen_private::types::StaticTextures {{
1036                        .size = {{ {width}, {height} }},
1037                        .original_size = {{ {unscaled_width}, {unscaled_height} }},
1038                        .data = slint::private_api::make_slice({data_name} , {count} ),
1039                        .textures = slint::private_api::make_slice(&{texture_name}, 1)
1040                    }}"
1041            );
1042            declarations.push(Declaration::Var(Var {
1043                ty: "const slint::cbindgen_private::types::StaticTextures".into(),
1044                name: format_smolstr!("slint_embedded_resource_{}", resource_id),
1045                array_size: None,
1046                init: Some(init),
1047                ..Default::default()
1048            }))
1049        }
1050        #[cfg(feature = "software-renderer")]
1051        crate::embedded_resources::EmbeddedResourcesKind::BitmapFontData(
1052            crate::embedded_resources::BitmapFont {
1053                family_name,
1054                character_map,
1055                units_per_em,
1056                ascent,
1057                descent,
1058                x_height,
1059                cap_height,
1060                glyphs,
1061                weight,
1062                italic,
1063                sdf,
1064            },
1065        ) => {
1066            let family_name_var =
1067                format_smolstr!("slint_embedded_resource_{}_family_name", resource_id);
1068            let family_name_size = family_name.len();
1069            declarations.push(Declaration::Var(Var {
1070                ty: "const uint8_t".into(),
1071                name: family_name_var.clone(),
1072                array_size: Some(family_name_size),
1073                init: Some(format!(
1074                    "{{ {} }}",
1075                    family_name.as_bytes().iter().map(ToString::to_string).join(", ")
1076                )),
1077                ..Default::default()
1078            }));
1079
1080            let charmap_var = format_smolstr!("slint_embedded_resource_{}_charmap", resource_id);
1081            let charmap_size = character_map.len();
1082            declarations.push(Declaration::Var(Var {
1083                ty: "const slint::cbindgen_private::CharacterMapEntry".into(),
1084                name: charmap_var.clone(),
1085                array_size: Some(charmap_size),
1086                init: Some(format!(
1087                    "{{ {} }}",
1088                    character_map
1089                        .iter()
1090                        .map(|entry| format!(
1091                            "{{ .code_point = {}, .glyph_index = {} }}",
1092                            entry.code_point as u32, entry.glyph_index
1093                        ))
1094                        .join(", ")
1095                )),
1096                ..Default::default()
1097            }));
1098
1099            for (glyphset_index, glyphset) in glyphs.iter().enumerate() {
1100                for (glyph_index, glyph) in glyphset.glyph_data.iter().enumerate() {
1101                    declarations.push(Declaration::Var(Var {
1102                        ty: "const uint8_t".into(),
1103                        name: format_smolstr!(
1104                            "slint_embedded_resource_{}_gs_{}_gd_{}",
1105                            resource_id,
1106                            glyphset_index,
1107                            glyph_index
1108                        ),
1109                        array_size: Some(glyph.data.len()),
1110                        init: Some(format!(
1111                            "{{ {} }}",
1112                            glyph.data.iter().map(ToString::to_string).join(", ")
1113                        )),
1114                        ..Default::default()
1115                    }));
1116                }
1117
1118                declarations.push(Declaration::Var(Var{
1119                    ty: "const slint::cbindgen_private::BitmapGlyph".into(),
1120                    name: format_smolstr!("slint_embedded_resource_{}_glyphset_{}", resource_id, glyphset_index),
1121                    array_size: Some(glyphset.glyph_data.len()),
1122                    init: Some(format!("{{ {} }}", glyphset.glyph_data.iter().enumerate().map(|(glyph_index, glyph)| {
1123                        format!("{{ .x = {}, .y = {}, .width = {}, .height = {}, .x_advance = {}, .data = slint::private_api::make_slice({}, {}) }}",
1124                        glyph.x, glyph.y, glyph.width, glyph.height, glyph.x_advance,
1125                        format_args!("slint_embedded_resource_{}_gs_{}_gd_{}", resource_id, glyphset_index, glyph_index),
1126                        glyph.data.len()
1127                    )
1128                    }).join(", \n"))),
1129                    ..Default::default()
1130                }));
1131            }
1132
1133            let glyphsets_var =
1134                format_smolstr!("slint_embedded_resource_{}_glyphsets", resource_id);
1135            let glyphsets_size = glyphs.len();
1136            declarations.push(Declaration::Var(Var {
1137                ty: "const slint::cbindgen_private::BitmapGlyphs".into(),
1138                name: glyphsets_var.clone(),
1139                array_size: Some(glyphsets_size),
1140                init: Some(format!(
1141                    "{{ {} }}",
1142                    glyphs
1143                        .iter()
1144                        .enumerate()
1145                        .map(|(glyphset_index, glyphset)| format!(
1146                            "{{ .pixel_size = {}, .glyph_data = slint::private_api::make_slice({}, {}) }}",
1147                            glyphset.pixel_size, format_args!("slint_embedded_resource_{}_glyphset_{}", resource_id, glyphset_index), glyphset.glyph_data.len()
1148                        ))
1149                        .join(", \n")
1150                )),
1151                ..Default::default()
1152            }));
1153
1154            let init = format!(
1155                "slint::cbindgen_private::BitmapFont {{
1156                        .family_name = slint::private_api::make_slice({family_name_var} , {family_name_size}),
1157                        .character_map = slint::private_api::make_slice({charmap_var}, {charmap_size}),
1158                        .units_per_em = {units_per_em},
1159                        .ascent = {ascent},
1160                        .descent = {descent},
1161                        .x_height = {x_height},
1162                        .cap_height = {cap_height},
1163                        .glyphs = slint::private_api::make_slice({glyphsets_var}, {glyphsets_size}),
1164                        .weight = {weight},
1165                        .italic = {italic},
1166                        .sdf = {sdf},
1167                }}"
1168            );
1169
1170            declarations.push(Declaration::Var(Var {
1171                ty: "const slint::cbindgen_private::BitmapFont".into(),
1172                name: format_smolstr!("slint_embedded_resource_{}", resource_id),
1173                array_size: None,
1174                init: Some(init),
1175                ..Default::default()
1176            }))
1177        }
1178    }
1179}
1180
1181fn generate_struct(file: &mut File, name: &StructName, fields: &BTreeMap<SmolStr, Type>) {
1182    let StructName::User { name: user_name, node } = name else {
1183        panic!("internal error: Cannot generate anonymous struct");
1184    };
1185    let name = ident(user_name);
1186    let mut members = node
1187        .ObjectTypeMember()
1188        .map(|n| crate::parser::identifier_text(&n).unwrap())
1189        .map(|name| {
1190            (
1191                Access::Public,
1192                Declaration::Var(Var {
1193                    ty: fields.get(&name).unwrap().cpp_type().unwrap(),
1194                    name: ident(&name),
1195                    ..Default::default()
1196                }),
1197            )
1198        })
1199        .collect::<Vec<_>>();
1200
1201    members.push((
1202        Access::Public,
1203        Declaration::Function(Function {
1204            name: "operator==".into(),
1205            signature: format!("(const class {name} &a, const class {name} &b) -> bool = default"),
1206            is_friend: true,
1207            statements: None,
1208            ..Function::default()
1209        }),
1210    ));
1211
1212    file.declarations.push(Declaration::Struct(Struct { name, members, ..Default::default() }))
1213}
1214
1215fn generate_enum(file: &mut File, en: &std::rc::Rc<Enumeration>) {
1216    file.declarations.push(Declaration::Enum(Enum {
1217        name: ident(&en.name),
1218        values: (0..en.values.len())
1219            .map(|value| {
1220                ident(&EnumerationValue { value, enumeration: en.clone() }.to_pascal_case())
1221            })
1222            .collect(),
1223    }))
1224}
1225
1226/// Generate the component in `file`.
1227///
1228/// `sub_components`, if Some, will be filled with all the sub component which needs to be added as friends
1229fn generate_public_component(
1230    file: &mut File,
1231    conditional_includes: &ConditionalIncludes,
1232    component: &llr::PublicComponent,
1233    unit: &llr::CompilationUnit,
1234) {
1235    let component_id = ident(&component.name);
1236
1237    let mut component_struct = Struct { name: component_id.clone(), ..Default::default() };
1238
1239    // need to be the first member, because it contains the window which is to be destroyed last
1240    component_struct.members.push((
1241        Access::Private,
1242        Declaration::Var(Var {
1243            ty: SmolStr::new_static(SHARED_GLOBAL_CLASS),
1244            name: "m_globals".into(),
1245            ..Default::default()
1246        }),
1247    ));
1248
1249    for glob in unit.globals.iter().filter(|glob| glob.must_generate() && !glob.is_builtin) {
1250        component_struct.friends.push(ident(&glob.name));
1251    }
1252
1253    let mut global_accessor_function_body = Vec::new();
1254    let mut builtin_globals = Vec::new();
1255    for glob in unit.globals.iter().filter(|glob| glob.exported && glob.must_generate()) {
1256        let accessor_statement = if glob.is_builtin {
1257            builtin_globals.push(format!("std::is_same_v<T, {}>", ident(&glob.name)));
1258            format!(
1259                "{0}if constexpr(std::is_same_v<T, {1}>) {{ return {1}(m_globals.global_{1}); }}",
1260                if global_accessor_function_body.is_empty() { "" } else { "else " },
1261                concatenate_ident(&glob.name),
1262            )
1263        } else {
1264            format!(
1265                "{0}if constexpr(std::is_same_v<T, {1}>) {{ return *m_globals.global_{1}.get(); }}",
1266                if global_accessor_function_body.is_empty() { "" } else { "else " },
1267                concatenate_ident(&glob.name),
1268            )
1269        };
1270        global_accessor_function_body.push(accessor_statement);
1271    }
1272    if !global_accessor_function_body.is_empty() {
1273        global_accessor_function_body.push(
1274            "else { static_assert(!sizeof(T*), \"The type is not global/or exported\"); }".into(),
1275        );
1276
1277        component_struct.members.push((
1278            Access::Public,
1279            Declaration::Function(Function {
1280                name: "global".into(),
1281                signature: if builtin_globals.is_empty() {
1282                    "() const -> const T&".into()
1283                } else {
1284                    format!(
1285                        "() const -> std::conditional_t<{} , T, const T&>",
1286                        builtin_globals.iter().join(" || ")
1287                    )
1288                },
1289                statements: Some(global_accessor_function_body),
1290                template_parameters: Some("typename T".into()),
1291                ..Default::default()
1292            }),
1293        ));
1294    }
1295
1296    let ctx = EvaluationContext {
1297        compilation_unit: unit,
1298        current_scope: EvaluationScope::SubComponent(component.item_tree.root, None),
1299        generator_state: CppGeneratorContext {
1300            global_access: "(&this->m_globals)".to_string(),
1301            conditional_includes,
1302        },
1303        argument_types: &[],
1304    };
1305
1306    let old_declarations = file.declarations.len();
1307
1308    generate_item_tree(
1309        &mut component_struct,
1310        &component.item_tree,
1311        unit,
1312        None,
1313        false,
1314        component_id,
1315        Access::Private, // Hide properties and other fields from the C++ API
1316        file,
1317        conditional_includes,
1318    );
1319
1320    // Give generated sub-components, etc. access to our fields
1321
1322    for new_decl in file.declarations.iter().skip(old_declarations) {
1323        if let Declaration::Struct(struc @ Struct { .. }) = new_decl {
1324            component_struct.friends.push(struc.name.clone());
1325        };
1326    }
1327
1328    generate_public_api_for_properties(
1329        &mut component_struct.members,
1330        &component.public_properties,
1331        &component.private_properties,
1332        &ctx,
1333    );
1334
1335    component_struct.members.push((
1336        Access::Public,
1337        Declaration::Function(Function {
1338            name: "show".into(),
1339            signature: "() -> void".into(),
1340            statements: Some(vec!["window().show();".into()]),
1341            ..Default::default()
1342        }),
1343    ));
1344
1345    component_struct.members.push((
1346        Access::Public,
1347        Declaration::Function(Function {
1348            name: "hide".into(),
1349            signature: "() -> void".into(),
1350            statements: Some(vec!["window().hide();".into()]),
1351            ..Default::default()
1352        }),
1353    ));
1354
1355    component_struct.members.push((
1356        Access::Public,
1357        Declaration::Function(Function {
1358            name: "window".into(),
1359            signature: "() const -> slint::Window&".into(),
1360            statements: Some(vec!["return m_globals.window();".into()]),
1361            ..Default::default()
1362        }),
1363    ));
1364
1365    component_struct.members.push((
1366        Access::Public,
1367        Declaration::Function(Function {
1368            name: "run".into(),
1369            signature: "() -> void".into(),
1370            statements: Some(vec![
1371                "show();".into(),
1372                "slint::run_event_loop();".into(),
1373                "hide();".into(),
1374            ]),
1375            ..Default::default()
1376        }),
1377    ));
1378
1379    component_struct.friends.push("slint::private_api::WindowAdapterRc".into());
1380
1381    add_friends(&mut component_struct.friends, unit, component.item_tree.root, true);
1382
1383    fn add_friends(
1384        friends: &mut Vec<SmolStr>,
1385        unit: &llr::CompilationUnit,
1386        c: llr::SubComponentIdx,
1387        is_root: bool,
1388    ) {
1389        let sc = &unit.sub_components[c];
1390        if !is_root {
1391            friends.push(ident(&sc.name));
1392        }
1393        for repeater in &sc.repeated {
1394            add_friends(friends, unit, repeater.sub_tree.root, false)
1395        }
1396        for popup in &sc.popup_windows {
1397            add_friends(friends, unit, popup.item_tree.root, false)
1398        }
1399        for menu in &sc.menu_item_trees {
1400            add_friends(friends, unit, menu.root, false)
1401        }
1402    }
1403
1404    file.definitions.extend(component_struct.extract_definitions().collect::<Vec<_>>());
1405    file.declarations.push(Declaration::Struct(component_struct));
1406}
1407
1408fn generate_item_tree(
1409    target_struct: &mut Struct,
1410    sub_tree: &llr::ItemTree,
1411    root: &llr::CompilationUnit,
1412    parent_ctx: Option<&ParentScope>,
1413    is_popup_menu: bool,
1414    item_tree_class_name: SmolStr,
1415    field_access: Access,
1416    file: &mut File,
1417    conditional_includes: &ConditionalIncludes,
1418) {
1419    target_struct.friends.push(format_smolstr!(
1420        "vtable::VRc<slint::private_api::ItemTreeVTable, {}>",
1421        item_tree_class_name
1422    ));
1423
1424    generate_sub_component(
1425        target_struct,
1426        sub_tree.root,
1427        root,
1428        parent_ctx,
1429        field_access,
1430        file,
1431        conditional_includes,
1432    );
1433
1434    let mut item_tree_array: Vec<String> = Default::default();
1435    let mut item_array: Vec<String> = Default::default();
1436
1437    sub_tree.tree.visit_in_array(&mut |node, children_offset, parent_index| {
1438        let parent_index = parent_index as u32;
1439
1440        match node.item_index {
1441            Either::Right(mut repeater_index) => {
1442                assert_eq!(node.children.len(), 0);
1443                let mut sub_component = &root.sub_components[sub_tree.root];
1444                for i in &node.sub_component_path {
1445                    repeater_index += sub_component.sub_components[*i].repeater_offset;
1446                    sub_component = &root.sub_components[sub_component.sub_components[*i].ty];
1447                }
1448                item_tree_array.push(format!(
1449                    "slint::private_api::make_dyn_node({repeater_index}, {parent_index})"
1450                ));
1451            }
1452            Either::Left(item_index) => {
1453                let mut compo_offset = String::new();
1454                let mut sub_component = &root.sub_components[sub_tree.root];
1455                for i in &node.sub_component_path {
1456                    let next_sub_component_name = ident(&sub_component.sub_components[*i].name);
1457                    write!(
1458                        compo_offset,
1459                        "offsetof({}, {}) + ",
1460                        ident(&sub_component.name),
1461                        next_sub_component_name
1462                    )
1463                    .unwrap();
1464                    sub_component = &root.sub_components[sub_component.sub_components[*i].ty];
1465                }
1466
1467                let item = &sub_component.items[item_index];
1468                let children_count = node.children.len() as u32;
1469                let children_index = children_offset as u32;
1470                let item_array_index = item_array.len() as u32;
1471
1472                item_tree_array.push(format!(
1473                    "slint::private_api::make_item_node({}, {}, {}, {}, {})",
1474                    children_count,
1475                    children_index,
1476                    parent_index,
1477                    item_array_index,
1478                    node.is_accessible
1479                ));
1480                item_array.push(format!(
1481                    "{{ {}, {} offsetof({}, {}) }}",
1482                    item.ty.cpp_vtable_getter,
1483                    compo_offset,
1484                    &ident(&sub_component.name),
1485                    ident(&item.name),
1486                ));
1487            }
1488        }
1489    });
1490
1491    let mut visit_children_statements = vec![
1492        "static const auto dyn_visit = [] (const void *base,  [[maybe_unused]] slint::private_api::TraversalOrder order, [[maybe_unused]] slint::private_api::ItemVisitorRefMut visitor, [[maybe_unused]] uint32_t dyn_index) -> uint64_t {".to_owned(),
1493        format!("    [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", item_tree_class_name)];
1494    let mut subtree_range_statement = vec!["    std::abort();".into()];
1495    let mut subtree_component_statement = vec!["    std::abort();".into()];
1496
1497    if target_struct.members.iter().any(|(_, declaration)| {
1498        matches!(&declaration, Declaration::Function(func @ Function { .. }) if func.name == "visit_dynamic_children")
1499    }) {
1500        visit_children_statements
1501            .push("    return self->visit_dynamic_children(dyn_index, order, visitor);".into());
1502        subtree_range_statement = vec![
1503                format!("auto self = reinterpret_cast<const {}*>(component.instance);", item_tree_class_name),
1504                "return self->subtree_range(dyn_index);".to_owned(),
1505        ];
1506        subtree_component_statement = vec![
1507                format!("auto self = reinterpret_cast<const {}*>(component.instance);", item_tree_class_name),
1508                "self->subtree_component(dyn_index, subtree_index, result);".to_owned(),
1509        ];
1510    } else {
1511        visit_children_statements.push("    std::abort();".into());
1512     }
1513
1514    visit_children_statements.extend([
1515        "};".into(),
1516        format!("auto self_rc = reinterpret_cast<const {item_tree_class_name}*>(component.instance)->self_weak.lock()->into_dyn();"),
1517        "return slint::cbindgen_private::slint_visit_item_tree(&self_rc, get_item_tree(component) , index, order, visitor, dyn_visit);".to_owned(),
1518    ]);
1519
1520    target_struct.members.push((
1521        Access::Private,
1522        Declaration::Function(Function {
1523            name: "visit_children".into(),
1524            signature: "(slint::private_api::ItemTreeRef component, intptr_t index, slint::private_api::TraversalOrder order, slint::private_api::ItemVisitorRefMut visitor) -> uint64_t".into(),
1525            is_static: true,
1526            statements: Some(visit_children_statements),
1527            ..Default::default()
1528        }),
1529    ));
1530
1531    target_struct.members.push((
1532        Access::Private,
1533        Declaration::Function(Function {
1534            name: "get_item_ref".into(),
1535            signature: "(slint::private_api::ItemTreeRef component, uint32_t index) -> slint::private_api::ItemRef".into(),
1536            is_static: true,
1537            statements: Some(vec![
1538                "return slint::private_api::get_item_ref(component, get_item_tree(component), item_array(), index);".to_owned(),
1539            ]),
1540            ..Default::default()
1541        }),
1542    ));
1543
1544    target_struct.members.push((
1545        Access::Private,
1546        Declaration::Function(Function {
1547            name: "get_subtree_range".into(),
1548            signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] uint32_t dyn_index) -> slint::private_api::IndexRange".into(),
1549            is_static: true,
1550            statements: Some(subtree_range_statement),
1551            ..Default::default()
1552        }),
1553    ));
1554
1555    target_struct.members.push((
1556        Access::Private,
1557        Declaration::Function(Function {
1558            name: "get_subtree".into(),
1559            signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] uint32_t dyn_index, [[maybe_unused]] uintptr_t subtree_index, [[maybe_unused]] slint::private_api::ItemTreeWeak *result) -> void".into(),
1560            is_static: true,
1561            statements: Some(subtree_component_statement),
1562            ..Default::default()
1563        }),
1564    ));
1565
1566    target_struct.members.push((
1567        Access::Private,
1568        Declaration::Function(Function {
1569            name: "get_item_tree".into(),
1570            signature: "(slint::private_api::ItemTreeRef) -> slint::cbindgen_private::Slice<slint::private_api::ItemTreeNode>".into(),
1571            is_static: true,
1572            statements: Some(vec![
1573                "return item_tree();".to_owned(),
1574            ]),
1575            ..Default::default()
1576        }),
1577    ));
1578
1579    let parent_item_from_parent_component = parent_ctx.as_ref()
1580        .map(|parent| {
1581            parent.repeater_index.map_or_else(|| {
1582                // No repeater index, this could be a PopupWindow
1583                vec![
1584                    format!("auto self = reinterpret_cast<const {item_tree_class_name}*>(component.instance);"),
1585                    format!("auto parent = self->parent.lock().value();"),
1586                    // TODO: store popup index in ctx and set it here instead of 0?
1587                    format!("*result = {{ parent->self_weak, 0 }};"),
1588                    ]
1589                }, |idx| {
1590                let current_sub_component = &root.sub_components[parent.sub_component];
1591                let parent_index = current_sub_component.repeated[idx].index_in_tree;
1592                vec![
1593                    format!("auto self = reinterpret_cast<const {item_tree_class_name}*>(component.instance);"),
1594                    format!("auto parent = self->parent.lock().value();"),
1595                    format!("*result = {{ parent->self_weak, parent->tree_index_of_first_child + {} }};", parent_index - 1),
1596                ]
1597            })
1598        })
1599        .unwrap_or_default();
1600    target_struct.members.push((
1601        Access::Private,
1602        Declaration::Function(Function {
1603            name: "parent_node".into(),
1604            signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] slint::private_api::ItemWeak *result) -> void".into(),
1605            is_static: true,
1606            statements: Some(parent_item_from_parent_component,),
1607            ..Default::default()
1608        }),
1609    ));
1610
1611    target_struct.members.push((
1612        Access::Private,
1613        Declaration::Function(Function {
1614            name: "embed_component".into(),
1615            signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] const slint::private_api::ItemTreeWeak *parent_component, [[maybe_unused]] const uint32_t parent_index) -> bool".into(),
1616            is_static: true,
1617            statements: Some(vec!["return false; /* todo! */".into()]),
1618            ..Default::default()
1619        }),
1620    ));
1621
1622    // Statements will be overridden for repeated components!
1623    target_struct.members.push((
1624        Access::Private,
1625        Declaration::Function(Function {
1626            name: "subtree_index".into(),
1627            signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component) -> uintptr_t"
1628                .into(),
1629            is_static: true,
1630            statements: Some(vec!["return std::numeric_limits<uintptr_t>::max();".into()]),
1631            ..Default::default()
1632        }),
1633    ));
1634
1635    target_struct.members.push((
1636        Access::Private,
1637        Declaration::Function(Function {
1638            name: "item_tree".into(),
1639            signature: "() -> slint::cbindgen_private::Slice<slint::private_api::ItemTreeNode>"
1640                .into(),
1641            is_static: true,
1642            statements: Some(vec![
1643                "static const slint::private_api::ItemTreeNode children[] {".to_owned(),
1644                format!("    {} }};", item_tree_array.join(", \n")),
1645                "return slint::private_api::make_slice(std::span(children));".to_owned(),
1646            ]),
1647            ..Default::default()
1648        }),
1649    ));
1650
1651    target_struct.members.push((
1652        Access::Private,
1653        Declaration::Function(Function {
1654            name: "item_array".into(),
1655            signature: "() -> const slint::private_api::ItemArray".into(),
1656            is_static: true,
1657            statements: Some(vec![
1658                "static const slint::private_api::ItemArrayEntry items[] {".to_owned(),
1659                format!("    {} }};", item_array.join(", \n")),
1660                "return slint::private_api::make_slice(std::span(items));".to_owned(),
1661            ]),
1662            ..Default::default()
1663        }),
1664    ));
1665
1666    target_struct.members.push((
1667        Access::Private,
1668        Declaration::Function(Function {
1669            name: "layout_info".into(),
1670            signature:
1671                "([[maybe_unused]] slint::private_api::ItemTreeRef component, slint::cbindgen_private::Orientation o) -> slint::cbindgen_private::LayoutInfo"
1672                    .into(),
1673            is_static: true,
1674            statements: Some(vec![format!(
1675                "return reinterpret_cast<const {}*>(component.instance)->layout_info(o);",
1676                item_tree_class_name
1677            )]),
1678            ..Default::default()
1679        }),
1680    ));
1681
1682    target_struct.members.push((
1683        Access::Private,
1684        Declaration::Function(Function {
1685            name: "item_geometry".into(),
1686            signature:
1687                "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index) -> slint::cbindgen_private::LogicalRect"
1688                    .into(),
1689            is_static: true,
1690            statements: Some(vec![format!(
1691                "return reinterpret_cast<const {}*>(component.instance)->item_geometry(index);",
1692                item_tree_class_name
1693            ), ]),
1694            ..Default::default()
1695        }),
1696    ));
1697
1698    target_struct.members.push((
1699        Access::Private,
1700        Declaration::Function(Function {
1701            name: "accessible_role".into(),
1702            signature:
1703                "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index) -> slint::cbindgen_private::AccessibleRole"
1704                    .into(),
1705            is_static: true,
1706            statements: Some(vec![format!(
1707                "return reinterpret_cast<const {}*>(component.instance)->accessible_role(index);",
1708                item_tree_class_name
1709            )]),
1710            ..Default::default()
1711        }),
1712    ));
1713
1714    target_struct.members.push((
1715        Access::Private,
1716        Declaration::Function(Function {
1717            name: "accessible_string_property".into(),
1718            signature:
1719                "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index, slint::cbindgen_private::AccessibleStringProperty what, slint::SharedString *result) -> bool"
1720                    .into(),
1721            is_static: true,
1722            statements: Some(vec![format!(
1723                "if (auto r = reinterpret_cast<const {}*>(component.instance)->accessible_string_property(index, what)) {{ *result = *r; return true; }} else {{ return false; }}",
1724                item_tree_class_name
1725            )]),
1726            ..Default::default()
1727        }),
1728    ));
1729
1730    target_struct.members.push((
1731        Access::Private,
1732        Declaration::Function(Function {
1733            name: "accessibility_action".into(),
1734            signature:
1735                "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index, const slint::cbindgen_private::AccessibilityAction *action) -> void"
1736                    .into(),
1737            is_static: true,
1738            statements: Some(vec![format!(
1739                "reinterpret_cast<const {}*>(component.instance)->accessibility_action(index, *action);",
1740                item_tree_class_name
1741            )]),
1742            ..Default::default()
1743        }),
1744    ));
1745
1746    target_struct.members.push((
1747        Access::Private,
1748        Declaration::Function(Function {
1749            name: "supported_accessibility_actions".into(),
1750            signature:
1751                "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index) -> uint32_t"
1752                    .into(),
1753            is_static: true,
1754            statements: Some(vec![format!(
1755                "return reinterpret_cast<const {}*>(component.instance)->supported_accessibility_actions(index);",
1756                item_tree_class_name
1757            )]),
1758            ..Default::default()
1759        }),
1760    ));
1761
1762    target_struct.members.push((
1763        Access::Private,
1764        Declaration::Function(Function {
1765            name: "element_infos".into(),
1766            signature:
1767                "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] uint32_t index, [[maybe_unused]] slint::SharedString *result) -> bool"
1768                    .into(),
1769            is_static: true,
1770            statements: Some(if root.has_debug_info {
1771                vec![
1772                    format!("if (auto infos = reinterpret_cast<const {}*>(component.instance)->element_infos(index)) {{ *result = *infos; }};",
1773                    item_tree_class_name),
1774                    "return true;".into()
1775                ]
1776            } else {
1777                vec!["return false;".into()]
1778            }),
1779            ..Default::default()
1780        }),
1781    ));
1782
1783    target_struct.members.push((
1784        Access::Private,
1785        Declaration::Function(Function {
1786            name: "window_adapter".into(),
1787            signature:
1788                "(slint::private_api::ItemTreeRef component, [[maybe_unused]] bool do_create, slint::cbindgen_private::Option<slint::private_api::WindowAdapterRc>* result) -> void"
1789                    .into(),
1790            is_static: true,
1791            statements: Some(vec![format!(
1792                "*reinterpret_cast<slint::private_api::WindowAdapterRc*>(result) = reinterpret_cast<const {item_tree_class_name}*>(component.instance)->globals->window().window_handle();"
1793            )]),
1794            ..Default::default()
1795        }),
1796    ));
1797
1798    target_struct.members.push((
1799        Access::Public,
1800        Declaration::Var(Var {
1801            ty: "static const slint::private_api::ItemTreeVTable".into(),
1802            name: "static_vtable".into(),
1803            ..Default::default()
1804        }),
1805    ));
1806
1807    file.definitions.push(Declaration::Var(Var {
1808        ty: "const slint::private_api::ItemTreeVTable".into(),
1809        name: format_smolstr!("{}::static_vtable", item_tree_class_name),
1810        init: Some(format!(
1811            "{{ visit_children, get_item_ref, get_subtree_range, get_subtree, \
1812                get_item_tree, parent_node, embed_component, subtree_index, layout_info, \
1813                item_geometry, accessible_role, accessible_string_property, accessibility_action, \
1814                supported_accessibility_actions, element_infos, window_adapter, \
1815                slint::private_api::drop_in_place<{item_tree_class_name}>, slint::private_api::dealloc }}"
1816        )),
1817        ..Default::default()
1818    }));
1819
1820    let mut create_parameters = Vec::new();
1821    let mut init_parent_parameters = "";
1822
1823    if let Some(parent) = &parent_ctx {
1824        let parent_type =
1825            format!("class {} const *", ident(&root.sub_components[parent.sub_component].name));
1826        create_parameters.push(format!("{parent_type} parent"));
1827
1828        init_parent_parameters = ", parent";
1829    }
1830
1831    let mut create_code = vec![
1832        format!(
1833            "auto self_rc = vtable::VRc<slint::private_api::ItemTreeVTable, {0}>::make();",
1834            target_struct.name
1835        ),
1836        format!("auto self = const_cast<{0} *>(&*self_rc);", target_struct.name),
1837        "self->self_weak = vtable::VWeak(self_rc).into_dyn();".into(),
1838    ];
1839
1840    if is_popup_menu {
1841        create_code.push("self->globals = globals;".into());
1842        create_parameters.push("const SharedGlobals *globals".into());
1843    } else if parent_ctx.is_none() {
1844        create_code.push("slint::cbindgen_private::slint_ensure_backend();".into());
1845
1846        #[cfg(feature = "bundle-translations")]
1847        if let Some(translations) = &root.translations {
1848            let lang_len = translations.languages.len();
1849            create_code.push(format!(
1850                "std::array<slint::cbindgen_private::Slice<uint8_t>, {lang_len}> languages {{ {} }};",
1851                translations
1852                    .languages
1853                    .iter()
1854                    .map(|l| format!("slint::private_api::string_to_slice({l:?})"))
1855                    .join(", ")
1856            ));
1857            create_code.push("slint::cbindgen_private::slint_translate_set_bundled_languages(slint::private_api::make_slice(std::span(languages)));".to_string());
1858        }
1859
1860        create_code.push("self->globals = &self->m_globals;".into());
1861        create_code.push("self->m_globals.root_weak = self->self_weak;".into());
1862    }
1863
1864    let global_access = if parent_ctx.is_some() { "parent->globals" } else { "self->globals" };
1865    create_code.extend([
1866        format!(
1867            "slint::private_api::register_item_tree(&self_rc.into_dyn(), {global_access}->m_window);",
1868        ),
1869        format!("self->init({global_access}, self->self_weak, 0, 1 {init_parent_parameters});"),
1870    ]);
1871
1872    // Repeaters run their user_init() code from Repeater::ensure_updated() after update() initialized model_data/index.
1873    // And in PopupWindow this is also called by the runtime
1874    if parent_ctx.is_none() && !is_popup_menu {
1875        create_code.push("self->user_init();".to_string());
1876        // initialize the Window in this point to be consistent with Rust
1877        create_code.push("self->window();".to_string())
1878    }
1879
1880    create_code
1881        .push(format!("return slint::ComponentHandle<{0}>{{ self_rc }};", target_struct.name));
1882
1883    target_struct.members.push((
1884        Access::Public,
1885        Declaration::Function(Function {
1886            name: "create".into(),
1887            signature: format!(
1888                "({}) -> slint::ComponentHandle<{}>",
1889                create_parameters.join(","),
1890                target_struct.name
1891            ),
1892            statements: Some(create_code),
1893            is_static: true,
1894            ..Default::default()
1895        }),
1896    ));
1897
1898    let destructor = vec![format!(
1899        "if (auto &window = globals->m_window) window->window_handle().unregister_item_tree(this, item_array());"
1900    )];
1901
1902    target_struct.members.push((
1903        Access::Public,
1904        Declaration::Function(Function {
1905            name: format_smolstr!("~{}", target_struct.name),
1906            signature: "()".to_owned(),
1907            is_constructor_or_destructor: true,
1908            statements: Some(destructor),
1909            ..Default::default()
1910        }),
1911    ));
1912}
1913
1914fn generate_sub_component(
1915    target_struct: &mut Struct,
1916    component: llr::SubComponentIdx,
1917    root: &llr::CompilationUnit,
1918    parent_ctx: Option<&ParentScope>,
1919    field_access: Access,
1920    file: &mut File,
1921    conditional_includes: &ConditionalIncludes,
1922) {
1923    let globals_type_ptr = "const class SharedGlobals*";
1924
1925    let mut init_parameters = vec![
1926        format!("{} globals", globals_type_ptr),
1927        "slint::cbindgen_private::ItemTreeWeak enclosing_component".into(),
1928        "uint32_t tree_index".into(),
1929        "uint32_t tree_index_of_first_child".into(),
1930    ];
1931
1932    let mut init: Vec<String> =
1933        vec!["auto self = this;".into(), "self->self_weak = enclosing_component;".into()];
1934
1935    target_struct.members.push((
1936        Access::Public,
1937        Declaration::Var(Var {
1938            ty: "slint::cbindgen_private::ItemTreeWeak".into(),
1939            name: "self_weak".into(),
1940            ..Default::default()
1941        }),
1942    ));
1943
1944    target_struct.members.push((
1945        field_access,
1946        Declaration::Var(Var {
1947            ty: globals_type_ptr.into(),
1948            name: "globals".into(),
1949            ..Default::default()
1950        }),
1951    ));
1952    init.push("self->globals = globals;".into());
1953
1954    target_struct.members.push((
1955        field_access,
1956        Declaration::Var(Var {
1957            ty: "uint32_t".into(),
1958            name: "tree_index_of_first_child".into(),
1959            ..Default::default()
1960        }),
1961    ));
1962    init.push("this->tree_index_of_first_child = tree_index_of_first_child;".into());
1963
1964    target_struct.members.push((
1965        field_access,
1966        Declaration::Var(Var {
1967            ty: "uint32_t".into(),
1968            name: "tree_index".into(),
1969            ..Default::default()
1970        }),
1971    ));
1972    init.push("self->tree_index = tree_index;".into());
1973
1974    if let Some(parent_ctx) = &parent_ctx {
1975        let parent_type = ident(&root.sub_components[parent_ctx.sub_component].name);
1976        init_parameters.push(format!("class {parent_type} const *parent"));
1977
1978        target_struct.members.push((
1979            field_access,
1980            Declaration::Var(Var {
1981                ty: format_smolstr!(
1982                    "vtable::VWeakMapped<slint::private_api::ItemTreeVTable, class {parent_type} const>"
1983                )
1984                .clone(),
1985                name: "parent".into(),
1986                ..Default::default()
1987            }),
1988        ));
1989        init.push(format!("self->parent = vtable::VRcMapped<slint::private_api::ItemTreeVTable, const {parent_type}>(parent->self_weak.lock().value(), parent);"));
1990    }
1991
1992    let ctx = EvaluationContext::new_sub_component(
1993        root,
1994        component,
1995        CppGeneratorContext { global_access: "self->globals".into(), conditional_includes },
1996        parent_ctx,
1997    );
1998
1999    let component = &root.sub_components[component];
2000
2001    let parent_ctx = ParentScope::new(&ctx, None);
2002
2003    component.popup_windows.iter().for_each(|popup| {
2004        let component_id = ident(&root.sub_components[popup.item_tree.root].name);
2005        let mut popup_struct = Struct { name: component_id.clone(), ..Default::default() };
2006        generate_item_tree(
2007            &mut popup_struct,
2008            &popup.item_tree,
2009            root,
2010            Some(&parent_ctx),
2011            false,
2012            component_id,
2013            Access::Public,
2014            file,
2015            conditional_includes,
2016        );
2017        file.definitions.extend(popup_struct.extract_definitions());
2018        file.declarations.push(Declaration::Struct(popup_struct));
2019    });
2020    for menu in &component.menu_item_trees {
2021        let component_id = ident(&root.sub_components[menu.root].name);
2022        let mut menu_struct = Struct { name: component_id.clone(), ..Default::default() };
2023        generate_item_tree(
2024            &mut menu_struct,
2025            menu,
2026            root,
2027            Some(&parent_ctx),
2028            false,
2029            component_id,
2030            Access::Public,
2031            file,
2032            conditional_includes,
2033        );
2034        file.definitions.extend(menu_struct.extract_definitions());
2035        file.declarations.push(Declaration::Struct(menu_struct));
2036    }
2037
2038    for property in component.properties.iter() {
2039        let cpp_name = ident(&property.name);
2040        let ty =
2041            format_smolstr!("slint::private_api::Property<{}>", property.ty.cpp_type().unwrap());
2042        target_struct.members.push((
2043            field_access,
2044            Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2045        ));
2046    }
2047    for callback in component.callbacks.iter() {
2048        let cpp_name = ident(&callback.name);
2049        let param_types = callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
2050        let ty = format_smolstr!(
2051            "slint::private_api::Callback<{}({})>",
2052            callback.ret_ty.cpp_type().unwrap(),
2053            param_types.join(", ")
2054        );
2055        target_struct.members.push((
2056            field_access,
2057            Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2058        ));
2059    }
2060
2061    for (i, _) in component.change_callbacks.iter().enumerate() {
2062        target_struct.members.push((
2063            field_access,
2064            Declaration::Var(Var {
2065                ty: "slint::private_api::ChangeTracker".into(),
2066                name: format_smolstr!("change_tracker{}", i),
2067                ..Default::default()
2068            }),
2069        ));
2070    }
2071
2072    let mut user_init = vec!["[[maybe_unused]] auto self = this;".into()];
2073
2074    let mut children_visitor_cases = Vec::new();
2075    let mut subtrees_ranges_cases = Vec::new();
2076    let mut subtrees_components_cases = Vec::new();
2077
2078    for sub in &component.sub_components {
2079        let field_name = ident(&sub.name);
2080        let sub_sc = &root.sub_components[sub.ty];
2081        let local_tree_index: u32 = sub.index_in_tree as _;
2082        let local_index_of_first_child: u32 = sub.index_of_first_child_in_tree as _;
2083
2084        // For children of sub-components, the item index generated by the generate_item_indices pass
2085        // starts at 1 (0 is the root element).
2086        let global_index = if local_tree_index == 0 {
2087            "tree_index".into()
2088        } else {
2089            format!("tree_index_of_first_child + {local_tree_index} - 1")
2090        };
2091        let global_children = if local_index_of_first_child == 0 {
2092            "0".into()
2093        } else {
2094            format!("tree_index_of_first_child + {local_index_of_first_child} - 1")
2095        };
2096
2097        init.push(format!(
2098            "this->{field_name}.init(globals, self_weak.into_dyn(), {global_index}, {global_children});"
2099        ));
2100        user_init.push(format!("this->{field_name}.user_init();"));
2101
2102        let sub_component_repeater_count = sub_sc.repeater_count(root);
2103        if sub_component_repeater_count > 0 {
2104            let mut case_code = String::new();
2105            let repeater_offset = sub.repeater_offset;
2106
2107            for local_repeater_index in 0..sub_component_repeater_count {
2108                write!(case_code, "case {}: ", repeater_offset + local_repeater_index).unwrap();
2109            }
2110
2111            children_visitor_cases.push(format!(
2112                "\n        {case_code} {{
2113                        return self->{field_name}.visit_dynamic_children(dyn_index - {repeater_offset}, order, visitor);
2114                    }}",
2115            ));
2116            subtrees_ranges_cases.push(format!(
2117                "\n        {case_code} {{
2118                        return self->{field_name}.subtree_range(dyn_index - {repeater_offset});
2119                    }}",
2120            ));
2121            subtrees_components_cases.push(format!(
2122                "\n        {case_code} {{
2123                        self->{field_name}.subtree_component(dyn_index - {repeater_offset}, subtree_index, result);
2124                        return;
2125                    }}",
2126            ));
2127        }
2128
2129        target_struct.members.push((
2130            field_access,
2131            Declaration::Var(Var {
2132                ty: ident(&sub_sc.name),
2133                name: field_name,
2134                ..Default::default()
2135            }),
2136        ));
2137    }
2138
2139    for (i, _) in component.popup_windows.iter().enumerate() {
2140        target_struct.members.push((
2141            field_access,
2142            Declaration::Var(Var {
2143                ty: ident("mutable uint32_t"),
2144                name: format_smolstr!("popup_id_{}", i),
2145                ..Default::default()
2146            }),
2147        ));
2148    }
2149
2150    for (prop1, prop2, fields) in &component.two_way_bindings {
2151        if fields.is_empty() {
2152            let ty = ctx.property_ty(prop1).cpp_type().unwrap();
2153            let p1 = access_member(prop1, &ctx).unwrap();
2154            init.push(
2155                access_member(prop2, &ctx).then(|p2| {
2156                    format!("slint::private_api::Property<{ty}>::link_two_way(&{p1}, &{p2})",)
2157                }) + ";",
2158            );
2159        } else {
2160            let mut access = "x".to_string();
2161            let mut ty = ctx.property_ty(prop2);
2162            let cpp_ty = ty.cpp_type().unwrap();
2163            for f in fields {
2164                let Type::Struct(s) = &ty else {
2165                    panic!("Field of two way binding on a non-struct type")
2166                };
2167                access = struct_field_access(access, s, f);
2168                ty = s.fields.get(f).unwrap();
2169            }
2170
2171            let p1 = access_member(prop1, &ctx).unwrap();
2172            init.push(
2173                access_member(prop2, &ctx).then(|p2|
2174                    format!("slint::private_api::Property<{cpp_ty}>::link_two_way_with_map(&{p2}, &{p1}, [](const auto &x){{ return {access}; }}, [](auto &x, const auto &v){{ {access} = v; }})")
2175                ) + ";",
2176            );
2177        }
2178    }
2179
2180    let mut properties_init_code = Vec::new();
2181    for (prop, expression) in &component.property_init {
2182        handle_property_init(prop, expression, &mut properties_init_code, &ctx)
2183    }
2184    for prop in &component.const_properties {
2185        let p = access_local_member(prop, &ctx);
2186        properties_init_code.push(format!("{p}.set_constant();"));
2187    }
2188
2189    for item in &component.items {
2190        target_struct.members.push((
2191            field_access,
2192            Declaration::Var(Var {
2193                ty: format_smolstr!("slint::cbindgen_private::{}", ident(&item.ty.class_name)),
2194                name: ident(&item.name),
2195                init: Some("{}".to_owned()),
2196                ..Default::default()
2197            }),
2198        ));
2199    }
2200
2201    for (idx, repeated) in component.repeated.iter_enumerated() {
2202        let sc = &root.sub_components[repeated.sub_tree.root];
2203        let data_type = repeated.data_prop.map(|data_prop| sc.properties[data_prop].ty.clone());
2204
2205        generate_repeated_component(
2206            repeated,
2207            root,
2208            ParentScope::new(&ctx, Some(idx)),
2209            data_type.as_ref(),
2210            file,
2211            conditional_includes,
2212        );
2213
2214        let idx = usize::from(idx);
2215        let repeater_id = format_smolstr!("repeater_{}", idx);
2216
2217        let model = compile_expression(&repeated.model.borrow(), &ctx);
2218
2219        // FIXME: optimize  if repeated.model.is_constant()
2220        properties_init_code.push(format!(
2221            "self->{repeater_id}.set_model_binding([self] {{ (void)self; return {model}; }});",
2222        ));
2223
2224        let ensure_updated = if let Some(listview) = &repeated.listview {
2225            let vp_y = access_member(&listview.viewport_y, &ctx).unwrap();
2226            let vp_h = access_member(&listview.viewport_height, &ctx).unwrap();
2227            let lv_h = access_member(&listview.listview_height, &ctx).unwrap();
2228            let vp_w = access_member(&listview.viewport_width, &ctx).unwrap();
2229            let lv_w = access_member(&listview.listview_width, &ctx).unwrap();
2230
2231            format!(
2232                "self->{repeater_id}.ensure_updated_listview(self, &{vp_w}, &{vp_h}, &{vp_y}, {lv_w}.get(), {lv_h}.get());"
2233            )
2234        } else {
2235            format!("self->{repeater_id}.ensure_updated(self);")
2236        };
2237
2238        children_visitor_cases.push(format!(
2239            "\n        case {idx}: {{
2240                {ensure_updated}
2241                return self->{repeater_id}.visit(order, visitor);
2242            }}",
2243        ));
2244        subtrees_ranges_cases.push(format!(
2245            "\n        case {idx}: {{
2246                {ensure_updated}
2247                return self->{repeater_id}.index_range();
2248            }}",
2249        ));
2250        subtrees_components_cases.push(format!(
2251            "\n        case {idx}: {{
2252                {ensure_updated}
2253                *result = self->{repeater_id}.instance_at(subtree_index);
2254                return;
2255            }}",
2256        ));
2257
2258        let rep_type = match data_type {
2259            Some(data_type) => {
2260                format_smolstr!(
2261                    "slint::private_api::Repeater<class {}, {}>",
2262                    ident(&sc.name),
2263                    data_type.cpp_type().unwrap()
2264                )
2265            }
2266            None => format_smolstr!("slint::private_api::Conditional<class {}>", ident(&sc.name)),
2267        };
2268        target_struct.members.push((
2269            field_access,
2270            Declaration::Var(Var { ty: rep_type, name: repeater_id, ..Default::default() }),
2271        ));
2272    }
2273
2274    init.extend(properties_init_code);
2275
2276    user_init.extend(component.init_code.iter().map(|e| {
2277        let mut expr_str = compile_expression(&e.borrow(), &ctx);
2278        expr_str.push(';');
2279        expr_str
2280    }));
2281
2282    user_init.extend(component.change_callbacks.iter().enumerate().map(|(idx, (p, e))| {
2283        let code = compile_expression(&e.borrow(), &ctx);
2284        let prop = compile_expression(&llr::Expression::PropertyReference(p.clone()), &ctx);
2285        format!("self->change_tracker{idx}.init(self, [](auto self) {{ return {prop}; }}, []([[maybe_unused]] auto self, auto) {{ {code}; }});")
2286    }));
2287
2288    if !component.timers.is_empty() {
2289        let mut update_timers = vec!["auto self = this;".into()];
2290        for (i, tmr) in component.timers.iter().enumerate() {
2291            user_init.push("self->update_timers();".to_string());
2292            let name = format_smolstr!("timer{}", i);
2293            let running = compile_expression(&tmr.running.borrow(), &ctx);
2294            let interval = compile_expression(&tmr.interval.borrow(), &ctx);
2295            let callback = compile_expression(&tmr.triggered.borrow(), &ctx);
2296            update_timers.push(format!("if ({running}) {{"));
2297            update_timers
2298                .push(format!("   auto interval = std::chrono::milliseconds({interval});"));
2299            update_timers.push(format!(
2300                "   if (!self->{name}.running() || self->{name}.interval() != interval)"
2301            ));
2302            update_timers.push(format!("       self->{name}.start(slint::TimerMode::Repeated, interval, [self] {{ {callback}; }});"));
2303            update_timers.push(format!("}} else {{ self->{name}.stop(); }}"));
2304            target_struct.members.push((
2305                field_access,
2306                Declaration::Var(Var { ty: "slint::Timer".into(), name, ..Default::default() }),
2307            ));
2308        }
2309        target_struct.members.push((
2310            field_access,
2311            Declaration::Function(Function {
2312                name: "update_timers".into(),
2313                signature: "() -> void".into(),
2314                statements: Some(update_timers),
2315                ..Default::default()
2316            }),
2317        ));
2318    }
2319
2320    target_struct.members.extend(
2321        generate_functions(component.functions.as_ref(), &ctx).map(|x| (Access::Public, x)),
2322    );
2323
2324    target_struct.members.push((
2325        field_access,
2326        Declaration::Function(Function {
2327            name: "init".into(),
2328            signature: format!("({}) -> void", init_parameters.join(",")),
2329            statements: Some(init),
2330            ..Default::default()
2331        }),
2332    ));
2333
2334    target_struct.members.push((
2335        field_access,
2336        Declaration::Function(Function {
2337            name: "user_init".into(),
2338            signature: "() -> void".into(),
2339            statements: Some(user_init),
2340            ..Default::default()
2341        }),
2342    ));
2343
2344    target_struct.members.push((
2345        field_access,
2346        Declaration::Function(Function {
2347            name: "layout_info".into(),
2348            signature: "(slint::cbindgen_private::Orientation o) const -> slint::cbindgen_private::LayoutInfo"
2349                .into(),
2350            statements: Some(vec![
2351                "[[maybe_unused]] auto self = this;".into(),
2352                format!(
2353                    "return o == slint::cbindgen_private::Orientation::Horizontal ? {} : {};",
2354                    compile_expression(&component.layout_info_h.borrow(), &ctx),
2355                    compile_expression(&component.layout_info_v.borrow(), &ctx)
2356                ),
2357            ]),
2358            ..Default::default()
2359        }),
2360    ));
2361
2362    let mut dispatch_item_function = |name: &str,
2363                                      signature: &str,
2364                                      forward_args: &str,
2365                                      code: Vec<String>| {
2366        let mut code = ["[[maybe_unused]] auto self = this;".into()]
2367            .into_iter()
2368            .chain(code)
2369            .collect::<Vec<_>>();
2370
2371        let mut else_ = "";
2372        for sub in &component.sub_components {
2373            let sub_sc = &ctx.compilation_unit.sub_components[sub.ty];
2374            let sub_items_count = sub_sc.child_item_count(ctx.compilation_unit);
2375            code.push(format!("{else_}if (index == {}) {{", sub.index_in_tree,));
2376            code.push(format!("    return self->{}.{name}(0{forward_args});", ident(&sub.name)));
2377            if sub_items_count > 1 {
2378                code.push(format!(
2379                    "}} else if (index >= {} && index < {}) {{",
2380                    sub.index_of_first_child_in_tree,
2381                    sub.index_of_first_child_in_tree + sub_items_count - 1
2382                        + sub_sc.repeater_count(ctx.compilation_unit)
2383                ));
2384                code.push(format!(
2385                    "    return self->{}.{name}(index - {}{forward_args});",
2386                    ident(&sub.name),
2387                    sub.index_of_first_child_in_tree - 1
2388                ));
2389            }
2390            else_ = "} else ";
2391        }
2392        let ret =
2393            if signature.contains("->") && !signature.contains("-> void") { "{}" } else { "" };
2394        code.push(format!("{else_}return {ret};"));
2395        target_struct.members.push((
2396            field_access,
2397            Declaration::Function(Function {
2398                name: name.into(),
2399                signature: signature.into(),
2400                statements: Some(code),
2401                ..Default::default()
2402            }),
2403        ));
2404    };
2405
2406    let mut item_geometry_cases = vec!["switch (index) {".to_string()];
2407    item_geometry_cases.extend(
2408        component
2409            .geometries
2410            .iter()
2411            .enumerate()
2412            .filter_map(|(i, x)| x.as_ref().map(|x| (i, x)))
2413            .map(|(index, expr)| {
2414                format!(
2415                    "    case {index}: return slint::private_api::convert_anonymous_rect({});",
2416                    compile_expression(&expr.borrow(), &ctx)
2417                )
2418            }),
2419    );
2420    item_geometry_cases.push("}".into());
2421
2422    dispatch_item_function(
2423        "item_geometry",
2424        "(uint32_t index) const -> slint::cbindgen_private::Rect",
2425        "",
2426        item_geometry_cases,
2427    );
2428
2429    let mut accessible_role_cases = vec!["switch (index) {".into()];
2430    let mut accessible_string_cases = vec!["switch ((index << 8) | uintptr_t(what)) {".into()];
2431    let mut accessibility_action_cases =
2432        vec!["switch ((index << 8) | uintptr_t(action.tag)) {".into()];
2433    let mut supported_accessibility_actions = BTreeMap::<u32, BTreeSet<_>>::new();
2434    for ((index, what), expr) in &component.accessible_prop {
2435        let e = compile_expression(&expr.borrow(), &ctx);
2436        if what == "Role" {
2437            accessible_role_cases.push(format!("    case {index}: return {e};"));
2438        } else if let Some(what) = what.strip_prefix("Action") {
2439            let has_args = matches!(&*expr.borrow(), llr::Expression::CallBackCall { arguments, .. } if !arguments.is_empty());
2440
2441            accessibility_action_cases.push(if has_args {
2442                let member = ident(&crate::generator::to_kebab_case(what));
2443                format!("    case ({index} << 8) | uintptr_t(slint::cbindgen_private::AccessibilityAction::Tag::{what}): {{ auto arg_0 = action.{member}._0; return {e}; }}")
2444            } else {
2445                format!("    case ({index} << 8) | uintptr_t(slint::cbindgen_private::AccessibilityAction::Tag::{what}): return {e};")
2446            });
2447            supported_accessibility_actions
2448                .entry(*index)
2449                .or_default()
2450                .insert(format!("slint::cbindgen_private::SupportedAccessibilityAction_{what}"));
2451        } else {
2452            accessible_string_cases.push(format!("    case ({index} << 8) | uintptr_t(slint::cbindgen_private::AccessibleStringProperty::{what}): return {e};"));
2453        }
2454    }
2455    accessible_role_cases.push("}".into());
2456    accessible_string_cases.push("}".into());
2457    accessibility_action_cases.push("}".into());
2458
2459    let mut supported_accessibility_actions_cases = vec!["switch (index) {".into()];
2460    supported_accessibility_actions_cases.extend(supported_accessibility_actions.into_iter().map(
2461        |(index, values)| format!("    case {index}: return {};", values.into_iter().join("|")),
2462    ));
2463    supported_accessibility_actions_cases.push("}".into());
2464
2465    dispatch_item_function(
2466        "accessible_role",
2467        "(uint32_t index) const -> slint::cbindgen_private::AccessibleRole",
2468        "",
2469        accessible_role_cases,
2470    );
2471    dispatch_item_function(
2472        "accessible_string_property",
2473        "(uint32_t index, slint::cbindgen_private::AccessibleStringProperty what) const -> std::optional<slint::SharedString>",
2474        ", what",
2475        accessible_string_cases,
2476    );
2477
2478    dispatch_item_function(
2479        "accessibility_action",
2480        "(uint32_t index, const slint::cbindgen_private::AccessibilityAction &action) const -> void",
2481        ", action",
2482        accessibility_action_cases,
2483    );
2484
2485    dispatch_item_function(
2486        "supported_accessibility_actions",
2487        "(uint32_t index) const -> uint32_t",
2488        "",
2489        supported_accessibility_actions_cases,
2490    );
2491
2492    let mut element_infos_cases = vec!["switch (index) {".to_string()];
2493    element_infos_cases.extend(
2494        component
2495            .element_infos
2496            .iter()
2497            .map(|(index, ids)| format!("    case {index}: return \"{ids}\";")),
2498    );
2499    element_infos_cases.push("}".into());
2500
2501    dispatch_item_function(
2502        "element_infos",
2503        "(uint32_t index) const -> std::optional<slint::SharedString>",
2504        "",
2505        element_infos_cases,
2506    );
2507
2508    if !children_visitor_cases.is_empty() {
2509        target_struct.members.push((
2510            field_access,
2511            Declaration::Function(Function {
2512                name: "visit_dynamic_children".into(),
2513                signature: "(uint32_t dyn_index, [[maybe_unused]] slint::private_api::TraversalOrder order, [[maybe_unused]] slint::private_api::ItemVisitorRefMut visitor) const -> uint64_t".into(),
2514                statements: Some(vec![
2515                    "    auto self = this;".to_owned(),
2516                    format!("    switch(dyn_index) {{ {} }};", children_visitor_cases.join("")),
2517                    "    std::abort();".to_owned(),
2518                ]),
2519                ..Default::default()
2520            }),
2521        ));
2522        target_struct.members.push((
2523            field_access,
2524            Declaration::Function(Function {
2525                name: "subtree_range".into(),
2526                signature: "(uintptr_t dyn_index) const -> slint::private_api::IndexRange".into(),
2527                statements: Some(vec![
2528                    "[[maybe_unused]] auto self = this;".to_owned(),
2529                    format!("    switch(dyn_index) {{ {} }};", subtrees_ranges_cases.join("")),
2530                    "    std::abort();".to_owned(),
2531                ]),
2532                ..Default::default()
2533            }),
2534        ));
2535        target_struct.members.push((
2536            field_access,
2537            Declaration::Function(Function {
2538                name: "subtree_component".into(),
2539                signature: "(uintptr_t dyn_index, [[maybe_unused]] uintptr_t subtree_index, [[maybe_unused]] slint::private_api::ItemTreeWeak *result) const -> void".into(),
2540                statements: Some(vec![
2541                    "[[maybe_unused]] auto self = this;".to_owned(),
2542                    format!("    switch(dyn_index) {{ {} }};", subtrees_components_cases.join("")),
2543                    "    std::abort();".to_owned(),
2544                ]),
2545                ..Default::default()
2546            }),
2547        ));
2548    }
2549}
2550
2551/// Generates the `layout_item_info` member function for a repeated component struct.
2552/// Dispatches by `child_index` to per-child layout info queries, supporting static children
2553/// and inner repeaters within a row child template.
2554fn generate_layout_item_info_decl(
2555    root_sc: &llr::SubComponent,
2556    ctx: &EvaluationContext,
2557) -> Declaration {
2558    const SIGNATURE: &str = "(slint::cbindgen_private::Orientation o, [[maybe_unused]] std::optional<size_t> child_index) const -> slint::cbindgen_private::LayoutItemInfo";
2559
2560    if root_sc.row_child_templates.is_none()
2561        || (root_sc.grid_layout_children.is_empty()
2562            && !llr::has_inner_repeaters(&root_sc.row_child_templates))
2563    {
2564        return Declaration::Function(Function {
2565            name: "layout_item_info".into(),
2566            signature: SIGNATURE.to_owned(),
2567            statements: Some(vec!["return { layout_info({&static_vtable, const_cast<void *>(static_cast<const void *>(this))}, o) };".into()]),
2568            ..Function::default()
2569        });
2570    }
2571
2572    let templates = root_sc.row_child_templates.as_ref().unwrap();
2573    let n = templates.len();
2574
2575    // Generate a sequential scan through all templates in declaration order.
2576    // Count up from 0; for Static entries check count == index, for Repeated entries
2577    // check whether index falls within [count, count + inner_len).
2578    let mut body = String::from(
2579        "[[maybe_unused]] auto self = this;\n\
2580         if (child_index.has_value()) {\n\
2581             size_t index = *child_index;\n\
2582             size_t count = 0;\n",
2583    );
2584    for (i, entry) in templates.iter().enumerate() {
2585        let is_last = i + 1 == n;
2586        match entry {
2587            llr::RowChildTemplateInfo::Static { child_index } => {
2588                let child = &root_sc.grid_layout_children[*child_index];
2589                let layout_info_h_code = compile_expression(&child.layout_info_h.borrow(), ctx);
2590                let layout_info_v_code = compile_expression(&child.layout_info_v.borrow(), ctx);
2591                let advance = if is_last { String::new() } else { "count += 1;\n".to_owned() };
2592                write!(
2593                    body,
2594                    "if (count == index) {{\n\
2595                         return {{ (o == slint::cbindgen_private::Orientation::Horizontal) ? ({layout_info_h_code}) : ({layout_info_v_code}) }};\n\
2596                     }}\n\
2597                     {advance}",
2598                )
2599                .unwrap();
2600            }
2601            llr::RowChildTemplateInfo::Repeated { repeater_index } => {
2602                let inner_rep_id = format!("repeater_{}", usize::from(*repeater_index));
2603                let advance =
2604                    if is_last { String::new() } else { "count += inner_len;\n".to_owned() };
2605                write!(
2606                    body,
2607                    "{{\n\
2608                     size_t inner_len = {inner_rep_id}.len();\n\
2609                     if (index >= count && index - count < inner_len) {{\n\
2610                         if (auto vrc = {inner_rep_id}.instance_at(index - count).lock()) {{\n\
2611                             auto vref = vrc->borrow();\n\
2612                             return {{ vref.vtable->layout_info(vref, o) }};\n\
2613                         }}\n\
2614                     }}\n\
2615                     {advance}}}\n",
2616                )
2617                .unwrap();
2618            }
2619        }
2620    }
2621    body.push_str(
2622        // Phantom cell: return "unconstrained" info (matches Rust's LayoutInfo::default()).
2623        // field order: max, max_percent, min, min_percent, preferred, stretch
2624        "return { slint::cbindgen_private::LayoutInfo{ std::numeric_limits<float>::max(), 100.f, 0, 0, 0, 0 } };\n\
2625         }\n\
2626         return { layout_info({&static_vtable, const_cast<void *>(static_cast<const void *>(this))}, o) };",
2627    );
2628    Declaration::Function(Function {
2629        name: "layout_item_info".into(),
2630        signature: SIGNATURE.to_owned(),
2631        statements: Some(vec![body]),
2632        ..Function::default()
2633    })
2634}
2635
2636fn generate_flexbox_layout_item_info_decl(
2637    root_sc: &llr::SubComponent,
2638    ctx: &EvaluationContext,
2639) -> Declaration {
2640    const SIGNATURE: &str = "(slint::cbindgen_private::Orientation o, [[maybe_unused]] std::optional<size_t> child_index) const -> slint::cbindgen_private::FlexboxLayoutItemInfo";
2641
2642    let body = if let Some(expr) = &root_sc.flexbox_layout_item_info_for_repeated {
2643        let compiled = compile_expression(&expr.borrow(), ctx);
2644        format!(
2645            "[[maybe_unused]] auto self = this; \
2646             auto info = {compiled}; \
2647             info.constraint = layout_item_info(o, child_index).constraint; \
2648             return info;"
2649        )
2650    } else {
2651        "auto base = layout_item_info(o, child_index); \
2652         return { base.constraint, 0.0f, 0.0f, -1.0f, slint::cbindgen_private::FlexboxLayoutAlignSelf::Auto, 0 };"
2653            .to_owned()
2654    };
2655
2656    Declaration::Function(Function {
2657        name: "flexbox_layout_item_info".into(),
2658        signature: SIGNATURE.to_owned(),
2659        statements: Some(vec![body]),
2660        ..Function::default()
2661    })
2662}
2663
2664/// Generates the `grid_layout_input_for_repeated` member function for a repeated component struct,
2665/// or returns `None` if the sub-component doesn't participate in a grid layout as a repeated row.
2666fn generate_grid_layout_input_decl(
2667    root_sc: &llr::SubComponent,
2668    ctx: &EvaluationContext,
2669) -> Option<Declaration> {
2670    let expr = root_sc.grid_layout_input_for_repeated.as_ref()?;
2671    let compiled_expr = compile_expression(&expr.borrow(), ctx);
2672    // Ensure the expression is terminated as a statement (CodeBlock with 1 item doesn't add semicolon)
2673    let statement =
2674        if compiled_expr.is_empty() || compiled_expr.ends_with(';') || compiled_expr.ends_with('}')
2675        {
2676            compiled_expr
2677        } else {
2678            format!("{compiled_expr};")
2679        };
2680
2681    // Generate fill code for all template children in declaration order
2682    let fn_body: Vec<String> = if llr::has_inner_repeaters(&root_sc.row_child_templates) {
2683        let templates = root_sc.row_child_templates.as_ref().unwrap();
2684        let static_count = llr::static_child_count(templates);
2685        let auto_val = i_slint_common::ROW_COL_AUTO;
2686        // When static children are present: fill them via the compiled expression into a temp
2687        // array, then interleave with inner-repeater cells in declaration order.
2688        // When there are no static children: skip the array/index variables entirely to avoid
2689        // unused-variable warnings when compiling the generated C++ with -Werror.
2690        let mut fill_code = if static_count > 0 {
2691            format!(
2692                "std::array<slint::cbindgen_private::GridLayoutInputData, {static_count}> statics{{}};\n\
2693                 {{\n\
2694                     // Intentionally shadows the outer `result` so the compiled statement fills `statics`.\n\
2695                     auto result = std::span<slint::cbindgen_private::GridLayoutInputData>{{statics.data(), statics.size()}};\n\
2696                     {statement}\n\
2697                 }}\n\
2698                 size_t static_idx = 0;\n\
2699                 size_t write_idx = 0;\n"
2700            )
2701        } else {
2702            String::from("size_t write_idx = 0;\n")
2703        };
2704        for entry in templates {
2705            match entry {
2706                llr::RowChildTemplateInfo::Static { .. } => {
2707                    write!(
2708                        fill_code,
2709                        "if (write_idx < result.size()) {{\n\
2710                             auto data = statics[static_idx];\n\
2711                             data.new_row = (write_idx == 0) && new_row;\n\
2712                             result[write_idx] = data;\n\
2713                         }}\n\
2714                         ++write_idx; ++static_idx;\n"
2715                    )
2716                    .unwrap();
2717                }
2718                llr::RowChildTemplateInfo::Repeated { repeater_index } => {
2719                    let inner_rep_id = format!("repeater_{}", usize::from(*repeater_index));
2720                    write!(
2721                        fill_code,
2722                        "this->{inner_rep_id}.ensure_updated(this);\n\
2723                         {inner_rep_id}.for_each([&]([[maybe_unused]] const auto &) {{\n\
2724                             if (write_idx < result.size()) {{\n\
2725                                 result[write_idx] = slint::cbindgen_private::GridLayoutInputData {{ (write_idx == 0) && new_row, {auto_val:.1}f, {auto_val:.1}f, 1.0f, 1.0f }};\n\
2726                             }}\n\
2727                             ++write_idx;\n\
2728                         }});\n"
2729                    )
2730                    .unwrap();
2731                }
2732            }
2733        }
2734        // Padding loop: fill remaining slots with sentinel values. C++ zero-initializes
2735        // result (col=0, row=0), so we need to specify auto explicitly.
2736        write!(
2737            fill_code,
2738            "while (write_idx < result.size()) {{\n\
2739                 result[write_idx] = slint::cbindgen_private::GridLayoutInputData {{ false, {auto_val:.1}f, {auto_val:.1}f, 1.0f, 1.0f }};\n\
2740                 ++write_idx;\n\
2741             }}\n"
2742        )
2743        .unwrap();
2744        vec!["[[maybe_unused]] auto self = this;".into(), fill_code]
2745    } else {
2746        vec!["[[maybe_unused]] auto self = this;".into(), statement]
2747    };
2748
2749    Some(Declaration::Function(Function {
2750        name: "grid_layout_input_for_repeated".into(),
2751        signature: "([[maybe_unused]] bool new_row, [[maybe_unused]] std::span<slint::cbindgen_private::GridLayoutInputData> result) const -> void"
2752            .to_owned(),
2753        statements: Some(fn_body),
2754        ..Function::default()
2755    }))
2756}
2757
2758fn generate_repeated_component(
2759    repeated: &llr::RepeatedElement,
2760    unit: &llr::CompilationUnit,
2761    parent_ctx: ParentScope,
2762    model_data_type: Option<&Type>,
2763    file: &mut File,
2764    conditional_includes: &ConditionalIncludes,
2765) {
2766    let root_sc = &unit.sub_components[repeated.sub_tree.root];
2767    let repeater_id = ident(&root_sc.name);
2768    let mut repeater_struct = Struct { name: repeater_id.clone(), ..Default::default() };
2769    generate_item_tree(
2770        &mut repeater_struct,
2771        &repeated.sub_tree,
2772        unit,
2773        Some(&parent_ctx),
2774        false,
2775        repeater_id.clone(),
2776        Access::Public,
2777        file,
2778        conditional_includes,
2779    );
2780
2781    let ctx = EvaluationContext {
2782        compilation_unit: unit,
2783        current_scope: EvaluationScope::SubComponent(repeated.sub_tree.root, Some(&parent_ctx)),
2784        generator_state: CppGeneratorContext {
2785            global_access: "self->globals".into(),
2786            conditional_includes,
2787        },
2788        argument_types: &[],
2789    };
2790
2791    let access_prop = |idx: &llr::PropertyIdx| {
2792        access_member(
2793            &llr::LocalMemberReference { sub_component_path: Vec::new(), reference: (*idx).into() }
2794                .into(),
2795            &ctx,
2796        )
2797        .unwrap()
2798    };
2799    let index_prop = repeated.index_prop.iter().map(access_prop);
2800    let data_prop = repeated.data_prop.iter().map(access_prop);
2801
2802    if let Some(model_data_type) = model_data_type {
2803        let mut update_statements = vec!["[[maybe_unused]] auto self = this;".into()];
2804        update_statements.extend(index_prop.map(|prop| format!("{prop}.set(i);")));
2805        update_statements.extend(data_prop.map(|prop| format!("{prop}.set(data);")));
2806
2807        repeater_struct.members.push((
2808            Access::Public, // Because Repeater accesses it
2809            Declaration::Function(Function {
2810                name: "update_data".into(),
2811                signature: format!(
2812                    "([[maybe_unused]] int i, [[maybe_unused]] const {} &data) const -> void",
2813                    model_data_type.cpp_type().unwrap()
2814                ),
2815                statements: Some(update_statements),
2816                ..Function::default()
2817            }),
2818        ));
2819    }
2820
2821    repeater_struct.members.push((
2822        Access::Public, // Because Repeater accesses it
2823        Declaration::Function(Function {
2824            name: "init".into(),
2825            signature: "() -> void".into(),
2826            statements: Some(vec!["user_init();".into()]),
2827            ..Function::default()
2828        }),
2829    ));
2830
2831    if let Some(listview) = &repeated.listview {
2832        let p_y = access_member(&listview.prop_y, &ctx).unwrap();
2833        let p_height = access_member(&listview.prop_height, &ctx).unwrap();
2834
2835        repeater_struct.members.push((
2836            Access::Public, // Because Repeater accesses it
2837            Declaration::Function(Function {
2838                name: "listview_layout".into(),
2839                signature: "(float *offset_y) const -> float".to_owned(),
2840                statements: Some(vec![
2841                    "[[maybe_unused]] auto self = this;".into(),
2842                    format!("{}.set(*offset_y);", p_y),
2843                    format!("*offset_y += {}.get();", p_height),
2844                    "return layout_info({&static_vtable, const_cast<void *>(static_cast<const void *>(this))}, slint::cbindgen_private::Orientation::Horizontal).min;".into(),
2845                ]),
2846                ..Function::default()
2847            }),
2848        ));
2849    } else {
2850        repeater_struct.members.push((
2851            Access::Public, // Because Repeater accesses it
2852            generate_layout_item_info_decl(root_sc, &ctx),
2853        ));
2854        repeater_struct
2855            .members
2856            .push((Access::Public, generate_flexbox_layout_item_info_decl(root_sc, &ctx)));
2857        if let Some(decl) = generate_grid_layout_input_decl(root_sc, &ctx) {
2858            repeater_struct.members.push((Access::Public, decl));
2859        }
2860    }
2861
2862    if let Some(index_prop) = repeated.index_prop {
2863        // Override default subtree_index function implementation
2864        let subtree_index_func = repeater_struct
2865            .members
2866            .iter_mut()
2867            .find(|(_, d)| matches!(d, Declaration::Function(f) if f.name == "subtree_index"));
2868
2869        if let Declaration::Function(f) = &mut subtree_index_func.unwrap().1 {
2870            let index = access_prop(&index_prop);
2871            f.statements = Some(vec![
2872                format!(
2873                    "auto self = reinterpret_cast<const {}*>(component.instance);",
2874                    repeater_id
2875                ),
2876                format!("return {index}.get();"),
2877            ]);
2878        }
2879    }
2880
2881    file.definitions.extend(repeater_struct.extract_definitions().collect::<Vec<_>>());
2882    file.declarations.push(Declaration::Struct(repeater_struct));
2883}
2884
2885fn generate_global(
2886    file: &mut File,
2887    conditional_includes: &ConditionalIncludes,
2888    global_idx: llr::GlobalIdx,
2889    global: &llr::GlobalComponent,
2890    root: &llr::CompilationUnit,
2891) {
2892    let mut global_struct = Struct { name: ident(&global.name), ..Default::default() };
2893
2894    for property in global.properties.iter() {
2895        let cpp_name = ident(&property.name);
2896        let ty =
2897            format_smolstr!("slint::private_api::Property<{}>", property.ty.cpp_type().unwrap());
2898        global_struct.members.push((
2899            // FIXME: this is public (and also was public in the pre-llr generator) because other generated code accesses the
2900            // fields directly. But it shouldn't be from an API point of view since the same `global_struct` class is public API
2901            // when the global is exported and exposed in the public component.
2902            Access::Public,
2903            Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2904        ));
2905    }
2906    for callback in global.callbacks.iter().filter(|p| p.use_count.get() > 0) {
2907        let cpp_name = ident(&callback.name);
2908        let param_types = callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
2909        let ty = format_smolstr!(
2910            "slint::private_api::Callback<{}({})>",
2911            callback.ret_ty.cpp_type().unwrap(),
2912            param_types.join(", ")
2913        );
2914        global_struct.members.push((
2915            // FIXME: this is public (and also was public in the pre-llr generator) because other generated code accesses the
2916            // fields directly. But it shouldn't be from an API point of view since the same `global_struct` class is public API
2917            // when the global is exported and exposed in the public component.
2918            Access::Public,
2919            Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2920        ));
2921    }
2922
2923    let mut init = vec!["(void)this->globals;".into()];
2924    let ctx = EvaluationContext::new_global(
2925        root,
2926        global_idx,
2927        CppGeneratorContext { global_access: "this->globals".into(), conditional_includes },
2928    );
2929
2930    for (property_index, expression) in &global.init_values {
2931        handle_property_init(
2932            &llr::LocalMemberReference::from(property_index.clone()).into(),
2933            expression,
2934            &mut init,
2935            &ctx,
2936        )
2937    }
2938
2939    for (i, _) in global.change_callbacks.iter() {
2940        global_struct.members.push((
2941            Access::Private,
2942            Declaration::Var(Var {
2943                ty: "slint::private_api::ChangeTracker".into(),
2944                name: format_smolstr!("change_tracker{}", usize::from(*i)),
2945                ..Default::default()
2946            }),
2947        ));
2948    }
2949
2950    init.extend(global.change_callbacks.iter().map(|(p, e)| {
2951        let code = compile_expression(&e.borrow(), &ctx);
2952        let prop = access_member(&llr::LocalMemberReference::from(*p).into(), &ctx);
2953        prop.then(|prop| {
2954            format!("this->change_tracker{}.init(this, [this]([[maybe_unused]] auto self) {{ return {prop}.get(); }}, [this]([[maybe_unused]] auto self, auto) {{ {code}; }});", usize::from(*p))
2955        })
2956    }));
2957
2958    global_struct.members.push((
2959        Access::Public,
2960        Declaration::Function(Function {
2961            name: ident(&global.name),
2962            signature: "(const class SharedGlobals *globals)".into(),
2963            is_constructor_or_destructor: true,
2964            statements: Some(Vec::new()),
2965            constructor_member_initializers: vec!["globals(globals)".into()],
2966            ..Default::default()
2967        }),
2968    ));
2969    global_struct.members.push((
2970        Access::Private,
2971        Declaration::Function(Function {
2972            name: ident("init"),
2973            signature: "() -> void".into(),
2974            statements: Some(init),
2975            ..Default::default()
2976        }),
2977    ));
2978    global_struct.members.push((
2979        Access::Private,
2980        Declaration::Var(Var {
2981            ty: "const class SharedGlobals*".into(),
2982            name: "globals".into(),
2983            ..Default::default()
2984        }),
2985    ));
2986    global_struct.friends.push(SmolStr::new_static(SHARED_GLOBAL_CLASS));
2987
2988    generate_public_api_for_properties(
2989        &mut global_struct.members,
2990        &global.public_properties,
2991        &global.private_properties,
2992        &ctx,
2993    );
2994    global_struct
2995        .members
2996        .extend(generate_functions(global.functions.as_ref(), &ctx).map(|x| (Access::Public, x)));
2997
2998    file.definitions.extend(global_struct.extract_definitions().collect::<Vec<_>>());
2999    file.declarations.push(Declaration::Struct(global_struct));
3000}
3001
3002fn generate_global_builtin(
3003    file: &mut File,
3004    conditional_includes: &ConditionalIncludes,
3005    global_idx: llr::GlobalIdx,
3006    global: &llr::GlobalComponent,
3007    root: &llr::CompilationUnit,
3008) {
3009    let mut global_struct = Struct { name: ident(&global.name), ..Default::default() };
3010    let ctx = EvaluationContext::new_global(
3011        root,
3012        global_idx,
3013        CppGeneratorContext {
3014            global_access: "\n#error binding in builtin global\n".into(),
3015            conditional_includes,
3016        },
3017    );
3018
3019    global_struct.members.push((
3020        Access::Public,
3021        Declaration::Function(Function {
3022            name: ident(&global.name),
3023            signature: format!(
3024                "(std::shared_ptr<slint::cbindgen_private::{}> builtin)",
3025                ident(&global.name)
3026            ),
3027            is_constructor_or_destructor: true,
3028            statements: Some(Vec::new()),
3029            constructor_member_initializers: vec!["builtin(std::move(builtin))".into()],
3030            ..Default::default()
3031        }),
3032    ));
3033    global_struct.members.push((
3034        Access::Private,
3035        Declaration::Var(Var {
3036            ty: format_smolstr!(
3037                "std::shared_ptr<slint::cbindgen_private::{}>",
3038                ident(&global.name)
3039            ),
3040            name: "builtin".into(),
3041            ..Default::default()
3042        }),
3043    ));
3044    global_struct.friends.push(SmolStr::new_static(SHARED_GLOBAL_CLASS));
3045
3046    generate_public_api_for_properties(
3047        &mut global_struct.members,
3048        &global.public_properties,
3049        &global.private_properties,
3050        &ctx,
3051    );
3052    file.definitions.extend(global_struct.extract_definitions().collect::<Vec<_>>());
3053    file.declarations.push(Declaration::Struct(global_struct));
3054}
3055
3056fn generate_functions<'a>(
3057    functions: &'a [llr::Function],
3058    ctx: &'a EvaluationContext<'_>,
3059) -> impl Iterator<Item = Declaration> + 'a {
3060    functions.iter().map(|f| {
3061        let mut ctx2 = ctx.clone();
3062        ctx2.argument_types = &f.args;
3063        let ret = if f.ret_ty != Type::Void { "return " } else { "" };
3064        let body = vec![
3065            "[[maybe_unused]] auto self = this;".into(),
3066            format!("{ret}{};", compile_expression(&f.code, &ctx2)),
3067        ];
3068        Declaration::Function(Function {
3069            name: concatenate_ident(&format_smolstr!("fn_{}", f.name)),
3070            signature: format!(
3071                "({}) const -> {}",
3072                f.args
3073                    .iter()
3074                    .enumerate()
3075                    .map(|(i, ty)| format!("[[maybe_unused]] {} arg_{}", ty.cpp_type().unwrap(), i))
3076                    .join(", "),
3077                f.ret_ty.cpp_type().unwrap()
3078            ),
3079            statements: Some(body),
3080            ..Default::default()
3081        })
3082    })
3083}
3084
3085fn generate_public_api_for_properties(
3086    declarations: &mut Vec<(Access, Declaration)>,
3087    public_properties: &llr::PublicProperties,
3088    private_properties: &llr::PrivateProperties,
3089    ctx: &EvaluationContext,
3090) {
3091    for p in public_properties {
3092        let prop_ident = concatenate_ident(&p.name);
3093
3094        let access = access_member(&p.prop, ctx).unwrap();
3095
3096        if let Type::Callback(callback) = &p.ty {
3097            let param_types =
3098                callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
3099            let callback_emitter = vec![
3100                "slint::private_api::assert_main_thread();".into(),
3101                "[[maybe_unused]] auto self = this;".into(),
3102                format!(
3103                    "return {}.call({});",
3104                    access,
3105                    (0..callback.args.len()).map(|i| format!("arg_{i}")).join(", ")
3106                ),
3107            ];
3108            declarations.push((
3109                Access::Public,
3110                Declaration::Function(Function {
3111                    name: format_smolstr!("invoke_{prop_ident}"),
3112                    signature: format!(
3113                        "({}) const -> {}",
3114                        param_types
3115                            .iter()
3116                            .enumerate()
3117                            .map(|(i, ty)| format!("{ty} arg_{i}"))
3118                            .join(", "),
3119                        callback.return_type.cpp_type().unwrap()
3120                    ),
3121                    statements: Some(callback_emitter),
3122                    ..Default::default()
3123                }),
3124            ));
3125            declarations.push((
3126                Access::Public,
3127                Declaration::Function(Function {
3128                    name: format_smolstr!("on_{}", concatenate_ident(&p.name)),
3129                    template_parameters: Some(format!(
3130                        "std::invocable<{}> Functor",
3131                        param_types.join(", "),
3132                    )),
3133                    signature: "(Functor && callback_handler) const".into(),
3134                    statements: Some(vec![
3135                        "slint::private_api::assert_main_thread();".into(),
3136                        "[[maybe_unused]] auto self = this;".into(),
3137                        format!("{}.set_handler(std::forward<Functor>(callback_handler));", access),
3138                    ]),
3139                    ..Default::default()
3140                }),
3141            ));
3142        } else if let Type::Function(function) = &p.ty {
3143            let param_types =
3144                function.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
3145            let ret = function.return_type.cpp_type().unwrap();
3146            let call_code = vec![
3147                "[[maybe_unused]] auto self = this;".into(),
3148                format!(
3149                    "{}{access}({});",
3150                    if function.return_type == Type::Void { "" } else { "return " },
3151                    (0..function.args.len()).map(|i| format!("arg_{i}")).join(", ")
3152                ),
3153            ];
3154            declarations.push((
3155                Access::Public,
3156                Declaration::Function(Function {
3157                    name: format_smolstr!("invoke_{}", concatenate_ident(&p.name)),
3158                    signature: format!(
3159                        "({}) const -> {ret}",
3160                        param_types
3161                            .iter()
3162                            .enumerate()
3163                            .map(|(i, ty)| format!("{ty} arg_{i}"))
3164                            .join(", "),
3165                    ),
3166                    statements: Some(call_code),
3167                    ..Default::default()
3168                }),
3169            ));
3170        } else {
3171            let cpp_property_type = p.ty.cpp_type().expect("Invalid type in public properties");
3172            let prop_getter: Vec<String> = vec![
3173                "slint::private_api::assert_main_thread();".into(),
3174                "[[maybe_unused]] auto self = this;".into(),
3175                format!("return {}.get();", access),
3176            ];
3177            declarations.push((
3178                Access::Public,
3179                Declaration::Function(Function {
3180                    name: format_smolstr!("get_{}", &prop_ident),
3181                    signature: format!("() const -> {}", &cpp_property_type),
3182                    statements: Some(prop_getter),
3183                    ..Default::default()
3184                }),
3185            ));
3186
3187            if !p.read_only {
3188                let prop_setter: Vec<String> = vec![
3189                    "slint::private_api::assert_main_thread();".into(),
3190                    "[[maybe_unused]] auto self = this;".into(),
3191                    property_set_value_code(&p.prop, "value", ctx) + ";",
3192                ];
3193                declarations.push((
3194                    Access::Public,
3195                    Declaration::Function(Function {
3196                        name: format_smolstr!("set_{}", &prop_ident),
3197                        signature: format!("(const {} &value) const -> void", &cpp_property_type),
3198                        statements: Some(prop_setter),
3199                        ..Default::default()
3200                    }),
3201                ));
3202            } else {
3203                declarations.push((
3204                    Access::Private,
3205                    Declaration::Function(Function {
3206                        name: format_smolstr!("set_{}", &prop_ident),
3207                        signature: format!(
3208                            "(const {cpp_property_type} &) const = delete /* property '{}' is declared as 'out' (read-only). Declare it as 'in' or 'in-out' to enable the setter */", p.name
3209                        ),
3210                        ..Default::default()
3211                    }),
3212                ));
3213            }
3214        }
3215    }
3216
3217    for (name, ty) in private_properties {
3218        let prop_ident = concatenate_ident(name);
3219
3220        if let Type::Function(function) = &ty {
3221            let param_types = function.args.iter().map(|t| t.cpp_type().unwrap()).join(", ");
3222            declarations.push((
3223                Access::Private,
3224                Declaration::Function(Function {
3225                    name: format_smolstr!("invoke_{prop_ident}"),
3226                    signature: format!(
3227                        "({param_types}) const = delete /* the function '{name}' is declared as private. Declare it as 'public' */",
3228                    ),
3229                    ..Default::default()
3230                }),
3231            ));
3232        } else {
3233            declarations.push((
3234                Access::Private,
3235                Declaration::Function(Function {
3236                    name: format_smolstr!("get_{prop_ident}"),
3237                    signature: format!(
3238                        "() const = delete /* the property '{name}' is declared as private. Declare it as 'in', 'out', or 'in-out' to make it public */",
3239                    ),
3240                    ..Default::default()
3241                }),
3242            ));
3243            declarations.push((
3244                Access::Private,
3245                Declaration::Function(Function {
3246                    name: format_smolstr!("set_{}", &prop_ident),
3247                    signature: format!(
3248                        "(const auto &) const = delete /* property '{name}' is declared as private. Declare it as 'in' or 'in-out' to make it public */",
3249                    ),
3250                    ..Default::default()
3251                }),
3252            ));
3253        }
3254    }
3255}
3256
3257fn follow_sub_component_path<'a>(
3258    compilation_unit: &'a llr::CompilationUnit,
3259    root: llr::SubComponentIdx,
3260    sub_component_path: &[llr::SubComponentInstanceIdx],
3261) -> (String, &'a llr::SubComponent) {
3262    let mut compo_path = String::new();
3263    let mut sub_component = &compilation_unit.sub_components[root];
3264    for i in sub_component_path {
3265        let sub_component_name = ident(&sub_component.sub_components[*i].name);
3266        write!(compo_path, "{sub_component_name}.").unwrap();
3267        sub_component = &compilation_unit.sub_components[sub_component.sub_components[*i].ty];
3268    }
3269    (compo_path, sub_component)
3270}
3271
3272fn access_window_field(ctx: &EvaluationContext) -> String {
3273    format!("{}->window().window_handle()", ctx.generator_state.global_access)
3274}
3275
3276/// Returns the code that can access the given property (but without the set or get)
3277fn access_member(reference: &llr::MemberReference, ctx: &EvaluationContext) -> MemberAccess {
3278    match reference {
3279        llr::MemberReference::Relative { parent_level, local_reference } => {
3280            let mut path = MemberAccess::Direct("self".to_string());
3281            for _ in 0..*parent_level {
3282                path = path.and_then(|x| format!("{x}->parent.lock()"));
3283            }
3284            if let Some(sub_component) = ctx.parent_sub_component_idx(*parent_level) {
3285                let (compo_path, sub_component) = follow_sub_component_path(
3286                    ctx.compilation_unit,
3287                    sub_component,
3288                    &local_reference.sub_component_path,
3289                );
3290                match &local_reference.reference {
3291                    llr::LocalMemberIndex::Property(property_index) => {
3292                        let property_name = ident(&sub_component.properties[*property_index].name);
3293                        path.with_member(format!("->{compo_path}{property_name}"))
3294                    }
3295                    llr::LocalMemberIndex::Callback(callback_index) => {
3296                        let callback_name = ident(&sub_component.callbacks[*callback_index].name);
3297                        path.with_member(format!("->{compo_path}{callback_name}"))
3298                    }
3299                    llr::LocalMemberIndex::Function(function_index) => {
3300                        let function_name = ident(&sub_component.functions[*function_index].name);
3301                        path.with_member(format!("->{compo_path}fn_{function_name}"))
3302                    }
3303                    llr::LocalMemberIndex::Native { item_index, prop_name } => {
3304                        let item_name = ident(&sub_component.items[*item_index].name);
3305                        if prop_name.is_empty()
3306                            || matches!(
3307                                sub_component.items[*item_index].ty.lookup_property(prop_name),
3308                                Some(Type::Function { .. })
3309                            )
3310                        {
3311                            // then this is actually a reference to the element itself
3312                            // (or a call to a builtin member function)
3313                            path.with_member(format!("->{compo_path}{item_name}"))
3314                        } else {
3315                            let property_name = ident(prop_name);
3316                            path.with_member(format!("->{compo_path}{item_name}.{property_name}"))
3317                        }
3318                    }
3319                }
3320            } else if let Some(current_global) = ctx.current_global() {
3321                match &local_reference.reference {
3322                    llr::LocalMemberIndex::Property(property_index) => {
3323                        let property_name = ident(&current_global.properties[*property_index].name);
3324                        MemberAccess::Direct(format!("this->{property_name}"))
3325                    }
3326                    llr::LocalMemberIndex::Function(function_index) => {
3327                        let function_name = ident(&current_global.functions[*function_index].name);
3328                        MemberAccess::Direct(format!("this->fn_{function_name}"))
3329                    }
3330                    llr::LocalMemberIndex::Callback(callback_index) => {
3331                        let callback_name = ident(&current_global.callbacks[*callback_index].name);
3332                        MemberAccess::Direct(format!("this->{callback_name}"))
3333                    }
3334                    _ => unreachable!(),
3335                }
3336            } else {
3337                unreachable!()
3338            }
3339        }
3340        llr::MemberReference::Global { global_index, member } => {
3341            let global = &ctx.compilation_unit.globals[*global_index];
3342            let name = match member {
3343                llr::LocalMemberIndex::Property(property_index) => ident(
3344                    &ctx.compilation_unit.globals[*global_index].properties[*property_index].name,
3345                ),
3346                llr::LocalMemberIndex::Callback(callback_index) => ident(
3347                    &ctx.compilation_unit.globals[*global_index].callbacks[*callback_index].name,
3348                ),
3349                llr::LocalMemberIndex::Function(function_index) => ident(&format!(
3350                    "fn_{}",
3351                    &ctx.compilation_unit.globals[*global_index].functions[*function_index].name,
3352                )),
3353                _ => unreachable!(),
3354            };
3355            if matches!(ctx.current_scope, EvaluationScope::Global(i) if i == *global_index) {
3356                if global.is_builtin {
3357                    MemberAccess::Direct(format!("builtin->{name}"))
3358                } else {
3359                    MemberAccess::Direct(format!("this->{name}"))
3360                }
3361            } else {
3362                let global_access = &ctx.generator_state.global_access;
3363                let global_id = format!("global_{}", concatenate_ident(&global.name));
3364                MemberAccess::Direct(format!("{global_access}->{global_id}->{name}"))
3365            }
3366        }
3367    }
3368}
3369
3370fn access_local_member(reference: &llr::LocalMemberReference, ctx: &EvaluationContext) -> String {
3371    access_member(&reference.clone().into(), ctx).unwrap()
3372}
3373
3374/// Helper to access a member property/callback of a component.
3375///
3376/// Because the parent can be deleted (issue #3464), this might be an option when accessing the parent
3377#[derive(Clone)]
3378enum MemberAccess {
3379    /// The string is just an expression
3380    Direct(String),
3381    /// The string is a an expression to an `std::optional`
3382    Option(String),
3383    /// The first string is an expression to an `std::optional`,
3384    /// the second is a string to be appended after dereferncing the optional
3385    /// like so: `<1>.transform([](auto &&x) { return x<2>; })`
3386    OptionWithMember(String, String),
3387}
3388
3389impl MemberAccess {
3390    /// Used for code that is meant to return `()`
3391    fn then(&self, f: impl FnOnce(&str) -> String) -> String {
3392        match self {
3393            MemberAccess::Direct(t) => f(t),
3394            MemberAccess::Option(t) => {
3395                format!("slint::private_api::optional_then({t}, [&](auto&&x) {{ {}; }})", f("x"))
3396            }
3397            MemberAccess::OptionWithMember(t, m) => {
3398                format!(
3399                    "slint::private_api::optional_then({t}, [&](auto&&x) {{ {}; }})",
3400                    f(&format!("x{}", m))
3401                )
3402            }
3403        }
3404    }
3405
3406    fn map_or_default(&self, f: impl FnOnce(&str) -> String) -> String {
3407        match self {
3408            MemberAccess::Direct(t) => f(t),
3409            MemberAccess::Option(t) => {
3410                format!(
3411                    "slint::private_api::optional_or_default(slint::private_api::optional_transform({t}, [&](auto&&x) {{ return {}; }}))",
3412                    f("x")
3413                )
3414            }
3415            MemberAccess::OptionWithMember(t, m) => {
3416                format!(
3417                    "slint::private_api::optional_or_default(slint::private_api::optional_transform({t}, [&](auto&&x) {{ return {}; }}))",
3418                    f(&format!("x{}", m))
3419                )
3420            }
3421        }
3422    }
3423
3424    fn and_then(&self, f: impl Fn(&str) -> String) -> MemberAccess {
3425        match self {
3426            MemberAccess::Direct(t) => MemberAccess::Option(f(t)),
3427            MemberAccess::Option(t) => MemberAccess::Option(format!(
3428                "slint::private_api::optional_and_then({t}, [&](auto&&x) {{ return {}; }})",
3429                f("x")
3430            )),
3431            MemberAccess::OptionWithMember(t, m) => MemberAccess::Option(format!(
3432                "slint::private_api::optional_and_then({t}, [&](auto&&x) {{ return {}; }})",
3433                f(&format!("x{}", m))
3434            )),
3435        }
3436    }
3437
3438    fn get_property(self) -> String {
3439        self.map_or_default(|x| format!("{x}.get()"))
3440    }
3441
3442    /// To be used when we know that the reference was local
3443    #[track_caller]
3444    fn unwrap(self) -> String {
3445        match self {
3446            MemberAccess::Direct(t) => t,
3447            _ => panic!("not a local property?"),
3448        }
3449    }
3450
3451    fn with_member(self, member: String) -> MemberAccess {
3452        match self {
3453            MemberAccess::Direct(t) => MemberAccess::Direct(format!("{t}{member}")),
3454            MemberAccess::Option(t) => MemberAccess::OptionWithMember(t, member),
3455            MemberAccess::OptionWithMember(t, m) => {
3456                MemberAccess::OptionWithMember(t, format!("{m}{member}"))
3457            }
3458        }
3459    }
3460}
3461
3462/// Returns the NativeClass for a PropertyReference::InNativeItem
3463/// (or a InParent of InNativeItem )
3464/// As well as the property name
3465fn native_prop_info<'a, 'b>(
3466    item_ref: &'b llr::MemberReference,
3467    ctx: &'a EvaluationContext,
3468) -> (&'a NativeClass, &'b str) {
3469    let llr::MemberReference::Relative { parent_level, local_reference } = item_ref else {
3470        unreachable!()
3471    };
3472    let llr::LocalMemberIndex::Native { item_index, prop_name } = &local_reference.reference else {
3473        unreachable!()
3474    };
3475
3476    let (_, sub_component) = follow_sub_component_path(
3477        ctx.compilation_unit,
3478        ctx.parent_sub_component_idx(*parent_level).unwrap(),
3479        &local_reference.sub_component_path,
3480    );
3481    (&sub_component.items[*item_index].ty, prop_name)
3482}
3483
3484fn shared_string_literal(string: &str) -> String {
3485    format!(r#"slint::SharedString(u8"{}")"#, escape_string(string))
3486}
3487
3488fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String {
3489    use llr::Expression;
3490    match expr {
3491        Expression::StringLiteral(s) => shared_string_literal(s),
3492        Expression::NumberLiteral(num) => {
3493            if !num.is_finite() {
3494                // just print something
3495                "0.0".to_string()
3496            } else if num.abs() > 1_000_000_000. {
3497                // If the numbers are too big, decimal notation will give too many digit
3498                format!("{num:+e}")
3499            } else {
3500                num.to_string()
3501            }
3502        }
3503        Expression::BoolLiteral(b) => b.to_string(),
3504        Expression::KeysLiteral(ks) => {
3505            format!(
3506                "[&](const slint::SharedString &key, bool alt, bool control, bool shift, bool meta, bool ignoreShift, bool ignoreAlt) {{
3507                    slint::Keys out;
3508                    slint::private_api::make_keys(out, key, alt, control, shift, meta, ignoreShift, ignoreAlt);
3509                    return out;
3510                }}({}, {}, {}, {}, {}, {}, {})",
3511                shared_string_literal(&ks.key),
3512                ks.modifiers.alt,
3513                ks.modifiers.control,
3514                ks.modifiers.shift,
3515                ks.modifiers.meta,
3516                ks.ignore_shift,
3517                ks.ignore_alt,
3518            )
3519        }
3520        Expression::PropertyReference(nr) => access_member(nr, ctx).get_property(),
3521        Expression::BuiltinFunctionCall { function, arguments } => {
3522            compile_builtin_function_call(function.clone(), arguments, ctx)
3523        }
3524        Expression::CallBackCall { callback, arguments } => {
3525            let f = access_member(callback, ctx);
3526            let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
3527            if expr.ty(ctx) == Type::Void {
3528                f.then(|f| format!("{f}.call({})", a.join(",")))
3529            } else {
3530                f.map_or_default(|f| format!("{f}.call({})", a.join(",")))
3531            }
3532        }
3533        Expression::FunctionCall { function, arguments } => {
3534            let f = access_member(function, ctx);
3535            let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
3536            if expr.ty(ctx) == Type::Void {
3537                f.then(|f| format!("{}({})", f, a.join(",")))
3538            } else {
3539                f.map_or_default(|f| format!("{}({})", f, a.join(",")))
3540            }
3541        }
3542        Expression::ItemMemberFunctionCall { function } => {
3543            let item = access_member(function, ctx);
3544            let item_rc = access_item_rc(function, ctx);
3545            let window = access_window_field(ctx);
3546            let (native, name) = native_prop_info(function, ctx);
3547            let function_name = format!(
3548                "slint_{}_{}",
3549                native.class_name.to_lowercase(),
3550                ident(name).to_lowercase()
3551            );
3552            if expr.ty(ctx) == Type::Void {
3553                item.then(|item| {
3554                    format!("{function_name}(&{item}, &{window}.handle(), &{item_rc})")
3555                })
3556            } else {
3557                item.map_or_default(|item| {
3558                    format!("{function_name}(&{item}, &{window}.handle(), &{item_rc})")
3559                })
3560            }
3561        }
3562        Expression::ExtraBuiltinFunctionCall { function, arguments, return_ty: _ } => {
3563            let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
3564            format!("slint::private_api::{}({})", ident(function), a.join(","))
3565        }
3566        Expression::FunctionParameterReference { index, .. } => format!("arg_{index}"),
3567        Expression::StoreLocalVariable { name, value } => {
3568            format!("[[maybe_unused]] auto {} = {};", ident(name), compile_expression(value, ctx))
3569        }
3570        Expression::ReadLocalVariable { name, .. } => ident(name).to_string(),
3571        Expression::StructFieldAccess { base, name } => match base.ty(ctx) {
3572            Type::Struct(s) => struct_field_access(compile_expression(base, ctx), &s, name),
3573            _ => panic!("Expression::ObjectAccess's base expression is not an Object type"),
3574        },
3575        Expression::ArrayIndex { array, index } => {
3576            format!(
3577                "slint::private_api::access_array_index({}, {})",
3578                compile_expression(array, ctx),
3579                compile_expression(index, ctx)
3580            )
3581        }
3582        Expression::Cast { from, to } => {
3583            let f = compile_expression(from, ctx);
3584            match (from.ty(ctx), to) {
3585                (Type::Float32, Type::Int32) => {
3586                    format!("static_cast<int>({f})")
3587                }
3588                (from, Type::String) if from.as_unit_product().is_some() => {
3589                    format!("slint::SharedString::from_number({f})")
3590                }
3591                (Type::Float32, Type::Model) | (Type::Int32, Type::Model) => {
3592                    format!(
3593                        "std::make_shared<slint::private_api::UIntModel>(std::max<int>(0, {f}))"
3594                    )
3595                }
3596                (Type::Array(_), Type::Model) => f,
3597                (Type::Float32, Type::Color) => {
3598                    format!("slint::Color::from_argb_encoded({f})")
3599                }
3600                (Type::Color, Type::Brush) => {
3601                    format!("slint::Brush({f})")
3602                }
3603                (Type::Brush, Type::Color) => {
3604                    format!("{f}.color()")
3605                }
3606                (Type::Struct(lhs), Type::Struct(rhs)) => {
3607                    debug_assert_eq!(
3608                        lhs.fields, rhs.fields,
3609                        "cast of struct with deferent fields should be handled before llr"
3610                    );
3611                    match (&lhs.name, &rhs.name) {
3612                        (StructName::None, targetstruct) if targetstruct.is_some() => {
3613                            // Convert from an anonymous struct to a named one
3614                            format!(
3615                                "[&](const auto &o){{ {struct_name} s; {fields} return s; }}({obj})",
3616                                struct_name = to.cpp_type().unwrap(),
3617                                fields = lhs
3618                                    .fields
3619                                    .keys()
3620                                    .enumerate()
3621                                    .map(|(i, n)| format!("s.{} = std::get<{}>(o); ", ident(n), i))
3622                                    .join(""),
3623                                obj = f,
3624                            )
3625                        }
3626                        (sourcestruct, StructName::None) if sourcestruct.is_some() => {
3627                            // Convert from a named struct to an anonymous one
3628                            format!(
3629                                "[&](const auto &o){{ return std::make_tuple({}); }}({f})",
3630                                rhs.fields.keys().map(|n| format!("o.{}", ident(n))).join(", ")
3631                            )
3632                        }
3633                        _ => f,
3634                    }
3635                }
3636                (Type::Array(..), Type::PathData)
3637                    if matches!(
3638                        from.as_ref(),
3639                        Expression::Array { element_ty: Type::Struct { .. }, .. }
3640                    ) =>
3641                {
3642                    let path_elements = match from.as_ref() {
3643                        Expression::Array { element_ty: _, values, output: _ } => {
3644                            values.iter().map(|path_elem_expr| {
3645                                let (field_count, qualified_elem_type_name) =
3646                                    match path_elem_expr.ty(ctx) {
3647                                        Type::Struct(s) if s.name.is_some() => {
3648                                            (s.fields.len(), s.name.cpp_type().unwrap().clone())
3649                                        }
3650                                        _ => unreachable!(),
3651                                    };
3652                                // Turn slint::private_api::PathLineTo into `LineTo`
3653                                let elem_type_name = qualified_elem_type_name
3654                                    .split("::")
3655                                    .last()
3656                                    .unwrap()
3657                                    .strip_prefix("Path")
3658                                    .unwrap();
3659                                let elem_init = if field_count > 0 {
3660                                    compile_expression(path_elem_expr, ctx)
3661                                } else {
3662                                    String::new()
3663                                };
3664                                format!(
3665                                    "slint::private_api::PathElement::{elem_type_name}({elem_init})"
3666                                )
3667                            })
3668                        }
3669                        _ => {
3670                            unreachable!()
3671                        }
3672                    }
3673                    .collect::<Vec<_>>();
3674                    if !path_elements.is_empty() {
3675                        format!(
3676                            r#"[&](){{
3677                                slint::private_api::PathElement elements[{}] = {{
3678                                    {}
3679                                }};
3680                                return slint::private_api::PathData(&elements[0], std::size(elements));
3681                            }}()"#,
3682                            path_elements.len(),
3683                            path_elements.join(",")
3684                        )
3685                    } else {
3686                        "slint::private_api::PathData()".into()
3687                    }
3688                }
3689                (Type::Struct { .. }, Type::PathData)
3690                    if matches!(from.as_ref(), Expression::Struct { .. }) =>
3691                {
3692                    let (events, points) = match from.as_ref() {
3693                        Expression::Struct { ty: _, values } => (
3694                            compile_expression(&values["events"], ctx),
3695                            compile_expression(&values["points"], ctx),
3696                        ),
3697                        _ => {
3698                            unreachable!()
3699                        }
3700                    };
3701                    format!(
3702                        r#"[&](auto events, auto points){{
3703                            return slint::private_api::PathData(events.ptr, events.len, points.ptr, points.len);
3704                        }}({events}, {points})"#
3705                    )
3706                }
3707                (Type::Enumeration(e), Type::String) => {
3708                    let mut cases = e.values.iter().enumerate().map(|(idx, v)| {
3709                        let c = compile_expression(
3710                            &Expression::EnumerationValue(EnumerationValue {
3711                                value: idx,
3712                                enumeration: e.clone(),
3713                            }),
3714                            ctx,
3715                        );
3716                        format!("case {c}: return {v:?};")
3717                    });
3718                    format!(
3719                        "[&]() -> slint::SharedString {{ switch ({f}) {{ {} default: return {{}}; }} }}()",
3720                        cases.join(" ")
3721                    )
3722                }
3723                _ => f,
3724            }
3725        }
3726        Expression::CodeBlock(sub) => match sub.len() {
3727            0 => String::new(),
3728            1 => compile_expression(&sub[0], ctx),
3729            len => {
3730                let mut x = sub.iter().enumerate().map(|(i, e)| {
3731                    if i == len - 1 {
3732                        return_compile_expression(e, ctx, None) + ";"
3733                    } else {
3734                        compile_expression(e, ctx)
3735                    }
3736                });
3737                format!("[&]{{ {} }}()", x.join(";"))
3738            }
3739        },
3740        Expression::PropertyAssignment { property, value } => {
3741            let value = compile_expression(value, ctx);
3742            property_set_value_code(property, &value, ctx)
3743        }
3744        Expression::ModelDataAssignment { level, value } => {
3745            let value = compile_expression(value, ctx);
3746            let mut path = "self".to_string();
3747            let EvaluationScope::SubComponent(mut sc, mut par) = ctx.current_scope else {
3748                unreachable!()
3749            };
3750            let mut repeater_index = None;
3751            for _ in 0..=*level {
3752                let x = par.unwrap();
3753                par = x.parent;
3754                repeater_index = x.repeater_index;
3755                sc = x.sub_component;
3756                write!(path, "->parent.lock().value()").unwrap();
3757            }
3758            let repeater_index = repeater_index.unwrap();
3759            let local_reference = ctx.compilation_unit.sub_components[sc].repeated[repeater_index]
3760                .index_prop
3761                .unwrap()
3762                .into();
3763            let index_prop =
3764                llr::MemberReference::Relative { parent_level: *level, local_reference };
3765            let index_access = access_member(&index_prop, ctx).get_property();
3766            write!(path, "->repeater_{}", usize::from(repeater_index)).unwrap();
3767            format!("{path}.model_set_row_data({index_access}, {value})")
3768        }
3769        Expression::ArrayIndexAssignment { array, index, value } => {
3770            debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
3771            let base_e = compile_expression(array, ctx);
3772            let index_e = compile_expression(index, ctx);
3773            let value_e = compile_expression(value, ctx);
3774            format!(
3775                "[&](auto index, const auto &base) {{ if (index >= 0. && std::size_t(index) < base->row_count()) base->set_row_data(index, {value_e}); }}({index_e}, {base_e})"
3776            )
3777        }
3778        Expression::SliceIndexAssignment { slice_name, index, value } => {
3779            let value_e = compile_expression(value, ctx);
3780            format!("{slice_name}[{index}] = {value_e}")
3781        }
3782        Expression::BinaryExpression { lhs, rhs, op } => {
3783            let lhs_str = compile_expression(lhs, ctx);
3784            let rhs_str = compile_expression(rhs, ctx);
3785
3786            let lhs_ty = lhs.ty(ctx);
3787
3788            if lhs_ty.as_unit_product().is_some() && (*op == '=' || *op == '!') {
3789                let op = if *op == '=' { "<" } else { ">=" };
3790                format!(
3791                    "(std::abs(float({lhs_str} - {rhs_str})) {op} std::numeric_limits<float>::epsilon())"
3792                )
3793            } else {
3794                let mut buffer = [0; 3];
3795                format!(
3796                    "({lhs_str} {op} {rhs_str})",
3797                    op = match op {
3798                        '=' => "==",
3799                        '!' => "!=",
3800                        '≤' => "<=",
3801                        '≥' => ">=",
3802                        '&' => "&&",
3803                        '|' => "||",
3804                        '/' => "/(float)",
3805                        '-' => "-(float)", // conversion to float to avoid overflow between unsigned
3806                        _ => op.encode_utf8(&mut buffer),
3807                    },
3808                )
3809            }
3810        }
3811        Expression::UnaryOp { sub, op } => {
3812            format!("({op} {sub})", sub = compile_expression(sub, ctx), op = op,)
3813        }
3814        Expression::ImageReference { resource_ref, nine_slice } => {
3815            let image = match resource_ref {
3816                crate::expression_tree::ImageReference::None => r#"slint::Image()"#.to_string(),
3817                crate::expression_tree::ImageReference::AbsolutePath(path) => format!(
3818                    r#"slint::Image::load_from_path(slint::SharedString(u8"{}"))"#,
3819                    escape_string(path.as_str())
3820                ),
3821                crate::expression_tree::ImageReference::EmbeddedData { resource_id, extension } => {
3822                    let symbol = format!("slint_embedded_resource_{resource_id}");
3823                    format!(
3824                        r#"slint::private_api::load_image_from_embedded_data({symbol}, "{}")"#,
3825                        escape_string(extension)
3826                    )
3827                }
3828                crate::expression_tree::ImageReference::EmbeddedTexture { resource_id } => {
3829                    format!(
3830                        "slint::private_api::image_from_embedded_textures(&slint_embedded_resource_{resource_id})"
3831                    )
3832                }
3833            };
3834            match &nine_slice {
3835                Some([a, b, c, d]) => {
3836                    format!(
3837                        "([&] {{ auto image = {image}; image.set_nine_slice_edges({a}, {b}, {c}, {d}); return image; }})()"
3838                    )
3839                }
3840                None => image,
3841            }
3842        }
3843        Expression::Condition { condition, true_expr, false_expr } => {
3844            let ty = expr.ty(ctx);
3845            let cond_code = compile_expression(condition, ctx);
3846            let cond_code = remove_parentheses(&cond_code);
3847            let true_code = compile_expression(true_expr, ctx);
3848            let false_code = compile_expression(false_expr, ctx);
3849            if ty == Type::Void {
3850                format!("if ({cond_code}) {{ {true_code}; }} else {{ {false_code}; }}")
3851            } else {
3852                format!("({cond_code} ? {true_code} : {false_code})")
3853            }
3854        }
3855        Expression::Array { element_ty, values, output } => {
3856            let ty = element_ty.cpp_type().unwrap();
3857            let mut val = values
3858                .iter()
3859                .map(|e| format!("{ty} ( {expr} )", expr = compile_expression(e, ctx), ty = ty));
3860            match output {
3861                llr::ArrayOutput::Model => format!(
3862                    "std::make_shared<slint::private_api::ArrayModel<{count},{ty}>>({val})",
3863                    count = values.len(),
3864                    ty = ty,
3865                    val = val.join(", ")
3866                ),
3867                llr::ArrayOutput::Slice => format!(
3868                    "slint::private_api::make_slice<{ty}>(std::array<{ty}, {count}>{{ {val} }}.data(), {count})",
3869                    count = values.len(),
3870                    ty = ty,
3871                    val = val.join(", ")
3872                ),
3873                llr::ArrayOutput::Vector => {
3874                    format!("std::vector<{ty}>{{ {val} }}", ty = ty, val = val.join(", "))
3875                }
3876            }
3877        }
3878        Expression::Struct { ty, values } => {
3879            if ty.name.is_none() {
3880                let mut elem = ty.fields.iter().map(|(k, t)| {
3881                    values
3882                        .get(k)
3883                        .map(|e| compile_expression(e, ctx))
3884                        .map(|e| {
3885                            // explicit conversion to avoid warning C4244 (possible loss of data) with MSVC
3886                            if t.as_unit_product().is_some() {
3887                                format!("{}({e})", t.cpp_type().unwrap())
3888                            } else {
3889                                e
3890                            }
3891                        })
3892                        .unwrap_or_else(|| "(Error: missing member in object)".to_owned())
3893                });
3894                format!("std::make_tuple({})", elem.join(", "))
3895            } else {
3896                format!(
3897                    "[&]({args}){{ {ty} o{{}}; {fields}return o; }}({vals})",
3898                    args = (0..values.len()).map(|i| format!("const auto &a_{i}")).join(", "),
3899                    ty = Type::Struct(ty.clone()).cpp_type().unwrap(),
3900                    fields = values
3901                        .keys()
3902                        .enumerate()
3903                        .map(|(i, f)| format!("o.{} = a_{}; ", ident(f), i))
3904                        .join(""),
3905                    vals = values.values().map(|e| compile_expression(e, ctx)).join(", "),
3906                )
3907            }
3908        }
3909        Expression::EasingCurve(EasingCurve::Linear) => {
3910            "slint::cbindgen_private::EasingCurve()".into()
3911        }
3912        Expression::EasingCurve(EasingCurve::CubicBezier(a, b, c, d)) => format!(
3913            "slint::cbindgen_private::EasingCurve(slint::cbindgen_private::EasingCurve::Tag::CubicBezier, {a}, {b}, {c}, {d})"
3914        ),
3915        Expression::EasingCurve(EasingCurve::EaseInElastic) => {
3916            "slint::cbindgen_private::EasingCurve::Tag::EaseInElastic".into()
3917        }
3918        Expression::EasingCurve(EasingCurve::EaseOutElastic) => {
3919            "slint::cbindgen_private::EasingCurve::Tag::EaseOutElastic".into()
3920        }
3921        Expression::EasingCurve(EasingCurve::EaseInOutElastic) => {
3922            "slint::cbindgen_private::EasingCurve::Tag::EaseInOutElastic".into()
3923        }
3924        Expression::EasingCurve(EasingCurve::EaseInBounce) => {
3925            "slint::cbindgen_private::EasingCurve::Tag::EaseInBounce".into()
3926        }
3927        Expression::EasingCurve(EasingCurve::EaseOutBounce) => {
3928            "slint::cbindgen_private::EasingCurve::Tag::EaseOutElastic".into()
3929        }
3930        Expression::EasingCurve(EasingCurve::EaseInOutBounce) => {
3931            "slint::cbindgen_private::EasingCurve::Tag::EaseInOutElastic".into()
3932        }
3933        Expression::LinearGradient { angle, stops } => {
3934            let angle = compile_expression(angle, ctx);
3935            let mut stops_it = stops.iter().map(|(color, stop)| {
3936                let color = compile_expression(color, ctx);
3937                let position = compile_expression(stop, ctx);
3938                format!("slint::private_api::GradientStop{{ {color}, float({position}), }}")
3939            });
3940            format!(
3941                "[&] {{ const slint::private_api::GradientStop stops[] = {{ {} }}; return slint::Brush(slint::private_api::LinearGradientBrush({}, stops, {})); }}()",
3942                stops_it.join(", "),
3943                angle,
3944                stops.len()
3945            )
3946        }
3947        Expression::RadialGradient { stops } => {
3948            let mut stops_it = stops.iter().map(|(color, stop)| {
3949                let color = compile_expression(color, ctx);
3950                let position = compile_expression(stop, ctx);
3951                format!("slint::private_api::GradientStop{{ {color}, float({position}), }}")
3952            });
3953            format!(
3954                "[&] {{ const slint::private_api::GradientStop stops[] = {{ {} }}; return slint::Brush(slint::private_api::RadialGradientBrush(stops, {})); }}()",
3955                stops_it.join(", "),
3956                stops.len()
3957            )
3958        }
3959        Expression::ConicGradient { from_angle, stops } => {
3960            let from_angle = compile_expression(from_angle, ctx);
3961            let mut stops_it = stops.iter().map(|(color, stop)| {
3962                let color = compile_expression(color, ctx);
3963                let position = compile_expression(stop, ctx);
3964                format!("slint::private_api::GradientStop{{ {color}, float({position}), }}")
3965            });
3966            format!(
3967                "[&] {{ const slint::private_api::GradientStop stops[] = {{ {} }}; return slint::Brush(slint::private_api::ConicGradientBrush(float({}), stops, {})); }}()",
3968                stops_it.join(", "),
3969                from_angle,
3970                stops.len()
3971            )
3972        }
3973        Expression::EnumerationValue(value) => {
3974            let prefix =
3975                if value.enumeration.node.is_some() { "" } else { "slint::cbindgen_private::" };
3976            format!(
3977                "{prefix}{}::{}",
3978                ident(&value.enumeration.name),
3979                ident(&value.to_pascal_case()),
3980            )
3981        }
3982        Expression::LayoutCacheAccess {
3983            layout_cache_prop,
3984            index,
3985            repeater_index,
3986            entries_per_item,
3987        } => {
3988            let cache = access_member(layout_cache_prop, ctx);
3989            cache.map_or_default(|cache| {
3990                if let Some(ri) = repeater_index {
3991                    format!(
3992                        "slint::private_api::layout_cache_access({}.get(), {}, {}, {})",
3993                        cache,
3994                        index,
3995                        compile_expression(ri, ctx),
3996                        entries_per_item
3997                    )
3998                } else {
3999                    format!("{cache}.get()[{index}]")
4000                }
4001            })
4002        }
4003        Expression::GridRepeaterCacheAccess {
4004            layout_cache_prop,
4005            index,
4006            repeater_index,
4007            stride,
4008            child_offset,
4009            inner_repeater_index,
4010            entries_per_item,
4011        } => {
4012            let cache = access_member(layout_cache_prop, ctx);
4013            cache.map_or_default(|cache| {
4014                let stride_val = compile_expression(stride, ctx);
4015                let col_offset = if let Some(inner_ri) = inner_repeater_index {
4016                    format!(
4017                        "{} + {} * {}",
4018                        child_offset,
4019                        compile_expression(inner_ri, ctx),
4020                        entries_per_item
4021                    )
4022                } else {
4023                    child_offset.to_string()
4024                };
4025                format!(
4026                    "slint::private_api::layout_cache_grid_repeater_access({}.get(), {}, {}, {}, {})",
4027                    cache,
4028                    index,
4029                    compile_expression(repeater_index, ctx),
4030                    stride_val,
4031                    col_offset
4032                )
4033            })
4034        }
4035        Expression::WithLayoutItemInfo {
4036            cells_variable,
4037            repeater_indices_var_name,
4038            repeater_steps_var_name,
4039            elements,
4040            orientation,
4041            sub_expression,
4042        } => generate_with_layout_item_info(
4043            cells_variable,
4044            repeater_indices_var_name.as_ref().map(SmolStr::as_str),
4045            repeater_steps_var_name.as_ref().map(SmolStr::as_str),
4046            elements.as_ref(),
4047            *orientation,
4048            sub_expression,
4049            ctx,
4050        ),
4051        Expression::WithFlexboxLayoutItemInfo {
4052            cells_h_variable,
4053            cells_v_variable,
4054            repeater_indices_var_name,
4055            elements,
4056            sub_expression,
4057        } => generate_with_flexbox_layout_item_info(
4058            cells_h_variable,
4059            cells_v_variable,
4060            repeater_indices_var_name.as_ref().map(SmolStr::as_str),
4061            elements.as_ref(),
4062            sub_expression,
4063            ctx,
4064        ),
4065        Expression::WithGridInputData {
4066            cells_variable,
4067            repeater_indices_var_name,
4068            repeater_steps_var_name,
4069            elements,
4070            sub_expression,
4071        } => generate_with_grid_input_data(
4072            cells_variable,
4073            repeater_indices_var_name,
4074            repeater_steps_var_name,
4075            elements.as_ref(),
4076            sub_expression,
4077            ctx,
4078        ),
4079        Expression::MinMax { ty, op, lhs, rhs } => {
4080            let ident = match op {
4081                MinMaxOp::Min => "min",
4082                MinMaxOp::Max => "max",
4083            };
4084            let lhs_code = compile_expression(lhs, ctx);
4085            let rhs_code = compile_expression(rhs, ctx);
4086            format!(
4087                r#"std::{ident}<{ty}>({lhs_code}, {rhs_code})"#,
4088                ty = ty.cpp_type().unwrap_or_default(),
4089                ident = ident,
4090                lhs_code = lhs_code,
4091                rhs_code = rhs_code
4092            )
4093        }
4094        Expression::EmptyComponentFactory => panic!("component-factory not yet supported in C++"),
4095        Expression::TranslationReference { format_args, string_index, plural } => {
4096            let args = compile_expression(format_args, ctx);
4097            match plural {
4098                Some(plural) => {
4099                    let plural = compile_expression(plural, ctx);
4100                    format!(
4101                        "slint::private_api::translate_from_bundle_with_plural(slint_translation_bundle_plural_{string_index}_str, slint_translation_bundle_plural_{string_index}_idx,  slint_translated_plural_rules, {args}, {plural})"
4102                    )
4103                }
4104                None => format!(
4105                    "slint::private_api::translate_from_bundle(slint_translation_bundle_{string_index}, {args})"
4106                ),
4107            }
4108        }
4109    }
4110}
4111
4112fn struct_field_access(base: String, s: &crate::langtype::Struct, name: &str) -> String {
4113    if s.name.is_none() {
4114        let index = s
4115            .fields
4116            .keys()
4117            .position(|k| k == name)
4118            .expect("Expression::ObjectAccess: Cannot find a key in an object");
4119        format!("std::get<{}>({})", index, base)
4120    } else {
4121        format!("{}.{}", base, ident(name))
4122    }
4123}
4124
4125fn compile_builtin_function_call(
4126    function: BuiltinFunction,
4127    arguments: &[llr::Expression],
4128    ctx: &EvaluationContext,
4129) -> String {
4130    let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
4131    let pi_180 = std::f64::consts::PI / 180.0;
4132
4133    match function {
4134        BuiltinFunction::GetWindowScaleFactor => {
4135            format!("{}.scale_factor()", access_window_field(ctx))
4136        }
4137        BuiltinFunction::GetWindowDefaultFontSize => {
4138            "slint::private_api::get_resolved_default_font_size(*this)".to_string()
4139        }
4140        BuiltinFunction::AnimationTick => "slint::cbindgen_private::slint_animation_tick()".into(),
4141        BuiltinFunction::Debug => {
4142            ctx.generator_state.conditional_includes.iostream.set(true);
4143            format!("slint::private_api::debug({});", a.join(","))
4144        }
4145        BuiltinFunction::Mod => {
4146            ctx.generator_state.conditional_includes.cmath.set(true);
4147            format!("([](float a, float b) {{ auto r = std::fmod(a, b); return r >= 0 ? r : r + std::abs(b); }})({},{})", a.next().unwrap(), a.next().unwrap())
4148        }
4149        BuiltinFunction::Round => {
4150            ctx.generator_state.conditional_includes.cmath.set(true);
4151            format!("std::round({})", a.next().unwrap())
4152        }
4153        BuiltinFunction::Ceil => {
4154            ctx.generator_state.conditional_includes.cmath.set(true);
4155            format!("std::ceil({})", a.next().unwrap())
4156        }
4157        BuiltinFunction::Floor => {
4158            ctx.generator_state.conditional_includes.cmath.set(true);
4159            format!("std::floor({})", a.next().unwrap())
4160        }
4161        BuiltinFunction::Sqrt => {
4162            ctx.generator_state.conditional_includes.cmath.set(true);
4163            format!("std::sqrt({})", a.next().unwrap())
4164        }
4165        BuiltinFunction::Abs => {
4166            ctx.generator_state.conditional_includes.cmath.set(true);
4167            format!("std::abs({})", a.next().unwrap())
4168        }
4169        BuiltinFunction::Log => {
4170            ctx.generator_state.conditional_includes.cmath.set(true);
4171            format!("std::log({}) / std::log({})", a.next().unwrap(), a.next().unwrap())
4172        }
4173        BuiltinFunction::Ln => {
4174            ctx.generator_state.conditional_includes.cmath.set(true);
4175            format!("std::log({})", a.next().unwrap())
4176        }
4177        BuiltinFunction::Pow => {
4178            ctx.generator_state.conditional_includes.cmath.set(true);
4179            format!("std::pow(({}), ({}))", a.next().unwrap(), a.next().unwrap())
4180        }
4181        BuiltinFunction::Exp => {
4182            ctx.generator_state.conditional_includes.cmath.set(true);
4183            format!("std::exp({})", a.next().unwrap())
4184        }
4185        BuiltinFunction::Sin => {
4186            ctx.generator_state.conditional_includes.cmath.set(true);
4187            format!("std::sin(({}) * {})", a.next().unwrap(), pi_180)
4188        }
4189        BuiltinFunction::Cos => {
4190            ctx.generator_state.conditional_includes.cmath.set(true);
4191            format!("std::cos(({}) * {})", a.next().unwrap(), pi_180)
4192        }
4193        BuiltinFunction::Tan => {
4194            ctx.generator_state.conditional_includes.cmath.set(true);
4195            format!("std::tan(({}) * {})", a.next().unwrap(), pi_180)
4196        }
4197        BuiltinFunction::ASin => {
4198            ctx.generator_state.conditional_includes.cmath.set(true);
4199            format!("std::asin({}) / {}", a.next().unwrap(), pi_180)
4200        }
4201        BuiltinFunction::ACos => {
4202            ctx.generator_state.conditional_includes.cmath.set(true);
4203            format!("std::acos({}) / {}", a.next().unwrap(), pi_180)
4204        }
4205        BuiltinFunction::ATan => {
4206            ctx.generator_state.conditional_includes.cmath.set(true);
4207            format!("std::atan({}) / {}", a.next().unwrap(), pi_180)
4208        }
4209        BuiltinFunction::ATan2 => {
4210            ctx.generator_state.conditional_includes.cmath.set(true);
4211            format!("std::atan2({}, {}) / {}", a.next().unwrap(), a.next().unwrap(), pi_180)
4212        }
4213        BuiltinFunction::ToFixed => {
4214            format!("[](double n, int d) {{ slint::SharedString out; slint::cbindgen_private::slint_shared_string_from_number_fixed(&out, n, std::max(d, 0)); return out; }}({}, {})",
4215                a.next().unwrap(), a.next().unwrap(),
4216            )
4217        }
4218        BuiltinFunction::ToPrecision => {
4219            format!("[](double n, int p) {{ slint::SharedString out; slint::cbindgen_private::slint_shared_string_from_number_precision(&out, n, std::max(p, 0)); return out; }}({}, {})",
4220                a.next().unwrap(), a.next().unwrap(),
4221            )
4222        }
4223        BuiltinFunction::SetFocusItem => {
4224            if let [llr::Expression::PropertyReference(pr)] = arguments {
4225                let window = access_window_field(ctx);
4226                let focus_item = access_item_rc(pr, ctx);
4227                format!("{window}.set_focus_item({focus_item}, true, slint::cbindgen_private::FocusReason::Programmatic);")
4228            } else {
4229                panic!("internal error: invalid args to SetFocusItem {arguments:?}")
4230            }
4231        }
4232        BuiltinFunction::ClearFocusItem => {
4233            if let [llr::Expression::PropertyReference(pr)] = arguments {
4234                let window = access_window_field(ctx);
4235                let focus_item = access_item_rc(pr, ctx);
4236                format!("{window}.set_focus_item({focus_item}, false, slint::cbindgen_private::FocusReason::Programmatic);")
4237            } else {
4238                panic!("internal error: invalid args to ClearFocusItem {arguments:?}")
4239            }
4240        }
4241        /* std::from_chars is unfortunately not yet implemented in all stdlib compiler we support.
4242         * And std::strtod depends on the locale. Use slint_string_to_float implemented in Rust
4243        BuiltinFunction::StringIsFloat => {
4244            "[](const auto &a){ double v; auto r = std::from_chars(std::begin(a), std::end(a), v); return r.ptr == std::end(a); }"
4245                .into()
4246        }
4247        BuiltinFunction::StringToFloat => {
4248            "[](const auto &a){ double v; auto r = std::from_chars(std::begin(a), std::end(a), v); return r.ptr == std::end(a) ? v : 0; }"
4249                .into()
4250        }*/
4251        BuiltinFunction::StringIsFloat => {
4252            ctx.generator_state.conditional_includes.cstdlib.set(true);
4253            format!("[](const auto &a){{ float res = 0; return slint::cbindgen_private::slint_string_to_float(&a, &res); }}({})", a.next().unwrap())
4254        }
4255        BuiltinFunction::StringToFloat => {
4256            ctx.generator_state.conditional_includes.cstdlib.set(true);
4257            format!("[](const auto &a){{ float res = 0; slint::cbindgen_private::slint_string_to_float(&a, &res); return res; }}({})", a.next().unwrap())
4258        }
4259        BuiltinFunction::StringIsEmpty => {
4260            format!("{}.empty()", a.next().unwrap())
4261        }
4262        BuiltinFunction::StringCharacterCount => {
4263            format!("[](const auto &a){{ return slint::cbindgen_private::slint_string_character_count(&a); }}({})", a.next().unwrap())
4264        }
4265        BuiltinFunction::StringToLowercase => {
4266            format!("{}.to_lowercase()", a.next().unwrap())
4267        }
4268        BuiltinFunction::StringToUppercase => {
4269            format!("{}.to_uppercase()", a.next().unwrap())
4270        }
4271        BuiltinFunction::KeysToString => {
4272            format!("{}.to_string()", a.next().unwrap())
4273        }
4274        BuiltinFunction::ColorRgbaStruct => {
4275            format!("{}.to_argb_uint()", a.next().unwrap())
4276        }
4277        BuiltinFunction::ColorHsvaStruct => {
4278            format!("{}.to_hsva()", a.next().unwrap())
4279        }
4280        BuiltinFunction::ColorOklchStruct => {
4281            format!("{}.to_oklch()", a.next().unwrap())
4282        }
4283        BuiltinFunction::ColorBrighter => {
4284            format!("{}.brighter({})", a.next().unwrap(), a.next().unwrap())
4285        }
4286        BuiltinFunction::ColorDarker => {
4287            format!("{}.darker({})", a.next().unwrap(), a.next().unwrap())
4288        }
4289        BuiltinFunction::ColorTransparentize => {
4290            format!("{}.transparentize({})", a.next().unwrap(), a.next().unwrap())
4291        }
4292        BuiltinFunction::ColorMix => {
4293            format!("{}.mix({}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
4294        }
4295        BuiltinFunction::ColorWithAlpha => {
4296            format!("{}.with_alpha({})", a.next().unwrap(), a.next().unwrap())
4297        }
4298        BuiltinFunction::ImageSize => {
4299            format!("{}.size()", a.next().unwrap())
4300        }
4301        BuiltinFunction::ArrayLength => {
4302            format!("slint::private_api::model_length({})", a.next().unwrap())
4303        }
4304        BuiltinFunction::Rgb => {
4305            format!("slint::Color::from_argb_uint8(std::clamp(static_cast<float>({a}) * 255., 0., 255.), std::clamp(static_cast<int>({r}), 0, 255), std::clamp(static_cast<int>({g}), 0, 255), std::clamp(static_cast<int>({b}), 0, 255))",
4306                r = a.next().unwrap(),
4307                g = a.next().unwrap(),
4308                b = a.next().unwrap(),
4309                a = a.next().unwrap(),
4310            )
4311        }
4312        BuiltinFunction::Hsv => {
4313            format!("slint::Color::from_hsva(static_cast<float>({h}), std::clamp(static_cast<float>({s}), 0.f, 1.f), std::clamp(static_cast<float>({v}), 0.f, 1.f), std::clamp(static_cast<float>({a}), 0.f, 1.f))",
4314                h = a.next().unwrap(),
4315                s = a.next().unwrap(),
4316                v = a.next().unwrap(),
4317                a = a.next().unwrap(),
4318            )
4319        }
4320        BuiltinFunction::Oklch => {
4321            format!("slint::Color::from_oklch(std::clamp(static_cast<float>({l}), 0.f, 1.f), std::max(static_cast<float>({c}), 0.f), static_cast<float>({h}), std::clamp(static_cast<float>({alpha}), 0.f, 1.f))",
4322                l = a.next().unwrap(),
4323                c = a.next().unwrap(),
4324                h = a.next().unwrap(),
4325                alpha = a.next().unwrap(),
4326            )
4327        }
4328        BuiltinFunction::ColorScheme => {
4329            format!("{}.color_scheme()", access_window_field(ctx))
4330        }
4331        BuiltinFunction::AccentColor => {
4332            format!("{}.accent_color()", access_window_field(ctx))
4333        }
4334        BuiltinFunction::SupportsNativeMenuBar => {
4335            format!("{}.supports_native_menu_bar()", access_window_field(ctx))
4336        }
4337        BuiltinFunction::SetupMenuBar => {
4338            let window = access_window_field(ctx);
4339            let [llr::Expression::PropertyReference(entries_r), llr::Expression::PropertyReference(sub_menu_r), llr::Expression::PropertyReference(activated_r), llr::Expression::NumberLiteral(tree_index), llr::Expression::BoolLiteral(no_native), rest @ ..] = arguments
4340            else {
4341                panic!("internal error: incorrect argument count to SetupMenuBar")
4342            };
4343
4344            let current_sub_component = ctx.current_sub_component().unwrap();
4345            let item_tree_id = ident(&ctx.compilation_unit.sub_components[current_sub_component.menu_item_trees[*tree_index as usize].root].name);
4346            let access_entries = access_member(entries_r, ctx).unwrap();
4347            let access_sub_menu = access_member(sub_menu_r, ctx).unwrap();
4348            let access_activated = access_member(activated_r, ctx).unwrap();
4349            if *no_native {
4350                format!(r"{{
4351                    auto item_tree = {item_tree_id}::create(self);
4352                    auto item_tree_dyn = item_tree.into_dyn();
4353                    auto menu_wrapper = slint::private_api::create_menu_wrapper(item_tree_dyn);
4354                    slint::private_api::slint_windowrc_setup_menu_bar_shortcuts(&{window}.handle(), &menu_wrapper);
4355                    slint::private_api::setup_popup_menu_from_menu_item_tree(menu_wrapper, {access_entries}, {access_sub_menu}, {access_activated});
4356                }}")
4357            } else {
4358                let condition = if let [condition] = &rest {
4359                    let condition = compile_expression(condition, ctx);
4360                    format!(r"[](auto menu_tree) {{
4361                                auto self_mapped = reinterpret_cast<const {item_tree_id} *>(menu_tree->operator->())->parent.lock();
4362                                [[maybe_unused]] auto self = &**self_mapped;
4363                                return {condition};
4364                            }}")
4365                } else {
4366                    "nullptr".to_string()
4367                };
4368
4369                format!(r"{{
4370                    auto item_tree = {item_tree_id}::create(self);
4371                    auto item_tree_dyn = item_tree.into_dyn();
4372                    auto menu_wrapper = slint::private_api::create_menu_wrapper(item_tree_dyn, {condition});
4373                    slint::private_api::slint_windowrc_setup_menu_bar_shortcuts(&{window}.handle(), &menu_wrapper);
4374                    if ({window}.supports_native_menu_bar()) {{
4375                        slint::cbindgen_private::slint_windowrc_setup_native_menu_bar(&{window}.handle(), &menu_wrapper);
4376                    }} else {{
4377                        slint::private_api::setup_popup_menu_from_menu_item_tree(menu_wrapper, {access_entries}, {access_sub_menu}, {access_activated});
4378                    }}
4379                }}")
4380            }
4381        }
4382        BuiltinFunction::Use24HourFormat => {
4383            "slint::cbindgen_private::slint_date_time_use_24_hour_format()".to_string()
4384        }
4385        BuiltinFunction::MonthDayCount => {
4386            format!("slint::cbindgen_private::slint_date_time_month_day_count({}, {})", a.next().unwrap(), a.next().unwrap())
4387        }
4388        BuiltinFunction::MonthOffset => {
4389            format!("slint::cbindgen_private::slint_date_time_month_offset({}, {})", a.next().unwrap(), a.next().unwrap())
4390        }
4391        BuiltinFunction::FormatDate => {
4392            format!("[](const auto &format, int d, int m, int y) {{ slint::SharedString out; slint::cbindgen_private::slint_date_time_format_date(&format, d, m, y, &out); return out; }}({}, {}, {}, {})",
4393                a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap()
4394            )
4395        }
4396        BuiltinFunction::DateNow => {
4397            "[] { int32_t d=0, m=0, y=0; slint::cbindgen_private::slint_date_time_date_now(&d, &m, &y); return std::make_shared<slint::private_api::ArrayModel<3,int32_t>>(d, m, y); }()".into()
4398        }
4399        BuiltinFunction::ValidDate => {
4400            format!(
4401                "[](const auto &a, const auto &b) {{ int32_t d=0, m=0, y=0; return slint::cbindgen_private::slint_date_time_parse_date(&a, &b, &d, &m, &y); }}({}, {})",
4402                a.next().unwrap(), a.next().unwrap()
4403            )
4404        }
4405        BuiltinFunction::ParseDate => {
4406            format!(
4407                "[](const auto &a, const auto &b) {{ int32_t d=0, m=0, y=0; slint::cbindgen_private::slint_date_time_parse_date(&a, &b, &d, &m, &y); return std::make_shared<slint::private_api::ArrayModel<3,int32_t>>(d, m, y); }}({}, {})",
4408                a.next().unwrap(), a.next().unwrap()
4409            )
4410        }
4411        BuiltinFunction::SetTextInputFocused => {
4412            format!("{}.set_text_input_focused({})", access_window_field(ctx), a.next().unwrap())
4413        }
4414        BuiltinFunction::TextInputFocused => {
4415            format!("{}.text_input_focused()", access_window_field(ctx))
4416        }
4417        BuiltinFunction::ShowPopupWindow => {
4418            if let [llr::Expression::NumberLiteral(popup_index), close_policy, llr::Expression::PropertyReference(parent_ref)] =
4419                arguments
4420            {
4421                let mut component_access = MemberAccess::Direct("self".into());
4422                let llr::MemberReference::Relative { parent_level, .. } = parent_ref else {unreachable!()};
4423                for _ in 0..*parent_level {
4424                    component_access = component_access.and_then(|x| format!("{x}->parent.lock()"));
4425                }
4426
4427                let window = access_window_field(ctx);
4428                let current_sub_component = &ctx.compilation_unit.sub_components[ctx.parent_sub_component_idx(*parent_level).unwrap()];
4429                let popup = &current_sub_component.popup_windows[*popup_index as usize];
4430                let popup_window_id =
4431                    ident(&ctx.compilation_unit.sub_components[popup.item_tree.root].name);
4432                let parent_component = access_item_rc(parent_ref, ctx);
4433                let parent_ctx = ParentScope::new(ctx, None);
4434                let popup_ctx = EvaluationContext::new_sub_component(
4435                    ctx.compilation_unit,
4436                    popup.item_tree.root,
4437                    CppGeneratorContext { global_access: "self->globals".into(), conditional_includes: ctx.generator_state.conditional_includes },
4438                    Some(&parent_ctx),
4439                );
4440                let position = compile_expression(&popup.position.borrow(), &popup_ctx);
4441                let close_policy = compile_expression(close_policy, ctx);
4442                component_access.then(|component_access| format!(
4443                    "{window}.close_popup({component_access}->popup_id_{popup_index}); {component_access}->popup_id_{popup_index} = {window}.template show_popup<{popup_window_id}>(&*({component_access}), [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
4444                ))
4445            } else {
4446                panic!("internal error: invalid args to ShowPopupWindow {arguments:?}")
4447            }
4448        }
4449        BuiltinFunction::ClosePopupWindow => {
4450            if let [llr::Expression::NumberLiteral(popup_index), llr::Expression::PropertyReference(parent_ref)] = arguments {
4451                let mut component_access = MemberAccess::Direct("self".into());
4452                let llr::MemberReference::Relative { parent_level, .. } = parent_ref else {unreachable!()};
4453                for _ in 0..*parent_level {
4454                    component_access = component_access.and_then(|x| format!("{x}->parent.lock()"));
4455                }
4456
4457                let window = access_window_field(ctx);
4458                component_access.then(|component_access| format!("{window}.close_popup({component_access}->popup_id_{popup_index})"))
4459            } else {
4460                panic!("internal error: invalid args to ClosePopupWindow {arguments:?}")
4461            }
4462        }
4463
4464        BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
4465            let [llr::Expression::PropertyReference(context_menu_ref), entries, position] = arguments
4466            else {
4467                panic!("internal error: invalid args to ShowPopupMenu {arguments:?}")
4468            };
4469
4470            let context_menu = access_member(context_menu_ref, ctx);
4471            let context_menu_rc = access_item_rc(context_menu_ref, ctx);
4472            let position = compile_expression(position, ctx);
4473            let popup = ctx
4474                .compilation_unit
4475                .popup_menu
4476                .as_ref()
4477                .expect("there should be a popup menu if we want to show it");
4478            let popup_id = ident(&ctx.compilation_unit.sub_components[popup.item_tree.root].name);
4479            let window = access_window_field(ctx);
4480
4481            let popup_ctx = EvaluationContext::new_sub_component(
4482                ctx.compilation_unit,
4483                popup.item_tree.root,
4484                CppGeneratorContext { global_access: "self->globals".into(), conditional_includes: ctx.generator_state.conditional_includes },
4485                None,
4486            );
4487            let access_entries = access_member(&popup.entries, &popup_ctx).unwrap();
4488            let access_sub_menu = access_member(&popup.sub_menu, &popup_ctx).unwrap();
4489            let access_activated = access_member(&popup.activated, &popup_ctx).unwrap();
4490            let access_close = access_member(&popup.close, &popup_ctx).unwrap();
4491
4492            let close_popup = context_menu.then(|context_menu| {
4493                format!("{window}.close_popup({context_menu}.popup_id)")
4494            });
4495            let set_id = context_menu
4496                .then(|context_menu| format!("{context_menu}.popup_id = id"));
4497
4498            if let llr::Expression::NumberLiteral(tree_index) = entries {
4499                // We have an MenuItem tree
4500                let current_sub_component = ctx.current_sub_component().unwrap();
4501                let item_tree_id = ident(&ctx.compilation_unit.sub_components[current_sub_component.menu_item_trees[*tree_index as usize].root].name);
4502                format!(r"{{
4503                    auto item_tree = {item_tree_id}::create(self);
4504                    auto item_tree_dyn = item_tree.into_dyn();
4505                    auto menu_wrapper = slint::private_api::create_menu_wrapper(item_tree_dyn);
4506                    {close_popup};
4507                    auto id = {window}.template show_popup_menu<{popup_id}>({globals}, {position}, {{ {context_menu_rc} }}, [self, &menu_wrapper](auto popup_menu) {{
4508                        auto parent_weak = self->self_weak;
4509                        auto self_ = self;
4510                        auto self = popup_menu;
4511                        slint::private_api::setup_popup_menu_from_menu_item_tree(menu_wrapper, {access_entries}, {access_sub_menu}, {access_activated});
4512                        {access_close}.set_handler([parent_weak,self = self_] {{ if(auto lock = parent_weak.lock()) {{ {close_popup}; }} }});
4513                    }}, menu_wrapper);
4514                    {set_id};
4515                }}", globals = ctx.generator_state.global_access)
4516            } else {
4517                // ShowPopupMenuInternal
4518                let forward_callback = |access, cb, default| {
4519                    let call = context_menu.map_or_default(|context_menu| format!("{context_menu}.{cb}.call(entry)"));
4520                    format!("{access}.set_handler(
4521                        [parent_weak,self = self_](const auto &entry) {{
4522                            if(auto lock = parent_weak.lock()) {{
4523                                return {call};
4524                            }} else {{
4525                                return {default};
4526                            }}
4527                        }});")
4528                };
4529                let fw_sub_menu = forward_callback(access_sub_menu, "sub_menu", "std::shared_ptr<slint::Model<slint::cbindgen_private::MenuEntry>>()");
4530                let fw_activated = forward_callback(access_activated, "activated", "");
4531                let entries = compile_expression(entries, ctx);
4532                format!(r"
4533                    {close_popup};
4534                    auto id = {window}.template show_popup_menu<{popup_id}>({globals}, {position}, {{ {context_menu_rc} }}, [self](auto popup_menu) {{
4535                        auto parent_weak = self->self_weak;
4536                        auto self_ = self;
4537                        auto entries = {entries};
4538                        auto self = popup_menu;
4539                        {access_entries}.set(std::move(entries));
4540                        {fw_sub_menu}
4541                        {fw_activated}
4542                        {access_close}.set_handler([parent_weak,self = self_] {{ if(auto lock = parent_weak.lock()) {{ {close_popup}; }} }});
4543                    }});
4544                    {set_id};
4545                ", globals = ctx.generator_state.global_access)
4546            }
4547        }
4548        BuiltinFunction::SetSelectionOffsets => {
4549            if let [llr::Expression::PropertyReference(pr), from, to] = arguments {
4550                let item = access_member(pr, ctx);
4551                let item_rc = access_item_rc(pr, ctx);
4552                let window = access_window_field(ctx);
4553                let start = compile_expression(from, ctx);
4554                let end = compile_expression(to, ctx);
4555                item.then(|item| {
4556                    format!("slint_textinput_set_selection_offsets(&{item}, &{window}.handle(), &{item_rc}, static_cast<int>({start}), static_cast<int>({end}))")
4557                })
4558            } else {
4559                panic!("internal error: invalid args to set-selection-offsets {arguments:?}")
4560            }
4561        }
4562        BuiltinFunction::ItemFontMetrics => {
4563            if let [llr::Expression::PropertyReference(pr)] = arguments {
4564                let item_rc = access_item_rc(pr, ctx);
4565                let window = access_window_field(ctx);
4566                format!("slint_cpp_text_item_fontmetrics(&{window}.handle(), &{item_rc})")
4567            } else {
4568                panic!("internal error: invalid args to ItemFontMetrics {arguments:?}")
4569            }
4570        }
4571        BuiltinFunction::ItemAbsolutePosition => {
4572            if let [llr::Expression::PropertyReference(pr)] = arguments {
4573                let item_rc = access_item_rc(pr, ctx);
4574                format!("slint::LogicalPosition(slint::cbindgen_private::slint_item_absolute_position(&{item_rc}))")
4575            } else {
4576                panic!("internal error: invalid args to ItemAbsolutePosition {arguments:?}")
4577            }
4578        }
4579        BuiltinFunction::RegisterCustomFontByPath => {
4580            if let [llr::Expression::StringLiteral(path)] = arguments {
4581                let window = access_window_field(ctx);
4582                format!("{window}.register_font_from_path(\"{}\");", escape_string(path))
4583            } else {
4584                panic!(
4585                    "internal error: argument to RegisterCustomFontByPath must be a string literal"
4586                )
4587            }
4588        }
4589        BuiltinFunction::RegisterCustomFontByMemory => {
4590            if let [llr::Expression::NumberLiteral(resource_id)] = &arguments {
4591                let window = access_window_field(ctx);
4592                let resource_id: usize = *resource_id as _;
4593                let symbol = format!("slint_embedded_resource_{resource_id}");
4594                format!("{window}.register_font_from_data({symbol}, std::size({symbol}));")
4595            } else {
4596                panic!("internal error: invalid args to RegisterCustomFontByMemory {arguments:?}")
4597            }
4598        }
4599        BuiltinFunction::RegisterBitmapFont => {
4600            if let [llr::Expression::NumberLiteral(resource_id)] = &arguments {
4601                let window = access_window_field(ctx);
4602                let resource_id: usize = *resource_id as _;
4603                let symbol = format!("slint_embedded_resource_{resource_id}");
4604                format!("{window}.register_bitmap_font({symbol});")
4605            } else {
4606                panic!("internal error: invalid args to RegisterBitmapFont {arguments:?}")
4607            }
4608        }
4609        BuiltinFunction::ImplicitLayoutInfo(orient) => {
4610            if let [llr::Expression::PropertyReference(pr), constraint_expr] = arguments {
4611                let native = native_prop_info(pr, ctx).0;
4612                let item_rc = access_item_rc(pr, ctx);
4613                let constraint = compile_expression(constraint_expr, ctx);
4614                access_member(pr, ctx).then(|item|
4615                format!(
4616                    "slint::private_api::item_layout_info({vt}, const_cast<slint::cbindgen_private::{ty}*>(&{item}), {o}, {constraint}, &{window}, {item_rc})",
4617                    vt = native.cpp_vtable_getter,
4618                    ty = native.class_name,
4619                    o = to_cpp_orientation(orient),
4620                    window = access_window_field(ctx),
4621                ))
4622            } else {
4623                panic!("internal error: invalid args to ImplicitLayoutInfo {arguments:?}")
4624            }
4625        }
4626        BuiltinFunction::Translate => {
4627            format!("slint::private_api::translate({})", a.join(","))
4628        }
4629        BuiltinFunction::UpdateTimers => {
4630            "self->update_timers()".into()
4631        }
4632        BuiltinFunction::DetectOperatingSystem => {
4633            "slint::cbindgen_private::slint_detect_operating_system()".to_string()
4634        }
4635        // start and stop are unreachable because they are lowered to simple assignment of running
4636        BuiltinFunction::StartTimer => unreachable!(),
4637        BuiltinFunction::StopTimer => unreachable!(),
4638        BuiltinFunction::RestartTimer => {
4639            if let [llr::Expression::NumberLiteral(timer_index)] = arguments {
4640                format!("const_cast<slint::Timer&>(self->timer{}).restart()", timer_index)
4641            } else {
4642                panic!("internal error: invalid args to RetartTimer {arguments:?}")
4643            }
4644        }
4645        BuiltinFunction::OpenUrl => {
4646            let url = a.next().unwrap();
4647            let window = access_window_field(ctx);
4648            format!("slint::private_api::open_url({url}, {window})")
4649        }
4650        BuiltinFunction::ParseMarkdown => {
4651            let format_string = a.next().unwrap();
4652            let args = a.next().unwrap();
4653            format!("slint::private_api::parse_markdown({}, {})", format_string, args)
4654        }
4655        BuiltinFunction::StringToStyledText => {
4656            let string = a.next().unwrap();
4657            format!("slint::private_api::string_to_styled_text({})", string)
4658        }
4659    }
4660}
4661
4662/// Builds the C++ snippet that, for each inner repeater in `templates`, calls
4663/// `ensure_updated` on the sub-component and updates `max_total`.
4664fn build_inner_ensure_code(templates: &[llr::RowChildTemplateInfo], static_count: usize) -> String {
4665    templates
4666        .iter()
4667        .filter_map(|e| match e {
4668            llr::RowChildTemplateInfo::Repeated { repeater_index } => {
4669                let inner_rep_id = format!("repeater_{}", usize::from(*repeater_index));
4670                Some(format!(
4671                    "sub_comp->{inner_rep_id}.ensure_updated(&*sub_comp);\n\
4672                     max_total = std::max(max_total, {static_count} + sub_comp->{inner_rep_id}.len());\n"
4673                ))
4674            }
4675            _ => None,
4676        })
4677        .collect()
4678}
4679
4680fn generate_repeater_loop_code(
4681    repeater_index: llr::RepeatedElementIdx,
4682    row_child_templates: &Option<Vec<llr::RowChildTemplateInfo>>,
4683    repeater_steps_var_name: &Option<SmolStr>,
4684    repeater_idx: usize,
4685    dynamic_stride_var_name: &str,
4686    dynamic_loop_code: impl FnOnce(String, usize, String, String) -> String,
4687    static_loop_code: impl FnOnce(String, usize, bool, String) -> String,
4688) -> String {
4689    let repeater_id = format!("repeater_{}", usize::from(repeater_index));
4690    if llr::has_inner_repeaters(row_child_templates) {
4691        let templates = row_child_templates.as_ref().unwrap();
4692        let static_count = llr::static_child_count(templates);
4693        let inner_ensure = build_inner_ensure_code(templates, static_count);
4694        let rs_init = repeater_steps_var_name.as_ref().map_or(String::new(), |rs| {
4695            format!("{rs}_array[{repeater_idx}] = {dynamic_stride_var_name};")
4696        });
4697        dynamic_loop_code(repeater_id, static_count, inner_ensure, rs_init)
4698    } else {
4699        let step = row_child_templates.as_deref().map_or(1, |t| t.len());
4700        let rs_init = repeater_steps_var_name
4701            .as_ref()
4702            .map_or(String::new(), |rs| format!("{rs}_array[{repeater_idx}] = {step};"));
4703        static_loop_code(repeater_id, step, row_child_templates.is_none(), rs_init)
4704    }
4705}
4706
4707fn generate_with_layout_item_info(
4708    cells_variable: &str,
4709    repeated_indices_var_name: Option<&str>,
4710    repeater_steps_var_name: Option<&str>,
4711    elements: &[Either<llr::Expression, llr::LayoutRepeatedElement>],
4712    orientation: Orientation,
4713    sub_expression: &llr::Expression,
4714    ctx: &llr_EvaluationContext<CppGeneratorContext>,
4715) -> String {
4716    let repeated_indices_var_name = repeated_indices_var_name.map(ident);
4717    let repeater_steps_var_name = repeater_steps_var_name.map(ident);
4718    let mut push_code =
4719        "std::vector<slint::cbindgen_private::LayoutItemInfo> cells_vector;".to_owned();
4720    let mut repeater_idx = 0usize;
4721
4722    for item in elements {
4723        match item {
4724            Either::Left(value) => {
4725                write!(
4726                    push_code,
4727                    "cells_vector.push_back({{ {} }});",
4728                    compile_expression(value, ctx)
4729                )
4730                .unwrap();
4731            }
4732            Either::Right(repeater) => {
4733                let repeater_index = usize::from(repeater.repeater_index);
4734                write!(push_code, "self->repeater_{repeater_index}.ensure_updated(self);").unwrap();
4735
4736                if let Some(ri) = &repeated_indices_var_name {
4737                    write!(
4738                        push_code,
4739                        "{ri}_array[{c}] = cells_vector.size();",
4740                        c = repeater_idx * 2
4741                    )
4742                    .unwrap();
4743                    write!(
4744                        push_code,
4745                        "{ri}_array[{c}] = self->repeater_{repeater_index}.len();",
4746                        c = repeater_idx * 2 + 1,
4747                    )
4748                    .unwrap();
4749                }
4750                let repeater_loop_code = generate_repeater_loop_code(
4751                    repeater.repeater_index,
4752                    &repeater.row_child_templates,
4753                    &repeater_steps_var_name,
4754                    repeater_idx,
4755                    "max_total",
4756                    |repeater_id, static_count, inner_ensure, rs_init| {
4757                        format!(
4758                            "{{
4759                                size_t max_total = {static_count};
4760                                self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4761                                    {inner_ensure}
4762                                }});
4763                                {rs_init}
4764                                self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4765                                    for (size_t child_idx = 0; child_idx < max_total; ++child_idx) {{
4766                                        cells_vector.push_back(sub_comp->layout_item_info({o}, child_idx));
4767                                    }}
4768                                }});
4769                            }}",
4770                            o = to_cpp_orientation(orientation),
4771                        )
4772                    },
4773                    |repeater_id, step, is_column_repeater, rs_init| {
4774                        if step == 0 {
4775                            rs_init
4776                        } else if step == 1 && is_column_repeater {
4777                            // Column-repeater: each sub-component IS a cell; nullopt returns its own layout_info
4778                            format!(
4779                                "{rs_init}self->{repeater_id}.for_each([&](const auto &sub_comp){{ cells_vector.push_back(sub_comp->layout_item_info({o}, std::nullopt)); }});",
4780                                o = to_cpp_orientation(orientation),
4781                            )
4782                        } else {
4783                            format!(
4784                                "{rs_init}self->{repeater_id}.for_each([&](const auto &sub_comp){{
4785                                    for (size_t child_idx = 0; child_idx < {step}; ++child_idx) {{
4786                                        cells_vector.push_back(sub_comp->layout_item_info({o}, child_idx));
4787                                    }}
4788                                }});",
4789                                o = to_cpp_orientation(orientation),
4790                            )
4791                        }
4792                    },
4793                );
4794                push_code.push_str(&repeater_loop_code);
4795                repeater_idx += 1;
4796            }
4797        }
4798    }
4799
4800    let ri = repeated_indices_var_name.as_ref().map_or(String::new(), |ri| {
4801        write!(
4802            push_code,
4803            "slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4804        )
4805        .unwrap();
4806        format!("std::array<int, {}> {ri}_array;", 2 * repeater_idx)
4807    });
4808    let rs = repeater_steps_var_name.as_ref().map_or(String::new(), |rs| {
4809        write!(
4810            push_code,
4811            "slint::cbindgen_private::Slice<int> {rs} = slint::private_api::make_slice(std::span({rs}_array));"
4812        )
4813        .unwrap();
4814        format!("std::array<int, {}> {rs}_array;", repeater_idx)
4815    });
4816    format!(
4817        "[&]{{ {ri} {rs} {push_code} slint::cbindgen_private::Slice<slint::cbindgen_private::LayoutItemInfo>{} = slint::private_api::make_slice(std::span(cells_vector)); return {}; }}()",
4818        ident(cells_variable),
4819        compile_expression(sub_expression, ctx)
4820    )
4821}
4822
4823fn generate_with_flexbox_layout_item_info(
4824    cells_h_variable: &str,
4825    cells_v_variable: &str,
4826    repeated_indices_var_name: Option<&str>,
4827    elements: &[Either<(llr::Expression, llr::Expression), llr::LayoutRepeatedElement>],
4828    sub_expression: &llr::Expression,
4829    ctx: &llr_EvaluationContext<CppGeneratorContext>,
4830) -> String {
4831    let repeated_indices_var_name = repeated_indices_var_name.map(ident);
4832    let mut push_code =
4833        "std::vector<slint::cbindgen_private::FlexboxLayoutItemInfo> cells_vector_h; std::vector<slint::cbindgen_private::FlexboxLayoutItemInfo> cells_vector_v;".to_owned();
4834    let mut repeater_idx = 0usize;
4835
4836    for item in elements {
4837        match item {
4838            Either::Left((value_h, value_v)) => {
4839                write!(
4840                    push_code,
4841                    "cells_vector_h.push_back({{ {} }}); cells_vector_v.push_back({{ {} }});",
4842                    compile_expression(value_h, ctx),
4843                    compile_expression(value_v, ctx)
4844                )
4845                .unwrap();
4846            }
4847            Either::Right(repeater) => {
4848                let repeater_index = usize::from(repeater.repeater_index);
4849                write!(push_code, "self->repeater_{repeater_index}.ensure_updated(self);").unwrap();
4850
4851                if let Some(ri) = &repeated_indices_var_name {
4852                    write!(
4853                        push_code,
4854                        "{ri}_array[{c}] = cells_vector_h.size();",
4855                        c = repeater_idx * 2
4856                    )
4857                    .unwrap();
4858                    write!(
4859                        push_code,
4860                        "{ri}_array[{c}] = self->repeater_{repeater_index}.len();",
4861                        c = repeater_idx * 2 + 1,
4862                    )
4863                    .unwrap();
4864                }
4865                repeater_idx += 1;
4866                write!(
4867                    push_code,
4868                    "self->repeater_{repeater_index}.for_each([&](const auto &sub_comp){{ \
4869                     cells_vector_h.push_back(sub_comp->flexbox_layout_item_info(slint::cbindgen_private::Orientation::Horizontal, std::nullopt)); \
4870                     cells_vector_v.push_back(sub_comp->flexbox_layout_item_info(slint::cbindgen_private::Orientation::Vertical, std::nullopt)); }});"
4871                )
4872                .unwrap();
4873            }
4874        }
4875    }
4876
4877    let ri = repeated_indices_var_name.as_ref().map_or(String::new(), |ri| {
4878        write!(
4879            push_code,
4880            "slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4881        )
4882        .unwrap();
4883        format!("std::array<int, {}> {ri}_array;", 2 * repeater_idx)
4884    });
4885    format!(
4886        "[&]{{ {ri} {push_code} [[maybe_unused]] slint::cbindgen_private::Slice<slint::cbindgen_private::FlexboxLayoutItemInfo>{cells_h} = slint::private_api::make_slice(std::span(cells_vector_h)); [[maybe_unused]] slint::cbindgen_private::Slice<slint::cbindgen_private::FlexboxLayoutItemInfo>{cells_v} = slint::private_api::make_slice(std::span(cells_vector_v)); return {}; }}()",
4887        compile_expression(sub_expression, ctx),
4888        cells_h = ident(cells_h_variable),
4889        cells_v = ident(cells_v_variable),
4890    )
4891}
4892
4893fn generate_with_grid_input_data(
4894    cells_variable: &str,
4895    repeated_indices_var_name: &SmolStr,
4896    repeater_steps_var_name: &SmolStr,
4897    elements: &[Either<llr::Expression, llr::GridLayoutRepeatedElement>],
4898    sub_expression: &llr::Expression,
4899    ctx: &llr_EvaluationContext<CppGeneratorContext>,
4900) -> String {
4901    let repeated_indices_var_name = Some(ident(repeated_indices_var_name));
4902    let repeater_steps_var_name = Some(ident(repeater_steps_var_name));
4903    let mut push_code =
4904        "std::vector<slint::cbindgen_private::GridLayoutInputData> cells_vector;".to_owned();
4905    let mut repeater_idx = 0usize;
4906    let mut has_new_row_bool = false;
4907
4908    for item in elements {
4909        match item {
4910            Either::Left(value) => {
4911                write!(
4912                    push_code,
4913                    "cells_vector.push_back({{ {} }});",
4914                    compile_expression(value, ctx)
4915                )
4916                .unwrap();
4917            }
4918            Either::Right(repeater) => {
4919                let repeater_id = format!("repeater_{}", usize::from(repeater.repeater_index));
4920                write!(push_code, "self->{repeater_id}.ensure_updated(self);").unwrap();
4921
4922                if let Some(ri) = &repeated_indices_var_name {
4923                    write!(push_code, "{ri}_array[{}] = cells_vector.size();", repeater_idx * 2)
4924                        .unwrap();
4925                    write!(
4926                        push_code,
4927                        "{ri}_array[{c}] = self->{repeater_id}.len();",
4928                        c = repeater_idx * 2 + 1,
4929                    )
4930                    .unwrap();
4931                }
4932                let maybe_bool = if has_new_row_bool { "" } else { "bool " };
4933                let repeater_loop_code = generate_repeater_loop_code(
4934                    repeater.repeater_index,
4935                    &repeater.row_child_templates,
4936                    &repeater_steps_var_name,
4937                    repeater_idx,
4938                    "total_item_count",
4939                    |repeater_id, static_count, inner_ensure, rs_init| {
4940                        format!(
4941                            "{maybe_bool} new_row = {new_row};
4942                            {{
4943                                size_t max_total = {static_count};
4944                                self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4945                                    {inner_ensure}
4946                                }});
4947                                size_t total_item_count = max_total;
4948                                {rs_init}
4949                                auto start_offset = cells_vector.size();
4950                                cells_vector.resize(start_offset + self->{repeater_id}.len() * total_item_count);
4951                                std::size_t i = 0;
4952                                self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4953                                    auto offset = start_offset + i * total_item_count;
4954                                    sub_comp->grid_layout_input_for_repeated(new_row, std::span(cells_vector).subspan(offset, total_item_count));
4955                                    ++i;
4956                                }});
4957                            }}",
4958                            new_row = repeater.new_row,
4959                        )
4960                    },
4961                    |repeater_id, step, is_column_repeater, rs_init| {
4962                        let reset_new_row =
4963                            if is_column_repeater { "new_row = false;" } else { "" };
4964                        format!(
4965                            "{rs_init}{maybe_bool} new_row = {new_row};
4966                            {{
4967                                auto start_offset = cells_vector.size();
4968                                cells_vector.resize(start_offset + self->{repeater_id}.len() * {step});
4969                                std::size_t i = 0;
4970                                self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4971                                    auto offset = start_offset + i * {step};
4972                                    sub_comp->grid_layout_input_for_repeated(new_row, std::span(cells_vector).subspan(offset, {step}));
4973                                    {reset_new_row}
4974                                    ++i;
4975                                }});
4976                            }}",
4977                            new_row = repeater.new_row,
4978                        )
4979                    },
4980                );
4981                push_code.push_str(&repeater_loop_code);
4982                repeater_idx += 1;
4983                has_new_row_bool = true;
4984            }
4985        }
4986    }
4987
4988    let ri = repeated_indices_var_name.as_ref().map_or(String::new(), |ri| {
4989        write!(
4990            push_code,
4991            "slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4992        )
4993        .unwrap();
4994        format!("std::array<int, {}> {ri}_array;", 2 * repeater_idx)
4995    });
4996    let rs = repeater_steps_var_name.as_ref().map_or(String::new(), |rs| {
4997        write!(
4998            push_code,
4999            "slint::cbindgen_private::Slice<int> {rs} = slint::private_api::make_slice(std::span({rs}_array));"
5000        )
5001        .unwrap();
5002        format!("std::array<int, {}> {rs}_array;", repeater_idx)
5003    });
5004    format!(
5005        "[&]{{ {ri} {rs} {push_code} slint::cbindgen_private::Slice<slint::cbindgen_private::GridLayoutInputData>{} = slint::private_api::make_slice(std::span(cells_vector)); return {}; }}()",
5006        ident(cells_variable),
5007        compile_expression(sub_expression, ctx)
5008    )
5009}
5010
5011/// Like compile expression, but prepended with `return` if not void.
5012/// ret_type is the expecting type that should be returned with that return statement
5013fn return_compile_expression(
5014    expr: &llr::Expression,
5015    ctx: &EvaluationContext,
5016    ret_type: Option<&Type>,
5017) -> String {
5018    let e = compile_expression(expr, ctx);
5019    if ret_type == Some(&Type::Void) || ret_type == Some(&Type::Invalid) {
5020        e
5021    } else {
5022        let ty = expr.ty(ctx);
5023        if ty == Type::Invalid && ret_type.is_some() {
5024            // e is unreachable so it probably throws. But we still need to return something to avoid a warning
5025            format!("{e}; return {{}}")
5026        } else if ty == Type::Invalid || ty == Type::Void {
5027            e
5028        } else {
5029            format!("return {e}")
5030        }
5031    }
5032}
5033
5034pub fn generate_type_aliases(file: &mut File, doc: &Document) {
5035    let type_aliases = doc
5036        .exports
5037        .iter()
5038        .filter_map(|export| match &export.1 {
5039            Either::Left(component) if !component.is_global() => {
5040                Some((&export.0.name, component.id.clone()))
5041            }
5042            Either::Right(ty) => match &ty {
5043                Type::Struct(s) if s.node().is_some() => {
5044                    Some((&export.0.name, s.name.cpp_type().unwrap()))
5045                }
5046                Type::Enumeration(en) => Some((&export.0.name, en.name.clone())),
5047                _ => None,
5048            },
5049            _ => None,
5050        })
5051        .filter(|(export_name, type_name)| *export_name != type_name)
5052        .map(|(export_name, type_name)| {
5053            Declaration::TypeAlias(TypeAlias {
5054                old_name: ident(&type_name),
5055                new_name: ident(export_name),
5056            })
5057        });
5058
5059    file.declarations.extend(type_aliases);
5060}
5061
5062#[cfg(feature = "bundle-translations")]
5063fn generate_translation(
5064    translations: &crate::translations::Translations,
5065    compilation_unit: &llr::CompilationUnit,
5066    declarations: &mut Vec<Declaration>,
5067) {
5068    for (idx, m) in translations.strings.iter().enumerate() {
5069        declarations.push(Declaration::Var(Var {
5070            ty: "const char8_t* const".into(),
5071            name: format_smolstr!("slint_translation_bundle_{idx}"),
5072            array_size: Some(m.len()),
5073            init: Some(format!(
5074                "{{ {} }}",
5075                m.iter()
5076                    .map(|s| match s {
5077                        Some(s) => format_smolstr!("u8\"{}\"", escape_string(s.as_str())),
5078                        None => "nullptr".into(),
5079                    })
5080                    .join(", ")
5081            )),
5082            ..Default::default()
5083        }));
5084    }
5085    for (idx, ms) in translations.plurals.iter().enumerate() {
5086        let all_strs = ms.iter().flatten().flatten();
5087        let all_strs_len = all_strs.clone().count();
5088        declarations.push(Declaration::Var(Var {
5089            ty: "const char8_t* const".into(),
5090            name: format_smolstr!("slint_translation_bundle_plural_{}_str", idx),
5091            array_size: Some(all_strs_len),
5092            init: Some(format!(
5093                "{{ {} }}",
5094                all_strs.map(|s| format_smolstr!("u8\"{}\"", escape_string(s.as_str()))).join(", ")
5095            )),
5096            ..Default::default()
5097        }));
5098
5099        let mut count = 0;
5100        declarations.push(Declaration::Var(Var {
5101            ty: "const uint32_t".into(),
5102            name: format_smolstr!("slint_translation_bundle_plural_{}_idx", idx),
5103            array_size: Some(ms.len()),
5104            init: Some(format!(
5105                "{{ {} }}",
5106                ms.iter()
5107                    .map(|x| {
5108                        count += x.as_ref().map_or(0, |x| x.len());
5109                        count
5110                    })
5111                    .join(", ")
5112            )),
5113            ..Default::default()
5114        }));
5115    }
5116
5117    if !translations.plurals.is_empty() {
5118        let ctx = EvaluationContext {
5119            compilation_unit,
5120            current_scope: EvaluationScope::Global(0.into()),
5121            generator_state: CppGeneratorContext {
5122                global_access: "\n#error \"language rule can't access state\";".into(),
5123                conditional_includes: &Default::default(),
5124            },
5125            argument_types: &[Type::Int32],
5126        };
5127
5128        declarations.push(Declaration::Var(Var {
5129            ty: format_smolstr!(
5130                "const std::array<uintptr_t (*const)(int32_t), {}>",
5131                translations.plural_rules.len()
5132            ),
5133            name: "slint_translated_plural_rules".into(),
5134            init: Some(format!(
5135                "{{ {} }}",
5136                translations
5137                    .plural_rules
5138                    .iter()
5139                    .map(|s| match s {
5140                        Some(s) => {
5141                            format!(
5142                                "[]([[maybe_unused]] int32_t arg_0) -> uintptr_t {{ return {}; }}",
5143                                compile_expression(s, &ctx)
5144                            )
5145                        }
5146                        None => "nullptr".into(),
5147                    })
5148                    .join(", ")
5149            )),
5150            ..Default::default()
5151        }));
5152    }
5153}