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