Skip to main content

i_slint_compiler/generator/
rust.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// cSpell: ignore conv gdata powf punct vref
5
6/*! module for the Rust code generator
7
8Some convention used in the generated code:
9 - `_self` is of type `Pin<&ComponentType>`  where ComponentType is the type of the generated sub component,
10   this is existing for any evaluation of a binding
11 - `self_rc` is of type `VRc<ItemTreeVTable, ComponentType>` or `Rc<ComponentType>` for globals
12   this is usually a local variable to the init code that shouldn't be relied upon by the binding code.
13*/
14
15use crate::CompilerConfiguration;
16use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp, OperatorClass};
17use crate::langtype::{Enumeration, EnumerationValue, Struct, StructName, Type};
18use crate::layout::Orientation;
19use crate::llr::{
20    self, ArrayOutput, EvaluationContext as llr_EvaluationContext, EvaluationScope, Expression,
21    ParentScope, TypeResolutionContext as _,
22};
23use crate::object_tree::Document;
24use crate::typeloader::LibraryInfo;
25use itertools::Either;
26use proc_macro2::{Ident, TokenStream, TokenTree};
27use quote::{format_ident, quote};
28use smol_str::SmolStr;
29use std::collections::{BTreeMap, BTreeSet};
30use std::str::FromStr;
31
32#[derive(Clone)]
33struct RustGeneratorContext {
34    /// Path to the SharedGlobals structure that contains the global and the WindowAdapter
35    global_access: TokenStream,
36}
37
38type EvaluationContext<'a> = llr_EvaluationContext<'a, RustGeneratorContext>;
39
40pub fn ident(ident: &str) -> proc_macro2::Ident {
41    if ident.contains('-') {
42        format_ident!("r#{}", ident.replace('-', "_"))
43    } else {
44        format_ident!("r#{}", ident)
45    }
46}
47
48impl quote::ToTokens for Orientation {
49    fn to_tokens(&self, tokens: &mut TokenStream) {
50        let tks = match self {
51            Orientation::Horizontal => {
52                quote!(sp::Orientation::Horizontal)
53            }
54            Orientation::Vertical => {
55                quote!(sp::Orientation::Vertical)
56            }
57        };
58        tokens.extend(tks);
59    }
60}
61
62impl quote::ToTokens for crate::embedded_resources::PixelFormat {
63    fn to_tokens(&self, tokens: &mut TokenStream) {
64        use crate::embedded_resources::PixelFormat::*;
65        let tks = match self {
66            Rgb => quote!(sp::TexturePixelFormat::Rgb),
67            Rgba => quote!(sp::TexturePixelFormat::Rgba),
68            RgbaPremultiplied => {
69                quote!(sp::TexturePixelFormat::RgbaPremultiplied)
70            }
71            AlphaMap(_) => quote!(sp::TexturePixelFormat::AlphaMap),
72        };
73        tokens.extend(tks);
74    }
75}
76
77pub fn rust_primitive_type(ty: &Type) -> Option<proc_macro2::TokenStream> {
78    match ty {
79        Type::Void => Some(quote!(())),
80        Type::Int32 => Some(quote!(i32)),
81        Type::Float32 => Some(quote!(f32)),
82        Type::String => Some(quote!(sp::SharedString)),
83        Type::Color => Some(quote!(sp::Color)),
84        Type::Easing => Some(quote!(sp::EasingCurve)),
85        Type::ComponentFactory => Some(quote!(slint::ComponentFactory)),
86        Type::Duration => Some(quote!(i64)),
87        Type::Angle => Some(quote!(f32)),
88        Type::PhysicalLength => Some(quote!(sp::Coord)),
89        Type::LogicalLength => Some(quote!(sp::Coord)),
90        Type::Rem => Some(quote!(f32)),
91        Type::Percent => Some(quote!(f32)),
92        Type::Bool => Some(quote!(bool)),
93        Type::Image => Some(quote!(sp::Image)),
94        Type::StyledText => Some(quote!(sp::StyledText)),
95        Type::Struct(s) => {
96            struct_name_to_tokens(&s.name).or_else(|| {
97                let elem =
98                    s.fields.values().map(rust_primitive_type).collect::<Option<Vec<_>>>()?;
99                // This will produce a tuple
100                Some(quote!((#(#elem,)*)))
101            })
102        }
103        Type::Array(o) => {
104            let inner = rust_primitive_type(o)?;
105            Some(quote!(sp::ModelRc<#inner>))
106        }
107        Type::Enumeration(e) => {
108            let i = ident(&e.name);
109            if e.node.is_some() { Some(quote!(#i)) } else { Some(quote!(sp::#i)) }
110        }
111        Type::Keys => Some(quote!(sp::Keys)),
112        Type::Brush => Some(quote!(slint::Brush)),
113        Type::LayoutCache => Some(quote!(
114            sp::SharedVector<
115                sp::Coord,
116            >
117        )),
118        Type::ArrayOfU16 => Some(quote!(
119            sp::SharedVector<
120                u16,
121            >
122        )),
123        _ => None,
124    }
125}
126
127fn rust_property_type(ty: &Type) -> Option<proc_macro2::TokenStream> {
128    match ty {
129        Type::LogicalLength => Some(quote!(sp::LogicalLength)),
130        Type::Easing => Some(quote!(sp::EasingCurve)),
131        _ => rust_primitive_type(ty),
132    }
133}
134
135fn primitive_property_value(ty: &Type, property_accessor: MemberAccess) -> TokenStream {
136    primitive_value_from_property_value(ty, property_accessor.get_property())
137}
138
139fn primitive_value_from_property_value(ty: &Type, value: TokenStream) -> TokenStream {
140    match ty {
141        Type::LogicalLength => quote!(#value.get()),
142        _ => value,
143    }
144}
145
146fn set_primitive_property_value(ty: &Type, value_expression: TokenStream) -> TokenStream {
147    match ty {
148        Type::LogicalLength => {
149            let rust_ty = rust_primitive_type(ty).unwrap_or(quote!(_));
150            quote!(sp::LogicalLength::new(#value_expression as #rust_ty))
151        }
152        _ => value_expression,
153    }
154}
155
156/// Generate the rust code for the given component.
157pub fn generate(
158    doc: &Document,
159    compiler_config: &CompilerConfiguration,
160) -> std::io::Result<TokenStream> {
161    if std::env::var("SLINT_LIVE_PREVIEW").is_ok() {
162        return super::rust_live_preview::generate(doc, compiler_config);
163    }
164
165    let module_header = generate_module_header();
166    let qualified_name_ident = |symbol: &SmolStr, library_info: &LibraryInfo| {
167        let symbol = ident(symbol);
168        let package = ident(&library_info.package);
169        if let Some(module) = &library_info.module {
170            let module = ident(module);
171            quote!(#package :: #module :: #symbol)
172        } else {
173            quote!(#package :: #symbol)
174        }
175    };
176
177    let library_imports = {
178        let doc_used_types = doc.used_types.borrow();
179        doc_used_types
180            .library_types_imports
181            .iter()
182            .map(|(symbol, library_info)| {
183                let ident = qualified_name_ident(symbol, library_info);
184                quote!(
185                    #[allow(unused_imports)]
186                    pub use #ident;
187                )
188            })
189            .chain(doc_used_types.library_global_imports.iter().map(|(symbol, library_info)| {
190                let ident = qualified_name_ident(symbol, library_info);
191                let inner_symbol_name = smol_str::format_smolstr!("Inner{}", symbol);
192                let inner_ident = qualified_name_ident(&inner_symbol_name, library_info);
193                quote!(pub use #ident, #inner_ident;)
194            }))
195            .collect::<Vec<_>>()
196    };
197
198    let (structs_and_enums_ids, inner_module) =
199        generate_types(&doc.used_types.borrow().structs_and_enums);
200
201    let llr = crate::llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config);
202
203    if llr.public_components.is_empty() {
204        return Ok(Default::default());
205    }
206
207    let sub_compos = llr
208        .used_sub_components
209        .iter()
210        .map(|sub_compo| generate_sub_component(*sub_compo, &llr, None, None, false))
211        .collect::<Vec<_>>();
212    let public_components =
213        llr.public_components.iter().map(|p| generate_public_component(p, &llr, compiler_config));
214
215    let popup_menu =
216        llr.popup_menu.as_ref().map(|p| generate_item_tree(&p.item_tree, &llr, None, None, true));
217
218    let mut global_exports = Vec::<TokenStream>::new();
219    if let Some(library_name) = &compiler_config.library_name {
220        // Building as a library, SharedGlobals needs to be exported
221        let ident = format_ident!("{}SharedGlobals", library_name);
222        global_exports.push(quote!(SharedGlobals as #ident));
223    }
224    let globals =
225        llr.globals.iter_enumerated().filter(|(_, glob)| glob.must_generate()).map(
226            |(idx, glob)| generate_global(idx, glob, &llr, compiler_config, &mut global_exports),
227        );
228    let library_globals_getters = llr
229        .globals
230        .iter_enumerated()
231        .filter(|(_, glob)| glob.from_library)
232        .map(|(_idx, glob)| generate_global_getters(glob, &llr));
233    let shared_globals = generate_shared_globals(doc, &llr, compiler_config);
234    let globals_ids = llr.globals.iter().filter(|glob| glob.exported).flat_map(|glob| {
235        std::iter::once(ident(&glob.name)).chain(glob.aliases.iter().map(|x| ident(x)))
236    });
237    let compo_ids = llr.public_components.iter().map(|c| ident(&c.name));
238
239    let resource_symbols = generate_resources(doc);
240    let named_exports = generate_named_exports(&doc.exports);
241    // The inner module was meant to be internal private, but projects have been reaching into it
242    // so we can't change the name of this module
243    let generated_mod = doc
244        .last_exported_component()
245        .map(|c| format_ident!("slint_generated{}", ident(&c.id)))
246        .unwrap_or_else(|| format_ident!("slint_generated"));
247
248    #[cfg(not(feature = "bundle-translations"))]
249    let translations = quote!();
250    #[cfg(feature = "bundle-translations")]
251    let translations = llr.translations.as_ref().map(|t| generate_translations(t, &llr));
252
253    Ok(quote! {
254        mod #generated_mod {
255            #module_header
256            #(#library_imports)*
257            #inner_module
258            #(#globals)*
259            #(#library_globals_getters)*
260            #(#sub_compos)*
261            #popup_menu
262            #(#public_components)*
263            #shared_globals
264            #(#resource_symbols)*
265            #translations
266        }
267        #[allow(unused_imports)]
268        pub use #generated_mod::{#(#compo_ids,)* #(#structs_and_enums_ids,)* #(#globals_ids,)* #(#named_exports,)* #(#global_exports,)*};
269        #[allow(unused_imports)]
270        pub use slint::{ComponentHandle as _, Global as _, ModelExt as _};
271    })
272}
273
274pub(super) fn generate_module_header() -> TokenStream {
275    quote! {
276        #![allow(non_snake_case, non_camel_case_types)]
277        #![allow(unused_braces, unused_parens)]
278        #![allow(clippy::all, clippy::pedantic, clippy::nursery)]
279        #![allow(unknown_lints, if_let_rescope, tail_expr_drop_order)] // We don't have fancy Drop
280
281        use slint::private_unstable_api::re_exports as sp;
282        #[allow(unused_imports)]
283        use sp::{RepeatedItemTree as _, ModelExt as _, Model as _, Float as _};
284    }
285}
286
287/// Generate the struct and enums. Return a vector of names to import and a token stream with the inner module
288pub fn generate_types(used_types: &[Type]) -> (Vec<Ident>, TokenStream) {
289    let (structs_and_enums_ids, structs_and_enum_def): (Vec<_>, Vec<_>) = used_types
290        .iter()
291        .filter_map(|ty| match ty {
292            Type::Struct(s) => match s.as_ref() {
293                Struct { fields, name: struct_name @ StructName::User { name, .. } } => {
294                    Some((ident(name), generate_struct(struct_name, fields)))
295                }
296                _ => None,
297            },
298            Type::Enumeration(en) => Some((ident(&en.name), generate_enum(en))),
299            _ => None,
300        })
301        .unzip();
302
303    let version_check = format_ident!(
304        "VersionCheck_{}_{}_{}",
305        env!("CARGO_PKG_VERSION_MAJOR"),
306        env!("CARGO_PKG_VERSION_MINOR"),
307        env!("CARGO_PKG_VERSION_PATCH"),
308    );
309
310    let inner_module = quote! {
311        #(#structs_and_enum_def)*
312        const _THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME : slint::#version_check = slint::#version_check;
313    };
314
315    (structs_and_enums_ids, inner_module)
316}
317
318fn generate_public_component(
319    llr: &llr::PublicComponent,
320    unit: &llr::CompilationUnit,
321    compiler_config: &CompilerConfiguration,
322) -> TokenStream {
323    let public_component_id = ident(&llr.name);
324    let inner_component_id = inner_component_id(&unit.sub_components[llr.item_tree.root]);
325
326    let component = generate_item_tree(&llr.item_tree, unit, None, None, false);
327
328    let ctx = EvaluationContext {
329        compilation_unit: unit,
330        current_scope: EvaluationScope::SubComponent(llr.item_tree.root, None),
331        generator_state: RustGeneratorContext {
332            global_access: quote!(_self.globals.get().unwrap()),
333        },
334        argument_types: &[],
335    };
336
337    let property_and_callback_accessors = public_api(
338        &llr.public_properties,
339        &llr.private_properties,
340        quote!(sp::VRc::as_pin_ref(&self.0)),
341        &ctx,
342    );
343
344    #[cfg(feature = "bundle-translations")]
345    let init_bundle_translations = unit
346        .translations
347        .as_ref()
348        .map(|_| quote!(sp::set_bundled_languages(_SLINT_BUNDLED_LANGUAGES);));
349    #[cfg(not(feature = "bundle-translations"))]
350    let init_bundle_translations = quote!();
351
352    let experimental = compiler_config.enable_experimental;
353
354    quote!(
355        #component
356        pub struct #public_component_id(sp::VRc<sp::ItemTreeVTable, #inner_component_id>);
357
358        impl #public_component_id {
359            pub fn new() -> ::core::result::Result<Self, slint::PlatformError> {
360                slint::private_unstable_api::ensure_backend()?;
361                let inner = #inner_component_id::new()?;
362                #init_bundle_translations
363                // ensure that the window exist as this point so further call to window() don't panic
364                inner.globals.get().unwrap().window_adapter_ref()?;
365                #inner_component_id::user_init(sp::VRc::map(inner.clone(), |x| x));
366                ::core::result::Result::Ok(Self(inner))
367            }
368
369            #[cfg(#experimental)]
370            pub fn new_with_context(ctx: sp::SlintContext) -> ::core::result::Result<Self, slint::PlatformError> {
371                let inner = #inner_component_id::new()?;
372                #init_bundle_translations
373
374                inner.globals.get().unwrap().create_window_from_context(ctx)?;
375
376                #inner_component_id::user_init(sp::VRc::map(inner.clone(), |x| x));
377                ::core::result::Result::Ok(Self(inner))
378            }
379
380            #property_and_callback_accessors
381        }
382
383        impl From<#public_component_id> for sp::VRc<sp::ItemTreeVTable, #inner_component_id> {
384            fn from(value: #public_component_id) -> Self {
385                value.0
386            }
387        }
388
389        impl slint::StrongHandle for #public_component_id {
390            type WeakInner = sp::VWeak<sp::ItemTreeVTable, #inner_component_id>;
391
392            fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> sp::Option<Self> {
393                sp::Some(Self(inner.upgrade()?))
394            }
395        }
396
397        impl slint::ComponentHandle for #public_component_id {
398            fn as_weak(&self) -> slint::Weak<Self> {
399                slint::Weak::new(sp::VRc::downgrade(&self.0))
400            }
401
402            fn clone_strong(&self) -> Self {
403                Self(self.0.clone())
404            }
405
406            fn run(&self) -> ::core::result::Result<(), slint::PlatformError> {
407                self.show()?;
408                sp::WindowInner::from_pub(self.window()).context().run_event_loop()?;
409                self.hide()?;
410                ::core::result::Result::Ok(())
411            }
412
413            fn show(&self) -> ::core::result::Result<(), slint::PlatformError> {
414                self.0.globals.get().unwrap().window_adapter_ref()?.window().show()
415            }
416
417            fn hide(&self) -> ::core::result::Result<(), slint::PlatformError> {
418                self.0.globals.get().unwrap().window_adapter_ref()?.window().hide()
419            }
420
421            fn window(&self) -> &slint::Window {
422                self.0.globals.get().unwrap().window_adapter_ref().unwrap().window()
423            }
424
425            fn global<'a, T: slint::Global<'a, Self>>(&'a self) -> T {
426                T::get(&self)
427            }
428        }
429    )
430}
431
432fn generate_shared_globals(
433    doc: &Document,
434    llr: &llr::CompilationUnit,
435    compiler_config: &CompilerConfiguration,
436) -> TokenStream {
437    let global_names = llr
438        .globals
439        .iter()
440        .filter(|g| g.must_generate())
441        .map(|g| format_ident!("global_{}", ident(&g.name)))
442        .collect::<Vec<_>>();
443    let global_types =
444        llr.globals.iter().filter(|g| g.must_generate()).map(global_inner_name).collect::<Vec<_>>();
445
446    let from_library_global_names = llr
447        .globals
448        .iter()
449        .filter(|g| g.from_library)
450        .map(|g| format_ident!("global_{}", ident(&g.name)))
451        .collect::<Vec<_>>();
452
453    let from_library_global_types =
454        llr.globals.iter().filter(|g| g.from_library).map(global_inner_name).collect::<Vec<_>>();
455    let apply_constant_scale_factor = compiler_config.const_scale_factor.map(|factor| {
456        quote!(sp::WindowInner::from_pub(adapter.window()).set_const_scale_factor(#factor);)
457    });
458
459    let library_global_vars = llr
460        .globals
461        .iter()
462        .filter(|g| g.from_library)
463        .map(|g| {
464            let library_info = doc.library_exports.get(g.name.as_str()).unwrap();
465            let shared_globals_var_name =
466                format_ident!("library_{}_shared_globals", library_info.name);
467            let global_name = format_ident!("global_{}", ident(&g.name));
468            quote!( #shared_globals_var_name.#global_name )
469        })
470        .collect::<Vec<_>>();
471    let pub_token = if compiler_config.library_name.is_some() { quote!(pub) } else { quote!() };
472
473    let experimental = compiler_config.enable_experimental;
474
475    let (library_shared_globals_names, library_shared_globals_types): (Vec<_>, Vec<_>) = doc
476        .imports
477        .iter()
478        .filter_map(|import| import.library_info.clone())
479        .map(|library_info| {
480            let struct_name = format_ident!("{}SharedGlobals", library_info.name);
481            let shared_globals_var_name =
482                format_ident!("library_{}_shared_globals", library_info.name);
483            let shared_globals_type_name = if let Some(module) = library_info.module {
484                let package = ident(&library_info.package);
485                let module = ident(&module);
486                //(quote!(#shared_gloabls_var_name),quote!(let #shared_globals_var_name = #package::#module::#shared_globals_type_name::new(root_item_tree_weak.clone());))
487                quote!(#package::#module::#struct_name)
488            } else {
489                let package = ident(&library_info.package);
490                quote!(#package::#struct_name)
491            };
492            (quote!(#shared_globals_var_name), shared_globals_type_name)
493        })
494        .unzip();
495
496    quote! {
497        #pub_token struct SharedGlobals {
498            #(#pub_token #global_names : ::core::pin::Pin<sp::Rc<#global_types>>,)*
499            #(#pub_token #from_library_global_names : ::core::pin::Pin<sp::Rc<#from_library_global_types>>,)*
500            window_adapter : sp::OnceCell<sp::WindowAdapterRc>,
501            root_item_tree_weak : sp::VWeak<sp::ItemTreeVTable>,
502            #(#[allow(dead_code)]
503            #library_shared_globals_names : sp::Rc<#library_shared_globals_types>,)*
504        }
505        impl SharedGlobals {
506            #pub_token fn new(root_item_tree_weak : sp::VWeak<sp::ItemTreeVTable>) -> sp::Rc<Self> {
507                #(let #library_shared_globals_names = #library_shared_globals_types::new(root_item_tree_weak.clone());)*
508                let _self = sp::Rc::new(Self {
509                    #(#global_names : #global_types::new(),)*
510                    #(#from_library_global_names : #library_global_vars.clone(),)*
511                    window_adapter : ::core::default::Default::default(),
512                    root_item_tree_weak,
513                    #(#library_shared_globals_names,)*
514                });
515                #(_self.#global_names.clone().init(&_self);)*
516                _self
517            }
518
519            fn window_adapter_impl(&self) -> sp::Rc<dyn sp::WindowAdapter> {
520                sp::Rc::clone(self.window_adapter_ref().unwrap())
521            }
522
523            fn window_adapter_ref(&self) -> sp::Result<&sp::Rc<dyn sp::WindowAdapter>, slint::PlatformError>
524            {
525                self.window_adapter.get_or_try_init(|| {
526                    let adapter = slint::private_unstable_api::create_window_adapter()?;
527                    let root_rc = self.root_item_tree_weak.upgrade().unwrap();
528                    sp::WindowInner::from_pub(adapter.window()).set_component(&root_rc);
529                    #apply_constant_scale_factor
530                    ::core::result::Result::Ok(adapter)
531                })
532            }
533
534            #[cfg(#experimental)]
535            fn create_window_from_context(&self, ctx: sp::SlintContext) -> sp::Result<(), slint::PlatformError> {
536                let adapter = ctx.platform().create_window_adapter()?;
537                sp::WindowInner::from_pub(adapter.window()).set_context(ctx);
538                let root_rc = self.root_item_tree_weak.upgrade().unwrap();
539                sp::WindowInner::from_pub(adapter.window()).set_component(&root_rc);
540                #apply_constant_scale_factor
541                self.window_adapter.set(adapter).map_err(|_|()).expect("The window shouldn't be initialized before this call");
542                sp::Ok(())
543            }
544
545            fn maybe_window_adapter_impl(&self) -> sp::Option<sp::Rc<dyn sp::WindowAdapter>> {
546                self.window_adapter.get().cloned()
547            }
548        }
549    }
550}
551
552fn generate_struct(name: &StructName, fields: &BTreeMap<SmolStr, Type>) -> TokenStream {
553    let component_id = struct_name_to_tokens(name).unwrap();
554    let (declared_property_vars, declared_property_types): (Vec<_>, Vec<_>) =
555        fields.iter().map(|(name, ty)| (ident(name), rust_primitive_type(ty).unwrap())).unzip();
556
557    let StructName::User { name, node } = name else { unreachable!("generating non-user struct") };
558
559    let attributes =
560        node.parent().and_then(crate::parser::syntax_nodes::StructDeclaration::new).map(|node| {
561            let attrs = node.AtRustAttr().map(|attr| {
562                match TokenStream::from_str(&attr.text().to_string()) {
563                    Ok(t) => quote!(#[#t]),
564                    Err(_) => {
565                        let source_location = crate::diagnostics::Spanned::to_source_location(&attr);
566                        let error = format!(
567                            "Error parsing @rust-attr for struct '{name}' declared at {source_location}"
568                        );
569                        quote!(compile_error!(#error);)
570                    }
571                }
572            });
573            quote! { #(#attrs)* }
574        });
575
576    quote! {
577        #attributes
578        #[derive(Default, PartialEq, Debug, Clone)]
579        pub struct #component_id {
580            #(pub #declared_property_vars : #declared_property_types),*
581        }
582    }
583}
584
585fn generate_enum(en: &std::rc::Rc<Enumeration>) -> TokenStream {
586    let enum_name = ident(&en.name);
587
588    let enum_values = (0..en.values.len()).map(|value| {
589        let i = ident(&EnumerationValue { value, enumeration: en.clone() }.to_pascal_case());
590        if value == en.default_value { quote!(#[default] #i) } else { quote!(#i) }
591    });
592    let attributes = en.node.as_ref().map(|node| {
593        let attrs =
594            node.AtRustAttr().map(|attr| match TokenStream::from_str(&attr.text().to_string()) {
595                Ok(t) => quote!(#[#t]),
596                Err(_) => {
597                    let name = &en.name;
598                    let source_location = crate::diagnostics::Spanned::to_source_location(&attr);
599                    let error = format!(
600                        "Error parsing @rust-attr for enum '{name}' declared at {source_location}"
601                    );
602                    quote!(compile_error!(#error);)
603                }
604            });
605        quote! { #(#attrs)* }
606    });
607    quote! {
608        #attributes
609        #[allow(dead_code)]
610        #[derive(Default, Copy, Clone, PartialEq, Debug)]
611        pub enum #enum_name {
612            #(#enum_values,)*
613        }
614    }
615}
616
617fn handle_property_init(
618    prop: &llr::MemberReference,
619    binding_expression: &llr::BindingExpression,
620    init: &mut Vec<TokenStream>,
621    ctx: &EvaluationContext,
622) {
623    let rust_property = access_member(prop, ctx).unwrap();
624    let prop_type = ctx.property_ty(prop);
625
626    let init_self_pin_ref = if ctx.current_global().is_some() {
627        quote!(let _self = self_rc.as_ref();)
628    } else {
629        quote!(let _self = self_rc.as_pin_ref();)
630    };
631
632    if let Type::Callback(callback) = &prop_type {
633        let mut ctx2 = ctx.clone();
634        ctx2.argument_types = &callback.args;
635        let tokens_for_expression =
636            compile_expression(&binding_expression.expression.borrow(), &ctx2);
637        let as_ = if matches!(callback.return_type, Type::Void) { quote!(;) } else { quote!(as _) };
638        init.push(quote!({
639            #[allow(unreachable_code, unused)]
640            slint::private_unstable_api::set_callback_handler(#rust_property, &self_rc, {
641                move |self_rc, args| {
642                    #init_self_pin_ref
643                    (#tokens_for_expression) #as_
644                }
645            });
646        }));
647    } else {
648        let tokens_for_expression =
649            compile_expression(&binding_expression.expression.borrow(), ctx);
650
651        let tokens_for_expression = set_primitive_property_value(prop_type, tokens_for_expression);
652
653        init.push(if binding_expression.is_constant && !binding_expression.is_state_info {
654            let t = rust_property_type(prop_type).unwrap_or(quote!(_));
655            quote! { #rust_property.set({ (#tokens_for_expression) as #t }); }
656        } else {
657            let maybe_cast_to_property_type = if binding_expression.expression.borrow().ty(ctx) == Type::Invalid {
658                // Don't cast if the Rust code is the never type, as with return statements inside a block, the
659                // type of the return expression is `()` instead of `!`.
660                None
661            } else {
662                Some(quote!(as _))
663            };
664
665            let binding_tokens = quote!(move |self_rc| {
666                #init_self_pin_ref
667                (#tokens_for_expression) #maybe_cast_to_property_type
668            });
669
670            if binding_expression.is_state_info {
671                quote! { {
672                    slint::private_unstable_api::set_property_state_binding(#rust_property, &self_rc, #binding_tokens);
673                } }
674            } else {
675                match &binding_expression.animation {
676                    Some(llr::Animation::Static(anim)) => {
677                        let anim = compile_expression(anim, ctx);
678                        quote! { {
679                            #init_self_pin_ref
680                            slint::private_unstable_api::set_animated_property_binding(
681                                #rust_property, &self_rc, #binding_tokens, move |self_rc| {
682                                    #init_self_pin_ref
683                                    (#anim, None)
684                                });
685                        } }
686                    }
687                    Some(llr::Animation::Transition(animation)) => {
688                        let animation = compile_expression(animation, ctx);
689                        quote! {
690                            slint::private_unstable_api::set_animated_property_binding(
691                                #rust_property, &self_rc, #binding_tokens, move |self_rc| {
692                                    #init_self_pin_ref
693                                    let (animation, change_time) = #animation;
694                                    (animation, Some(change_time))
695                                }
696                            );
697                        }
698                    }
699                    None => {
700                        quote! { {
701                            slint::private_unstable_api::set_property_binding(#rust_property, &self_rc, #binding_tokens);
702                        } }
703                    }
704                }
705            }
706        });
707    }
708}
709
710/// Public API for Global and root component
711fn public_api(
712    public_properties: &llr::PublicProperties,
713    private_properties: &llr::PrivateProperties,
714    self_init: TokenStream,
715    ctx: &EvaluationContext,
716) -> TokenStream {
717    let mut property_and_callback_accessors: Vec<TokenStream> = Vec::new();
718    for p in public_properties {
719        let prop_ident = ident(&p.name);
720        let prop = access_member(&p.prop, ctx).unwrap();
721
722        if let Type::Callback(callback) = &p.ty {
723            let callback_args =
724                callback.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
725            let return_type = rust_primitive_type(&callback.return_type).unwrap();
726            let args_name =
727                (0..callback.args.len()).map(|i| format_ident!("arg_{}", i)).collect::<Vec<_>>();
728            let caller_ident = format_ident!("invoke_{}", prop_ident);
729            property_and_callback_accessors.push(quote!(
730                #[allow(dead_code)]
731                pub fn #caller_ident(&self, #(#args_name : #callback_args,)*) -> #return_type {
732                    let _self = #self_init;
733                    #prop.call(&(#(#args_name,)*))
734                }
735            ));
736            let on_ident = format_ident!("on_{}", prop_ident);
737            let args_index = (0..callback_args.len()).map(proc_macro2::Literal::usize_unsuffixed);
738            property_and_callback_accessors.push(quote!(
739                #[allow(dead_code)]
740                pub fn #on_ident(&self, mut f: impl FnMut(#(#callback_args),*) -> #return_type + 'static) {
741                    let _self = #self_init;
742                    #[allow(unused)]
743                    #prop.set_handler(
744                        // FIXME: why do i need to clone here?
745                        move |args| f(#(args.#args_index.clone()),*)
746                    )
747                }
748            ));
749        } else if let Type::Function(function) = &p.ty {
750            let callback_args =
751                function.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
752            let return_type = rust_primitive_type(&function.return_type).unwrap();
753            let args_name =
754                (0..function.args.len()).map(|i| format_ident!("arg_{}", i)).collect::<Vec<_>>();
755            let caller_ident = format_ident!("invoke_{}", prop_ident);
756            property_and_callback_accessors.push(quote!(
757                #[allow(dead_code)]
758                pub fn #caller_ident(&self, #(#args_name : #callback_args,)*) -> #return_type {
759                    let _self = #self_init;
760                    #prop(#(#args_name,)*)
761                }
762            ));
763        } else {
764            let rust_property_type = rust_primitive_type(&p.ty).unwrap();
765
766            let getter_ident = format_ident!("get_{}", prop_ident);
767
768            let prop_expression = primitive_property_value(&p.ty, MemberAccess::Direct(prop));
769
770            property_and_callback_accessors.push(quote!(
771                #[allow(dead_code)]
772                pub fn #getter_ident(&self) -> #rust_property_type {
773                    #[allow(unused_imports)]
774                    let _self = #self_init;
775                    #prop_expression
776                }
777            ));
778
779            let setter_ident = format_ident!("set_{}", prop_ident);
780            if !p.read_only {
781                let set_value = property_set_value_tokens(&p.prop, quote!(value), ctx);
782                property_and_callback_accessors.push(quote!(
783                    #[allow(dead_code)]
784                    pub fn #setter_ident(&self, value: #rust_property_type) {
785                        #[allow(unused_imports)]
786                        let _self = #self_init;
787                        #set_value
788                    }
789                ));
790            } else {
791                property_and_callback_accessors.push(quote!(
792                    #[allow(dead_code)] fn #setter_ident(&self, _read_only_property : ()) { }
793                ));
794            }
795        }
796    }
797
798    for (name, ty) in private_properties {
799        let prop_ident = ident(name);
800        if let Type::Function { .. } = ty {
801            let caller_ident = format_ident!("invoke_{}", prop_ident);
802            property_and_callback_accessors.push(
803                quote!( #[allow(dead_code)] fn #caller_ident(&self, _private_function: ()) {} ),
804            );
805        } else {
806            let getter_ident = format_ident!("get_{}", prop_ident);
807            let setter_ident = format_ident!("set_{}", prop_ident);
808            property_and_callback_accessors.push(quote!(
809                #[allow(dead_code)] fn #getter_ident(&self, _private_property: ()) {}
810                #[allow(dead_code)] fn #setter_ident(&self, _private_property: ()) {}
811            ));
812        }
813    }
814
815    quote!(#(#property_and_callback_accessors)*)
816}
817
818/// Generate the rust code for the given component.
819fn generate_sub_component(
820    component_idx: llr::SubComponentIdx,
821    root: &llr::CompilationUnit,
822    parent_ctx: Option<&ParentScope>,
823    index_property: Option<llr::PropertyIdx>,
824    pinned_drop: bool,
825) -> TokenStream {
826    let component = &root.sub_components[component_idx];
827    let inner_component_id = inner_component_id(component);
828
829    let ctx = EvaluationContext::new_sub_component(
830        root,
831        component_idx,
832        RustGeneratorContext { global_access: quote!(_self.globals.get().unwrap()) },
833        parent_ctx,
834    );
835    let mut extra_components = component
836        .popup_windows
837        .iter()
838        .map(|popup| {
839            generate_item_tree(
840                &popup.item_tree,
841                root,
842                Some(&ParentScope::new(&ctx, None)),
843                None,
844                false,
845            )
846        })
847        .chain(component.menu_item_trees.iter().map(|tree| {
848            generate_item_tree(tree, root, Some(&ParentScope::new(&ctx, None)), None, false)
849        }))
850        .collect::<Vec<_>>();
851
852    let mut declared_property_vars = Vec::new();
853    let mut declared_property_types = Vec::new();
854    let mut declared_callbacks = Vec::new();
855    let mut declared_callbacks_types = Vec::new();
856    let mut declared_callbacks_ret = Vec::new();
857
858    for property in component.properties.iter() {
859        let prop_ident = ident(&property.name);
860        let rust_property_type = rust_property_type(&property.ty).unwrap();
861        declared_property_vars.push(prop_ident.clone());
862        declared_property_types.push(rust_property_type.clone());
863    }
864    for callback in component.callbacks.iter() {
865        let cb_ident = ident(&callback.name);
866        let callback_args =
867            callback.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
868        let return_type = rust_primitive_type(&callback.ret_ty).unwrap();
869        declared_callbacks.push(cb_ident.clone());
870        declared_callbacks_types.push(callback_args);
871        declared_callbacks_ret.push(return_type);
872    }
873
874    let change_tracker_names = component
875        .change_callbacks
876        .iter()
877        .enumerate()
878        .map(|(idx, _)| format_ident!("change_tracker{idx}"));
879
880    let declared_functions = generate_functions(component.functions.as_ref(), &ctx);
881
882    let mut init = Vec::new();
883    let mut item_names = Vec::new();
884    let mut item_types = Vec::new();
885
886    #[cfg(slint_debug_property)]
887    init.push(quote!(
888        #(self_rc.#declared_property_vars.debug_name.replace(
889            concat!(stringify!(#inner_component_id), ".", stringify!(#declared_property_vars)).into());)*
890    ));
891
892    for item in &component.items {
893        item_names.push(ident(&item.name));
894        item_types.push(ident(&item.ty.class_name));
895        #[cfg(slint_debug_property)]
896        {
897            let mut it = Some(&item.ty);
898            let elem_name = ident(&item.name);
899            while let Some(ty) = it {
900                for (prop, info) in &ty.properties {
901                    if info.ty.is_property_type() && prop != "commands" {
902                        let name = format!("{}::{}.{}", component.name, item.name, prop);
903                        let prop = ident(&prop);
904                        init.push(
905                            quote!(self_rc.#elem_name.#prop.debug_name.replace(#name.into());),
906                        );
907                    }
908                }
909                it = ty.parent.as_ref();
910            }
911        }
912    }
913
914    let mut repeated_visit_branch: Vec<TokenStream> = Vec::new();
915    let mut repeated_element_components: Vec<TokenStream> = Vec::new();
916    let mut repeated_subtree_ranges: Vec<TokenStream> = Vec::new();
917    let mut repeated_subtree_components: Vec<TokenStream> = Vec::new();
918
919    for (idx, repeated) in component.repeated.iter_enumerated() {
920        extra_components.push(generate_repeated_component(
921            repeated,
922            root,
923            &ParentScope::new(&ctx, Some(idx)),
924        ));
925
926        let idx = usize::from(idx) as u32;
927
928        if let Some(item_index) = repeated.container_item_index {
929            let embed_item = access_local_member(
930                &llr::LocalMemberIndex::Native { item_index, prop_name: Default::default() }.into(),
931                &ctx,
932            );
933
934            let ensure_updated = {
935                quote! {
936                    #embed_item.ensure_updated();
937                }
938            };
939
940            repeated_visit_branch.push(quote!(
941                #idx => {
942                    #ensure_updated
943                    #embed_item.visit_children_item(-1, order, visitor)
944                }
945            ));
946            repeated_subtree_ranges.push(quote!(
947                #idx => {
948                    #ensure_updated
949                    #embed_item.subtree_range()
950                }
951            ));
952            repeated_subtree_components.push(quote!(
953                #idx => {
954                    #ensure_updated
955                    if subtree_index == 0 {
956                        *result = #embed_item.subtree_component()
957                    }
958                }
959            ));
960        } else {
961            let repeater_id = format_ident!("repeater{}", idx);
962            let rep_inner_component_id =
963                self::inner_component_id(&root.sub_components[repeated.sub_tree.root]);
964
965            let model = compile_expression(&repeated.model.borrow(), &ctx);
966            init.push(quote! {
967                _self.#repeater_id.set_model_binding({
968                    let self_weak = sp::VRcMapped::downgrade(&self_rc);
969                    move || {
970                        let self_rc = self_weak.upgrade().unwrap();
971                        let _self = self_rc.as_pin_ref();
972                        (#model) as _
973                    }
974                });
975            });
976            let ensure_updated = if let Some(listview) = &repeated.listview {
977                let vp_y = access_member(&listview.viewport_y, &ctx).unwrap();
978                let vp_h = access_member(&listview.viewport_height, &ctx).unwrap();
979                let lv_h = access_member(&listview.listview_height, &ctx).unwrap();
980                let vp_w = access_member(&listview.viewport_width, &ctx).unwrap();
981                let lv_w = access_member(&listview.listview_width, &ctx).unwrap();
982
983                quote! {
984                    #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).ensure_updated_listview(
985                        || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into() },
986                        #vp_w, #vp_h, #vp_y, #lv_w.get(), #lv_h
987                    );
988                }
989            } else {
990                quote! {
991                    #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).ensure_updated(
992                        || #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into()
993                    );
994                }
995            };
996            repeated_visit_branch.push(quote!(
997                #idx => {
998                    #ensure_updated
999                    _self.#repeater_id.visit(order, visitor)
1000                }
1001            ));
1002            repeated_subtree_ranges.push(quote!(
1003                #idx => {
1004                    #ensure_updated
1005                    sp::IndexRange::from(_self.#repeater_id.range())
1006                }
1007            ));
1008            repeated_subtree_components.push(quote!(
1009                #idx => {
1010                    #ensure_updated
1011                    if let Some(instance) = _self.#repeater_id.instance_at(subtree_index) {
1012                        *result = sp::VRc::downgrade(&sp::VRc::into_dyn(instance));
1013                    }
1014                }
1015            ));
1016            repeated_element_components.push(if repeated.index_prop.is_some() {
1017                quote!(#repeater_id: sp::Repeater<#rep_inner_component_id>)
1018            } else {
1019                quote!(#repeater_id: sp::Conditional<#rep_inner_component_id>)
1020            });
1021        }
1022    }
1023
1024    let mut accessible_role_branch = Vec::new();
1025    let mut accessible_string_property_branch = Vec::new();
1026    let mut accessibility_action_branch = Vec::new();
1027    let mut supported_accessibility_actions = BTreeMap::<u32, BTreeSet<_>>::new();
1028    for ((index, what), expr) in &component.accessible_prop {
1029        let e = compile_expression(&expr.borrow(), &ctx);
1030        if what == "Role" {
1031            accessible_role_branch.push(quote!(#index => #e,));
1032        } else if let Some(what) = what.strip_prefix("Action") {
1033            let what = ident(what);
1034            let has_args = matches!(&*expr.borrow(), Expression::CallBackCall { arguments, .. } if !arguments.is_empty());
1035            accessibility_action_branch.push(if has_args {
1036                quote!((#index, sp::AccessibilityAction::#what(args)) => { let args = (args,); #e })
1037            } else {
1038                quote!((#index, sp::AccessibilityAction::#what) => { #e })
1039            });
1040            supported_accessibility_actions.entry(*index).or_default().insert(what);
1041        } else {
1042            let what = ident(what);
1043            accessible_string_property_branch
1044                .push(quote!((#index, sp::AccessibleStringProperty::#what) => sp::Some(#e),));
1045        }
1046    }
1047    let mut supported_accessibility_actions_branch = supported_accessibility_actions
1048        .into_iter()
1049        .map(|(index, values)| quote!(#index => #(sp::SupportedAccessibilityAction::#values)|*,))
1050        .collect::<Vec<_>>();
1051
1052    let mut item_geometry_branch = component
1053        .geometries
1054        .iter()
1055        .enumerate()
1056        .filter_map(|(i, x)| x.as_ref().map(|x| (i, x)))
1057        .map(|(index, expr)| {
1058            let expr = compile_expression(&expr.borrow(), &ctx);
1059            let index = index as u32;
1060            quote!(#index => #expr,)
1061        })
1062        .collect::<Vec<_>>();
1063
1064    let mut item_element_infos_branch = component
1065        .element_infos
1066        .iter()
1067        .map(|(item_index, ids)| quote!(#item_index => { return sp::Some(#ids.into()); }))
1068        .collect::<Vec<_>>();
1069
1070    let mut user_init_code: Vec<TokenStream> = Vec::new();
1071
1072    let mut sub_component_names: Vec<Ident> = Vec::new();
1073    let mut sub_component_types: Vec<Ident> = Vec::new();
1074
1075    for sub in &component.sub_components {
1076        let field_name = ident(&sub.name);
1077        let sc = &root.sub_components[sub.ty];
1078        let sub_component_id = self::inner_component_id(sc);
1079        let local_tree_index: u32 = sub.index_in_tree as _;
1080        let local_index_of_first_child: u32 = sub.index_of_first_child_in_tree as _;
1081        let global_access = &ctx.generator_state.global_access;
1082
1083        // For children of sub-components, the item index generated by the generate_item_indices pass
1084        // starts at 1 (0 is the root element).
1085        let global_index = if local_tree_index == 0 {
1086            quote!(tree_index)
1087        } else {
1088            quote!(tree_index_of_first_child + #local_tree_index - 1)
1089        };
1090        let global_children = if local_index_of_first_child == 0 {
1091            quote!(0)
1092        } else {
1093            quote!(tree_index_of_first_child + #local_index_of_first_child - 1)
1094        };
1095
1096        let sub_compo_field = access_component_field_offset(&inner_component_id, &field_name);
1097
1098        init.push(quote!(#sub_component_id::init(
1099            sp::VRcMapped::map(self_rc.clone(), |x| #sub_compo_field.apply_pin(x)),
1100            #global_access.clone(), #global_index, #global_children
1101        );));
1102        user_init_code.push(quote!(#sub_component_id::user_init(
1103            sp::VRcMapped::map(self_rc.clone(), |x| #sub_compo_field.apply_pin(x)),
1104        );));
1105
1106        let sub_component_repeater_count = sc.repeater_count(root);
1107        if sub_component_repeater_count > 0 {
1108            let repeater_offset = sub.repeater_offset;
1109            let last_repeater = repeater_offset + sub_component_repeater_count - 1;
1110            repeated_visit_branch.push(quote!(
1111                #repeater_offset..=#last_repeater => {
1112                    #sub_compo_field.apply_pin(_self).visit_dynamic_children(dyn_index - #repeater_offset, order, visitor)
1113                }
1114            ));
1115            repeated_subtree_ranges.push(quote!(
1116                #repeater_offset..=#last_repeater => {
1117                    #sub_compo_field.apply_pin(_self).subtree_range(dyn_index - #repeater_offset)
1118                }
1119            ));
1120            repeated_subtree_components.push(quote!(
1121                #repeater_offset..=#last_repeater => {
1122                    #sub_compo_field.apply_pin(_self).subtree_component(dyn_index - #repeater_offset, subtree_index, result)
1123                }
1124            ));
1125        }
1126
1127        let sub_items_count = sc.child_item_count(root);
1128        accessible_role_branch.push(quote!(
1129            #local_tree_index => #sub_compo_field.apply_pin(_self).accessible_role(0),
1130        ));
1131        accessible_string_property_branch.push(quote!(
1132            (#local_tree_index, _) => #sub_compo_field.apply_pin(_self).accessible_string_property(0, what),
1133        ));
1134        accessibility_action_branch.push(quote!(
1135            (#local_tree_index, _) => #sub_compo_field.apply_pin(_self).accessibility_action(0, action),
1136        ));
1137        supported_accessibility_actions_branch.push(quote!(
1138            #local_tree_index => #sub_compo_field.apply_pin(_self).supported_accessibility_actions(0),
1139        ));
1140        if sub_items_count > 1 {
1141            let range_begin = local_index_of_first_child;
1142            let range_end = range_begin + sub_items_count - 2 + sc.repeater_count(root);
1143            accessible_role_branch.push(quote!(
1144                #range_begin..=#range_end => #sub_compo_field.apply_pin(_self).accessible_role(index - #range_begin + 1),
1145            ));
1146            accessible_string_property_branch.push(quote!(
1147                (#range_begin..=#range_end, _) => #sub_compo_field.apply_pin(_self).accessible_string_property(index - #range_begin + 1, what),
1148            ));
1149            item_geometry_branch.push(quote!(
1150                #range_begin..=#range_end => return #sub_compo_field.apply_pin(_self).item_geometry(index - #range_begin + 1),
1151            ));
1152            accessibility_action_branch.push(quote!(
1153                (#range_begin..=#range_end, _) => #sub_compo_field.apply_pin(_self).accessibility_action(index - #range_begin + 1, action),
1154            ));
1155            supported_accessibility_actions_branch.push(quote!(
1156                #range_begin..=#range_end => #sub_compo_field.apply_pin(_self).supported_accessibility_actions(index - #range_begin + 1),
1157            ));
1158            item_element_infos_branch.push(quote!(
1159                #range_begin..=#range_end => #sub_compo_field.apply_pin(_self).item_element_infos(index - #range_begin + 1),
1160            ));
1161        }
1162
1163        sub_component_names.push(field_name);
1164        sub_component_types.push(sub_component_id);
1165    }
1166
1167    let popup_id_names =
1168        component.popup_windows.iter().enumerate().map(|(i, _)| internal_popup_id(i));
1169
1170    for (prop1, prop2, fields) in &component.two_way_bindings {
1171        let p1 = access_member(prop1, &ctx).unwrap();
1172        let p2 = access_member(prop2, &ctx);
1173        let r = p2.then(|p2| {
1174            if fields.is_empty() {
1175                quote!(sp::Property::link_two_way(#p1, #p2))
1176            } else {
1177                let mut access = quote!();
1178                let mut ty = ctx.property_ty(prop2);
1179                for f in fields {
1180                    let Type::Struct(s) = &ty else {
1181                        panic!("Field of two way binding on a non-struct type")
1182                    };
1183                    let a = struct_field_access(s, f);
1184                    access.extend(quote!(.#a));
1185                    ty = s.fields.get(f).unwrap();
1186                }
1187                let to_property_value =
1188                    set_primitive_property_value(ty, quote!(s #access .clone()));
1189                let to_struct_value = primitive_value_from_property_value(ty, quote!((*v).clone()));
1190                quote!(
1191                    sp::Property::link_two_way_with_map(
1192                        #p2,
1193                        #p1,
1194                        |s| #to_property_value,
1195                        |s, v| s #access = #to_struct_value,
1196                    )
1197                )
1198            }
1199        });
1200        init.push(quote!(#r;))
1201    }
1202
1203    for (prop, expression) in &component.property_init {
1204        handle_property_init(prop, expression, &mut init, &ctx)
1205    }
1206    for prop in &component.const_properties {
1207        let rust_property = access_local_member(prop, &ctx);
1208        init.push(quote!(#rust_property.set_constant();))
1209    }
1210
1211    let parent_component_type = parent_ctx.iter().map(|parent| {
1212        let parent_component_id =
1213            self::inner_component_id(&ctx.compilation_unit.sub_components[parent.sub_component]);
1214        quote!(sp::VWeakMapped::<sp::ItemTreeVTable, #parent_component_id>)
1215    });
1216
1217    user_init_code.extend(component.init_code.iter().map(|e| {
1218        let code = compile_expression(&e.borrow(), &ctx);
1219        quote!(#code;)
1220    }));
1221
1222    user_init_code.extend(component.change_callbacks.iter().enumerate().map(|(idx, (p, e))| {
1223        let code = compile_expression(&e.borrow(), &ctx);
1224        let prop = compile_expression(&Expression::PropertyReference(p.clone()), &ctx);
1225        let change_tracker = format_ident!("change_tracker{idx}");
1226        quote! {
1227            let self_weak = sp::VRcMapped::downgrade(&self_rc);
1228            #[allow(dead_code, unused)]
1229            _self.#change_tracker.init(
1230                self_weak,
1231                move |self_weak| {
1232                    let self_rc = self_weak.upgrade().unwrap();
1233                    let _self = self_rc.as_pin_ref();
1234                    #prop
1235                },
1236                move |self_weak, _| {
1237                    let self_rc = self_weak.upgrade().unwrap();
1238                    let _self = self_rc.as_pin_ref();
1239                    #code;
1240                }
1241            );
1242        }
1243    }));
1244
1245    let layout_info_h = compile_expression_no_parenthesis(&component.layout_info_h.borrow(), &ctx);
1246    let layout_info_v = compile_expression_no_parenthesis(&component.layout_info_v.borrow(), &ctx);
1247    let grid_layout_input_for_repeated_fn =
1248        component.grid_layout_input_for_repeated.as_ref().map(|expr| {
1249            let expr = compile_expression_no_parenthesis(&expr.borrow(), &ctx);
1250            quote! {
1251                fn grid_layout_input_for_repeated(
1252                    self: ::core::pin::Pin<&Self>,
1253                    new_row: bool,
1254                    result: &mut [sp::GridLayoutInputData],
1255                ) {
1256                    #![allow(unused)]
1257                    let _self = self;
1258                    #expr
1259                }
1260            }
1261        });
1262
1263    let flexbox_layout_item_info_for_repeated_fn =
1264        component.flexbox_layout_item_info_for_repeated.as_ref().map(|expr| {
1265            let expr = compile_expression(&expr.borrow(), &ctx);
1266            quote! {
1267                fn flexbox_layout_item_info_for_repeated(
1268                    self: ::core::pin::Pin<&Self>,
1269                ) -> sp::FlexboxLayoutItemInfo {
1270                    #![allow(unused)]
1271                    let _self = self;
1272                    #expr
1273                }
1274            }
1275        });
1276
1277    // FIXME! this is only public because of the ComponentHandle::WeakInner. we should find another way
1278    let visibility = parent_ctx.is_none().then(|| quote!(pub));
1279
1280    let subtree_index_function = if let Some(property_index) = index_property {
1281        let prop = access_local_member(&property_index.into(), &ctx);
1282        quote!(#prop.get() as usize)
1283    } else {
1284        quote!(usize::MAX)
1285    };
1286
1287    let timer_names =
1288        component.timers.iter().enumerate().map(|(idx, _)| format_ident!("timer{idx}"));
1289    let update_timers = (!component.timers.is_empty()).then(|| {
1290        let updt = component.timers.iter().enumerate().map(|(idx, tmr)| {
1291            let ident = format_ident!("timer{idx}");
1292            let interval = compile_expression(&tmr.interval.borrow(), &ctx);
1293            let running = compile_expression(&tmr.running.borrow(), &ctx);
1294            let callback = compile_expression(&tmr.triggered.borrow(), &ctx);
1295            quote!(
1296                if #running {
1297                    let interval = ::core::time::Duration::from_millis(#interval as u64);
1298                    if !self.#ident.running() || interval != self.#ident.interval() {
1299                        let self_weak = self.self_weak.get().unwrap().clone();
1300                        self.#ident.start(sp::TimerMode::Repeated, interval, move || {
1301                            if let Some(self_rc) = self_weak.upgrade() {
1302                                let _self = self_rc.as_pin_ref();
1303                                #callback
1304                            }
1305                        });
1306                    }
1307                } else {
1308                    self.#ident.stop();
1309                }
1310            )
1311        });
1312        user_init_code.push(quote!(_self.update_timers();));
1313        quote!(
1314            fn update_timers(self: ::core::pin::Pin<&Self>) {
1315                let _self = self;
1316                #(#updt)*
1317            }
1318        )
1319    });
1320
1321    let pin_macro = if pinned_drop { quote!(#[pin_drop]) } else { quote!(#[pin]) };
1322
1323    quote!(
1324        #[derive(sp::FieldOffsets, Default)]
1325        #[const_field_offset(sp::const_field_offset)]
1326        #[repr(C)]
1327        #pin_macro
1328        #visibility
1329        struct #inner_component_id {
1330            #(#item_names : sp::#item_types,)*
1331            #(#sub_component_names : #sub_component_types,)*
1332            #(#popup_id_names : ::core::cell::Cell<sp::Option<::core::num::NonZeroU32>>,)*
1333            #(#declared_property_vars : sp::Property<#declared_property_types>,)*
1334            #(#declared_callbacks : sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
1335            #(#repeated_element_components,)*
1336            #(#change_tracker_names : sp::ChangeTracker,)*
1337            #(#timer_names : sp::Timer,)*
1338            self_weak : sp::OnceCell<sp::VWeakMapped<sp::ItemTreeVTable, #inner_component_id>>,
1339            #(parent : #parent_component_type,)*
1340            globals: sp::OnceCell<sp::Rc<SharedGlobals>>,
1341            tree_index: ::core::cell::Cell<u32>,
1342            tree_index_of_first_child: ::core::cell::Cell<u32>,
1343        }
1344
1345        impl #inner_component_id {
1346            fn init(self_rc: sp::VRcMapped<sp::ItemTreeVTable, Self>,
1347                    globals : sp::Rc<SharedGlobals>,
1348                    tree_index: u32, tree_index_of_first_child: u32) {
1349                #![allow(unused)]
1350                let _self = self_rc.as_pin_ref();
1351                let _ = _self.self_weak.set(sp::VRcMapped::downgrade(&self_rc));
1352                let _ = _self.globals.set(globals);
1353                _self.tree_index.set(tree_index);
1354                _self.tree_index_of_first_child.set(tree_index_of_first_child);
1355                #(#init)*
1356            }
1357
1358            fn user_init(self_rc: sp::VRcMapped<sp::ItemTreeVTable, Self>) {
1359                #![allow(unused)]
1360                let _self = self_rc.as_pin_ref();
1361                #(#user_init_code)*
1362            }
1363
1364            fn visit_dynamic_children(
1365                self: ::core::pin::Pin<&Self>,
1366                dyn_index: u32,
1367                order: sp::TraversalOrder,
1368                visitor: sp::ItemVisitorRefMut<'_>
1369            ) -> sp::VisitChildrenResult {
1370                #![allow(unused)]
1371                let _self = self;
1372                match dyn_index {
1373                    #(#repeated_visit_branch)*
1374                    _ => panic!("invalid dyn_index {}", dyn_index),
1375                }
1376            }
1377
1378            fn layout_info(self: ::core::pin::Pin<&Self>, orientation: sp::Orientation) -> sp::LayoutInfo {
1379                #![allow(unused)]
1380                let _self = self;
1381                match orientation {
1382                    sp::Orientation::Horizontal => #layout_info_h,
1383                    sp::Orientation::Vertical => #layout_info_v,
1384                }
1385            }
1386
1387            #grid_layout_input_for_repeated_fn
1388
1389            #flexbox_layout_item_info_for_repeated_fn
1390
1391            fn subtree_range(self: ::core::pin::Pin<&Self>, dyn_index: u32) -> sp::IndexRange {
1392                #![allow(unused)]
1393                let _self = self;
1394                match dyn_index {
1395                    #(#repeated_subtree_ranges)*
1396                    _ => panic!("invalid dyn_index {}", dyn_index),
1397                }
1398            }
1399
1400            fn subtree_component(self: ::core::pin::Pin<&Self>, dyn_index: u32, subtree_index: usize, result: &mut sp::ItemTreeWeak) {
1401                #![allow(unused)]
1402                let _self = self;
1403                match dyn_index {
1404                    #(#repeated_subtree_components)*
1405                    _ => panic!("invalid dyn_index {}", dyn_index),
1406                };
1407            }
1408
1409            fn index_property(self: ::core::pin::Pin<&Self>) -> usize {
1410                #![allow(unused)]
1411                let _self = self;
1412                #subtree_index_function
1413            }
1414
1415            fn item_geometry(self: ::core::pin::Pin<&Self>, index: u32) -> sp::LogicalRect {
1416                #![allow(unused)]
1417                let _self = self;
1418                // The result of the expression is an anonymous struct, `{height: length, width: length, x: length, y: length}`
1419                // fields are in alphabetical order
1420                let (h, w, x, y) = match index {
1421                    #(#item_geometry_branch)*
1422                    _ => return ::core::default::Default::default()
1423                };
1424                sp::euclid::rect(x, y, w, h)
1425            }
1426
1427            fn accessible_role(self: ::core::pin::Pin<&Self>, index: u32) -> sp::AccessibleRole {
1428                #![allow(unused)]
1429                let _self = self;
1430                match index {
1431                    #(#accessible_role_branch)*
1432                    //#(#forward_sub_ranges => #forward_sub_field.apply_pin(_self).accessible_role())*
1433                    _ => sp::AccessibleRole::default(),
1434                }
1435            }
1436
1437            fn accessible_string_property(
1438                self: ::core::pin::Pin<&Self>,
1439                index: u32,
1440                what: sp::AccessibleStringProperty,
1441            ) -> sp::Option<sp::SharedString> {
1442                #![allow(unused)]
1443                let _self = self;
1444                match (index, what) {
1445                    #(#accessible_string_property_branch)*
1446                    _ => sp::None,
1447                }
1448            }
1449
1450            fn accessibility_action(self: ::core::pin::Pin<&Self>, index: u32, action: &sp::AccessibilityAction) {
1451                #![allow(unused)]
1452                let _self = self;
1453                match (index, action) {
1454                    #(#accessibility_action_branch)*
1455                    _ => (),
1456                }
1457            }
1458
1459            fn supported_accessibility_actions(self: ::core::pin::Pin<&Self>, index: u32) -> sp::SupportedAccessibilityAction {
1460                #![allow(unused)]
1461                let _self = self;
1462                match index {
1463                    #(#supported_accessibility_actions_branch)*
1464                    _ => ::core::default::Default::default(),
1465                }
1466            }
1467
1468            fn item_element_infos(self: ::core::pin::Pin<&Self>, index: u32) -> sp::Option<sp::SharedString> {
1469                #![allow(unused)]
1470                let _self = self;
1471                match index {
1472                    #(#item_element_infos_branch)*
1473                    _ => { ::core::default::Default::default() }
1474                }
1475            }
1476
1477            #update_timers
1478
1479            #(#declared_functions)*
1480        }
1481
1482        #(#extra_components)*
1483    )
1484}
1485
1486fn generate_functions(functions: &[llr::Function], ctx: &EvaluationContext) -> Vec<TokenStream> {
1487    functions
1488        .iter()
1489        .map(|f| {
1490            let mut ctx2 = ctx.clone();
1491            ctx2.argument_types = &f.args;
1492            let tokens_for_expression = compile_expression(&f.code, &ctx2);
1493            let as_ = if f.ret_ty == Type::Void {
1494                Some(quote!(;))
1495            } else if f.code.ty(&ctx2) == Type::Invalid {
1496                // Don't cast if the Rust code is the never type, as with return statements inside a block, the
1497                // type of the return expression is `()` instead of `!`.
1498                None
1499            } else {
1500                Some(quote!(as _))
1501            };
1502            let fn_id = ident(&format!("fn_{}", f.name));
1503            let args_ty =
1504                f.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
1505            let return_type = rust_primitive_type(&f.ret_ty).unwrap();
1506            let args_name =
1507                (0..f.args.len()).map(|i| format_ident!("arg_{}", i)).collect::<Vec<_>>();
1508
1509            quote! {
1510                #[allow(dead_code, unused)]
1511                pub fn #fn_id(self: ::core::pin::Pin<&Self>, #(#args_name : #args_ty,)*) -> #return_type {
1512                    let _self = self;
1513                    let args = (#(#args_name,)*);
1514                    (#tokens_for_expression) #as_
1515                }
1516            }
1517        })
1518        .collect()
1519}
1520
1521fn generate_global(
1522    global_idx: llr::GlobalIdx,
1523    global: &llr::GlobalComponent,
1524    root: &llr::CompilationUnit,
1525    compiler_config: &CompilerConfiguration,
1526    global_exports: &mut Vec<TokenStream>,
1527) -> TokenStream {
1528    let mut declared_property_vars = Vec::new();
1529    let mut declared_property_types = Vec::new();
1530    let mut declared_callbacks = Vec::new();
1531    let mut declared_callbacks_types = Vec::new();
1532    let mut declared_callbacks_ret = Vec::new();
1533
1534    for property in global.properties.iter() {
1535        declared_property_vars.push(ident(&property.name));
1536        declared_property_types.push(rust_property_type(&property.ty).unwrap());
1537    }
1538    for callback in &global.callbacks {
1539        let callback_args =
1540            callback.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
1541        declared_callbacks.push(ident(&callback.name));
1542        declared_callbacks_types.push(callback_args);
1543        declared_callbacks_ret.push(rust_primitive_type(&callback.ret_ty));
1544    }
1545
1546    let mut init = Vec::new();
1547    let inner_component_id = global_inner_name(global);
1548
1549    #[cfg(slint_debug_property)]
1550    init.push(quote!(
1551        #(self_rc.#declared_property_vars.debug_name.replace(
1552            concat!(stringify!(#inner_component_id), ".", stringify!(#declared_property_vars)).into());)*
1553    ));
1554
1555    let ctx = EvaluationContext::new_global(
1556        root,
1557        global_idx,
1558        RustGeneratorContext {
1559            global_access: quote!(_self.globals.get().unwrap().upgrade().unwrap()),
1560        },
1561    );
1562
1563    let declared_functions = generate_functions(global.functions.as_ref(), &ctx);
1564
1565    for (property_index, expression) in &global.init_values {
1566        handle_property_init(
1567            &llr::LocalMemberReference::from(property_index.clone()).into(),
1568            expression,
1569            &mut init,
1570            &ctx,
1571        )
1572    }
1573    for (property_index, cst) in global.const_properties.iter_enumerated() {
1574        if *cst {
1575            let rust_property = access_local_member(&property_index.into(), &ctx);
1576            init.push(quote!(#rust_property.set_constant();))
1577        }
1578    }
1579
1580    let public_component_id = ident(&global.name);
1581    let global_id = format_ident!("global_{}", public_component_id);
1582
1583    let change_tracker_names = global
1584        .change_callbacks
1585        .keys()
1586        .map(|idx| format_ident!("change_tracker{}", usize::from(*idx)));
1587    init.extend(global.change_callbacks.iter().map(|(p, e)| {
1588        let code = compile_expression(&e.borrow(), &ctx);
1589        let prop = access_local_member(&(*p).into(), &ctx);
1590        let change_tracker = format_ident!("change_tracker{}", usize::from(*p));
1591        quote! {
1592            #[allow(dead_code, unused)]
1593            _self.#change_tracker.init(
1594                self_rc.globals.get().unwrap().clone(),
1595                move |global_weak| {
1596                    let self_rc = global_weak.upgrade().unwrap().#global_id.clone();
1597                    let _self = self_rc.as_ref();
1598                    #prop.get()
1599                },
1600                move |global_weak, _| {
1601                    let self_rc = global_weak.upgrade().unwrap().#global_id.clone();
1602                    let _self = self_rc.as_ref();
1603                    #code;
1604                }
1605            );
1606        }
1607    }));
1608
1609    let pub_token = if compiler_config.library_name.is_some() && !global.is_builtin {
1610        global_exports.push(quote! (#inner_component_id));
1611        quote!(pub)
1612    } else {
1613        quote!()
1614    };
1615
1616    let public_interface = global.exported.then(|| {
1617        let property_and_callback_accessors = public_api(
1618            &global.public_properties,
1619            &global.private_properties,
1620            quote!(self.0.as_ref()),
1621            &ctx,
1622        );
1623        let aliases = global.aliases.iter().map(|name| ident(name));
1624        let getters = generate_global_getters(global, root);
1625
1626        let strong_handle_impl = quote!(
1627            impl slint::StrongHandle for #public_component_id<'static> {
1628                type WeakInner = sp::Weak<#inner_component_id>;
1629
1630                fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> ::core::option::Option<Self> {
1631                    let inner = ::core::pin::Pin::new(inner.upgrade()?);
1632                    ::core::option::Option::Some(Self(inner, ::core::marker::PhantomData::default()))
1633                }
1634            }
1635        );
1636
1637        quote!(
1638            #[allow(unused)]
1639            pub struct #public_component_id<'a>(#pub_token ::core::pin::Pin<sp::Rc<#inner_component_id>>, #pub_token ::core::marker::PhantomData<&'a #inner_component_id>);
1640
1641            impl<'a> #public_component_id<'a> {
1642                #property_and_callback_accessors
1643            }
1644            #(pub type #aliases<'a> = #public_component_id<'a>;)*
1645            #getters
1646
1647            #strong_handle_impl
1648        )
1649    });
1650
1651    let private_interface = (!global.is_builtin).then(|| {
1652        quote!(
1653            #[derive(sp::FieldOffsets, Default)]
1654            #[const_field_offset(sp::const_field_offset)]
1655            #[repr(C)]
1656            #[pin]
1657            pub struct #inner_component_id {
1658                #(#pub_token  #declared_property_vars: sp::Property<#declared_property_types>,)*
1659                #(#pub_token  #declared_callbacks: sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
1660                #(#pub_token  #change_tracker_names : sp::ChangeTracker,)*
1661                globals : sp::OnceCell<sp::Weak<SharedGlobals>>,
1662            }
1663
1664            impl #inner_component_id {
1665                fn new() -> ::core::pin::Pin<sp::Rc<Self>> {
1666                    sp::Rc::pin(Self::default())
1667                }
1668                fn init(self: ::core::pin::Pin<sp::Rc<Self>>, globals: &sp::Rc<SharedGlobals>) {
1669                    #![allow(unused)]
1670                    let _ = self.globals.set(sp::Rc::downgrade(globals));
1671                    let self_rc = self;
1672                    let _self = self_rc.as_ref();
1673                    #(#init)*
1674                }
1675
1676                #(#declared_functions)*
1677            }
1678        )
1679    });
1680
1681    quote!(#private_interface #public_interface)
1682}
1683
1684fn generate_global_getters(
1685    global: &llr::GlobalComponent,
1686    root: &llr::CompilationUnit,
1687) -> TokenStream {
1688    let public_component_id = ident(&global.name);
1689    let global_id = format_ident!("global_{}", public_component_id);
1690
1691    let getters = root.public_components.iter().map(|c| {
1692        let root_component_id = ident(&c.name);
1693        quote! {
1694            impl<'a> slint::Global<'a, #root_component_id> for #public_component_id<'a> {
1695                type StaticSelf = #public_component_id<'static>;
1696
1697                fn get(component: &'a #root_component_id) -> Self {
1698                    Self(component.0.globals.get().unwrap().#global_id.clone(), ::core::marker::PhantomData::default())
1699                }
1700
1701                fn as_weak(&self) -> slint::Weak<Self::StaticSelf> {
1702                    let inner = ::core::pin::Pin::into_inner(self.0.clone());
1703                    slint::Weak::new(sp::Rc::downgrade(&inner))
1704                }
1705            }
1706        }
1707    });
1708
1709    quote! (
1710        #(#getters)*
1711    )
1712}
1713
1714fn generate_item_tree(
1715    sub_tree: &llr::ItemTree,
1716    root: &llr::CompilationUnit,
1717    parent_ctx: Option<&ParentScope>,
1718    index_property: Option<llr::PropertyIdx>,
1719    is_popup_menu: bool,
1720) -> TokenStream {
1721    let sub_comp = generate_sub_component(sub_tree.root, root, parent_ctx, index_property, true);
1722    let inner_component_id = self::inner_component_id(&root.sub_components[sub_tree.root]);
1723    let parent_component_type = parent_ctx
1724        .iter()
1725        .map(|parent| {
1726            let parent_component_id =
1727                self::inner_component_id(&root.sub_components[parent.sub_component]);
1728            quote!(sp::VWeakMapped::<sp::ItemTreeVTable, #parent_component_id>)
1729        })
1730        .collect::<Vec<_>>();
1731
1732    let globals = if is_popup_menu {
1733        quote!(globals)
1734    } else if parent_ctx.is_some() {
1735        quote!(parent.upgrade().unwrap().globals.get().unwrap().clone())
1736    } else {
1737        quote!(SharedGlobals::new(sp::VRc::downgrade(&self_dyn_rc)))
1738    };
1739    let globals_arg = is_popup_menu.then(|| quote!(globals: sp::Rc<SharedGlobals>));
1740
1741    let embedding_function = if parent_ctx.is_some() {
1742        quote!(todo!("Components written in Rust can not get embedded yet."))
1743    } else {
1744        quote!(false)
1745    };
1746
1747    let parent_item_expression = parent_ctx.map(|parent| parent.repeater_index.map_or_else(|| {
1748        // No repeater index, this could be a PopupWindow
1749        quote!{
1750            if let Some(parent_rc) = self.parent.clone().upgrade() {
1751                let parent_origin = sp::VRcMapped::origin(&parent_rc);
1752                // TODO: store popup index in ctx and set it here instead of 0?
1753                *_result = sp::ItemRc::new_root(parent_origin).downgrade();
1754            }
1755        }
1756    }, |idx| {
1757        let current_sub_component = &root.sub_components[parent.sub_component];
1758        let sub_component_offset = current_sub_component.repeated[idx].index_in_tree;
1759
1760        quote!{
1761            if let Some((parent_component, parent_index)) = self
1762                .parent
1763                .clone()
1764                .upgrade()
1765                .map(|sc| (sp::VRcMapped::origin(&sc), sc.tree_index_of_first_child.get()))
1766            {
1767                *_result = sp::ItemRc::new(parent_component, parent_index + #sub_component_offset - 1)
1768                    .downgrade();
1769            }
1770        }
1771    }));
1772    let mut item_tree_array = Vec::new();
1773    let mut item_array = Vec::new();
1774    sub_tree.tree.visit_in_array(&mut |node, children_offset, parent_index| {
1775        let parent_index = parent_index as u32;
1776        let (path, component) =
1777            follow_sub_component_path(root, sub_tree.root, &node.sub_component_path);
1778        match node.item_index {
1779            Either::Right(mut repeater_index) => {
1780                assert_eq!(node.children.len(), 0);
1781                let mut sub_component = &root.sub_components[sub_tree.root];
1782                for i in &node.sub_component_path {
1783                    repeater_index += sub_component.sub_components[*i].repeater_offset;
1784                    sub_component = &root.sub_components[sub_component.sub_components[*i].ty];
1785                }
1786                item_tree_array.push(quote!(
1787                    sp::ItemTreeNode::DynamicTree {
1788                        index: #repeater_index,
1789                        parent_index: #parent_index,
1790                    }
1791                ));
1792            }
1793            Either::Left(item_index) => {
1794                let item = &component.items[item_index];
1795                let field = access_component_field_offset(
1796                    &self::inner_component_id(component),
1797                    &ident(&item.name),
1798                );
1799
1800                let children_count = node.children.len() as u32;
1801                let children_index = children_offset as u32;
1802                let item_array_len = item_array.len() as u32;
1803                let is_accessible = node.is_accessible;
1804                item_tree_array.push(quote!(
1805                    sp::ItemTreeNode::Item {
1806                        is_accessible: #is_accessible,
1807                        children_count: #children_count,
1808                        children_index: #children_index,
1809                        parent_index: #parent_index,
1810                        item_array_index: #item_array_len,
1811                    }
1812                ));
1813                item_array.push(quote!(sp::VOffset::new(#path #field)));
1814            }
1815        }
1816    });
1817
1818    let item_tree_array_len = item_tree_array.len();
1819    let item_array_len = item_array.len();
1820
1821    let element_info_body = if root.has_debug_info {
1822        quote!(
1823            *_result = self.item_element_infos(_index).unwrap_or_default();
1824            true
1825        )
1826    } else {
1827        quote!(false)
1828    };
1829
1830    quote!(
1831        #sub_comp
1832
1833        impl #inner_component_id {
1834            fn new(#(parent: #parent_component_type,)* #globals_arg) -> ::core::result::Result<sp::VRc<sp::ItemTreeVTable, Self>, slint::PlatformError> {
1835                #![allow(unused)]
1836                let mut _self = Self::default();
1837                #(_self.parent = parent.clone() as #parent_component_type;)*
1838                let self_rc = sp::VRc::new(_self);
1839                let self_dyn_rc = sp::VRc::into_dyn(self_rc.clone());
1840                let globals = #globals;
1841                sp::register_item_tree(&self_dyn_rc, globals.maybe_window_adapter_impl());
1842                Self::init(sp::VRc::map(self_rc.clone(), |x| x), globals, 0, 1);
1843                ::core::result::Result::Ok(self_rc)
1844            }
1845
1846            fn item_tree() -> &'static [sp::ItemTreeNode] {
1847                const ITEM_TREE : [sp::ItemTreeNode; #item_tree_array_len] = [#(#item_tree_array),*];
1848                &ITEM_TREE
1849            }
1850
1851            fn item_array() -> &'static [sp::VOffset<Self, sp::ItemVTable, sp::AllowPin>] {
1852                // FIXME: ideally this should be a const, but we can't because of the pointer to the vtable
1853                static ITEM_ARRAY : sp::OnceBox<
1854                    [sp::VOffset<#inner_component_id, sp::ItemVTable, sp::AllowPin>; #item_array_len]
1855                > = sp::OnceBox::new();
1856                &*ITEM_ARRAY.get_or_init(|| sp::vec![#(#item_array),*].into_boxed_slice().try_into().unwrap())
1857            }
1858        }
1859
1860        const _ : () = {
1861            use slint::private_unstable_api::re_exports::*;
1862            ItemTreeVTable_static!(static VT for self::#inner_component_id);
1863        };
1864
1865        impl sp::PinnedDrop for #inner_component_id {
1866            fn drop(self: ::core::pin::Pin<&mut #inner_component_id>) {
1867                sp::vtable::new_vref!(let vref : VRef<sp::ItemTreeVTable> for sp::ItemTree = self.as_ref().get_ref());
1868                if let Some(wa) = self.globals.get().unwrap().maybe_window_adapter_impl() {
1869                    sp::unregister_item_tree(self.as_ref(), vref, Self::item_array(), &wa);
1870                }
1871            }
1872        }
1873
1874        impl sp::ItemTree for #inner_component_id {
1875            fn visit_children_item(self: ::core::pin::Pin<&Self>, index: isize, order: sp::TraversalOrder, visitor: sp::ItemVisitorRefMut<'_>)
1876                -> sp::VisitChildrenResult
1877            {
1878                return sp::visit_item_tree(self, &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), self.get_item_tree().as_slice(), index, order, visitor, visit_dynamic);
1879                #[allow(unused)]
1880                fn visit_dynamic(_self: ::core::pin::Pin<&#inner_component_id>, order: sp::TraversalOrder, visitor: sp::ItemVisitorRefMut<'_>, dyn_index: u32) -> sp::VisitChildrenResult  {
1881                    _self.visit_dynamic_children(dyn_index, order, visitor)
1882                }
1883            }
1884
1885            fn get_item_ref(self: ::core::pin::Pin<&Self>, index: u32) -> ::core::pin::Pin<sp::ItemRef<'_>> {
1886                match &self.get_item_tree().as_slice()[index as usize] {
1887                    sp::ItemTreeNode::Item { item_array_index, .. } => {
1888                        Self::item_array()[*item_array_index as usize].apply_pin(self)
1889                    }
1890                    sp::ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
1891
1892                }
1893            }
1894
1895            fn get_item_tree(
1896                self: ::core::pin::Pin<&Self>) -> sp::Slice<'_, sp::ItemTreeNode>
1897            {
1898                Self::item_tree().into()
1899            }
1900
1901            fn get_subtree_range(
1902                self: ::core::pin::Pin<&Self>, index: u32) -> sp::IndexRange
1903            {
1904                self.subtree_range(index)
1905            }
1906
1907            fn get_subtree(
1908                self: ::core::pin::Pin<&Self>, index: u32, subtree_index: usize, result: &mut sp::ItemTreeWeak)
1909            {
1910                self.subtree_component(index, subtree_index, result);
1911            }
1912
1913            fn subtree_index(
1914                self: ::core::pin::Pin<&Self>) -> usize
1915            {
1916                self.index_property()
1917            }
1918
1919            fn parent_node(self: ::core::pin::Pin<&Self>, _result: &mut sp::ItemWeak) {
1920                #parent_item_expression
1921            }
1922
1923            fn embed_component(self: ::core::pin::Pin<&Self>, _parent_component: &sp::ItemTreeWeak, _item_tree_index: u32) -> bool {
1924                #embedding_function
1925            }
1926
1927            fn layout_info(self: ::core::pin::Pin<&Self>, orientation: sp::Orientation) -> sp::LayoutInfo {
1928                self.layout_info(orientation)
1929            }
1930
1931            fn item_geometry(self: ::core::pin::Pin<&Self>, index: u32) -> sp::LogicalRect {
1932                self.item_geometry(index)
1933            }
1934
1935            fn accessible_role(self: ::core::pin::Pin<&Self>, index: u32) -> sp::AccessibleRole {
1936                self.accessible_role(index)
1937            }
1938
1939            fn accessible_string_property(
1940                self: ::core::pin::Pin<&Self>,
1941                index: u32,
1942                what: sp::AccessibleStringProperty,
1943                result: &mut sp::SharedString,
1944            ) -> bool {
1945                if let Some(r) = self.accessible_string_property(index, what) {
1946                    *result = r;
1947                    true
1948                } else {
1949                    false
1950                }
1951            }
1952
1953            fn accessibility_action(self: ::core::pin::Pin<&Self>, index: u32, action: &sp::AccessibilityAction) {
1954                self.accessibility_action(index, action);
1955            }
1956
1957            fn supported_accessibility_actions(self: ::core::pin::Pin<&Self>, index: u32) -> sp::SupportedAccessibilityAction {
1958                self.supported_accessibility_actions(index)
1959            }
1960
1961            fn item_element_infos(
1962                self: ::core::pin::Pin<&Self>,
1963                _index: u32,
1964                _result: &mut sp::SharedString,
1965            ) -> bool {
1966                #element_info_body
1967            }
1968
1969            fn window_adapter(
1970                self: ::core::pin::Pin<&Self>,
1971                do_create: bool,
1972                result: &mut sp::Option<sp::Rc<dyn sp::WindowAdapter>>,
1973            ) {
1974                if do_create {
1975                    *result = sp::Some(self.globals.get().unwrap().window_adapter_impl());
1976                } else {
1977                    *result = self.globals.get().unwrap().maybe_window_adapter_impl();
1978                }
1979            }
1980        }
1981
1982
1983    )
1984}
1985
1986fn generate_repeated_component(
1987    repeated: &llr::RepeatedElement,
1988    unit: &llr::CompilationUnit,
1989    parent_ctx: &ParentScope,
1990) -> TokenStream {
1991    let component =
1992        generate_item_tree(&repeated.sub_tree, unit, Some(parent_ctx), repeated.index_prop, false);
1993
1994    let ctx = EvaluationContext {
1995        compilation_unit: unit,
1996        current_scope: EvaluationScope::SubComponent(repeated.sub_tree.root, Some(parent_ctx)),
1997        generator_state: RustGeneratorContext { global_access: quote!(_self) },
1998        argument_types: &[],
1999    };
2000
2001    let root_sc = &unit.sub_components[repeated.sub_tree.root];
2002    let inner_component_id = self::inner_component_id(root_sc);
2003
2004    let grid_layout_input_data_fn = root_sc.grid_layout_input_for_repeated.as_ref().map(|_| {
2005        let has_inner_repeaters = llr::has_inner_repeaters(&root_sc.row_child_templates);
2006        if has_inner_repeaters {
2007            let templates = root_sc.row_child_templates.as_ref().unwrap();
2008            let static_count = llr::static_child_count(templates);
2009
2010            // Generate fill code: one snippet per template entry.
2011            // new_row is re-evaluated per slot (write_idx == 0 && new_row) so that only the
2012            // first produced slot is marked as starting a new row.
2013            let fill_code: Vec<TokenStream> = templates
2014                .iter()
2015                .map(|entry| match entry {
2016                    llr::RowChildTemplateInfo::Static { .. } => quote! {
2017                        if write_idx < result.len() {
2018                            let mut data = statics[static_idx].clone();
2019                            data.new_row = write_idx == 0 && new_row;
2020                            result[write_idx] = data;
2021                        }
2022                        write_idx += 1;
2023                        static_idx += 1;
2024                    },
2025                    llr::RowChildTemplateInfo::Repeated { repeater_index } => {
2026                        let inner_rep_id =
2027                            format_ident!("repeater{}", usize::from(*repeater_index));
2028                        let inner_rep_sc_idx =
2029                            root_sc.repeated[*repeater_index].sub_tree.root;
2030                        let inner_inner_component_id = self::inner_component_id(
2031                            &unit.sub_components[inner_rep_sc_idx],
2032                        );
2033                        quote! {
2034                            #inner_component_id::FIELD_OFFSETS.#inner_rep_id().apply_pin(_self.as_ref()).ensure_updated(
2035                                || #inner_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into()
2036                            );
2037                            let inner_len = _self.as_ref().#inner_rep_id.len();
2038                            for _i in 0..inner_len {
2039                                if write_idx < result.len() {
2040                                    result[write_idx] = sp::GridLayoutInputData {
2041                                        new_row: write_idx == 0 && new_row,
2042                                        ..Default::default()
2043                                    };
2044                                }
2045                                write_idx += 1;
2046                            }
2047                        }
2048                    }
2049                })
2050                .collect();
2051            let static_setup = if static_count > 0 {
2052                quote! {
2053                    let mut statics: [sp::GridLayoutInputData; #static_count] =
2054                        ::core::array::from_fn(|_| Default::default());
2055                    _self.as_ref().grid_layout_input_for_repeated(new_row, &mut statics);
2056                    let mut static_idx: usize = 0;
2057                }
2058            } else {
2059                quote! {}
2060            };
2061            let static_finalize = if static_count > 0 {
2062                quote! {
2063                    let _ = static_idx; // avoid unused_assignments warning
2064                }
2065            } else {
2066                quote! {}
2067            };
2068
2069            quote! {
2070                fn grid_layout_input_data(
2071                    self: ::core::pin::Pin<&Self>,
2072                    new_row: bool,
2073                    result: &mut [sp::GridLayoutInputData],
2074                ) {
2075                    let _self = self;
2076                    #static_setup
2077                    let mut write_idx: usize = 0;
2078                    #(#fill_code)*
2079                    #static_finalize
2080                    // Fill any remaining slots with auto-placed placeholders
2081                    // (Default leads to col=ROW_COL_AUTO, row=ROW_COL_AUTO, colspan=1, rowspan=1).
2082                    result[write_idx..].fill(Default::default());
2083                }
2084            }
2085        } else {
2086            quote! {
2087                fn grid_layout_input_data(
2088                    self: ::core::pin::Pin<&Self>,
2089                    new_row: bool,
2090                    result: &mut [sp::GridLayoutInputData],
2091                ) {
2092                    self.as_ref().grid_layout_input_for_repeated(new_row, result)
2093                }
2094            }
2095        }
2096    });
2097
2098    let extra_fn = if let Some(listview) = &repeated.listview {
2099        let p_y = access_member(&listview.prop_y, &ctx).unwrap();
2100        let p_height = access_member(&listview.prop_height, &ctx).unwrap();
2101        quote! {
2102            fn listview_layout(
2103                self: ::core::pin::Pin<&Self>,
2104                offset_y: &mut sp::LogicalLength,
2105            ) -> sp::LogicalLength {
2106                let _self = self;
2107                #p_y.set(*offset_y);
2108                *offset_y += #p_height.get();
2109                sp::LogicalLength::new(self.as_ref().layout_info(sp::Orientation::Horizontal).min)
2110            }
2111        }
2112    } else {
2113        let layout_item_info_fn = root_sc.child_of_layout.then(|| {
2114            // Generate layout_item_info (from the RepeatedItemTree trait) in terms of ItemTree::layout_info
2115            if root_sc.is_repeated_row {
2116                // Create a context with proper global_access for compiling layout info expressions
2117                let layout_ctx = EvaluationContext {
2118                    compilation_unit: unit,
2119                    current_scope: EvaluationScope::SubComponent(
2120                        repeated.sub_tree.root,
2121                        Some(parent_ctx),
2122                    ),
2123                    generator_state: RustGeneratorContext {
2124                        global_access: quote!(_self.globals.get().unwrap()),
2125                    },
2126                    argument_types: &[],
2127                };
2128
2129                let body = if let Some(templates) = &root_sc.row_child_templates {
2130                    // Generate a sequential scan through all templates in declaration order.
2131                    // For each Static: check if count == index and return the precomputed info.
2132                    // For each Repeated: check if index falls within [count, count+len), and return the inner instance's info.
2133                    let n = templates.len();
2134                    let scan_steps: Vec<TokenStream> = templates
2135                        .iter()
2136                        .enumerate()
2137                        .map(|(i, entry)| {
2138                            let is_last = i + 1 == n;
2139                            match entry {
2140                            llr::RowChildTemplateInfo::Static { child_index } => {
2141                                let child = &root_sc.grid_layout_children[*child_index];
2142                                let layout_info_h_code =
2143                                    compile_expression(&child.layout_info_h.borrow(), &layout_ctx);
2144                                let layout_info_v_code =
2145                                    compile_expression(&child.layout_info_v.borrow(), &layout_ctx);
2146                                let advance = (!is_last).then(|| quote! { count += 1; });
2147                                quote! {
2148                                    if count == index {
2149                                        return sp::LayoutItemInfo {
2150                                            constraint: match o {
2151                                                sp::Orientation::Horizontal => #layout_info_h_code,
2152                                                sp::Orientation::Vertical => #layout_info_v_code,
2153                                            },
2154                                        };
2155                                    }
2156                                    #advance
2157                                }
2158                            }
2159                            llr::RowChildTemplateInfo::Repeated { repeater_index } => {
2160                                let inner_rep_id =
2161                                    format_ident!("repeater{}", usize::from(*repeater_index));
2162                                let advance = (!is_last).then(|| quote! { count += inner_len; });
2163                                quote! {
2164                                    {
2165                                        let inner_len = _self.#inner_rep_id.len();
2166                                        if index >= count && index - count < inner_len {
2167                                            if let Some(inner) = _self.#inner_rep_id.instance_at(index - count) {
2168                                                return sp::LayoutItemInfo {
2169                                                    constraint: inner.as_pin_ref().layout_info(o),
2170                                                };
2171                                            }
2172                                        }
2173                                        #advance
2174                                    }
2175                                }
2176                            }
2177                        }})
2178                        .collect();
2179
2180                    quote! {
2181                        #[allow(unused)]
2182                        if let Some(index) = child_index {
2183                            let _self = self.as_ref();
2184                            let mut count = 0usize;
2185                            #(#scan_steps)*
2186                            sp::LayoutItemInfo::default()
2187                        } else {
2188                            sp::LayoutItemInfo { constraint: self.as_ref().layout_info(o) }
2189                        }
2190                    }
2191                } else {
2192                    quote! {
2193                        sp::LayoutItemInfo { constraint: self.as_ref().layout_info(o) }
2194                    }
2195                };
2196
2197                quote! {
2198                    fn layout_item_info(
2199                        self: ::core::pin::Pin<&Self>,
2200                        o: sp::Orientation,
2201                        child_index: sp::Option<usize>,
2202                    ) -> sp::LayoutItemInfo {
2203                        #body
2204                    }
2205                }
2206            } else { // not a repeated row
2207                quote! {
2208                    fn layout_item_info(
2209                        self: ::core::pin::Pin<&Self>,
2210                        o: sp::Orientation,
2211                        _child_index: sp::Option<usize>,
2212                    ) -> sp::LayoutItemInfo {
2213                        sp::LayoutItemInfo { constraint: self.as_ref().layout_info(o) }
2214                    }
2215                }
2216            }
2217        });
2218        let flexbox_layout_item_info_fn =
2219            root_sc.flexbox_layout_item_info_for_repeated.as_ref().map(|_| {
2220                quote! {
2221                    fn flexbox_layout_item_info(
2222                        self: ::core::pin::Pin<&Self>,
2223                        o: sp::Orientation,
2224                        child_index: sp::Option<usize>,
2225                    ) -> sp::FlexboxLayoutItemInfo {
2226                        let mut info = self.as_ref().flexbox_layout_item_info_for_repeated();
2227                        info.constraint = self.layout_item_info(o, child_index).constraint;
2228                        info
2229                    }
2230                }
2231            });
2232        quote! {
2233            #layout_item_info_fn
2234            #flexbox_layout_item_info_fn
2235            #grid_layout_input_data_fn
2236        }
2237    };
2238
2239    let data_type = if let Some(data_prop) = repeated.data_prop {
2240        rust_primitive_type(&root_sc.properties[data_prop].ty).unwrap()
2241    } else {
2242        quote!(())
2243    };
2244
2245    let access_prop =
2246        |property_index: llr::PropertyIdx| access_local_member(&property_index.into(), &ctx);
2247    let index_prop = repeated.index_prop.into_iter().map(access_prop);
2248    let set_data_expr = repeated.data_prop.into_iter().map(|property_index| {
2249        let prop_type = ctx.relative_property_ty(&property_index.into(), 0);
2250        let data_prop = access_prop(property_index);
2251        let value_tokens = set_primitive_property_value(prop_type, quote!(_data));
2252        quote!(#data_prop.set(#value_tokens);)
2253    });
2254
2255    quote!(
2256        #component
2257
2258        impl sp::RepeatedItemTree for #inner_component_id {
2259            type Data = #data_type;
2260            fn update(&self, _index: usize, _data: Self::Data) {
2261                let self_rc = self.self_weak.get().unwrap().upgrade().unwrap();
2262                let _self = self_rc.as_pin_ref();
2263                #(#index_prop.set(_index as _);)*
2264                #(#set_data_expr)*
2265            }
2266            fn init(&self) {
2267                let self_rc = self.self_weak.get().unwrap().upgrade().unwrap();
2268                #inner_component_id::user_init(
2269                    sp::VRcMapped::map(self_rc, |x| x),
2270                );
2271            }
2272            #extra_fn
2273        }
2274    )
2275}
2276
2277/// Return an identifier suitable for this component for internal use
2278fn inner_component_id(component: &llr::SubComponent) -> proc_macro2::Ident {
2279    format_ident!("Inner{}", ident(&component.name))
2280}
2281
2282fn internal_popup_id(index: usize) -> proc_macro2::Ident {
2283    let mut name = index.to_string();
2284    name.insert_str(0, "popup_id_");
2285    ident(&name)
2286}
2287
2288fn global_inner_name(g: &llr::GlobalComponent) -> TokenStream {
2289    if g.is_builtin {
2290        let i = ident(&g.name);
2291        quote!(sp::#i)
2292    } else {
2293        let i = format_ident!("Inner{}", ident(&g.name));
2294        quote!(#i)
2295    }
2296}
2297
2298fn property_set_value_tokens(
2299    property: &llr::MemberReference,
2300    value_tokens: TokenStream,
2301    ctx: &EvaluationContext,
2302) -> TokenStream {
2303    let prop = access_member(property, ctx);
2304    let prop_type = ctx.property_ty(property);
2305    let value_tokens = set_primitive_property_value(prop_type, value_tokens);
2306    if let Some((animation, map)) = &ctx.property_info(property).animation {
2307        let mut animation = (*animation).clone();
2308        map.map_expression(&mut animation);
2309        let animation_tokens = compile_expression(&animation, ctx);
2310        return prop
2311            .then(|prop| quote!(#prop.set_animated_value(#value_tokens as _, #animation_tokens)));
2312    }
2313    prop.then(|prop| quote!(#prop.set(#value_tokens as _)))
2314}
2315
2316/// Returns the code that can access the given property or callback
2317fn access_member(reference: &llr::MemberReference, ctx: &EvaluationContext) -> MemberAccess {
2318    fn in_global(
2319        g: &llr::GlobalComponent,
2320        index: &llr::LocalMemberIndex,
2321        _self: TokenStream,
2322    ) -> MemberAccess {
2323        let global_name = global_inner_name(g);
2324        match index {
2325            llr::LocalMemberIndex::Property(property_idx) => {
2326                let property_name = ident(&g.properties[*property_idx].name);
2327                let property_field = quote!({ *&#global_name::FIELD_OFFSETS.#property_name() });
2328                MemberAccess::Direct(quote!(#property_field.apply_pin(#_self)))
2329            }
2330            llr::LocalMemberIndex::Callback(callback_idx) => {
2331                let callback_name = ident(&g.callbacks[*callback_idx].name);
2332                let callback_field = quote!({ *&#global_name::FIELD_OFFSETS.#callback_name() });
2333                MemberAccess::Direct(quote!(#callback_field.apply_pin(#_self)))
2334            }
2335            llr::LocalMemberIndex::Function(function_idx) => {
2336                let fn_id = ident(&format!("fn_{}", g.functions[*function_idx].name));
2337                MemberAccess::Direct(quote!(#_self.#fn_id))
2338            }
2339            llr::LocalMemberIndex::Native { .. } => unreachable!(),
2340        }
2341    }
2342
2343    match reference {
2344        llr::MemberReference::Relative { parent_level, local_reference } => {
2345            if let Some(current_global) = ctx.current_global() {
2346                return in_global(current_global, &local_reference.reference, quote!(_self));
2347            }
2348
2349            let parent_path = (*parent_level != 0).then(|| {
2350                let mut path = quote!(_self.parent.upgrade());
2351                for _ in 1..*parent_level {
2352                    path = quote!(#path.and_then(|x| x.parent.upgrade()));
2353                }
2354                path
2355            });
2356
2357            match &local_reference.reference {
2358                llr::LocalMemberIndex::Property(property_index) => {
2359                    let (compo_path, sub_component) = follow_sub_component_path(
2360                        ctx.compilation_unit,
2361                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
2362                        &local_reference.sub_component_path,
2363                    );
2364                    let component_id = inner_component_id(sub_component);
2365                    let property_name = ident(&sub_component.properties[*property_index].name);
2366                    let property_field =
2367                        access_component_field_offset(&component_id, &property_name);
2368                    parent_path.map_or_else(
2369                        || MemberAccess::Direct(quote!((#compo_path #property_field).apply_pin(_self))),
2370                        |parent_path| {
2371                            MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #property_field).apply_pin(x.as_pin_ref()))))
2372                        },
2373                    )
2374                }
2375                llr::LocalMemberIndex::Callback(callback_index) => {
2376                    let (compo_path, sub_component) = follow_sub_component_path(
2377                        ctx.compilation_unit,
2378                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
2379                        &local_reference.sub_component_path,
2380                    );
2381                    let component_id = inner_component_id(sub_component);
2382                    let callback_name = ident(&sub_component.callbacks[*callback_index].name);
2383                    let callback_field =
2384                        access_component_field_offset(&component_id, &callback_name);
2385                    parent_path.map_or_else(
2386                        || MemberAccess::Direct(quote!((#compo_path #callback_field).apply_pin(_self))),
2387                        |parent_path| {
2388                            MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #callback_field).apply_pin(x.as_pin_ref()))))
2389                        },
2390                    )
2391                }
2392                llr::LocalMemberIndex::Function(function_index) => {
2393                    let mut sub_component = &ctx.compilation_unit.sub_components
2394                        [ctx.parent_sub_component_idx(*parent_level).unwrap()];
2395                    let mut compo_path = parent_path
2396                        .as_ref()
2397                        .map_or_else(|| quote!(_self), |_| quote!(x.as_pin_ref()));
2398                    for i in &local_reference.sub_component_path {
2399                        let component_id = inner_component_id(sub_component);
2400                        let sub_component_name = ident(&sub_component.sub_components[*i].name);
2401                        let field =
2402                            access_component_field_offset(&component_id, &sub_component_name);
2403                        compo_path = quote!(#field.apply_pin(#compo_path));
2404                        sub_component = &ctx.compilation_unit.sub_components
2405                            [sub_component.sub_components[*i].ty];
2406                    }
2407                    let fn_id =
2408                        ident(&format!("fn_{}", sub_component.functions[*function_index].name));
2409                    parent_path.map_or_else(
2410                        || MemberAccess::Direct(quote!(#compo_path.#fn_id)),
2411                        |parent_path| {
2412                            MemberAccess::OptionFn(parent_path, quote!(|x| #compo_path.#fn_id))
2413                        },
2414                    )
2415                }
2416                llr::LocalMemberIndex::Native { item_index, prop_name } => {
2417                    let (compo_path, sub_component) = follow_sub_component_path(
2418                        ctx.compilation_unit,
2419                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
2420                        &local_reference.sub_component_path,
2421                    );
2422                    let component_id = inner_component_id(sub_component);
2423                    let item_name = ident(&sub_component.items[*item_index].name);
2424                    let item_field = access_component_field_offset(&component_id, &item_name);
2425                    if prop_name.is_empty() {
2426                        // then this is actually a reference to the element itself
2427                        parent_path.map_or_else(
2428                            || MemberAccess::Direct(quote!((#compo_path #item_field).apply_pin(_self))),
2429                            |parent_path| {
2430                                MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #item_field).apply_pin(x.as_pin_ref()))))
2431                            }
2432                        )
2433                    } else if matches!(
2434                        sub_component.items[*item_index].ty.lookup_property(prop_name),
2435                        Some(&Type::Function(..))
2436                    ) {
2437                        let property_name = ident(prop_name);
2438                        parent_path.map_or_else(
2439                            || MemberAccess::Direct(quote!((#compo_path #item_field).apply_pin(_self).#property_name)),
2440                            |parent_path| {
2441                                MemberAccess::OptionFn(quote!(#parent_path.as_ref().map(|x| (#compo_path #item_field).apply_pin(x.as_pin_ref()))), quote!(|x| x .#property_name))
2442                            }
2443                        )
2444                    } else {
2445                        let property_name = ident(prop_name);
2446                        let item_ty = ident(&sub_component.items[*item_index].ty.class_name);
2447                        let prop_offset = quote!((#compo_path #item_field + sp::#item_ty::FIELD_OFFSETS.#property_name()));
2448                        parent_path.map_or_else(
2449                            || MemberAccess::Direct(quote!(#prop_offset.apply_pin(_self))),
2450                            |parent_path| {
2451                                MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| #prop_offset.apply_pin(x.as_pin_ref()))))
2452                            }
2453                        )
2454                    }
2455                }
2456            }
2457        }
2458        llr::MemberReference::Global { global_index, member } => {
2459            let global = &ctx.compilation_unit.globals[*global_index];
2460            let s = if matches!(ctx.current_scope, EvaluationScope::Global(i) if i == *global_index)
2461            {
2462                quote!(_self)
2463            } else {
2464                let global_access = &ctx.generator_state.global_access;
2465                let global_id = format_ident!("global_{}", ident(&global.name));
2466                quote!(#global_access.#global_id.as_ref())
2467            };
2468            in_global(global, member, s)
2469        }
2470    }
2471}
2472
2473fn access_local_member(
2474    reference: &llr::LocalMemberReference,
2475    ctx: &EvaluationContext,
2476) -> TokenStream {
2477    access_member(&reference.clone().into(), ctx).unwrap()
2478}
2479
2480/// Helper to access a member property/callback of a component.
2481///
2482/// Because the parent can be deleted (issue #3464), this might be an option when accessing the parent
2483#[derive(Clone)]
2484enum MemberAccess {
2485    /// The token stream is just an expression to the member
2486    Direct(TokenStream),
2487    /// The token stream is a an expression to an option of the member
2488    Option(TokenStream),
2489    /// the first token stream is an option, and the second is a path to the function in a `.map` and it must be called
2490    OptionFn(TokenStream, TokenStream),
2491}
2492
2493impl MemberAccess {
2494    /// Used for code that is meant to return `()`
2495    fn then(self, f: impl FnOnce(TokenStream) -> TokenStream) -> TokenStream {
2496        match self {
2497            MemberAccess::Direct(t) => f(t),
2498            MemberAccess::Option(t) => {
2499                let r = f(quote!(x));
2500                quote!({ let _ = #t.map(|x| #r); })
2501            }
2502            MemberAccess::OptionFn(opt, inner) => {
2503                let r = f(inner);
2504                quote!({ let _ = #opt.as_ref().map(#r); })
2505            }
2506        }
2507    }
2508
2509    fn map_or_default(self, f: impl FnOnce(TokenStream) -> TokenStream) -> TokenStream {
2510        match self {
2511            MemberAccess::Direct(t) => f(t),
2512            MemberAccess::Option(t) => {
2513                let r = f(quote!(x));
2514                quote!(#t.map(|x| #r).unwrap_or_default())
2515            }
2516            MemberAccess::OptionFn(opt, inner) => {
2517                let r = f(inner);
2518                quote!(#opt.as_ref().map(#r).unwrap_or_default())
2519            }
2520        }
2521    }
2522
2523    fn get_property(self) -> TokenStream {
2524        match self {
2525            MemberAccess::Direct(t) => quote!(#t.get()),
2526            MemberAccess::Option(t) => {
2527                quote!(#t.map(|x| x.get()).unwrap_or_default())
2528            }
2529            MemberAccess::OptionFn(..) => panic!("function is not a property"),
2530        }
2531    }
2532
2533    /// To be used when we know that the reference was local
2534    #[track_caller]
2535    fn unwrap(&self) -> TokenStream {
2536        match self {
2537            MemberAccess::Direct(t) => quote!(#t),
2538            _ => panic!("not a local property?"),
2539        }
2540    }
2541}
2542
2543fn follow_sub_component_path<'a>(
2544    compilation_unit: &'a llr::CompilationUnit,
2545    root: llr::SubComponentIdx,
2546    sub_component_path: &[llr::SubComponentInstanceIdx],
2547) -> (TokenStream, &'a llr::SubComponent) {
2548    let mut compo_path = quote!();
2549    let mut sub_component = &compilation_unit.sub_components[root];
2550    for i in sub_component_path {
2551        let component_id = inner_component_id(sub_component);
2552        let sub_component_name = ident(&sub_component.sub_components[*i].name);
2553        let field = access_component_field_offset(&component_id, &sub_component_name);
2554        compo_path = quote!(#compo_path #field +);
2555        sub_component = &compilation_unit.sub_components[sub_component.sub_components[*i].ty];
2556    }
2557    (compo_path, sub_component)
2558}
2559
2560fn access_window_adapter_field(ctx: &EvaluationContext) -> TokenStream {
2561    let global_access = &ctx.generator_state.global_access;
2562    quote!(&#global_access.window_adapter_impl())
2563}
2564
2565/// Given a property reference to a native item (eg, the property name is empty)
2566/// return tokens to the `ItemRc`
2567fn access_item_rc(pr: &llr::MemberReference, ctx: &EvaluationContext) -> TokenStream {
2568    let mut component_access_tokens = quote!(_self);
2569
2570    let llr::MemberReference::Relative { parent_level, local_reference } = pr else {
2571        unreachable!()
2572    };
2573    let llr::LocalMemberIndex::Native { item_index, prop_name: _ } = &local_reference.reference
2574    else {
2575        unreachable!()
2576    };
2577
2578    for _ in 0..*parent_level {
2579        component_access_tokens =
2580            quote!(#component_access_tokens.parent.upgrade().unwrap().as_pin_ref());
2581    }
2582
2583    let mut sub_component =
2584        &ctx.compilation_unit.sub_components[ctx.parent_sub_component_idx(*parent_level).unwrap()];
2585    for i in &local_reference.sub_component_path {
2586        let sub_component_name = ident(&sub_component.sub_components[*i].name);
2587        component_access_tokens = quote!(#component_access_tokens . #sub_component_name);
2588        sub_component = &ctx.compilation_unit.sub_components[sub_component.sub_components[*i].ty];
2589    }
2590    let component_rc_tokens = quote!(sp::VRcMapped::origin(&#component_access_tokens.self_weak.get().unwrap().upgrade().unwrap()));
2591    let item_index_in_tree = sub_component.items[*item_index].index_in_tree;
2592    let item_index_tokens = if item_index_in_tree == 0 {
2593        quote!(#component_access_tokens.tree_index.get())
2594    } else {
2595        quote!(#component_access_tokens.tree_index_of_first_child.get() + #item_index_in_tree - 1)
2596    };
2597
2598    quote!(&sp::ItemRc::new(#component_rc_tokens, #item_index_tokens))
2599}
2600
2601/// Compile `expr` to a Rust expression returning an owned value.
2602fn compile_expression_to_value(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
2603    let compiled_expr = compile_expression(expr, ctx);
2604
2605    quote!((#compiled_expr).clone())
2606}
2607
2608/// Compile `expr` to a Rust expression which may potentially return a reference.
2609fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
2610    match expr {
2611        Expression::StringLiteral(s) => {
2612            let s = s.as_str();
2613            quote!(sp::SharedString::from(#s))
2614        }
2615        Expression::KeysLiteral(keys) => {
2616                let key = &*keys.key;
2617                let alt = keys.modifiers.alt;
2618                let control = keys.modifiers.control;
2619                let shift = keys.modifiers.shift;
2620                let meta = keys.modifiers.meta;
2621                let ignore_shift = keys.ignore_shift;
2622                let ignore_alt = keys.ignore_alt;
2623
2624                quote!(
2625                    sp::make_keys(
2626                        #key.into(),
2627                        {
2628                            let mut modifiers = sp::KeyboardModifiers::default();
2629                            modifiers.alt = #alt;
2630                            modifiers.control = #control;
2631                            modifiers.shift = #shift;
2632                            modifiers.meta = #meta;
2633                            modifiers
2634                        },
2635                        #ignore_shift,
2636                        #ignore_alt))
2637        },
2638        Expression::NumberLiteral(n) if n.is_finite() => quote!(#n),
2639        Expression::NumberLiteral(_) => quote!(0.),
2640        Expression::BoolLiteral(b) => quote!(#b),
2641        Expression::Cast { from, to } => {
2642            let f = compile_expression(from, ctx);
2643            match (from.ty(ctx), to) {
2644                (Type::Float32, Type::Int32) => {
2645                    quote!(((#f) as i32))
2646                }
2647                (from, Type::String) if from.as_unit_product().is_some() => {
2648                    quote!(sp::shared_string_from_number((#f) as f64))
2649                }
2650                (Type::Float32, Type::Model) | (Type::Int32, Type::Model) => {
2651                    quote!(sp::ModelRc::new(#f.max(::core::default::Default::default()) as usize))
2652                }
2653                (Type::Float32, Type::Color) => {
2654                    quote!(sp::Color::from_argb_encoded((#f) as u32))
2655                }
2656                (Type::Color, Type::Brush) => {
2657                    quote!(slint::Brush::SolidColor(#f))
2658                }
2659                (Type::Brush, Type::Color) => {
2660                    quote!(#f.color())
2661                }
2662                (Type::Struct(lhs), Type::Struct(rhs)) => {
2663                    debug_assert_eq!(
2664                        lhs.fields, rhs.fields,
2665                        "cast of struct with deferent fields should be handled before llr"
2666                    );
2667                    match (&lhs.name, &rhs.name) {
2668                        (StructName::None, targetstruct) if targetstruct.is_some() => {
2669                            // Convert from an anonymous struct to a named one
2670                            let fields = lhs.fields.iter().enumerate().map(|(index, (name, _))| {
2671                                let index = proc_macro2::Literal::usize_unsuffixed(index);
2672                                let name = ident(name);
2673                                quote!(the_struct.#name = (obj.#index).clone() as _;)
2674                            });
2675                            let id = struct_name_to_tokens(targetstruct).unwrap();
2676                            quote!({ let obj = #f; let mut the_struct = #id::default(); #(#fields)* the_struct })
2677                        }
2678                        (sourcestruct, StructName::None) if sourcestruct.is_some() => {
2679                            // Convert from a named struct to an anonymous one
2680                            let fields = lhs.fields.keys().map(|name| ident(name));
2681                            quote!({ let obj = #f; (#(obj.#fields,)*) })
2682                        }
2683                        _ => f,
2684                    }
2685                }
2686                (Type::Array(..), Type::PathData)
2687                    if matches!(
2688                        from.as_ref(),
2689                        Expression::Array { element_ty: Type::Struct { .. }, .. }
2690                    ) =>
2691                {
2692                    let path_elements = match from.as_ref() {
2693                        Expression::Array { element_ty: _, values, output: _ } => values
2694                            .iter()
2695                            .map(|path_elem_expr|
2696                                // Close{} is a struct with no fields in markup, and PathElement::Close has no fields
2697                                if matches!(path_elem_expr, Expression::Struct { ty, .. } if ty.fields.is_empty()) {
2698                                    quote!(sp::PathElement::Close)
2699                                } else {
2700                                    compile_expression(path_elem_expr, ctx)
2701                                }
2702                            ),
2703                        _ => {
2704                            unreachable!()
2705                        }
2706                    };
2707                    quote!(sp::PathData::Elements(sp::SharedVector::<_>::from_slice(&[#((#path_elements).into()),*])))
2708                }
2709                (Type::Struct { .. }, Type::PathData)
2710                    if matches!(from.as_ref(), Expression::Struct { .. }) =>
2711                {
2712                    let (events, points) = match from.as_ref() {
2713                        Expression::Struct { ty: _, values } => (
2714                            compile_expression(&values["events"], ctx),
2715                            compile_expression(&values["points"], ctx),
2716                        ),
2717                        _ => {
2718                            unreachable!()
2719                        }
2720                    };
2721                    quote!(sp::PathData::Events(sp::SharedVector::<_>::from_slice(&#events), sp::SharedVector::<_>::from_slice(&#points)))
2722                }
2723                (Type::String, Type::PathData) => {
2724                    quote!(sp::PathData::Commands(#f))
2725                }
2726                (Type::Enumeration(e), Type::String) => {
2727                    let cases = e.values.iter().enumerate().map(|(idx, v)| {
2728                        let c = compile_expression(
2729                            &Expression::EnumerationValue(EnumerationValue {
2730                                value: idx,
2731                                enumeration: e.clone(),
2732                            }),
2733                            ctx,
2734                        );
2735                        let v = v.as_str();
2736                        quote!(#c => sp::SharedString::from(#v))
2737                    });
2738                    quote!(match #f { #(#cases,)*  _ => sp::SharedString::default() })
2739                }
2740                (_, Type::Void) => {
2741                    quote!({#f;})
2742                }
2743                _ => f,
2744            }
2745        }
2746        Expression::PropertyReference(nr) => {
2747            let access = access_member(nr, ctx);
2748            let prop_type = ctx.property_ty(nr);
2749            primitive_property_value(prop_type, access)
2750        }
2751        Expression::BuiltinFunctionCall { function, arguments } => {
2752            compile_builtin_function_call(function.clone(), arguments, ctx)
2753        }
2754        Expression::CallBackCall { callback, arguments } => {
2755            let f = access_member(callback, ctx);
2756            let a = arguments.iter().map(|a| compile_expression_to_value(a, ctx));
2757            if expr.ty(ctx) == Type::Void {
2758                f.then(|f| quote!(#f.call(&(#(#a as _,)*))))
2759            } else {
2760                f.map_or_default(|f| quote!(#f.call(&(#(#a as _,)*))))
2761            }
2762        }
2763        Expression::FunctionCall { function, arguments } => {
2764            let a = arguments.iter().map(|a| compile_expression(a, ctx));
2765            let f = access_member(function, ctx);
2766            if expr.ty(ctx) == Type::Void {
2767                f.then(|f| quote!(#f( #(#a as _),*)))
2768            } else {
2769                f.map_or_default(|f| quote!(#f( #(#a as _),*)))
2770            }
2771        }
2772        Expression::ItemMemberFunctionCall { function } => {
2773            let fun = access_member(function, ctx);
2774            let item_rc = access_item_rc(function, ctx);
2775            let window_adapter_tokens = access_window_adapter_field(ctx);
2776            fun.map_or_default(|fun| quote!(#fun(#window_adapter_tokens, #item_rc)))
2777        }
2778        Expression::ExtraBuiltinFunctionCall { function, arguments, return_ty: _ } => {
2779            let f = ident(function);
2780            let a = arguments.iter().map(|a| {
2781                let arg = compile_expression(a, ctx);
2782                if matches!(a.ty(ctx), Type::Struct { .. }) { quote!(&#arg) } else { arg }
2783            });
2784            quote! { sp::#f(#(#a as _),*) }
2785        }
2786        Expression::FunctionParameterReference { index } => {
2787            let i = proc_macro2::Literal::usize_unsuffixed(*index);
2788            quote! {args.#i.clone()}
2789        }
2790        Expression::StructFieldAccess { base, name } => match base.ty(ctx) {
2791            Type::Struct(s) => {
2792                let base_e = compile_expression_no_parenthesis(base, ctx);
2793                let f = struct_field_access(&s, name);
2794                quote!((#base_e).#f)
2795            }
2796            _ => panic!("Expression::StructFieldAccess's base expression is not an Object type"),
2797        },
2798        Expression::ArrayIndex { array, index } => {
2799            debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
2800            let base_e = compile_expression(array, ctx);
2801            let index_e = compile_expression(index, ctx);
2802            quote!(match &#base_e { x => {
2803                let index = (#index_e) as usize;
2804                x.row_data_tracked(index).unwrap_or_default()
2805            }})
2806        }
2807        Expression::CodeBlock(sub) => {
2808            let mut body = TokenStream::new();
2809            for (i, e) in sub.iter().enumerate() {
2810                body.extend(compile_expression_no_parenthesis(e, ctx));
2811                if i + 1 < sub.len() && !matches!(e, Expression::StoreLocalVariable { .. }) {
2812                    body.extend(quote!(;));
2813                }
2814            }
2815            quote!({ #body })
2816        }
2817        Expression::PropertyAssignment { property, value } => {
2818            let value = compile_expression(value, ctx);
2819            property_set_value_tokens(property, value, ctx)
2820        }
2821        Expression::ModelDataAssignment { level, value } => {
2822            let value = compile_expression(value, ctx);
2823            let mut path = quote!(_self);
2824            let EvaluationScope::SubComponent(mut sc, mut par) = ctx.current_scope else {
2825                unreachable!()
2826            };
2827            let mut repeater_index = None;
2828            for _ in 0..=*level {
2829                let x = par.unwrap();
2830                par = x.parent;
2831                repeater_index = x.repeater_index;
2832                sc = x.sub_component;
2833                path = quote!(#path.parent.upgrade().unwrap());
2834            }
2835            let repeater_index = repeater_index.unwrap();
2836            let sub_component = &ctx.compilation_unit.sub_components[sc];
2837            let local_reference = sub_component.repeated[repeater_index].index_prop.unwrap().into();
2838            let index_prop =
2839                llr::MemberReference::Relative { parent_level: *level, local_reference };
2840            let index_access = access_member(&index_prop, ctx).get_property();
2841            let repeater = access_component_field_offset(
2842                &inner_component_id(sub_component),
2843                &format_ident!("repeater{}", usize::from(repeater_index)),
2844            );
2845            quote!(#repeater.apply_pin(#path.as_pin_ref()).model_set_row_data(#index_access as _, #value as _))
2846        }
2847        Expression::ArrayIndexAssignment { array, index, value } => {
2848            debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
2849            let base_e = compile_expression(array, ctx);
2850            let index_e = compile_expression(index, ctx);
2851            let value_e = compile_expression(value, ctx);
2852            quote!((#base_e).set_row_data(#index_e as isize as usize, #value_e as _))
2853        }
2854        Expression::SliceIndexAssignment { slice_name, index, value } => {
2855            let slice_ident = ident(slice_name);
2856            let value_e = compile_expression(value, ctx);
2857            quote!(#slice_ident[#index] = #value_e)
2858        }
2859        Expression::BinaryExpression { lhs, rhs, op } => {
2860            let lhs_ty = lhs.ty(ctx);
2861            let lhs = compile_expression_to_value_no_parenthesis(lhs, ctx);
2862            let rhs = compile_expression_to_value_no_parenthesis(rhs, ctx);
2863
2864            if lhs_ty.as_unit_product().is_some() && (*op == '=' || *op == '!') {
2865                let maybe_negate = if *op == '!' { quote!(!) } else { quote!() };
2866                quote!(#maybe_negate sp::ApproxEq::<f64>::approx_eq(&(#lhs as f64), &(#rhs as f64)))
2867            } else {
2868                let (conv1, conv2) = match crate::expression_tree::operator_class(*op) {
2869                    OperatorClass::ArithmeticOp => match lhs_ty {
2870                        Type::String => (None, Some(quote!(.as_str()))),
2871                        Type::Struct { .. } => (None, None),
2872                        _ => (Some(quote!(as f64)), Some(quote!(as f64))),
2873                    },
2874                    OperatorClass::ComparisonOp
2875                        if matches!(
2876                            lhs_ty,
2877                            Type::Int32
2878                                | Type::Float32
2879                                | Type::Duration
2880                                | Type::PhysicalLength
2881                                | Type::LogicalLength
2882                                | Type::Angle
2883                                | Type::Percent
2884                                | Type::Rem
2885                        ) =>
2886                    {
2887                        (Some(quote!(as f64)), Some(quote!(as f64)))
2888                    }
2889                    _ => (None, None),
2890                };
2891
2892                let op = match op {
2893                    '=' => quote!(==),
2894                    '!' => quote!(!=),
2895                    '≤' => quote!(<=),
2896                    '≥' => quote!(>=),
2897                    '&' => quote!(&&),
2898                    '|' => quote!(||),
2899                    _ => proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
2900                        *op,
2901                        proc_macro2::Spacing::Alone,
2902                    ))
2903                    .into(),
2904                };
2905                quote!( (((#lhs) #conv1 ) #op ((#rhs) #conv2)) )
2906            }
2907        }
2908        Expression::UnaryOp { sub, op } => {
2909            let sub = compile_expression(sub, ctx);
2910            if *op == '+' {
2911                // there is no unary '+' in rust
2912                return sub;
2913            }
2914            let op = proc_macro2::Punct::new(*op, proc_macro2::Spacing::Alone);
2915            quote!( (#op #sub) )
2916        }
2917        Expression::ImageReference { resource_ref, nine_slice } => {
2918            let image = match resource_ref {
2919                crate::expression_tree::ImageReference::None => {
2920                    quote!(sp::Image::default())
2921                }
2922                crate::expression_tree::ImageReference::AbsolutePath(path) => {
2923                    let path = path.as_str();
2924                    quote!(sp::Image::load_from_path(::std::path::Path::new(#path)).unwrap_or_default())
2925                }
2926                crate::expression_tree::ImageReference::EmbeddedData { resource_id, extension } => {
2927                    let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id.0);
2928                    let format = proc_macro2::Literal::byte_string(extension.as_bytes());
2929                    quote!(sp::load_image_from_embedded_data(#symbol.into(), sp::Slice::from_slice(#format)))
2930                }
2931                crate::expression_tree::ImageReference::EmbeddedTexture { resource_id } => {
2932                    let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id.0);
2933                    quote!(
2934                        sp::Image::from(sp::ImageInner::StaticTextures(&#symbol))
2935                    )
2936                }
2937            };
2938            match &nine_slice {
2939                Some([a, b, c, d]) => {
2940                    quote! {{ let mut image = #image; image.set_nine_slice_edges(#a, #b, #c, #d); image }}
2941                }
2942                None => image,
2943            }
2944        }
2945        Expression::Condition { condition, true_expr, false_expr } => {
2946            let condition_code = compile_expression_no_parenthesis(condition, ctx);
2947            let true_code = compile_expression(true_expr, ctx);
2948            let false_code = compile_expression_no_parenthesis(false_expr, ctx);
2949            let semi = if false_expr.ty(ctx) == Type::Void { quote!(;) } else { quote!(as _) };
2950            quote!(
2951                if #condition_code {
2952                    (#true_code) #semi
2953                } else {
2954                    #false_code
2955                }
2956            )
2957        }
2958        Expression::Array { values, element_ty, output } => {
2959            let val = values.iter().map(|e| compile_expression_to_value(e, ctx));
2960            match output {
2961                ArrayOutput::Model => {
2962                    let rust_element_ty = rust_primitive_type(element_ty).unwrap();
2963                    quote!(sp::ModelRc::new(
2964                        sp::VecModel::<#rust_element_ty>::from(
2965                            sp::vec![#(#val as _),*]
2966                        )
2967                    ))
2968                }
2969                ArrayOutput::Slice => quote!(sp::Slice::from_slice(&[#(#val),*])),
2970                ArrayOutput::Vector => quote!(sp::vec![#(#val as _),*]),
2971            }
2972        }
2973        Expression::Struct { ty, values } => {
2974            let elem = ty.fields.keys().map(|k| values.get(k).map(|e| compile_expression_to_value(e, ctx)));
2975            if ty.name.is_some() {
2976                let name_tokens = struct_name_to_tokens(&ty.name).unwrap();
2977                let keys = ty.fields.keys().map(|k| ident(k));
2978                if matches!(&ty.name, StructName::BuiltinPrivate(private_type) if private_type.is_layout_data())
2979                {
2980                    quote!(#name_tokens{#(#keys: #elem as _,)*})
2981                } else {
2982                    quote!({ let mut the_struct = #name_tokens::default(); #(the_struct.#keys = #elem as _;)* the_struct})
2983                }
2984            } else {
2985                let as_ = ty.fields.values().map(|t| {
2986                    if t.as_unit_product().is_some() {
2987                        // number needs to be converted to the right things because intermediate
2988                        // result might be f64 and that's usually not what the type of the tuple is in the end
2989                        let t = rust_primitive_type(t).unwrap();
2990                        quote!(as #t)
2991                    } else {
2992                        quote!()
2993                    }
2994                });
2995                // This will produce a tuple
2996                quote!((#((#elem).clone() #as_,)*))
2997            }
2998        }
2999
3000        Expression::StoreLocalVariable { name, value } => {
3001            let value = compile_expression_no_parenthesis(value, ctx);
3002            let name = ident(name);
3003            quote!(let #name = #value;)
3004        }
3005        Expression::ReadLocalVariable { name, .. } => {
3006            let name = ident(name);
3007            quote!(#name)
3008        }
3009        Expression::EasingCurve(EasingCurve::Linear) => {
3010            quote!(sp::EasingCurve::Linear)
3011        }
3012        Expression::EasingCurve(EasingCurve::CubicBezier(a, b, c, d)) => {
3013            quote!(sp::EasingCurve::CubicBezier([#a, #b, #c, #d]))
3014        }
3015        Expression::EasingCurve(EasingCurve::EaseInElastic) => {
3016            quote!(sp::EasingCurve::EaseInElastic)
3017        }
3018        Expression::EasingCurve(EasingCurve::EaseOutElastic) => {
3019            quote!(sp::EasingCurve::EaseOutElastic)
3020        }
3021        Expression::EasingCurve(EasingCurve::EaseInOutElastic) => {
3022            quote!(sp::EasingCurve::EaseInOutElastic)
3023        }
3024        Expression::EasingCurve(EasingCurve::EaseInBounce) => {
3025            quote!(sp::EasingCurve::EaseInBounce)
3026        }
3027        Expression::EasingCurve(EasingCurve::EaseOutBounce) => {
3028            quote!(sp::EasingCurve::EaseOutBounce)
3029        }
3030        Expression::EasingCurve(EasingCurve::EaseInOutBounce) => {
3031            quote!(sp::EasingCurve::EaseInOutBounce)
3032        }
3033        Expression::LinearGradient { angle, stops } => {
3034            let angle = compile_expression(angle, ctx);
3035            let stops = stops.iter().map(|(color, stop)| {
3036                let color = compile_expression(color, ctx);
3037                let position = compile_expression(stop, ctx);
3038                quote!(sp::GradientStop{ color: #color, position: #position as _ })
3039            });
3040            quote!(slint::Brush::LinearGradient(
3041                sp::LinearGradientBrush::new(#angle as _, [#(#stops),*])
3042            ))
3043        }
3044        Expression::RadialGradient { stops } => {
3045            let stops = stops.iter().map(|(color, stop)| {
3046                let color = compile_expression(color, ctx);
3047                let position = compile_expression(stop, ctx);
3048                quote!(sp::GradientStop{ color: #color, position: #position as _ })
3049            });
3050            quote!(slint::Brush::RadialGradient(
3051                sp::RadialGradientBrush::new_circle([#(#stops),*])
3052            ))
3053        }
3054        Expression::ConicGradient { from_angle, stops } => {
3055            let from_angle = compile_expression(from_angle, ctx);
3056            let stops = stops.iter().map(|(color, stop)| {
3057                let color = compile_expression(color, ctx);
3058                let position = compile_expression(stop, ctx);
3059                quote!(sp::GradientStop{ color: #color, position: #position as _ })
3060            });
3061            quote!(slint::Brush::ConicGradient(
3062                sp::ConicGradientBrush::new(#from_angle as _, [#(#stops),*])
3063            ))
3064        }
3065        Expression::EnumerationValue(value) => {
3066            let base_ident = ident(&value.enumeration.name);
3067            let value_ident = ident(&value.to_pascal_case());
3068            if value.enumeration.node.is_some() {
3069                quote!(#base_ident::#value_ident)
3070            } else {
3071                quote!(sp::#base_ident::#value_ident)
3072            }
3073        }
3074        Expression::LayoutCacheAccess {
3075            layout_cache_prop,
3076            index,
3077            repeater_index,
3078            entries_per_item,
3079        } => {
3080            access_member(layout_cache_prop, ctx).map_or_default(|cache| {
3081                if let Some(ri) = repeater_index {
3082                    let offset = compile_expression(ri, ctx);
3083                    quote!({
3084                        let cache = #cache.get();
3085                        *cache.get((cache[#index] as usize) + #offset as usize * #entries_per_item).unwrap_or(&(0 as _))
3086                    })
3087                } else {
3088                    quote!(#cache.get()[#index])
3089                }
3090            })
3091        }
3092        Expression::GridRepeaterCacheAccess {
3093            layout_cache_prop,
3094            index,
3095            repeater_index,
3096            stride,
3097            child_offset,
3098            inner_repeater_index,
3099            entries_per_item,
3100        } => access_member(layout_cache_prop, ctx).map_or_default(|cache| {
3101            let offset = compile_expression(repeater_index, ctx);
3102            let stride_val = compile_expression(stride, ctx);
3103            let inner_offset = inner_repeater_index.as_ref().map(|inner_ri| {
3104                let inner_offset = compile_expression(inner_ri, ctx);
3105                quote!(+ #inner_offset as usize * #entries_per_item)
3106            });
3107
3108            quote!({
3109                let cache = #cache.get();
3110                let base = cache[#index] as usize;
3111                let data_idx = base + #offset as usize * (#stride_val as usize) + #child_offset #inner_offset;
3112                *cache.get(data_idx).unwrap_or(&(0 as _))
3113            })
3114        }),
3115        Expression::WithLayoutItemInfo {
3116            cells_variable,
3117            repeater_indices_var_name,
3118            repeater_steps_var_name,
3119            elements,
3120            orientation,
3121            sub_expression,
3122        } => generate_with_layout_item_info(
3123            cells_variable,
3124            repeater_indices_var_name.as_ref().map(SmolStr::as_str),
3125            repeater_steps_var_name.as_ref().map(SmolStr::as_str),
3126            elements.as_ref(),
3127            *orientation,
3128            sub_expression,
3129            ctx,
3130        ),
3131
3132        Expression::WithFlexboxLayoutItemInfo {
3133            cells_h_variable,
3134            cells_v_variable,
3135            repeater_indices_var_name,
3136            elements,
3137            sub_expression,
3138        } => generate_with_flexbox_layout_item_info(
3139            cells_h_variable,
3140            cells_v_variable,
3141            repeater_indices_var_name.as_ref().map(SmolStr::as_str),
3142            elements.as_ref(),
3143            sub_expression,
3144            ctx,
3145        ),
3146
3147        Expression::WithGridInputData {
3148            cells_variable,
3149            repeater_indices_var_name,
3150            repeater_steps_var_name,
3151            elements,
3152            sub_expression,
3153        } => generate_with_grid_input_data(
3154            cells_variable,
3155            repeater_indices_var_name,
3156            repeater_steps_var_name,
3157            elements.as_ref(),
3158            sub_expression,
3159            ctx,
3160        ),
3161
3162        Expression::MinMax { ty, op, lhs, rhs } => {
3163            let lhs = compile_expression(lhs, ctx);
3164            let t = rust_primitive_type(ty);
3165            let (lhs, rhs) = match t {
3166                Some(t) => {
3167                    let rhs = compile_expression(rhs, ctx);
3168                    (quote!((#lhs as #t)), quote!(#rhs as #t))
3169                }
3170                None => {
3171                    let rhs = compile_expression_no_parenthesis(rhs, ctx);
3172                    (lhs, rhs)
3173                }
3174            };
3175            match op {
3176                MinMaxOp::Min => {
3177                    quote!(#lhs.min(#rhs))
3178                }
3179                MinMaxOp::Max => {
3180                    quote!(#lhs.max(#rhs))
3181                }
3182            }
3183        }
3184        Expression::EmptyComponentFactory => quote!(slint::ComponentFactory::default()),
3185        Expression::TranslationReference { format_args, string_index, plural } => {
3186            let args = compile_expression(format_args, ctx);
3187            match plural {
3188                Some(plural) => {
3189                    let plural = compile_expression(plural, ctx);
3190                    quote!(sp::translate_from_bundle_with_plural(
3191                        &self::_SLINT_TRANSLATED_STRINGS_PLURALS[#string_index],
3192                        &self::_SLINT_TRANSLATED_PLURAL_RULES,
3193                        sp::Slice::<sp::SharedString>::from(#args).as_slice(),
3194                        #plural as _
3195                    ))
3196                }
3197                None => {
3198                    quote!(sp::translate_from_bundle(&self::_SLINT_TRANSLATED_STRINGS[#string_index], sp::Slice::<sp::SharedString>::from(#args).as_slice()))
3199                }
3200            }
3201        }
3202    }
3203}
3204
3205fn struct_field_access(s: &Struct, name: &str) -> proc_macro2::TokenTree {
3206    if s.name.is_none() {
3207        let index = s
3208            .fields
3209            .keys()
3210            .position(|k| k == name)
3211            .expect("Expression::StructFieldAccess: Cannot find a key in an object");
3212        proc_macro2::Literal::usize_unsuffixed(index).into()
3213    } else {
3214        ident(name).into()
3215    }
3216}
3217
3218fn compile_builtin_function_call(
3219    function: BuiltinFunction,
3220    arguments: &[Expression],
3221    ctx: &EvaluationContext,
3222) -> TokenStream {
3223    let mut a = arguments.iter().map(|a| compile_expression_to_value(a, ctx));
3224    match function {
3225        BuiltinFunction::SetFocusItem => {
3226            if let [Expression::PropertyReference(pr)] = arguments {
3227                let window_tokens = access_window_adapter_field(ctx);
3228                let focus_item = access_item_rc(pr, ctx);
3229                quote!(
3230                    sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(#focus_item, true, sp::FocusReason::Programmatic)
3231                )
3232            } else {
3233                panic!("internal error: invalid args to SetFocusItem {arguments:?}")
3234            }
3235        }
3236        BuiltinFunction::ClearFocusItem => {
3237            if let [Expression::PropertyReference(pr)] = arguments {
3238                let window_tokens = access_window_adapter_field(ctx);
3239                let focus_item = access_item_rc(pr, ctx);
3240                quote!(
3241                    sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(#focus_item, false, sp::FocusReason::Programmatic)
3242                )
3243            } else {
3244                panic!("internal error: invalid args to ClearFocusItem {arguments:?}")
3245            }
3246        }
3247        BuiltinFunction::ShowPopupWindow => {
3248            if let [
3249                Expression::NumberLiteral(popup_index),
3250                close_policy,
3251                Expression::PropertyReference(parent_ref),
3252            ] = arguments
3253            {
3254                let mut component_access_tokens = MemberAccess::Direct(quote!(_self));
3255                let llr::MemberReference::Relative { parent_level, .. } = parent_ref else {
3256                    unreachable!()
3257                };
3258                for _ in 0..*parent_level {
3259                    component_access_tokens = match component_access_tokens {
3260                        MemberAccess::Option(token_stream) => MemberAccess::Option(
3261                            quote!(#token_stream.and_then(|a| a.as_pin_ref().parent.upgrade())),
3262                        ),
3263                        MemberAccess::Direct(token_stream) => {
3264                            MemberAccess::Option(quote!(#token_stream.parent.upgrade()))
3265                        }
3266                        _ => unreachable!(),
3267                    };
3268                }
3269
3270                let current_sub_component = &ctx.compilation_unit.sub_components
3271                    [ctx.parent_sub_component_idx(*parent_level).unwrap()];
3272                let popup = &current_sub_component.popup_windows[*popup_index as usize];
3273                let popup_window_id =
3274                    inner_component_id(&ctx.compilation_unit.sub_components[popup.item_tree.root]);
3275                let parent_item = access_item_rc(parent_ref, ctx);
3276
3277                let parent_ctx = ParentScope::new(ctx, None);
3278                let popup_ctx = EvaluationContext::new_sub_component(
3279                    ctx.compilation_unit,
3280                    popup.item_tree.root,
3281                    RustGeneratorContext { global_access: quote!(_self.globals.get().unwrap()) },
3282                    Some(&parent_ctx),
3283                );
3284                let position = compile_expression(&popup.position.borrow(), &popup_ctx);
3285
3286                let close_policy = compile_expression(close_policy, ctx);
3287                let window_adapter_tokens = access_window_adapter_field(ctx);
3288                let popup_id_name = internal_popup_id(*popup_index as usize);
3289                component_access_tokens.then(|component_access_tokens| quote!({
3290                    let parent_item = #parent_item;
3291                    let popup_instance = #popup_window_id::new(#component_access_tokens.self_weak.get().unwrap().clone()).unwrap();
3292                    let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
3293                    let position = { let _self = popup_instance_vrc.as_pin_ref(); #position };
3294                    if let Some(current_id) = #component_access_tokens.#popup_id_name.take() {
3295                        sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
3296                    }
3297                    #component_access_tokens.#popup_id_name.set(Some(
3298                        sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup(
3299                            &sp::VRc::into_dyn(popup_instance.into()),
3300                            position,
3301                            #close_policy,
3302                            parent_item,
3303                            false, // is_menu
3304                        ))
3305                    );
3306                    #popup_window_id::user_init(popup_instance_vrc.clone());
3307                }))
3308            } else {
3309                panic!("internal error: invalid args to ShowPopupWindow {arguments:?}")
3310            }
3311        }
3312        BuiltinFunction::ClosePopupWindow => {
3313            if let [
3314                Expression::NumberLiteral(popup_index),
3315                Expression::PropertyReference(parent_ref),
3316            ] = arguments
3317            {
3318                let mut component_access_tokens = MemberAccess::Direct(quote!(_self));
3319                let llr::MemberReference::Relative { parent_level, .. } = parent_ref else {
3320                    unreachable!()
3321                };
3322                for _ in 0..*parent_level {
3323                    component_access_tokens = match component_access_tokens {
3324                        MemberAccess::Option(token_stream) => MemberAccess::Option(
3325                            quote!(#token_stream.and_then(|a| a.parent.upgrade())),
3326                        ),
3327                        MemberAccess::Direct(token_stream) => {
3328                            MemberAccess::Option(quote!(#token_stream.parent.upgrade()))
3329                        }
3330                        _ => unreachable!(),
3331                    };
3332                }
3333                let window_adapter_tokens = access_window_adapter_field(ctx);
3334                let popup_id_name = internal_popup_id(*popup_index as usize);
3335                let current_id_tokens = match component_access_tokens {
3336                    MemberAccess::Option(token_stream) => quote!(
3337                        #token_stream.and_then(|a| a.as_pin_ref().#popup_id_name.take())
3338                    ),
3339                    MemberAccess::Direct(token_stream) => {
3340                        quote!(#token_stream.as_ref().#popup_id_name.take())
3341                    }
3342                    _ => unreachable!(),
3343                };
3344                quote!(
3345                    if let Some(current_id) = #current_id_tokens {
3346                        sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
3347                    }
3348                )
3349            } else {
3350                panic!("internal error: invalid args to ClosePopupWindow {arguments:?}")
3351            }
3352        }
3353        BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
3354            let [Expression::PropertyReference(context_menu_ref), entries, position] = arguments
3355            else {
3356                panic!("internal error: invalid args to ShowPopupMenu {arguments:?}")
3357            };
3358
3359            let context_menu = access_member(context_menu_ref, ctx);
3360            let context_menu_rc = access_item_rc(context_menu_ref, ctx);
3361            let position = compile_expression(position, ctx);
3362
3363            let popup = ctx
3364                .compilation_unit
3365                .popup_menu
3366                .as_ref()
3367                .expect("there should be a popup menu if we want to show it");
3368            let popup_id =
3369                inner_component_id(&ctx.compilation_unit.sub_components[popup.item_tree.root]);
3370            let window_adapter_tokens = access_window_adapter_field(ctx);
3371
3372            let popup_ctx = EvaluationContext::new_sub_component(
3373                ctx.compilation_unit,
3374                popup.item_tree.root,
3375                RustGeneratorContext { global_access: quote!(_self.globals.get().unwrap()) },
3376                None,
3377            );
3378            let access_entries = access_member(&popup.entries, &popup_ctx).unwrap();
3379            let access_sub_menu = access_member(&popup.sub_menu, &popup_ctx).unwrap();
3380            let access_activated = access_member(&popup.activated, &popup_ctx).unwrap();
3381            let access_close = access_member(&popup.close, &popup_ctx).unwrap();
3382
3383            let close_popup = context_menu.clone().then(|context_menu| quote!{
3384                if let Some(current_id) = #context_menu.popup_id.take() {
3385                    sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
3386                }
3387            });
3388
3389            let set_id = context_menu
3390                .clone()
3391                .then(|context_menu| quote!(#context_menu.popup_id.set(Some(id))));
3392            let slint_show = quote! {
3393                #close_popup
3394                let id = sp::WindowInner::from_pub(window_adapter.window()).show_popup(
3395                    &sp::VRc::into_dyn(popup_instance.into()),
3396                    position,
3397                    sp::PopupClosePolicy::CloseOnClickOutside,
3398                    #context_menu_rc,
3399                    true, // is_menu
3400                );
3401                #set_id;
3402                #popup_id::user_init(popup_instance_vrc);
3403            };
3404
3405            let common_init = quote! {
3406                let position = #position;
3407                let popup_instance = #popup_id::new(_self.globals.get().unwrap().clone()).unwrap();
3408                let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
3409                let parent_weak = _self.self_weak.get().unwrap().clone();
3410                let window_adapter = #window_adapter_tokens;
3411            };
3412
3413            if let Expression::NumberLiteral(tree_index) = entries {
3414                // We have an MenuItem tree
3415                let current_sub_component = ctx.current_sub_component().unwrap();
3416                let item_tree_id = inner_component_id(
3417                    &ctx.compilation_unit.sub_components
3418                        [current_sub_component.menu_item_trees[*tree_index as usize].root],
3419                );
3420                quote! {{
3421                    #common_init
3422                    let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
3423                    let context_menu_item_tree = sp::VRc::new(sp::MenuFromItemTree::new(sp::VRc::into_dyn(menu_item_tree_instance)));
3424                    let context_menu_item_tree_ = context_menu_item_tree.clone();
3425                    {
3426                        let mut entries = sp::SharedVector::default();
3427                        sp::Menu::sub_menu(&*context_menu_item_tree, sp::Option::None, &mut entries);
3428                        let _self = popup_instance_vrc.as_pin_ref();
3429                        #access_entries.set(sp::ModelRc::new(sp::SharedVectorModel::from(entries)));
3430                        let context_menu_item_tree = context_menu_item_tree_.clone();
3431                        #access_sub_menu.set_handler(move |entry| {
3432                            let mut entries = sp::SharedVector::default();
3433                            sp::Menu::sub_menu(&*context_menu_item_tree, sp::Option::Some(&entry.0), &mut entries);
3434                            sp::ModelRc::new(sp::SharedVectorModel::from(entries))
3435                        });
3436                        let context_menu_item_tree = context_menu_item_tree_.clone();
3437                        #access_activated.set_handler(move |entry| {
3438                            sp::Menu::activate(&*context_menu_item_tree_, &entry.0);
3439                        });
3440                        let self_weak = parent_weak.clone();
3441                        #access_close.set_handler(move |()| {
3442                            let Some(self_rc) = self_weak.upgrade() else { return };
3443                            let _self = self_rc.as_pin_ref();
3444                            #close_popup
3445                        });
3446                    }
3447                    let context_menu_item_tree = sp::VRc::into_dyn(context_menu_item_tree);
3448                    if !sp::WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(context_menu_item_tree, position, #context_menu_rc) {
3449                        #slint_show
3450                    }
3451                }}
3452            } else {
3453                // ShowPopupMenuInternal: entries should be an expression of type array of MenuEntry
3454                debug_assert!(
3455                    matches!(entries.ty(ctx), Type::Array(ty) if matches!(&*ty, Type::Struct{..}))
3456                );
3457                let entries = compile_expression(entries, ctx);
3458                let forward_callback = |access, cb| {
3459                    let call = context_menu
3460                        .clone()
3461                        .map_or_default(|context_menu| quote!(#context_menu.#cb.call(entry)));
3462                    quote!(
3463                        let self_weak = parent_weak.clone();
3464                        #access.set_handler(move |entry| {
3465                            if let Some(self_rc) = self_weak.upgrade() {
3466                                let _self = self_rc.as_pin_ref();
3467                                #call
3468                            } else { ::core::default::Default::default() }
3469                        });
3470                    )
3471                };
3472                let fw_sub_menu = forward_callback(access_sub_menu.clone(), quote!(sub_menu));
3473                let fw_activated = forward_callback(access_activated.clone(), quote!(activated));
3474                quote! {{
3475                    #common_init
3476                    let entries = #entries;
3477                    {
3478                        let _self = popup_instance_vrc.as_pin_ref();
3479                        #access_entries.set(entries.clone());
3480                        #fw_sub_menu
3481                        #fw_activated
3482                        let self_weak = parent_weak.clone();
3483                        #access_close.set_handler(move |()| {
3484                            let Some(self_rc) = self_weak.upgrade() else { return };
3485                            let _self = self_rc.as_pin_ref();
3486                            #close_popup
3487                        });
3488                    }
3489                    #slint_show
3490                }}
3491            }
3492        }
3493        BuiltinFunction::SetSelectionOffsets => {
3494            if let [llr::Expression::PropertyReference(pr), from, to] = arguments {
3495                let item = access_member(pr, ctx);
3496                let item_rc = access_item_rc(pr, ctx);
3497                let window_adapter_tokens = access_window_adapter_field(ctx);
3498                let start = compile_expression(from, ctx);
3499                let end = compile_expression(to, ctx);
3500
3501                item.then(|item| quote!(
3502                    #item.set_selection_offsets(#window_adapter_tokens, #item_rc, #start as i32, #end as i32)
3503                ))
3504            } else {
3505                panic!("internal error: invalid args to set-selection-offsets {arguments:?}")
3506            }
3507        }
3508        BuiltinFunction::ItemFontMetrics => {
3509            if let [Expression::PropertyReference(pr)] = arguments {
3510                let item = access_member(pr, ctx);
3511                let item_rc = access_item_rc(pr, ctx);
3512                let window_adapter_tokens = access_window_adapter_field(ctx);
3513                item.then(|item| {
3514                    quote!(
3515                        #item.font_metrics(#window_adapter_tokens, #item_rc)
3516                    )
3517                })
3518            } else {
3519                panic!("internal error: invalid args to ItemMemberFunction {arguments:?}")
3520            }
3521        }
3522        BuiltinFunction::ImplicitLayoutInfo(orient) => {
3523            if let [Expression::PropertyReference(pr), constraint_expr] = arguments {
3524                let item = access_member(pr, ctx);
3525                let window_adapter_tokens = access_window_adapter_field(ctx);
3526                let constraint = compile_expression(constraint_expr, ctx);
3527                item.then(|item| {
3528                    let item_rc = access_item_rc(pr, ctx);
3529                    quote!(
3530                        sp::Item::layout_info(#item, #orient, #constraint as _, #window_adapter_tokens, &#item_rc)
3531                    )
3532                })
3533            } else {
3534                panic!("internal error: invalid args to ImplicitLayoutInfo {arguments:?}")
3535            }
3536        }
3537        BuiltinFunction::RegisterCustomFontByPath => {
3538            if let [Expression::StringLiteral(path)] = arguments {
3539                let window_adapter_tokens = access_window_adapter_field(ctx);
3540                let path = path.as_str();
3541                quote!(#window_adapter_tokens.renderer().register_font_from_path(&std::path::PathBuf::from(#path)).unwrap())
3542            } else {
3543                panic!("internal error: invalid args to RegisterCustomFontByPath {arguments:?}")
3544            }
3545        }
3546        BuiltinFunction::RegisterCustomFontByMemory => {
3547            if let [Expression::NumberLiteral(resource_id)] = &arguments {
3548                let resource_id: usize = *resource_id as _;
3549                let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
3550                let window_adapter_tokens = access_window_adapter_field(ctx);
3551                quote!(#window_adapter_tokens.renderer().register_font_from_memory(#symbol.into()).unwrap())
3552            } else {
3553                panic!("internal error: invalid args to RegisterCustomFontByMemory {arguments:?}")
3554            }
3555        }
3556        BuiltinFunction::RegisterBitmapFont => {
3557            if let [Expression::NumberLiteral(resource_id)] = &arguments {
3558                let resource_id: usize = *resource_id as _;
3559                let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
3560                let window_adapter_tokens = access_window_adapter_field(ctx);
3561                quote!(#window_adapter_tokens.renderer().register_bitmap_font(&#symbol))
3562            } else {
3563                panic!("internal error: invalid args to RegisterBitmapFont must be a number")
3564            }
3565        }
3566        BuiltinFunction::GetWindowScaleFactor => {
3567            let window_adapter_tokens = access_window_adapter_field(ctx);
3568            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).scale_factor())
3569        }
3570        BuiltinFunction::GetWindowDefaultFontSize => {
3571            quote!(
3572                sp::WindowItem::resolved_default_font_size(sp::VRcMapped::origin(
3573                    &_self.self_weak.get().unwrap().upgrade().unwrap()
3574                ))
3575                .get()
3576            )
3577        }
3578        BuiltinFunction::AnimationTick => {
3579            quote!(sp::animation_tick())
3580        }
3581        BuiltinFunction::Debug => quote!(slint::private_unstable_api::debug(#(#a)*)),
3582        BuiltinFunction::Mod => {
3583            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
3584            quote!(sp::Euclid::rem_euclid(&(#a1 as f64), &(#a2 as f64)))
3585        }
3586        BuiltinFunction::Round => quote!((#(#a)* as f64).round()),
3587        BuiltinFunction::Ceil => quote!((#(#a)* as f64).ceil()),
3588        BuiltinFunction::Floor => quote!((#(#a)* as f64).floor()),
3589        BuiltinFunction::Sqrt => quote!((#(#a)* as f64).sqrt()),
3590        BuiltinFunction::Abs => quote!((#(#a)* as f64).abs()),
3591        BuiltinFunction::Sin => quote!((#(#a)* as f64).to_radians().sin()),
3592        BuiltinFunction::Cos => quote!((#(#a)* as f64).to_radians().cos()),
3593        BuiltinFunction::Tan => quote!((#(#a)* as f64).to_radians().tan()),
3594        BuiltinFunction::ASin => quote!((#(#a)* as f64).asin().to_degrees()),
3595        BuiltinFunction::ACos => quote!((#(#a)* as f64).acos().to_degrees()),
3596        BuiltinFunction::ATan => quote!((#(#a)* as f64).atan().to_degrees()),
3597        BuiltinFunction::ATan2 => {
3598            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
3599            quote!((#a1 as f64).atan2(#a2 as f64).to_degrees())
3600        }
3601        BuiltinFunction::Log => {
3602            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
3603            quote!((#a1 as f64).log(#a2 as f64))
3604        }
3605        BuiltinFunction::Ln => quote!((#(#a)* as f64).ln()),
3606        BuiltinFunction::Pow => {
3607            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
3608            quote!((#a1 as f64).powf(#a2 as f64))
3609        }
3610        BuiltinFunction::Exp => quote!((#(#a)* as f64).exp()),
3611        BuiltinFunction::ToFixed => {
3612            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
3613            quote!(sp::shared_string_from_number_fixed(#a1 as f64, (#a2 as i32).max(0) as usize))
3614        }
3615        BuiltinFunction::ToPrecision => {
3616            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
3617            quote!(sp::shared_string_from_number_precision(#a1 as f64, (#a2 as i32).max(0) as usize))
3618        }
3619        BuiltinFunction::StringToFloat => {
3620            quote!(#(#a)*.as_str().parse::<f64>().unwrap_or_default())
3621        }
3622        BuiltinFunction::StringIsFloat => quote!(#(#a)*.as_str().parse::<f64>().is_ok()),
3623        BuiltinFunction::StringIsEmpty => quote!(#(#a)*.is_empty()),
3624        BuiltinFunction::StringCharacterCount => {
3625            quote!( sp::UnicodeSegmentation::graphemes(#(#a)*.as_str(), true).count() as i32 )
3626        }
3627        BuiltinFunction::StringToLowercase => quote!(sp::SharedString::from(#(#a)*.to_lowercase())),
3628        BuiltinFunction::StringToUppercase => quote!(sp::SharedString::from(#(#a)*.to_uppercase())),
3629        BuiltinFunction::KeysToString => quote!(sp::ToSharedString::to_shared_string(&#(#a)*)),
3630        BuiltinFunction::ColorRgbaStruct => quote!( #(#a)*.to_argb_u8()),
3631        BuiltinFunction::ColorHsvaStruct => quote!( #(#a)*.to_hsva()),
3632        BuiltinFunction::ColorOklchStruct => quote!( #(#a)*.to_oklch()),
3633        BuiltinFunction::ColorBrighter => {
3634            let x = a.next().unwrap();
3635            let factor = a.next().unwrap();
3636            quote!(#x.brighter(#factor as f32))
3637        }
3638        BuiltinFunction::ColorDarker => {
3639            let x = a.next().unwrap();
3640            let factor = a.next().unwrap();
3641            quote!(#x.darker(#factor as f32))
3642        }
3643        BuiltinFunction::ColorTransparentize => {
3644            let x = a.next().unwrap();
3645            let factor = a.next().unwrap();
3646            quote!(#x.transparentize(#factor as f32))
3647        }
3648        BuiltinFunction::ColorMix => {
3649            let x = a.next().unwrap();
3650            let y = a.next().unwrap();
3651            let factor = a.next().unwrap();
3652            quote!(#x.mix(&#y.into(), #factor as f32))
3653        }
3654        BuiltinFunction::ColorWithAlpha => {
3655            let x = a.next().unwrap();
3656            let alpha = a.next().unwrap();
3657            quote!(#x.with_alpha(#alpha as f32))
3658        }
3659        BuiltinFunction::ImageSize => quote!( #(#a)*.size()),
3660        BuiltinFunction::ArrayLength => {
3661            quote!(match &#(#a)* { x => {
3662                x.model_tracker().track_row_count_changes();
3663                x.row_count() as i32
3664            }})
3665        }
3666
3667        BuiltinFunction::Rgb => {
3668            let (r, g, b, a) =
3669                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
3670            quote!({
3671                let r: u8 = (#r as u32).min(255) as u8;
3672                let g: u8 = (#g as u32).min(255) as u8;
3673                let b: u8 = (#b as u32).min(255) as u8;
3674                let a: u8 = (255. * (#a as f32)).max(0.).min(255.) as u8;
3675                sp::Color::from_argb_u8(a, r, g, b)
3676            })
3677        }
3678        BuiltinFunction::Hsv => {
3679            let (h, s, v, a) =
3680                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
3681            quote!({
3682                let s: f32 = (#s as f32).max(0.).min(1.) as f32;
3683                let v: f32 = (#v as f32).max(0.).min(1.) as f32;
3684                let a: f32 = (1. * (#a as f32)).max(0.).min(1.) as f32;
3685                sp::Color::from_hsva(#h as f32, s, v, a)
3686            })
3687        }
3688        BuiltinFunction::Oklch => {
3689            let (l, c, h, alpha) =
3690                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
3691            quote!({
3692                let l: f32 = (#l as f32).max(0.).min(1.) as f32;
3693                let c: f32 = (#c as f32).max(0.) as f32;
3694                let alpha: f32 = (#alpha as f32).max(0.).min(1.) as f32;
3695                sp::Color::from_oklch(l, c, #h as f32, alpha)
3696            })
3697        }
3698        BuiltinFunction::ColorScheme => {
3699            let window_adapter_tokens = access_window_adapter_field(ctx);
3700            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).color_scheme())
3701        }
3702        BuiltinFunction::AccentColor => {
3703            let window_adapter_tokens = access_window_adapter_field(ctx);
3704            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).accent_color())
3705        }
3706        BuiltinFunction::SupportsNativeMenuBar => {
3707            let window_adapter_tokens = access_window_adapter_field(ctx);
3708            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar())
3709        }
3710        BuiltinFunction::SetupMenuBar => {
3711            let window_adapter_tokens = access_window_adapter_field(ctx);
3712            let [
3713                Expression::PropertyReference(entries_r),
3714                Expression::PropertyReference(sub_menu_r),
3715                Expression::PropertyReference(activated_r),
3716                Expression::NumberLiteral(tree_index),
3717                Expression::BoolLiteral(no_native),
3718                rest @ ..,
3719            ] = arguments
3720            else {
3721                panic!("internal error: incorrect arguments to SetupMenuBar")
3722            };
3723
3724            // We have an MenuItem tree
3725            let current_sub_component = ctx.current_sub_component().unwrap();
3726            let item_tree_id = inner_component_id(
3727                &ctx.compilation_unit.sub_components
3728                    [current_sub_component.menu_item_trees[*tree_index as usize].root],
3729            );
3730
3731            let access_entries = access_member(entries_r, ctx).unwrap();
3732            let access_sub_menu = access_member(sub_menu_r, ctx).unwrap();
3733            let access_activated = access_member(activated_r, ctx).unwrap();
3734
3735            let native_impl = if *no_native {
3736                quote!(let menu_item_tree = sp::MenuFromItemTree::new(sp::VRc::into_dyn(menu_item_tree_instance));)
3737            } else {
3738                let menu_from_item_tree = if let Some(condition) = &rest.first() {
3739                    let binding = compile_expression(condition, ctx);
3740                    quote!(sp::MenuFromItemTree::new_with_condition(sp::VRc::into_dyn(menu_item_tree_instance), {
3741                        let self_weak = _self.self_weak.get().unwrap().clone();
3742                        move || {
3743                            let Some(self_rc) = self_weak.upgrade() else { return false };
3744                            let _self = self_rc.as_pin_ref();
3745                            #binding
3746                        }
3747                    }))
3748                } else {
3749                    quote!(sp::MenuFromItemTree::new(sp::VRc::into_dyn(menu_item_tree_instance)))
3750                };
3751                quote! {
3752                    let menu_item_tree = sp::VRc::new(#menu_from_item_tree);
3753                    if sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar() {
3754                        let menu_item_tree_dyn = sp::VRc::into_dyn(sp::VRc::clone(&menu_item_tree));
3755                        sp::WindowInner::from_pub(#window_adapter_tokens.window()).setup_menubar(menu_item_tree_dyn);
3756                    } else
3757                }
3758            };
3759
3760            quote!({
3761                let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
3762                #native_impl
3763                /*else*/ {
3764                    let menu_item_tree_ = sp::VRc::clone(&menu_item_tree);
3765                    #access_entries.set_binding(move || {
3766                        let mut entries = sp::SharedVector::default();
3767                        sp::VRc::borrow(&menu_item_tree_).sub_menu(sp::Option::None, &mut entries);
3768                        sp::ModelRc::new(sp::SharedVectorModel::from(entries))
3769                    });
3770                    let menu_item_tree_ = sp::VRc::clone(&menu_item_tree);
3771                    #access_sub_menu.set_handler(move |entry| {
3772                        let mut entries = sp::SharedVector::default();
3773                        sp::VRc::borrow(&menu_item_tree_).sub_menu(sp::Option::Some(&entry.0), &mut entries);
3774                        sp::ModelRc::new(sp::SharedVectorModel::from(entries))
3775                    });
3776                    let menu_item_tree_ = menu_item_tree.clone();
3777                    #access_activated.set_handler(move |entry| {
3778                        sp::VRc::borrow(&menu_item_tree_).activate(&entry.0);
3779                    });
3780                }
3781                sp::WindowInner::from_pub(#window_adapter_tokens.window())
3782                    .setup_menubar_shortcuts(sp::VRc::into_dyn(menu_item_tree));
3783            })
3784        }
3785        BuiltinFunction::MonthDayCount => {
3786            let (m, y) = (a.next().unwrap(), a.next().unwrap());
3787            quote!(sp::month_day_count(#m as u32, #y as i32).unwrap_or(0))
3788        }
3789        BuiltinFunction::MonthOffset => {
3790            let (m, y) = (a.next().unwrap(), a.next().unwrap());
3791            quote!(sp::month_offset(#m as u32, #y as i32))
3792        }
3793        BuiltinFunction::FormatDate => {
3794            let (f, d, m, y) =
3795                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
3796            quote!(sp::format_date(&#f, #d as u32, #m as u32, #y as i32))
3797        }
3798        BuiltinFunction::ValidDate => {
3799            let (d, f) = (a.next().unwrap(), a.next().unwrap());
3800            quote!(sp::parse_date(#d.as_str(), #f.as_str()).is_some())
3801        }
3802        BuiltinFunction::ParseDate => {
3803            let (d, f) = (a.next().unwrap(), a.next().unwrap());
3804            quote!(sp::ModelRc::new(sp::parse_date(#d.as_str(), #f.as_str()).map(|d| sp::VecModel::from_slice(&d)).unwrap_or_default()))
3805        }
3806        BuiltinFunction::DateNow => {
3807            quote!(sp::ModelRc::new(sp::VecModel::from_slice(&sp::date_now())))
3808        }
3809        BuiltinFunction::TextInputFocused => {
3810            let window_adapter_tokens = access_window_adapter_field(ctx);
3811            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).text_input_focused())
3812        }
3813        BuiltinFunction::SetTextInputFocused => {
3814            let window_adapter_tokens = access_window_adapter_field(ctx);
3815            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).set_text_input_focused(#(#a)*))
3816        }
3817        BuiltinFunction::Translate => {
3818            quote!(slint::private_unstable_api::translate(#((#a) as _),*))
3819        }
3820        BuiltinFunction::Use24HourFormat => {
3821            quote!(slint::private_unstable_api::use_24_hour_format())
3822        }
3823        BuiltinFunction::ItemAbsolutePosition => {
3824            if let [Expression::PropertyReference(pr)] = arguments {
3825                let item_rc = access_item_rc(pr, ctx);
3826                quote!(
3827                    sp::logical_position_to_api((*#item_rc).map_to_window(::core::default::Default::default()))
3828                )
3829            } else {
3830                panic!("internal error: invalid args to MapPointToWindow {arguments:?}")
3831            }
3832        }
3833        BuiltinFunction::UpdateTimers => {
3834            quote!(_self.update_timers())
3835        }
3836        BuiltinFunction::DetectOperatingSystem => {
3837            quote!(sp::detect_operating_system())
3838        }
3839        // start and stop are unreachable because they are lowered to simple assignment of running
3840        BuiltinFunction::StartTimer => unreachable!(),
3841        BuiltinFunction::StopTimer => unreachable!(),
3842        BuiltinFunction::RestartTimer => {
3843            if let [Expression::NumberLiteral(timer_index)] = arguments {
3844                let ident = format_ident!("timer{}", *timer_index as usize);
3845                quote!(_self.#ident.restart())
3846            } else {
3847                panic!("internal error: invalid args to RestartTimer {arguments:?}")
3848            }
3849        }
3850        BuiltinFunction::OpenUrl => {
3851            let url = a.next().unwrap();
3852            let window_adapter_tokens = access_window_adapter_field(ctx);
3853            quote!(sp::open_url(&#url, #window_adapter_tokens.window()).is_ok())
3854        }
3855        BuiltinFunction::ParseMarkdown => {
3856            let format_string = a.next().unwrap();
3857            let args = a.next().unwrap();
3858            quote!(sp::parse_markdown::<sp::StyledText>(&#format_string, &#args))
3859        }
3860        BuiltinFunction::StringToStyledText => {
3861            let string = a.next().unwrap();
3862            quote!(sp::string_to_styled_text(#string.to_string()))
3863        }
3864    }
3865}
3866
3867fn struct_name_to_tokens(name: &StructName) -> Option<proc_macro2::TokenStream> {
3868    match name {
3869        StructName::None => None,
3870        StructName::User { name, .. } => Some(proc_macro2::TokenTree::from(ident(name)).into()),
3871        StructName::BuiltinPrivate(builtin_private_struct) => {
3872            let name: &'static str = builtin_private_struct.into();
3873            let name = format_ident!("{}", name);
3874            Some(quote!(sp::#name))
3875        }
3876        StructName::BuiltinPublic(builtin_public_struct) => {
3877            let name: &'static str = builtin_public_struct.into();
3878            let name = format_ident!("{}", name);
3879            if matches!(
3880                builtin_public_struct,
3881                crate::langtype::BuiltinPublicStruct::Color
3882                    | crate::langtype::BuiltinPublicStruct::LogicalPosition
3883                    | crate::langtype::BuiltinPublicStruct::LogicalSize
3884            ) {
3885                Some(quote!(slint::#name))
3886            } else {
3887                Some(quote!(slint::language::#name))
3888            }
3889        }
3890    }
3891}
3892
3893fn generate_common_repeater_code(
3894    // Which repeater this is about
3895    repeater_index: llr::RepeatedElementIdx,
3896    // If not set, we're only going over repeaters and calling a function on repeated items
3897    // If set, we're also generating code to fill in this "repeated indices" array
3898    repeated_indices_var_name: &Option<Ident>,
3899    // ... which currently has this size, so this is where we'll write
3900    repeated_indices_size: &mut usize,
3901    // ... and this "repeater steps" array (number of items in repeated rows)
3902    repeater_steps_var_name: &Option<Ident>,
3903    repeater_count_code: &mut TokenStream,
3904    // The name of the items vector to use for measuring length (e.g., "items_vec" or "items_vec_h")
3905    items_vec_name: &str,
3906    ctx: &EvaluationContext,
3907) -> (TokenStream, Option<usize>) {
3908    let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
3909    let inner_component_id = self::inner_component_id(ctx.current_sub_component().unwrap());
3910    let rep_inner_component_id = self::inner_component_id(
3911        &ctx.compilation_unit.sub_components
3912            [ctx.current_sub_component().unwrap().repeated[repeater_index].sub_tree.root],
3913    );
3914    *repeater_count_code = quote!(#repeater_count_code + _self.#repeater_id.len());
3915
3916    let items_vec_ident = ident(items_vec_name);
3917    let mut repeater_code = quote!();
3918    let mut rs_idx_for_init = None;
3919    if let Some(ri) = repeated_indices_var_name {
3920        let ri_idx = *repeated_indices_size;
3921        repeater_code = quote!(
3922            #ri[#ri_idx] = #items_vec_ident.len() as u32;
3923            #ri[#ri_idx + 1] = _self.#repeater_id.len() as u32;
3924        );
3925        *repeated_indices_size += 2;
3926        if repeater_steps_var_name.is_some() {
3927            rs_idx_for_init = Some(ri_idx / 2);
3928        }
3929    }
3930
3931    let code = quote!(
3932        #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).ensure_updated(
3933            || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into() }
3934        );
3935        #repeater_code
3936    );
3937    (code, rs_idx_for_init)
3938}
3939
3940fn generate_common_repeater_indices_init_code(
3941    repeated_indices_var_name: &Option<Ident>,
3942    repeated_indices_size: usize,
3943    repeater_steps_var_name: &Option<Ident>,
3944) -> TokenStream {
3945    if let Some(ri) = repeated_indices_var_name {
3946        let rs_init = if let Some(rs) = repeater_steps_var_name {
3947            quote!(let mut #rs = [0u32; #repeated_indices_size / 2];)
3948        } else {
3949            quote!()
3950        };
3951        quote!(
3952            let mut #ri = [0u32; #repeated_indices_size];
3953            #rs_init
3954        )
3955    } else {
3956        quote!()
3957    }
3958}
3959
3960/// For each inner repeater in `templates`, generates code to `ensure_updated` it
3961/// and add its length to `total`.  `row_sc` / `row_inner_component_id` describe the
3962/// repeating Row sub-component; `unit` is the full compilation unit.
3963fn build_inner_ensure_and_len(
3964    templates: &[llr::RowChildTemplateInfo],
3965    row_sc: &llr::SubComponent,
3966    row_inner_component_id: &proc_macro2::Ident,
3967    unit: &llr::CompilationUnit,
3968) -> Vec<TokenStream> {
3969    templates
3970        .iter()
3971        .filter_map(|e| match e {
3972            llr::RowChildTemplateInfo::Repeated { repeater_index } => {
3973                let inner_rep_sc_idx = row_sc.repeated[*repeater_index].sub_tree.root;
3974                let inner_inner_component_id =
3975                    inner_component_id(&unit.sub_components[inner_rep_sc_idx]);
3976                let inner_rep_id = format_ident!("repeater{}", usize::from(*repeater_index));
3977                Some(quote! {
3978                    #row_inner_component_id::FIELD_OFFSETS.#inner_rep_id().apply_pin(pin).ensure_updated(
3979                        || #inner_inner_component_id::new(pin.self_weak.get().unwrap().clone()).unwrap().into()
3980                    );
3981                    total += pin.#inner_rep_id.len();
3982                })
3983            }
3984            _ => None,
3985        })
3986        .collect()
3987}
3988
3989fn generate_repeater_push_code(
3990    repeater_index: llr::RepeatedElementIdx,
3991    row_child_templates: &Option<Vec<llr::RowChildTemplateInfo>>,
3992    repeated_indices_var_name: &Option<proc_macro2::Ident>,
3993    repeated_indices_size: &mut usize,
3994    repeater_steps_var_name: &Option<proc_macro2::Ident>,
3995    repeated_count_code: &mut TokenStream,
3996    ctx: &EvaluationContext,
3997    dynamic_loop_code: impl FnOnce(
3998        proc_macro2::Ident,
3999        usize,
4000        Vec<TokenStream>,
4001        Option<TokenStream>,
4002    ) -> TokenStream,
4003    static_loop_code: impl FnOnce(proc_macro2::Ident, usize, bool) -> TokenStream,
4004) -> TokenStream {
4005    let row_templates = row_child_templates.as_deref();
4006    if llr::has_inner_repeaters(row_child_templates) {
4007        let templates = row_templates.unwrap();
4008        let static_count = llr::static_child_count(templates);
4009        let parent_sc = ctx.current_sub_component().unwrap();
4010        let row_sc_idx = parent_sc.repeated[repeater_index].sub_tree.root;
4011        let row_sc = &ctx.compilation_unit.sub_components[row_sc_idx];
4012        let row_inner_component_id = self::inner_component_id(row_sc);
4013        let inner_ensure_and_len = build_inner_ensure_and_len(
4014            templates,
4015            row_sc,
4016            &row_inner_component_id,
4017            ctx.compilation_unit,
4018        );
4019
4020        let (common_push_code, rs_idx) = self::generate_common_repeater_code(
4021            repeater_index,
4022            repeated_indices_var_name,
4023            repeated_indices_size,
4024            repeater_steps_var_name,
4025            repeated_count_code,
4026            "items_vec",
4027            ctx,
4028        );
4029        let rs_init = rs_idx.and_then(|idx| {
4030            repeater_steps_var_name.as_ref().map(|rs| quote!(#rs[#idx] = total_item_count as u32;))
4031        });
4032
4033        let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
4034        let loop_code = dynamic_loop_code(repeater_id, static_count, inner_ensure_and_len, rs_init);
4035        quote!(
4036            #common_push_code
4037            #loop_code
4038        )
4039    } else {
4040        let step = row_templates.map_or(1, |t| t.len());
4041        let (common_push_code, rs_idx) = self::generate_common_repeater_code(
4042            repeater_index,
4043            repeated_indices_var_name,
4044            repeated_indices_size,
4045            repeater_steps_var_name,
4046            repeated_count_code,
4047            "items_vec",
4048            ctx,
4049        );
4050        let rs_init = rs_idx.and_then(|idx| {
4051            repeater_steps_var_name.as_ref().map(|rs| quote!(#rs[#idx] = #step as u32;))
4052        });
4053        let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
4054        let loop_code = static_loop_code(repeater_id, step, row_templates.is_none());
4055        quote!(
4056            #common_push_code
4057            #rs_init
4058            #loop_code
4059        )
4060    }
4061}
4062
4063fn generate_with_grid_input_data(
4064    cells_variable: &str,
4065    repeated_indices_var_name: &SmolStr,
4066    repeater_steps_var_name: &SmolStr,
4067    elements: &[Either<Expression, llr::GridLayoutRepeatedElement>],
4068    sub_expression: &Expression,
4069    ctx: &EvaluationContext,
4070) -> TokenStream {
4071    let repeated_indices_var_name = Some(ident(repeated_indices_var_name));
4072    let repeater_steps_var_name = Some(ident(repeater_steps_var_name));
4073    let mut fixed_count = 0usize;
4074    let mut repeated_count_code = quote!();
4075    let mut push_code = Vec::new();
4076    let mut repeated_indices_size = 0usize;
4077    for item in elements {
4078        match item {
4079            Either::Left(value) => {
4080                let value = compile_expression(value, ctx);
4081                fixed_count += 1;
4082                push_code.push(quote!(items_vec.push(#value);))
4083            }
4084            Either::Right(repeater) => {
4085                let repeater_push_code = generate_repeater_push_code(
4086                    repeater.repeater_index,
4087                    &repeater.row_child_templates,
4088                    &repeated_indices_var_name,
4089                    &mut repeated_indices_size,
4090                    &repeater_steps_var_name,
4091                    &mut repeated_count_code,
4092                    ctx,
4093                    |repeater_id, static_count, inner_ensure_and_len, rs_init| {
4094                        quote!({
4095                            let len = _self.#repeater_id.len();
4096                            let max_total = (0..len).filter_map(|i| {
4097                                _self.#repeater_id.instance_at(i).map(|rc| {
4098                                    let pin = rc.as_pin_ref();
4099                                    let mut total = #static_count;
4100                                    #(#inner_ensure_and_len)*
4101                                    total
4102                                })
4103                            }).max().unwrap_or(#static_count);
4104                            let total_item_count = max_total;
4105                            #rs_init
4106                            let start_offset = items_vec.len();
4107                            items_vec.extend(core::iter::repeat_with(Default::default).take(len * total_item_count));
4108                            for i in 0..len {
4109                                if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
4110                                    let offset = start_offset + i * total_item_count;
4111                                    sub_comp.as_pin_ref().grid_layout_input_data(new_row, &mut items_vec[offset..offset + total_item_count]);
4112                                }
4113                            }
4114                        })
4115                    },
4116                    |repeater_id, step, is_column_repeater| {
4117                        // Only reset new_row for column-repeaters. For repeated rows, each sub-comp
4118                        // is its own row so new_row stays true.
4119                        let reset_new_row_code =
4120                            if is_column_repeater { quote!(new_row = false;) } else { quote!() };
4121                        quote!({
4122                            let len = _self.#repeater_id.len();
4123                            let start_offset = items_vec.len();
4124                            items_vec.extend(core::iter::repeat_with(Default::default).take(len * #step));
4125                            for i in 0..len {
4126                                if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
4127                                    let offset = start_offset + i * #step;
4128                                    sub_comp.as_pin_ref().grid_layout_input_data(new_row, &mut items_vec[offset..offset + #step]);
4129                                    #reset_new_row_code
4130                                }
4131                            }
4132                        })
4133                    },
4134                );
4135                let new_row = repeater.new_row;
4136                push_code.push(quote!(
4137                    let mut new_row = #new_row;
4138                    #repeater_push_code
4139                ));
4140            }
4141        }
4142    }
4143    let ri_init_code = generate_common_repeater_indices_init_code(
4144        &repeated_indices_var_name,
4145        repeated_indices_size,
4146        &repeater_steps_var_name,
4147    );
4148    let ri_from_slice =
4149        repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
4150    let rs_from_slice =
4151        repeater_steps_var_name.map(|rs| quote!(let #rs = sp::Slice::from_slice(&#rs);));
4152    let cells_variable = ident(cells_variable);
4153    let sub_expression = compile_expression(sub_expression, ctx);
4154
4155    quote! { {
4156        #ri_init_code
4157        let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
4158        #(#push_code)*
4159        let #cells_variable = sp::Slice::from_slice(&items_vec);
4160        #ri_from_slice
4161        #rs_from_slice
4162        #sub_expression
4163    } }
4164}
4165
4166fn generate_with_layout_item_info(
4167    cells_variable: &str,
4168    repeated_indices_var_name: Option<&str>,
4169    repeater_steps_var_name: Option<&str>,
4170    elements: &[Either<Expression, llr::LayoutRepeatedElement>],
4171    orientation: Orientation,
4172    sub_expression: &Expression,
4173    ctx: &EvaluationContext,
4174) -> TokenStream {
4175    let repeated_indices_var_name = repeated_indices_var_name.map(ident);
4176    let repeater_steps_var_name = repeater_steps_var_name.map(ident);
4177    let mut fixed_count = 0usize;
4178    let mut repeated_count_code = quote!();
4179    let mut push_code = Vec::new();
4180    let mut repeated_indices_size = 0usize;
4181    for item in elements {
4182        match item {
4183            Either::Left(value) => {
4184                let value = compile_expression(value, ctx);
4185                fixed_count += 1;
4186                push_code.push(quote!(items_vec.push(#value);))
4187            }
4188            Either::Right(repeater) => {
4189                let repeater_push_code = generate_repeater_push_code(
4190                    repeater.repeater_index,
4191                    &repeater.row_child_templates,
4192                    &repeated_indices_var_name,
4193                    &mut repeated_indices_size,
4194                    &repeater_steps_var_name,
4195                    &mut repeated_count_code,
4196                    ctx,
4197                    |repeater_id, static_count, inner_ensure_and_len, rs_init| {
4198                        quote!(
4199                            {
4200                                let len = _self.#repeater_id.len();
4201                                let max_total = (0..len).filter_map(|i| {
4202                                    _self.#repeater_id.instance_at(i).map(|rc| {
4203                                        let pin = rc.as_pin_ref();
4204                                        let mut total = #static_count;
4205                                        #(#inner_ensure_and_len)*
4206                                        total
4207                                    })
4208                                }).max().unwrap_or(#static_count);
4209                                let total_item_count = max_total;
4210                                #rs_init
4211                                for i in 0..len {
4212                                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
4213                                        for child_idx in 0..total_item_count {
4214                                            items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, Some(child_idx)));
4215                                        }
4216                                    }
4217                                }
4218                            }
4219                        )
4220                    },
4221                    |repeater_id, step, is_column_repeater| {
4222                        if step == 0 {
4223                            quote!()
4224                        } else if step == 1 && is_column_repeater {
4225                            // Column-repeater: each sub-component IS a cell; None returns its own layout_info
4226                            quote!(
4227                                for i in 0.._self.#repeater_id.len() {
4228                                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
4229                                       items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, None));
4230                                    }
4231                                }
4232                            )
4233                        } else {
4234                            quote!(
4235                                for i in 0.._self.#repeater_id.len() {
4236                                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
4237                                        for child_idx in 0..#step {
4238                                            items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, Some(child_idx)));
4239                                        }
4240                                    }
4241                                }
4242                            )
4243                        }
4244                    },
4245                );
4246                push_code.push(repeater_push_code);
4247            }
4248        }
4249    }
4250    let ri_init_code = generate_common_repeater_indices_init_code(
4251        &repeated_indices_var_name,
4252        repeated_indices_size,
4253        &repeater_steps_var_name,
4254    );
4255
4256    let ri_from_slice =
4257        repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
4258    let rs_from_slice =
4259        repeater_steps_var_name.map(|rs| quote!(let #rs = sp::Slice::from_slice(&#rs);));
4260    let cells_variable = ident(cells_variable);
4261    let sub_expression = compile_expression(sub_expression, ctx);
4262
4263    quote! { {
4264        #ri_init_code
4265        let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
4266        #(#push_code)*
4267        let #cells_variable = sp::Slice::from_slice(&items_vec);
4268        #ri_from_slice
4269        #rs_from_slice
4270        #sub_expression
4271    } }
4272}
4273
4274fn generate_with_flexbox_layout_item_info(
4275    cells_h_variable: &str,
4276    cells_v_variable: &str,
4277    repeated_indices_var_name: Option<&str>,
4278    elements: &[Either<(Expression, Expression), llr::LayoutRepeatedElement>],
4279    sub_expression: &Expression,
4280    ctx: &EvaluationContext,
4281) -> TokenStream {
4282    let repeated_indices_var_name = repeated_indices_var_name.map(ident);
4283    let mut fixed_count = 0usize;
4284    let mut repeated_count_code = quote!();
4285    let mut push_code = Vec::new();
4286    let mut repeated_indices_size = 0usize;
4287
4288    for item in elements {
4289        match item {
4290            Either::Left((value_h, value_v)) => {
4291                let value_h = compile_expression(value_h, ctx);
4292                let value_v = compile_expression(value_v, ctx);
4293                fixed_count += 1;
4294                push_code.push(quote!(
4295                    items_vec_h.push(#value_h);
4296                    items_vec_v.push(#value_v);
4297                ))
4298            }
4299            Either::Right(repeater) => {
4300                let (common_push_code, _rs_idx) = self::generate_common_repeater_code(
4301                    repeater.repeater_index,
4302                    &repeated_indices_var_name,
4303                    &mut repeated_indices_size,
4304                    &None, // No repeater_steps for flexbox
4305                    &mut repeated_count_code,
4306                    "items_vec_h", // Use items_vec_h for length tracking (same as items_vec_v)
4307                    ctx,
4308                );
4309                let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
4310                let loop_code = quote!(for i in 0.._self.#repeater_id.len() {
4311                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
4312                        items_vec_h.push(
4313                            sub_comp.as_pin_ref().flexbox_layout_item_info(sp::Orientation::Horizontal, None),
4314                        );
4315                        items_vec_v.push(
4316                            sub_comp.as_pin_ref().flexbox_layout_item_info(sp::Orientation::Vertical, None),
4317                        );
4318                    }
4319                });
4320                push_code.push(quote!(
4321                    #common_push_code
4322                    #loop_code
4323                ));
4324            }
4325        }
4326    }
4327
4328    let ri_init_code = generate_common_repeater_indices_init_code(
4329        &repeated_indices_var_name,
4330        repeated_indices_size,
4331        &None,
4332    );
4333
4334    let ri_from_slice =
4335        repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
4336    let cells_h_variable = ident(cells_h_variable);
4337    let cells_v_variable = ident(cells_v_variable);
4338    let sub_expression = compile_expression(sub_expression, ctx);
4339
4340    quote! { {
4341        #ri_init_code
4342        let mut items_vec_h = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
4343        let mut items_vec_v = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
4344        #(#push_code)*
4345        let #cells_h_variable = sp::Slice::from_slice(&items_vec_h);
4346        let #cells_v_variable = sp::Slice::from_slice(&items_vec_v);
4347        #ri_from_slice
4348        #sub_expression
4349    } }
4350}
4351
4352/// Access a field offset via `FIELD_OFFSETS.field()`. The `FIELD_OFFSETS`
4353/// constant is a ZST with const fn methods, so this does not create a MIR
4354/// local of any aggregate type.
4355fn access_component_field_offset(component_id: &Ident, field: &Ident) -> TokenStream {
4356    quote!(#component_id::FIELD_OFFSETS.#field())
4357}
4358
4359fn embedded_file_tokens(path: &str) -> TokenStream {
4360    let file = crate::fileaccess::load_file(std::path::Path::new(path)).unwrap(); // embedding pass ensured that the file exists
4361    match file.builtin_contents {
4362        Some(static_data) => {
4363            let literal = proc_macro2::Literal::byte_string(static_data);
4364            quote!(#literal)
4365        }
4366        None => quote!(::core::include_bytes!(#path)),
4367    }
4368}
4369
4370fn generate_resources(doc: &Document) -> Vec<TokenStream> {
4371    #[cfg(feature = "software-renderer")]
4372    let link_section = std::env::var("SLINT_ASSET_SECTION")
4373        .ok()
4374        .map(|section| quote!(#[unsafe(link_section = #section)]));
4375
4376    doc.embedded_file_resources
4377        .borrow()
4378        .iter_enumerated()
4379        .map(|(resource_id, er)| {
4380            let resource_id = resource_id.0;
4381            let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
4382            match &er.kind {
4383                &crate::embedded_resources::EmbeddedResourcesKind::ListOnly => {
4384                    quote!()
4385                },
4386                crate::embedded_resources::EmbeddedResourcesKind::FileData => {
4387                    let data = embedded_file_tokens(er.path.as_deref().unwrap());
4388                    quote!(static #symbol: &'static [u8] = #data;)
4389                }
4390                crate::embedded_resources::EmbeddedResourcesKind::DataUriPayload(bytes, _) => {
4391                    quote!(static #symbol: &'static [u8] = &[#(#bytes),*];)
4392                }
4393                #[cfg(feature = "software-renderer")]
4394                crate::embedded_resources::EmbeddedResourcesKind::TextureData(crate::embedded_resources::Texture {
4395                    data, format, rect,
4396                    total_size: crate::embedded_resources::Size{width, height},
4397                    original_size: crate::embedded_resources::Size{width: unscaled_width, height: unscaled_height},
4398                }) => {
4399                    let (r_x, r_y, r_w, r_h) = (rect.x(), rect.y(), rect.width(), rect.height());
4400                    let color = if let crate::embedded_resources::PixelFormat::AlphaMap([r, g, b]) = format {
4401                        quote!(sp::Color::from_rgb_u8(#r, #g, #b))
4402                    } else {
4403                        quote!(sp::Color::from_argb_encoded(0))
4404                    };
4405                    let symbol_data = format_ident!("SLINT_EMBEDDED_RESOURCE_{}_DATA", resource_id);
4406                    let data_size = data.len();
4407                    quote!(
4408                        #link_section
4409                        // (the second array is to ensure alignment)
4410                        static #symbol_data : ([u8; #data_size], [u32;0])= ([#(#data),*], []);
4411                        #link_section
4412                        static #symbol: sp::StaticTextures = sp::StaticTextures{
4413                            size: sp::IntSize::new(#width as _, #height as _),
4414                            original_size: sp::IntSize::new(#unscaled_width as _, #unscaled_height as _),
4415                            data: sp::Slice::from_slice(&#symbol_data.0),
4416                            textures: sp::Slice::from_slice(&[
4417                                sp::StaticTexture {
4418                                    rect: sp::euclid::rect(#r_x as _, #r_y as _, #r_w as _, #r_h as _),
4419                                    format: #format,
4420                                    color: #color,
4421                                    index: 0,
4422                                }
4423                            ])
4424                        };
4425                    )
4426                },
4427                #[cfg(feature = "software-renderer")]
4428                crate::embedded_resources::EmbeddedResourcesKind::BitmapFontData(crate::embedded_resources::BitmapFont { family_name, character_map, units_per_em, ascent, descent, x_height, cap_height, glyphs, weight, italic, sdf }) => {
4429
4430                    let character_map_size = character_map.len();
4431
4432                    let character_map = character_map.iter().map(|crate::embedded_resources::CharacterMapEntry{code_point, glyph_index}| quote!(sp::CharacterMapEntry { code_point: #code_point, glyph_index: #glyph_index }));
4433
4434                    let glyphs_size = glyphs.len();
4435
4436                    let glyphs = glyphs.iter().map(|crate::embedded_resources::BitmapGlyphs{pixel_size, glyph_data}| {
4437                        let glyph_data_size = glyph_data.len();
4438                        let glyph_data = glyph_data.iter().map(|crate::embedded_resources::BitmapGlyph{x, y, width, height, x_advance, data}|{
4439                            let data_size = data.len();
4440                            quote!(
4441                                sp::BitmapGlyph {
4442                                    x: #x,
4443                                    y: #y,
4444                                    width: #width,
4445                                    height: #height,
4446                                    x_advance: #x_advance,
4447                                    data: sp::Slice::from_slice({
4448                                        #link_section
4449                                        static DATA : [u8; #data_size] = [#(#data),*];
4450                                        &DATA
4451                                    }),
4452                                }
4453                            )
4454                        });
4455
4456                        quote!(
4457                            sp::BitmapGlyphs {
4458                                pixel_size: #pixel_size,
4459                                glyph_data: sp::Slice::from_slice({
4460                                    #link_section
4461                                    static GDATA : [sp::BitmapGlyph; #glyph_data_size] = [#(#glyph_data),*];
4462                                    &GDATA
4463                                }),
4464                            }
4465                        )
4466                    });
4467
4468                    quote!(
4469                        #link_section
4470                        static #symbol: sp::BitmapFont = sp::BitmapFont {
4471                            family_name: sp::Slice::from_slice(#family_name.as_bytes()),
4472                            character_map: sp::Slice::from_slice({
4473                                #link_section
4474                                static CM : [sp::CharacterMapEntry; #character_map_size] = [#(#character_map),*];
4475                                &CM
4476                            }),
4477                            units_per_em: #units_per_em,
4478                            ascent: #ascent,
4479                            descent: #descent,
4480                            x_height: #x_height,
4481                            cap_height: #cap_height,
4482                            glyphs: sp::Slice::from_slice({
4483                                #link_section
4484                                static GLYPHS : [sp::BitmapGlyphs; #glyphs_size] = [#(#glyphs),*];
4485                                &GLYPHS
4486                            }),
4487                            weight: #weight,
4488                            italic: #italic,
4489                            sdf: #sdf,
4490                        };
4491                    )
4492                },
4493            }
4494        })
4495        .collect()
4496}
4497
4498pub fn generate_named_exports(exports: &crate::object_tree::Exports) -> Vec<TokenStream> {
4499    exports
4500        .iter()
4501        .filter_map(|export| match &export.1 {
4502            Either::Left(component) if !component.is_global() => {
4503                if export.0.name != component.id {
4504                    Some((
4505                        &export.0.name,
4506                        proc_macro2::TokenTree::from(ident(&component.id)).into(),
4507                    ))
4508                } else {
4509                    None
4510                }
4511            }
4512            Either::Right(ty) => match &ty {
4513                Type::Struct(s) if s.node().is_some() => {
4514                    if let StructName::User { name, .. } = &s.name
4515                        && *name == export.0.name
4516                    {
4517                        None
4518                    } else {
4519                        Some((&export.0.name, struct_name_to_tokens(&s.name).unwrap()))
4520                    }
4521                }
4522                Type::Enumeration(en) => {
4523                    if export.0.name != en.name {
4524                        Some((&export.0.name, proc_macro2::TokenTree::from(ident(&en.name)).into()))
4525                    } else {
4526                        None
4527                    }
4528                }
4529                _ => None,
4530            },
4531            _ => None,
4532        })
4533        .map(|(export_name, type_id)| {
4534            let export_id = ident(export_name);
4535            quote!(#type_id as #export_id)
4536        })
4537        .collect::<Vec<_>>()
4538}
4539
4540fn remove_parenthesis(
4541    expr: &Expression,
4542    ctx: &EvaluationContext,
4543    compile: impl FnOnce(&Expression, &EvaluationContext) -> TokenStream,
4544) -> TokenStream {
4545    fn extract_single_group(stream: &TokenStream) -> Option<TokenStream> {
4546        let mut iter = stream.clone().into_iter();
4547        let elem = iter.next()?;
4548        let TokenTree::Group(elem) = elem else { return None };
4549        if elem.delimiter() != proc_macro2::Delimiter::Parenthesis {
4550            return None;
4551        }
4552        if iter.next().is_some() {
4553            return None;
4554        }
4555        Some(elem.stream())
4556    }
4557
4558    let mut stream = compile(expr, ctx);
4559    if !matches!(expr, Expression::Struct { .. }) {
4560        while let Some(s) = extract_single_group(&stream) {
4561            stream = s;
4562        }
4563    }
4564    stream
4565}
4566
4567fn compile_expression_no_parenthesis(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4568    remove_parenthesis(expr, ctx, compile_expression)
4569}
4570
4571fn compile_expression_to_value_no_parenthesis(
4572    expr: &Expression,
4573    ctx: &EvaluationContext,
4574) -> TokenStream {
4575    remove_parenthesis(expr, ctx, compile_expression_to_value)
4576}
4577
4578#[cfg(feature = "bundle-translations")]
4579fn generate_translations(
4580    translations: &crate::translations::Translations,
4581    compilation_unit: &llr::CompilationUnit,
4582) -> TokenStream {
4583    let strings = translations.strings.iter().map(|strings| {
4584        let array = strings.iter().map(|s| match s.as_ref().map(SmolStr::as_str) {
4585            Some(s) => quote!(Some(#s)),
4586            None => quote!(None),
4587        });
4588        quote!(&[#(#array),*])
4589    });
4590    let plurals = translations.plurals.iter().map(|plurals| {
4591        let array = plurals.iter().map(|p| match p {
4592            Some(p) => {
4593                let p = p.iter().map(SmolStr::as_str);
4594                quote!(Some(&[#(#p),*]))
4595            }
4596            None => quote!(None),
4597        });
4598        quote!(&[#(#array),*])
4599    });
4600
4601    let ctx = EvaluationContext {
4602        compilation_unit,
4603        current_scope: EvaluationScope::Global(0.into()),
4604        generator_state: RustGeneratorContext {
4605            global_access: quote!(compile_error!("language rule can't access state")),
4606        },
4607        argument_types: &[Type::Int32],
4608    };
4609    let rules = translations.plural_rules.iter().map(|rule| {
4610        let rule = match rule {
4611            Some(rule) => {
4612                let rule = compile_expression(rule, &ctx);
4613                quote!(Some(|arg: i32| { let args = (arg,); (#rule) as usize } ))
4614            }
4615            None => quote!(None),
4616        };
4617        quote!(#rule)
4618    });
4619    let lang = translations.languages.iter().map(SmolStr::as_str).map(|lang| quote!(#lang));
4620
4621    quote!(
4622        const _SLINT_TRANSLATED_STRINGS: &[&[sp::Option<&str>]] = &[#(#strings),*];
4623        const _SLINT_TRANSLATED_STRINGS_PLURALS: &[&[sp::Option<&[&str]>]] = &[#(#plurals),*];
4624        #[allow(unused)]
4625        const _SLINT_TRANSLATED_PLURAL_RULES: &[sp::Option<fn(i32) -> usize>] = &[#(#rules),*];
4626        const _SLINT_BUNDLED_LANGUAGES: &[&str] = &[#(#lang),*];
4627    )
4628}