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 rescope rfold updt
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 super::accessor_names::{self, AccessorKind};
16use crate::CompilerConfiguration;
17use crate::diagnostics::SourceLocation;
18use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp, OperatorClass};
19use crate::langtype::{Enumeration, EnumerationValue, Struct, StructName, Type};
20use crate::layout::Orientation;
21use crate::llr::lower_expression::lower_constant_expression;
22use crate::llr::lower_layout_expression::{
23    CROSS_WIDTH_LOCAL, GRID_MEASURE_CHILD_INDEX_LOCAL, GRID_MEASURE_REPEATER_INDEX_LOCAL,
24    MEASURE_KNOWN_W_LOCAL,
25};
26use crate::llr::{
27    self, ArrayOutput, EvaluationContext as llr_EvaluationContext, EvaluationScope, Expression,
28    ParentScope, TypeResolutionContext as _,
29};
30use crate::object_tree::Document;
31use crate::typeloader::LibraryInfo;
32use itertools::Either;
33use proc_macro2::{Ident, TokenStream, TokenTree};
34use quote::{format_ident, quote};
35use smol_str::SmolStr;
36use std::collections::{BTreeMap, BTreeSet};
37use std::str::FromStr;
38
39#[derive(Clone)]
40struct RustGeneratorContext {
41    /// Path to the SharedGlobals structure that contains the global and the WindowAdapter
42    global_access: TokenStream,
43}
44
45type EvaluationContext<'a> = llr_EvaluationContext<'a, RustGeneratorContext>;
46
47pub fn ident(ident: &str) -> proc_macro2::Ident {
48    if ident.contains('-') {
49        format_ident!("r#{}", ident.replace('-', "_"))
50    } else {
51        format_ident!("r#{}", ident)
52    }
53}
54
55/// Returns the identifier used for the Property<()> that tracks when a
56/// callback handler is changed from native code.
57fn callback_tracker_ident(callback_name: &str) -> proc_macro2::Ident {
58    format_ident!("callback_tracker_{}", callback_name.replace('-', "_"))
59}
60
61impl quote::ToTokens for Orientation {
62    fn to_tokens(&self, tokens: &mut TokenStream) {
63        let tks = match self {
64            Orientation::Horizontal => {
65                quote!(sp::Orientation::Horizontal)
66            }
67            Orientation::Vertical => {
68                quote!(sp::Orientation::Vertical)
69            }
70        };
71        tokens.extend(tks);
72    }
73}
74
75impl quote::ToTokens for crate::embedded_resources::PixelFormat {
76    fn to_tokens(&self, tokens: &mut TokenStream) {
77        use crate::embedded_resources::PixelFormat::*;
78        let tks = match self {
79            Rgb => quote!(sp::TexturePixelFormat::Rgb),
80            Rgba => quote!(sp::TexturePixelFormat::Rgba),
81            RgbaPremultiplied => {
82                quote!(sp::TexturePixelFormat::RgbaPremultiplied)
83            }
84            AlphaMap(_) => quote!(sp::TexturePixelFormat::AlphaMap),
85        };
86        tokens.extend(tks);
87    }
88}
89
90pub fn rust_primitive_type(ty: &Type) -> Option<proc_macro2::TokenStream> {
91    match ty {
92        Type::Void => Some(quote!(())),
93        Type::Int32 => Some(quote!(i32)),
94        Type::Float32 => Some(quote!(f32)),
95        Type::String => Some(quote!(sp::SharedString)),
96        Type::Color => Some(quote!(sp::Color)),
97        Type::DataTransfer => Some(quote!(sp::DataTransfer)),
98        Type::Easing => Some(quote!(sp::EasingCurve)),
99        Type::MouseCursor => Some(quote!(sp::MouseCursorInner)),
100        Type::ComponentFactory => Some(quote!(slint::ComponentFactory)),
101        Type::Duration => Some(quote!(i64)),
102        Type::Angle => Some(quote!(f32)),
103        Type::PhysicalLength => Some(quote!(sp::Coord)),
104        Type::LogicalLength => Some(quote!(sp::Coord)),
105        Type::Rem => Some(quote!(f32)),
106        Type::Percent => Some(quote!(f32)),
107        Type::Bool => Some(quote!(bool)),
108        Type::Image => Some(quote!(sp::Image)),
109        Type::StyledText => Some(quote!(sp::StyledText)),
110        Type::Struct(s) => {
111            struct_name_to_tokens(&s.name).or_else(|| {
112                let elem =
113                    s.fields.values().map(rust_primitive_type).collect::<Option<Vec<_>>>()?;
114                // This will produce a tuple
115                Some(quote!((#(#elem,)*)))
116            })
117        }
118        Type::Array(o) => {
119            let inner = rust_primitive_type(o)?;
120            Some(quote!(sp::ModelRc<#inner>))
121        }
122        Type::Enumeration(e) => {
123            let i = ident(&e.name);
124            if e.node.is_some() { Some(quote!(#i)) } else { Some(quote!(sp::#i)) }
125        }
126        Type::Keys => Some(quote!(sp::Keys)),
127        Type::Brush => Some(quote!(slint::Brush)),
128        Type::LayoutCache => Some(quote!(
129            sp::SharedVector<
130                sp::Coord,
131            >
132        )),
133        Type::ArrayOfU16 => Some(quote!(
134            sp::SharedVector<
135                u16,
136            >
137        )),
138        _ => None,
139    }
140}
141
142fn rust_property_type(ty: &Type) -> Option<proc_macro2::TokenStream> {
143    match ty {
144        Type::LogicalLength => Some(quote!(sp::LogicalLength)),
145        Type::Easing => Some(quote!(sp::EasingCurve)),
146        Type::MouseCursor => Some(quote!(sp::MouseCursorInner)),
147        _ => rust_primitive_type(ty),
148    }
149}
150
151fn primitive_property_value(ty: &Type, property_accessor: MemberAccess) -> TokenStream {
152    primitive_value_from_property_value(ty, property_accessor.get_property())
153}
154
155fn primitive_value_from_property_value(ty: &Type, value: TokenStream) -> TokenStream {
156    match ty {
157        Type::LogicalLength => quote!(#value.get()),
158        _ => value,
159    }
160}
161
162fn set_primitive_property_value(ty: &Type, value_expression: TokenStream) -> TokenStream {
163    match ty {
164        Type::LogicalLength => {
165            let rust_ty = rust_primitive_type(ty).unwrap_or(quote!(_));
166            quote!(sp::LogicalLength::new(#value_expression as #rust_ty))
167        }
168        _ => value_expression,
169    }
170}
171
172/// Generate the rust code for the given component.
173pub fn generate(
174    doc: &Document,
175    compiler_config: &CompilerConfiguration,
176) -> std::io::Result<TokenStream> {
177    if std::env::var("SLINT_LIVE_PREVIEW").is_ok() {
178        return super::rust_live_preview::generate(doc, compiler_config);
179    }
180
181    let module_header = generate_module_header();
182    let qualified_name_ident = |symbol: &SmolStr, library_info: &LibraryInfo| {
183        let symbol = ident(symbol);
184        let package = ident(&library_info.package);
185        if let Some(module) = &library_info.module {
186            let module = ident(module);
187            quote!(#package :: #module :: #symbol)
188        } else {
189            quote!(#package :: #symbol)
190        }
191    };
192
193    let library_imports = {
194        let doc_used_types = doc.used_types.borrow();
195        doc_used_types
196            .library_types_imports
197            .iter()
198            .map(|(symbol, library_info)| {
199                let ident = qualified_name_ident(symbol, library_info);
200                quote!(
201                    #[allow(unused_imports)]
202                    pub use #ident;
203                )
204            })
205            .chain(doc_used_types.library_global_imports.iter().map(|(symbol, library_info)| {
206                let ident = qualified_name_ident(symbol, library_info);
207                let inner_symbol_name = smol_str::format_smolstr!("Inner{}", symbol);
208                let inner_ident = qualified_name_ident(&inner_symbol_name, library_info);
209                quote!(pub use #ident, #inner_ident;)
210            }))
211            .collect::<Vec<_>>()
212    };
213
214    let llr = crate::llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config);
215
216    if llr.public_components.is_empty() {
217        return Ok(Default::default());
218    }
219
220    let inner_module = generate_types(&doc.used_types.borrow().structs_and_enums, &llr);
221
222    let sub_compos = llr
223        .used_sub_components
224        .iter()
225        .map(|sub_compo| generate_sub_component(*sub_compo, &llr, None, None, false))
226        .collect::<Vec<_>>();
227    let public_components =
228        llr.public_components.iter().map(|p| generate_public_component(p, &llr, compiler_config));
229
230    let popup_menu =
231        llr.popup_menu.as_ref().map(|p| generate_item_tree(&p.item_tree, &llr, None, None, true));
232
233    let mut global_exports = Vec::<TokenStream>::new();
234    if let Some(library_name) = &compiler_config.library_name {
235        // Building as a library, SharedGlobals needs to be exported
236        let ident = format_ident!("{}SharedGlobals", library_name);
237        global_exports.push(quote!(SharedGlobals as #ident));
238    }
239    let globals =
240        llr.globals.iter_enumerated().filter(|(_, glob)| glob.must_generate()).map(
241            |(idx, glob)| generate_global(idx, glob, &llr, compiler_config, &mut global_exports),
242        );
243    let library_globals_getters = llr
244        .globals
245        .iter_enumerated()
246        .filter(|(_, glob)| glob.from_library)
247        .map(|(_idx, glob)| generate_global_getters(glob, &llr));
248    let shared_globals = generate_shared_globals(doc, &llr, compiler_config);
249    let globals_ids = llr.globals.iter().filter(|glob| glob.exported).flat_map(|glob| {
250        std::iter::once(ident(&glob.name)).chain(glob.aliases.iter().map(|x| ident(x)))
251    });
252    let compo_ids = llr.public_components.iter().map(|c| ident(&c.name));
253
254    let resource_symbols = generate_resources(doc);
255    // The inner module was meant to be internal private, but projects have been reaching into it
256    // so we can't change the name of this module
257    let generated_mod = doc
258        .last_exported_component()
259        .map(|c| format_ident!("slint_generated{}", ident(&c.id)))
260        .unwrap_or_else(|| format_ident!("slint_generated"));
261
262    let (type_reexports, deprecated_type_exports) = type_exports(&llr, &generated_mod);
263
264    #[cfg(not(feature = "bundle-translations"))]
265    let translations = quote!();
266    #[cfg(feature = "bundle-translations")]
267    let translations = llr.translations.as_ref().map(|t| generate_translations(t, &llr));
268
269    Ok(quote! {
270        mod #generated_mod {
271            #module_header
272            #(#library_imports)*
273            #inner_module
274            #(#globals)*
275            #(#library_globals_getters)*
276            #(#sub_compos)*
277            #popup_menu
278            #(#public_components)*
279            #shared_globals
280            #(#resource_symbols)*
281            #translations
282        }
283        #[allow(unused_imports)]
284        pub use #generated_mod::{#(#compo_ids,)* #(#type_reexports,)* #(#globals_ids,)* #(#global_exports,)*};
285        #(#deprecated_type_exports)*
286        #[allow(unused_imports)]
287        pub use slint::{ComponentHandle as _, Global as _, ModelExt as _};
288    })
289}
290
291pub(super) fn generate_module_header() -> TokenStream {
292    quote! {
293        #![allow(non_snake_case, non_camel_case_types)]
294        #![allow(unused_braces, unused_parens, dead_code)]
295        #![allow(clippy::all, clippy::pedantic, clippy::nursery)]
296        #![allow(unknown_lints, if_let_rescope, tail_expr_drop_order)] // We don't have fancy Drop
297
298        use slint::private_unstable_api::re_exports as sp;
299        #[allow(unused_imports)]
300        use sp::{RepeatedItemTree as _, ModelExt as _, Model as _, Float as _};
301    }
302}
303
304/// Generate the definitions of the structs and enums, as the inner module's token stream.
305pub fn generate_types(used_types: &[Type], unit: &llr::CompilationUnit) -> TokenStream {
306    let structs_and_enum_def = used_types.iter().filter_map(|ty| match ty {
307        Type::Struct(s) => match s.as_ref() {
308            the_struct @ Struct { name: StructName::User { .. }, .. } => {
309                Some(generate_struct(the_struct, unit))
310            }
311            _ => None,
312        },
313        Type::Enumeration(en) => Some(generate_enum(en)),
314        _ => None,
315    });
316
317    let version_check = format_ident!(
318        "VersionCheck_{}_{}_{}",
319        env!("CARGO_PKG_VERSION_MAJOR"),
320        env!("CARGO_PKG_VERSION_MINOR"),
321        env!("CARGO_PKG_VERSION_PATCH"),
322    );
323
324    quote! {
325        #(#structs_and_enum_def)*
326        const _THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME : slint::#version_check = slint::#version_check;
327    }
328}
329
330/// Render the re-exports of [`llr::CompilationUnit::type_exports`]: `pub use` for a type
331/// exposed under a name, and a deprecated `pub type` alias for one kept only for backward
332/// compatibility. Returns the `pub use` items (spliced into the module's use list) and the
333/// deprecated aliases (emitted as standalone items).
334pub(super) fn type_exports(
335    unit: &llr::CompilationUnit,
336    generated_mod: &Ident,
337) -> (Vec<TokenStream>, Vec<TokenStream>) {
338    let mut reexports = Vec::new();
339    let mut deprecated_type_exports = Vec::new();
340    for e in &unit.type_exports {
341        let exported = ident(&e.exported_name);
342        let internal = ident(&e.internal_name);
343        if let Some(note) = e.deprecation_note() {
344            // A `#[deprecated] pub use` does not warn on use, but a deprecated type alias does.
345            deprecated_type_exports.push(quote! {
346                #[deprecated(note = #note)]
347                #[allow(dead_code)]
348                pub type #exported = #generated_mod::#internal;
349            });
350        } else if e.is_alias() {
351            reexports.push(quote!(#internal as #exported));
352        } else {
353            reexports.push(quote!(#exported));
354        }
355    }
356    (reexports, deprecated_type_exports)
357}
358
359fn generate_public_component(
360    llr: &llr::PublicComponent,
361    unit: &llr::CompilationUnit,
362    compiler_config: &CompilerConfiguration,
363) -> TokenStream {
364    let public_component_id = ident(&llr.name);
365    let inner_component_id = inner_component_id(&unit.sub_components[llr.item_tree.root]);
366
367    let component = generate_item_tree(&llr.item_tree, unit, None, None, false);
368
369    let ctx = EvaluationContext {
370        compilation_unit: unit,
371        current_scope: EvaluationScope::SubComponent(llr.item_tree.root, None),
372        generator_state: RustGeneratorContext { global_access: quote!(_self.globals()) },
373        argument_types: &[],
374    };
375
376    let property_and_callback_accessors = public_api(
377        &llr.public_properties,
378        &llr.private_properties,
379        quote!(sp::VRc::as_pin_ref(&self.0)),
380        &ctx,
381    );
382
383    // The SystemTrayIcon native item sits as item 0 of the root sub-component when the
384    // public component inherits SystemTrayIcon.
385    let tray_field =
386        matches!(llr.top_level_type, llr::TopLevelComponentType::SystemTrayIcon).then(|| {
387            let tray_item =
388                &unit.sub_components[llr.item_tree.root].items[llr::ItemInstanceIdx::from(0usize)];
389            debug_assert_eq!(
390                tray_item.ty.class_name.as_str(),
391                "SystemTrayIcon",
392                "TopLevelComponentType::SystemTrayIcon expects the root item to be a SystemTrayIcon"
393            );
394            ident(&tray_item.name)
395        });
396
397    // SystemTrayIcon-rooted components don't have a `WindowAdapter`. Skip the
398    // eager creation calls in `new` / `new_with_context` so instantiating
399    // a tray doesn't spin up a hidden window adapter as a side effect.
400    let (eager_create_window, init_with_context, ensure_tree_instantiated): (
401        Option<TokenStream>,
402        TokenStream,
403        Option<TokenStream>,
404    ) = match llr.top_level_type {
405        llr::TopLevelComponentType::Window => (
406            Some(quote!(
407                // ensure that the window exist as this point so further call to window() don't panic
408                inner.globals.get().unwrap().window_adapter_ref()?;
409            )),
410            quote!(inner.globals.get().unwrap().create_window_from_context(ctx)?;),
411            Some(quote!(
412                let window = inner.globals.get().unwrap().window_adapter_ref()?;
413                sp::WindowInner::from_pub(window.window()).ensure_tree_instantiated();
414            )),
415        ),
416        llr::TopLevelComponentType::SystemTrayIcon => {
417            let tray_field = tray_field.as_ref().unwrap();
418            (
419                None,
420                // A tray has no window to reach a context through, so hand it over here.
421                quote!(
422                    #inner_component_id::FIELD_OFFSETS
423                        .#tray_field()
424                        .apply_pin(sp::VRc::as_pin_ref(&inner))
425                        .set_context(&ctx);
426                ),
427                None,
428            )
429        }
430    };
431
432    #[cfg(feature = "bundle-translations")]
433    let init_bundle_translations = unit.translations.as_ref().map(|_| {
434        quote!(
435            sp::set_bundled_languages(_SLINT_BUNDLED_TRANSLATIONS);
436        )
437    });
438    #[cfg(not(feature = "bundle-translations"))]
439    let init_bundle_translations = quote!();
440
441    let experimental = compiler_config.enable_experimental;
442
443    let new_with_existing_window_impl: Option<TokenStream> = match llr.top_level_type {
444        llr::TopLevelComponentType::Window => Some(quote!(
445            #[cfg(#experimental)]
446            pub fn new_with_existing_window(window: &slint::Window) -> ::core::result::Result<Self, slint::PlatformError> {
447                slint::private_unstable_api::ensure_backend()?;
448                let inner = #inner_component_id::new()?;
449                #init_bundle_translations
450                inner.globals.get().unwrap().create_window_from_existing(window)?;
451                #inner_component_id::user_init(sp::VRc::map(inner.clone(), |x| x));
452                #ensure_tree_instantiated
453                ::core::result::Result::Ok(Self(inner))
454            }
455        )),
456        llr::TopLevelComponentType::SystemTrayIcon => None,
457    };
458
459    // Window-rooted components get the full `ComponentHandle` impl. SystemTrayIcon
460    // gets an inherent impl with no `window()` accessor: a tray icon is not a
461    // `slint::Window` and the previous accessor's body would panic at runtime.
462    let handle_impl = {
463        let common = |vis: TokenStream| {
464            quote!(
465                #vis fn as_weak(&self) -> slint::Weak<Self> {
466                    slint::Weak::new(sp::VRc::downgrade(&self.0))
467                }
468
469                #vis fn clone_strong(&self) -> Self {
470                    Self(self.0.clone())
471                }
472
473                #vis fn global<'a, T: slint::Global<'a, Self>>(&'a self) -> T {
474                    T::get(&self)
475                }
476            )
477        };
478        match llr.top_level_type {
479            llr::TopLevelComponentType::Window => {
480                let common = common(quote!());
481                quote!(
482                    impl slint::ComponentHandle for #public_component_id {
483                        #common
484
485                        fn run(&self) -> ::core::result::Result<(), slint::PlatformError> {
486                            self.show()?;
487                            sp::WindowInner::from_pub(self.window()).context().run_event_loop()?;
488                            self.hide()?;
489                            ::core::result::Result::Ok(())
490                        }
491
492                        fn show(&self) -> ::core::result::Result<(), slint::PlatformError> {
493                            self.0.globals.get().unwrap().window_adapter_ref()?.window().show()
494                        }
495
496                        fn hide(&self) -> ::core::result::Result<(), slint::PlatformError> {
497                            self.0.globals.get().unwrap().window_adapter_ref()?.window().hide()
498                        }
499
500                        fn window(&self) -> &slint::Window {
501                            self.0.globals.get().unwrap().window_adapter_ref().unwrap().window()
502                        }
503                    }
504                )
505            }
506            llr::TopLevelComponentType::SystemTrayIcon => {
507                let tray_field = tray_field.as_ref().unwrap();
508                let common = common(quote!(pub));
509                // No `run()`: a tray icon doesn't drive the event loop. `show`/`hide`
510                // toggle the `visible` property; the platform side of the change
511                // tracker turns that into a real show/hide of the OS tray icon.
512                quote!(
513                    impl #public_component_id {
514                        #common
515
516                        pub fn show(&self) -> ::core::result::Result<(), slint::PlatformError> {
517                            let _self = sp::VRc::as_pin_ref(&self.0);
518                            #inner_component_id::FIELD_OFFSETS.#tray_field()
519                                .apply_pin(_self)
520                                .visible
521                                .set(true);
522                            ::core::result::Result::Ok(())
523                        }
524
525                        pub fn hide(&self) -> ::core::result::Result<(), slint::PlatformError> {
526                            let _self = sp::VRc::as_pin_ref(&self.0);
527                            #inner_component_id::FIELD_OFFSETS.#tray_field()
528                                .apply_pin(_self)
529                                .visible
530                                .set(false);
531                            ::core::result::Result::Ok(())
532                        }
533                    }
534                )
535            }
536        }
537    };
538
539    quote!(
540        #component
541        pub struct #public_component_id(sp::VRc<sp::ItemTreeVTable, #inner_component_id>);
542
543        impl #public_component_id {
544            pub fn new() -> ::core::result::Result<Self, slint::PlatformError> {
545                slint::private_unstable_api::ensure_backend()?;
546                let inner = #inner_component_id::new()?;
547                #init_bundle_translations
548                #eager_create_window
549                #inner_component_id::user_init(sp::VRc::map(inner.clone(), |x| x));
550                #ensure_tree_instantiated
551                ::core::result::Result::Ok(Self(inner))
552            }
553
554            #[cfg(#experimental)]
555            pub fn new_with_context(ctx: sp::SlintContext) -> ::core::result::Result<Self, slint::PlatformError> {
556                let inner = #inner_component_id::new()?;
557                #init_bundle_translations
558
559                #init_with_context
560
561                #inner_component_id::user_init(sp::VRc::map(inner.clone(), |x| x));
562                #ensure_tree_instantiated
563                ::core::result::Result::Ok(Self(inner))
564            }
565
566            #new_with_existing_window_impl
567
568            #property_and_callback_accessors
569        }
570
571        impl From<#public_component_id> for sp::VRc<sp::ItemTreeVTable, #inner_component_id> {
572            fn from(value: #public_component_id) -> Self {
573                value.0
574            }
575        }
576
577        impl slint::StrongHandle for #public_component_id {
578            type WeakInner = sp::VWeak<sp::ItemTreeVTable, #inner_component_id>;
579
580            fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> sp::Option<Self> {
581                sp::Some(Self(inner.upgrade()?))
582            }
583        }
584
585        #handle_impl
586    )
587}
588
589fn generate_shared_globals(
590    doc: &Document,
591    llr: &llr::CompilationUnit,
592    compiler_config: &CompilerConfiguration,
593) -> TokenStream {
594    let global_names = llr
595        .globals
596        .iter()
597        .filter(|g| g.must_generate())
598        .map(|g| format_ident!("global_{}", ident(&g.name)))
599        .collect::<Vec<_>>();
600    let global_types =
601        llr.globals.iter().filter(|g| g.must_generate()).map(global_inner_name).collect::<Vec<_>>();
602
603    let from_library_global_names = llr
604        .globals
605        .iter()
606        .filter(|g| g.from_library)
607        .map(|g| format_ident!("global_{}", ident(&g.name)))
608        .collect::<Vec<_>>();
609
610    let from_library_global_types =
611        llr.globals.iter().filter(|g| g.from_library).map(global_inner_name).collect::<Vec<_>>();
612    let apply_constant_scale_factor = compiler_config.const_scale_factor.map(|factor| {
613        quote!(sp::WindowInner::from_pub(adapter.window()).set_const_scale_factor(#factor);)
614    });
615
616    let library_global_vars = llr
617        .globals
618        .iter()
619        .filter(|g| g.from_library)
620        .map(|g| {
621            let library_info = doc.library_exports.get(g.name.as_str()).unwrap();
622            let shared_globals_var_name =
623                format_ident!("library_{}_shared_globals", library_info.name);
624            let global_name = format_ident!("global_{}", ident(&g.name));
625            quote!( #shared_globals_var_name.#global_name )
626        })
627        .collect::<Vec<_>>();
628    let pub_token = if compiler_config.library_name.is_some() { quote!(pub) } else { quote!() };
629
630    let experimental = compiler_config.enable_experimental;
631
632    let (library_shared_globals_names, library_shared_globals_types): (Vec<_>, Vec<_>) = doc
633        .imports
634        .iter()
635        .filter_map(|import| import.library_info.clone())
636        .map(|library_info| {
637            let struct_name = format_ident!("{}SharedGlobals", library_info.name);
638            let shared_globals_var_name =
639                format_ident!("library_{}_shared_globals", library_info.name);
640            let shared_globals_type_name = if let Some(module) = library_info.module {
641                let package = ident(&library_info.package);
642                let module = ident(&module);
643                //(quote!(#shared_globals_var_name),quote!(let #shared_globals_var_name = #package::#module::#shared_globals_type_name::new(root_item_tree_weak.clone());))
644                quote!(#package::#module::#struct_name)
645            } else {
646                let package = ident(&library_info.package);
647                quote!(#package::#struct_name)
648            };
649            (quote!(#shared_globals_var_name), shared_globals_type_name)
650        })
651        .unzip();
652
653    let needs_window_adapter = llr.needs_window_adapter();
654
655    // `create_window_from_context` is only invoked from a Window-rooted
656    // public component's `new_with_context`, and `maybe_window_adapter_impl`
657    // is only invoked from per-tree `register_item_tree` / PinnedDrop hooks
658    // — both gated out for tray-only units. Emit them only when something
659    // actually calls them; otherwise `#![deny(warnings)]` builds (e.g.
660    // test-driver-rust with `--features build-time`) trip on dead_code.
661    // `window_adapter_impl` / `window_adapter_ref` are kept unconditionally
662    // because expression codegen (layout-info, font metrics) still
663    // references them on every tree.
664    let optional_window_adapter_helpers = needs_window_adapter.then(|| {
665        quote!(
666            #[cfg(#experimental)]
667            fn create_window_from_context(&self, ctx: sp::SlintContext) -> sp::Result<(), slint::PlatformError> {
668                let adapter = ctx.platform().create_window_adapter()?;
669                sp::WindowInner::from_pub(adapter.window()).set_context(ctx);
670                let root_rc = self.root_item_tree_weak.upgrade().unwrap();
671                sp::WindowInner::from_pub(adapter.window()).set_component(&root_rc);
672                #apply_constant_scale_factor
673                self.window_adapter.set(adapter).map_err(|_|()).expect("The window shouldn't be initialized before this call");
674                sp::Ok(())
675            }
676
677            #[cfg(#experimental)]
678            fn create_window_from_existing(&self, window: &slint::Window) -> sp::Result<(), slint::PlatformError> {
679                let adapter = sp::WindowInner::from_pub(window).window_adapter();
680                let root_rc = self.root_item_tree_weak.upgrade().unwrap();
681                sp::WindowInner::from_pub(adapter.window()).set_component(&root_rc);
682                #apply_constant_scale_factor
683                self.window_adapter.set(adapter).map_err(|_|()).expect("The window shouldn't be initialized before this call");
684                sp::Ok(())
685            }
686
687            fn maybe_window_adapter_impl(&self) -> sp::Option<sp::Rc<dyn sp::WindowAdapter>> {
688                self.window_adapter.get().cloned()
689            }
690        )
691    });
692
693    quote! {
694        #pub_token struct SharedGlobals {
695            #(#pub_token #global_names : ::core::pin::Pin<sp::Rc<#global_types>>,)*
696            #(#pub_token #from_library_global_names : ::core::pin::Pin<sp::Rc<#from_library_global_types>>,)*
697            window_adapter : sp::OnceCell<sp::WindowAdapterRc>,
698            root_item_tree_weak : sp::VWeak<sp::ItemTreeVTable>,
699            #(#[allow(dead_code)]
700            #library_shared_globals_names : sp::Rc<#library_shared_globals_types>,)*
701        }
702        impl SharedGlobals {
703            #pub_token fn new(root_item_tree_weak : sp::VWeak<sp::ItemTreeVTable>) -> sp::Rc<Self> {
704                #(let #library_shared_globals_names = #library_shared_globals_types::new(root_item_tree_weak.clone());)*
705                sp::Rc::new(Self {
706                    #(#global_names : #global_types::new(),)*
707                    #(#from_library_global_names : #library_global_vars.clone(),)*
708                    window_adapter : ::core::default::Default::default(),
709                    root_item_tree_weak,
710                    #(#library_shared_globals_names,)*
711                })
712            }
713
714            // Run the eager initialization of the globals. This must be called only *after* the
715            // root component's `globals` field has been set (see the root component's `new`),
716            // because a global's init may evaluate a binding (e.g. `Palette.color-scheme`) that
717            // resolves the root's window adapter through `globals`, which would otherwise panic.
718            #pub_token fn init_globals(self: &sp::Rc<Self>) {
719                #(self.#library_shared_globals_names.init_globals();)*
720                #(self.#global_names.clone().init(self);)*
721            }
722
723            // Clone the SharedGlobals struct but use a different window adapter. This is for example used for popup windows, because they need access to the globals, but need their own window adapter
724            #[allow(dead_code)]
725            #pub_token fn clone_with_window_adapter(&self, window_adapter: sp::WindowAdapterRc) -> sp::Rc<Self> {
726                sp::Rc::new(Self {
727                    #(#global_names : self.#global_names.clone(),)*
728                    #(#from_library_global_names : self.#from_library_global_names.clone(),)*
729                    window_adapter: window_adapter.into(),
730                    // `root_item_tree_weak` is only used to init the window_adapter. Since we have the window_adapter here already we don't need this variable
731                    root_item_tree_weak: ::core::default::Default::default(),
732                    #(#library_shared_globals_names: self.#library_shared_globals_names.clone(),)*
733                })
734            }
735
736            fn window_adapter_impl(&self) -> sp::Rc<dyn sp::WindowAdapter> {
737                sp::Rc::clone(self.window_adapter_ref().unwrap())
738            }
739
740            fn window_adapter_ref(&self) -> sp::Result<&sp::Rc<dyn sp::WindowAdapter>, slint::PlatformError>
741            {
742                self.window_adapter.get_or_try_init(|| {
743                    let adapter = slint::private_unstable_api::create_window_adapter()?;
744                    let root_rc = self.root_item_tree_weak.upgrade().unwrap();
745                    sp::WindowInner::from_pub(adapter.window()).set_component(&root_rc);
746                    #apply_constant_scale_factor
747                    ::core::result::Result::Ok(adapter)
748                })
749            }
750
751            #optional_window_adapter_helpers
752        }
753    }
754}
755
756/// Compile the `@rust-attr(...)` captured on a struct or enum declaration into outer
757/// attributes, emitting a `compile_error!` for any whose text does not tokenize.
758fn rust_attributes_tokens(
759    attributes: &[SmolStr],
760    kind: &str,
761    name: &SmolStr,
762    node: Option<&SourceLocation>,
763) -> TokenStream {
764    let attrs = attributes.iter().map(|attr| match TokenStream::from_str(attr) {
765        Ok(t) => quote!(#[#t]),
766        Err(_) => {
767            let source_location = node.cloned().unwrap_or_default();
768            let error = format!(
769                "Error parsing @rust-attr for {kind} '{name}' declared at {source_location}"
770            );
771            quote!(compile_error!(#error);)
772        }
773    });
774    quote! { #(#attrs)* }
775}
776
777fn generate_struct(the_struct: &Struct, unit: &llr::CompilationUnit) -> TokenStream {
778    let component_id = struct_name_to_tokens(&the_struct.name).unwrap();
779    let (declared_property_vars, declared_property_types): (Vec<_>, Vec<_>) = the_struct
780        .fields
781        .iter()
782        .map(|(name, ty)| (ident(name), rust_primitive_type(ty).unwrap()))
783        .unzip();
784
785    let StructName::User { name, .. } = &the_struct.name else {
786        unreachable!("generating non-user struct")
787    };
788
789    let attributes =
790        rust_attributes_tokens(the_struct.rust_attributes(), "struct", name, the_struct.node());
791
792    // With user-declared field default values, `Default` cannot be derived anymore
793    let default_impl = (!the_struct.field_defaults.is_empty()).then(|| {
794        // Constant expressions cannot access the globals; make sure a bug in that
795        // assumption breaks the build of the generated code with a clear message
796        let ctx = EvaluationContext::new_const(
797            unit,
798            RustGeneratorContext {
799                global_access: quote!(compile_error!("no global access in a constant expression")),
800            },
801        );
802        let (field_names, field_values): (Vec<_>, Vec<_>) = the_struct
803            .fields
804            .keys()
805            .map(|field_name| {
806                let value = match the_struct.field_defaults.get(field_name) {
807                    Some(expr) => {
808                        let value = compile_expression(&lower_constant_expression(expr), &ctx);
809                        quote!(#value as _)
810                    }
811                    None => quote!(::core::default::Default::default()),
812                };
813                (ident(field_name), value)
814            })
815            .unzip();
816        quote! {
817            impl ::core::default::Default for #component_id {
818                fn default() -> Self {
819                    Self { #(#field_names: #field_values,)* }
820                }
821            }
822        }
823    });
824    let default_derive = default_impl.is_none().then(|| quote!(Default,));
825
826    quote! {
827        #attributes
828        #[derive(#default_derive PartialEq, Debug, Clone)]
829        pub struct #component_id {
830            #(pub #declared_property_vars : #declared_property_types),*
831        }
832        #default_impl
833    }
834}
835
836fn generate_enum(en: &std::sync::Arc<Enumeration>) -> TokenStream {
837    let enum_name = ident(&en.name);
838
839    let enum_values = (0..en.values.len()).map(|value| {
840        let i = ident(&EnumerationValue { value, enumeration: en.clone() }.to_pascal_case());
841        if value == en.default_value { quote!(#[default] #i) } else { quote!(#i) }
842    });
843    let attributes =
844        rust_attributes_tokens(&en.rust_attributes, "enum", &en.name, en.node.as_ref());
845    quote! {
846        #attributes
847        #[allow(dead_code)]
848        #[derive(Default, Copy, Clone, PartialEq, Debug)]
849        pub enum #enum_name {
850            #(#enum_values,)*
851        }
852    }
853}
854
855/// Walk `field_access` on `root_ty`, producing the Rust access suffix
856/// (e.g. `.foo.bar`) and the type of the leaf field.
857fn lower_field_access_chain(root_ty: &Type, field_access: &[SmolStr]) -> (TokenStream, Type) {
858    let mut access = quote!();
859    let mut ty = root_ty;
860    for f in field_access {
861        let Type::Struct(s) = ty else { panic!("Field of two way binding on a non-struct type") };
862        let a = struct_field_access(s, f);
863        access.extend(quote!(.#a));
864        ty = s.fields.get(f).unwrap();
865    }
866    (access, ty.clone())
867}
868
869/// Emit a `link_two_way_to_model_data` call wiring `p1` to a row of the
870/// model described by `info`, optionally through a struct `field_access`.
871fn generate_model_two_way_binding(
872    ctx: &EvaluationContext,
873    info: &llr::ResolvedModelTwoWayBinding,
874    p1: &TokenStream,
875    field_access: &[SmolStr],
876) -> TokenStream {
877    let body_sc = &ctx.compilation_unit.sub_components[info.body_sub_component];
878    let parent_sc = &ctx.compilation_unit.sub_components[info.parent_sub_component];
879    let body_id = self::inner_component_id(body_sc);
880    let data_f =
881        access_component_field_offset(&body_id, &ident(&body_sc.properties[info.data_prop].name));
882    let index_f =
883        access_component_field_offset(&body_id, &ident(&body_sc.properties[info.index_prop].name));
884    let repeater = access_component_field_offset(
885        &self::inner_component_id(parent_sc),
886        &format_ident!("repeater{}", usize::from(info.repeater_index)),
887    );
888
889    let item_tree_weak = if info.parent_level == 0 {
890        quote!(sp::VRcMapped::downgrade(&self_rc))
891    } else {
892        let mut e = quote!(_self.parent.clone());
893        for _ in 1..info.parent_level {
894            e = quote!(#e.upgrade().unwrap().parent.clone());
895        }
896        e
897    };
898
899    // The leaf field type may differ from the property type (e.g. struct
900    // field `f32` vs property `LogicalLength`); apply the usual conversions.
901    let (access_model, getter_value) = if field_access.is_empty() {
902        (quote!(let data = value.clone();), quote!(#data_f.apply_pin(x.as_pin_ref()).get()))
903    } else {
904        let (access, ty) = lower_field_access_chain(info.data_prop_ty, field_access);
905        let to_struct_value = primitive_value_from_property_value(&ty, quote!(value.clone()));
906        let to_property_value = set_primitive_property_value(
907            &ty,
908            quote!(#data_f.apply_pin(x.as_pin_ref()).get() #access .clone()),
909        );
910        (
911            quote! {
912                let mut data = #data_f.apply_pin(x.as_pin_ref()).get();
913                data #access = #to_struct_value;
914            },
915            to_property_value,
916        )
917    };
918
919    quote! { sp::Property::link_two_way_to_model_data(#p1, #item_tree_weak,
920        |item_tree_weak| item_tree_weak.upgrade().map(|x| #getter_value),
921        |item_tree_weak, value| {
922            if let Some(x) = item_tree_weak.upgrade() {
923                if let Some(parent) = x.parent.upgrade() {
924                    let index = #index_f.apply_pin(x.as_pin_ref()).get();
925                    #access_model
926                    #repeater.apply_pin(parent.as_pin_ref()).model_set_row_data(index as usize, data);
927                }
928            }
929        }
930    )}
931}
932
933fn handle_property_init(
934    prop: &llr::MemberReference,
935    binding_expression: &llr::BindingExpression,
936    init: &mut Vec<TokenStream>,
937    ctx: &EvaluationContext,
938) {
939    let rust_property = access_member(prop, ctx).unwrap();
940    let prop_type = ctx.property_ty(prop);
941
942    // Components use the type-erased helpers, so the binding machinery is
943    // instantiated once per property type instead of per (property type,
944    // component). Globals are behind a plain Rc without a type-erased weak
945    // form and keep the helpers that are generic over the component.
946    let erased = ctx.current_global().is_none();
947    // A closure evaluating `body` on the component, in the form expected by
948    // the selected helper. Optionally takes extra arguments.
949    let self_closure = |args: TokenStream, body: TokenStream| {
950        if erased {
951            quote!(move |_self #args| #body)
952        } else {
953            quote!(move |self_rc #args| { let _self = self_rc.as_ref(); #body })
954        }
955    };
956    let helper = |name: &str| {
957        if erased {
958            let name = format_ident!("{name}_erased");
959            quote!(sp::#name)
960        } else {
961            let name = format_ident!("{name}");
962            quote!(slint::private_unstable_api::#name)
963        }
964    };
965
966    if let Type::Callback(callback) = &prop_type {
967        let mut ctx2 = ctx.clone();
968        ctx2.argument_types = &callback.args;
969        let tokens_for_expression =
970            compile_expression(&binding_expression.expression.borrow(), &ctx2);
971        let as_ = if matches!(callback.return_type, Type::Void) { quote!(;) } else { quote!(as _) };
972        let set_callback_handler = helper("set_callback_handler");
973        let handler = self_closure(quote!(, args), quote!({ (#tokens_for_expression) #as_ }));
974        init.push(quote!({
975            #[allow(unreachable_code, unused)]
976            #set_callback_handler(#rust_property, &self_rc, { #handler });
977        }));
978    } else {
979        let tokens_for_expression =
980            compile_expression(&binding_expression.expression.borrow(), ctx);
981
982        let tokens_for_expression = set_primitive_property_value(prop_type, tokens_for_expression);
983
984        // Cast to the property type unless the Rust code is the never type, as with return
985        // statements inside a block the type of the return expression is `()` instead of `!`.
986        let maybe_cast = if binding_expression.expression.borrow().ty(ctx) == Type::Invalid {
987            None
988        } else {
989            Some(quote!(as _))
990        };
991        // The erased helpers take functions with an extra unused `&()` argument, so that
992        // erasing them does not change their arity. The global (non-erased) helpers don't.
993        let unit_arg = if erased { quote!(, _: &()) } else { quote!() };
994        init.push(match binding_expression.kind {
995            llr::BindingKind::Constant => {
996                let t = rust_property_type(prop_type).unwrap_or(quote!(_));
997                quote! { #rust_property.set({ (#tokens_for_expression) as #t }); }
998            }
999            llr::BindingKind::State => {
1000                let binding_tokens =
1001                    self_closure(unit_arg.clone(), quote!((#tokens_for_expression) #maybe_cast));
1002                let set_property_state_binding = helper("set_property_state_binding");
1003                quote! { {
1004                    #set_property_state_binding(#rust_property, &self_rc, #binding_tokens);
1005                } }
1006            }
1007            llr::BindingKind::Normal => {
1008                let binding_tokens =
1009                    self_closure(unit_arg.clone(), quote!((#tokens_for_expression) #maybe_cast));
1010                match &binding_expression.animation {
1011                    Some(llr::Animation::Static(anim)) => {
1012                        let anim = compile_expression(anim, ctx);
1013                        let set_animated_property_binding = helper("set_animated_property_binding");
1014                        let details = self_closure(unit_arg.clone(), quote!((#anim, None)));
1015                        quote! { {
1016                            #set_animated_property_binding(
1017                                #rust_property, &self_rc, #binding_tokens, #details);
1018                        } }
1019                    }
1020                    Some(llr::Animation::Transition(animation)) => {
1021                        let animation = compile_expression(animation, ctx);
1022                        let set_animated_property_binding = helper("set_animated_property_binding");
1023                        let details = self_closure(
1024                            unit_arg.clone(),
1025                            quote!({
1026                                let (animation, change_time) = #animation;
1027                                (animation, Some(change_time))
1028                            }),
1029                        );
1030                        quote! { {
1031                            #set_animated_property_binding(
1032                                #rust_property, &self_rc, #binding_tokens, #details);
1033                        } }
1034                    }
1035                    None => {
1036                        let set_property_binding = helper("set_property_binding");
1037                        quote! { {
1038                            #set_property_binding(#rust_property, &self_rc, #binding_tokens);
1039                        } }
1040                    }
1041                }
1042            }
1043        });
1044    }
1045}
1046
1047/// Token stream that walks up `parent_level` parent pointers (`_self.parent.upgrade()...`),
1048/// or `None` when `parent_level` is 0 (the member lives on the current component).
1049fn parent_access_path(parent_level: usize) -> Option<TokenStream> {
1050    (parent_level != 0).then(|| {
1051        let mut path = quote!(_self.parent.upgrade());
1052        for _ in 1..parent_level {
1053            path = quote!(#path.and_then(|x| x.parent.upgrade()));
1054        }
1055        path
1056    })
1057}
1058
1059/// Returns the code to access the change-tracker `Property<()>` for an exported callback.
1060/// Returns `None` if the callback doesn't have a tracker.
1061fn access_callback_tracker(
1062    reference: &llr::MemberReference,
1063    ctx: &EvaluationContext,
1064) -> Option<MemberAccess> {
1065    fn in_global(
1066        g: &llr::GlobalComponent,
1067        callback_idx: &llr::CallbackIdx,
1068        _self: TokenStream,
1069    ) -> Option<MemberAccess> {
1070        if !g.callbacks[*callback_idx].needs_tracker {
1071            return None;
1072        }
1073        let tracker_name = callback_tracker_ident(&g.callbacks[*callback_idx].name);
1074        let global_name = global_inner_name(g);
1075        let tracker_field = quote!({ *&#global_name::FIELD_OFFSETS.#tracker_name() });
1076        Some(MemberAccess::Direct(quote!(#tracker_field.apply_pin(#_self))))
1077    }
1078
1079    match reference {
1080        llr::MemberReference::Global {
1081            global_index,
1082            member: llr::LocalMemberIndex::Callback(callback_idx),
1083        } => {
1084            let global = &ctx.compilation_unit.globals[*global_index];
1085            let s = if matches!(ctx.current_scope, EvaluationScope::Global(i) if i == *global_index)
1086            {
1087                quote!(_self)
1088            } else {
1089                let global_access = &ctx.generator_state.global_access;
1090                let global_id = format_ident!("global_{}", ident(&global.name));
1091                quote!(#global_access.#global_id.as_ref())
1092            };
1093            in_global(global, callback_idx, s)
1094        }
1095        llr::MemberReference::Relative { parent_level, local_reference } => {
1096            let llr::LocalMemberIndex::Callback(callback_idx) = &local_reference.reference else {
1097                return None;
1098            };
1099            if let Some(current_global) = ctx.current_global() {
1100                return in_global(current_global, callback_idx, quote!(_self));
1101            }
1102            let sc_idx = ctx.parent_sub_component_idx(*parent_level)?;
1103            let (compo_path, sub_component) = follow_sub_component_path(
1104                ctx.compilation_unit,
1105                sc_idx,
1106                &local_reference.sub_component_path,
1107            );
1108            if !sub_component.callbacks[*callback_idx].needs_tracker {
1109                return None;
1110            }
1111            let tracker_name = callback_tracker_ident(&sub_component.callbacks[*callback_idx].name);
1112            let component_id = inner_component_id(sub_component);
1113            let tracker_field = access_component_field_offset(&component_id, &tracker_name);
1114
1115            let parent_path = parent_access_path(*parent_level);
1116            Some(parent_path.map_or_else(
1117                || MemberAccess::Direct(quote!((#compo_path #tracker_field).apply_pin(_self))),
1118                |parent_path| {
1119                    MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #tracker_field).apply_pin(x.as_pin_ref()))))
1120                },
1121            ))
1122        }
1123        _ => None,
1124    }
1125}
1126
1127/// Public API for Global and root component
1128fn public_api(
1129    public_properties: &llr::PublicProperties,
1130    private_properties: &llr::PrivateProperties,
1131    self_init: TokenStream,
1132    ctx: &EvaluationContext,
1133) -> TokenStream {
1134    let mut property_and_callback_accessors: Vec<TokenStream> = Vec::new();
1135    for (name, p) in public_properties {
1136        let prop = access_member(&p.prop, ctx).unwrap();
1137
1138        if let Type::Callback(callback) = &p.ty {
1139            let callback_args =
1140                callback.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
1141            let return_type = rust_primitive_type(&callback.return_type).unwrap();
1142            let args_name =
1143                (0..callback.args.len()).map(|i| format_ident!("arg_{}", i)).collect::<Vec<_>>();
1144            let caller_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Invoker);
1145            property_and_callback_accessors.push(quote!(
1146                #[allow(dead_code)]
1147                pub fn #caller_ident(&self, #(#args_name : #callback_args,)*) -> #return_type {
1148                    let _self = #self_init;
1149                    #prop.call(&(#(#args_name,)*))
1150                }
1151            ));
1152            let on_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Handler);
1153            let args_index = (0..callback_args.len()).map(proc_macro2::Literal::usize_unsuffixed);
1154            let set_dirty = access_callback_tracker(&p.prop, ctx)
1155                .map(|t| t.then(|t| quote!({ #t.mark_dirty(); })));
1156            property_and_callback_accessors.push(quote!(
1157                #[allow(dead_code)]
1158                pub fn #on_ident(&self, mut f: impl FnMut(#(#callback_args),*) -> #return_type + 'static) {
1159                    let _self = #self_init;
1160                    #[allow(unused)]
1161                    #prop.set_handler(
1162                        // FIXME: why do i need to clone here?
1163                        move |args| f(#(args.#args_index.clone()),*)
1164                    );
1165                    #set_dirty
1166                }
1167            ));
1168        } else if let Type::Function(function) = &p.ty {
1169            let callback_args =
1170                function.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
1171            let return_type = rust_primitive_type(&function.return_type).unwrap();
1172            let args_name =
1173                (0..function.args.len()).map(|i| format_ident!("arg_{}", i)).collect::<Vec<_>>();
1174            let caller_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Invoker);
1175            property_and_callback_accessors.push(quote!(
1176                #[allow(dead_code)]
1177                pub fn #caller_ident(&self, #(#args_name : #callback_args,)*) -> #return_type {
1178                    let _self = #self_init;
1179                    #prop(#(#args_name,)*)
1180                }
1181            ));
1182        } else {
1183            let rust_property_type = rust_primitive_type(&p.ty).unwrap();
1184
1185            let getter_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Getter);
1186
1187            let prop_expression = primitive_property_value(&p.ty, MemberAccess::Direct(prop));
1188
1189            property_and_callback_accessors.push(quote!(
1190                #[allow(dead_code)]
1191                pub fn #getter_ident(&self) -> #rust_property_type {
1192                    #[allow(unused_imports)]
1193                    let _self = #self_init;
1194                    #prop_expression
1195                }
1196            ));
1197
1198            let setter_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Setter);
1199            if !p.read_only() {
1200                let set_value = property_set_value_tokens(&p.prop, quote!(value), ctx);
1201                property_and_callback_accessors.push(quote!(
1202                    #[allow(dead_code)]
1203                    pub fn #setter_ident(&self, value: #rust_property_type) {
1204                        #[allow(unused_imports)]
1205                        let _self = #self_init;
1206                        #set_value
1207                    }
1208                ));
1209            } else {
1210                property_and_callback_accessors.push(quote!(
1211                    #[allow(dead_code)] fn #setter_ident(&self, _read_only_property : ()) { }
1212                ));
1213            }
1214        }
1215    }
1216
1217    for (name, ty) in private_properties {
1218        if let Type::Function { .. } = ty {
1219            let caller_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Invoker);
1220            property_and_callback_accessors.push(
1221                quote!( #[allow(dead_code)] fn #caller_ident(&self, _private_function: ()) {} ),
1222            );
1223        } else {
1224            let getter_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Getter);
1225            let setter_ident = accessor_names::rust_accessor_ident(name, AccessorKind::Setter);
1226            property_and_callback_accessors.push(quote!(
1227                #[allow(dead_code)] fn #getter_ident(&self, _private_property: ()) {}
1228                #[allow(dead_code)] fn #setter_ident(&self, _private_property: ()) {}
1229            ));
1230        }
1231    }
1232
1233    quote!(#(#property_and_callback_accessors)*)
1234}
1235
1236/// Maximum number of statements in a generated `init`-like function body.
1237/// rustc's per-function-body work scales super-linearly, so bigger bodies are
1238/// split into chunk functions. Measured on a large project, the build time
1239/// stops improving below 128 statements per chunk.
1240const INIT_CHUNK_SIZE: usize = 128;
1241
1242/// When `stmts` is bigger than [`INIT_CHUNK_SIZE`], move the statements into
1243/// `{prefix}_chunk_{i}` functions (added to `chunk_fns`) and return the calls
1244/// to them. Each statement must only use the names bound by `params` and
1245/// `prologue` (`quote!` is not hygienic, so the exact names matter).
1246///
1247/// When `fallible` is set the chunks return `Result` and are called with `?`,
1248/// so `init`'s statements can use the `?` operator (e.g. for font registration).
1249fn emit_in_chunks(
1250    prefix: &str,
1251    stmts: Vec<TokenStream>,
1252    params: &TokenStream,
1253    args: &TokenStream,
1254    prologue: &TokenStream,
1255    fallible: bool,
1256    chunk_fns: &mut Vec<TokenStream>,
1257) -> Vec<TokenStream> {
1258    if stmts.len() <= INIT_CHUNK_SIZE {
1259        return stmts;
1260    }
1261    let (ret, ok, question) = if fallible {
1262        (
1263            quote!(-> ::core::result::Result<(), slint::PlatformError>),
1264            quote!(::core::result::Result::Ok(())),
1265            quote!(?),
1266        )
1267    } else {
1268        (quote!(), quote!(), quote!())
1269    };
1270    stmts
1271        .chunks(INIT_CHUNK_SIZE)
1272        .enumerate()
1273        .map(|(i, chunk)| {
1274            let name = format_ident!("{prefix}_chunk_{i}");
1275            // inline(never) stops the MIR inliner from re-merging the chunks
1276            // into one huge function.
1277            chunk_fns.push(quote!(
1278                #[inline(never)]
1279                fn #name(#params) #ret {
1280                    #![allow(unused)]
1281                    #prologue
1282                    #(#chunk)*
1283                    #ok
1284                }
1285            ));
1286            quote!(Self::#name(#args)#question;)
1287        })
1288        .collect()
1289}
1290
1291/// Generate the rust code for the given component.
1292fn generate_sub_component(
1293    component_idx: llr::SubComponentIdx,
1294    root: &llr::CompilationUnit,
1295    parent_ctx: Option<&ParentScope>,
1296    index_property: Option<llr::PropertyIdx>,
1297    pinned_drop: bool,
1298) -> TokenStream {
1299    let component = &root.sub_components[component_idx];
1300    let inner_component_id = inner_component_id(component);
1301
1302    let ctx = EvaluationContext::new_sub_component(
1303        root,
1304        component_idx,
1305        RustGeneratorContext { global_access: quote!(_self.globals()) },
1306        parent_ctx,
1307    );
1308    let mut extra_components = component
1309        .popup_windows
1310        .iter()
1311        .map(|popup| {
1312            generate_item_tree(
1313                &popup.item_tree,
1314                root,
1315                Some(&ParentScope::new(&ctx, None)),
1316                None,
1317                true,
1318            )
1319        })
1320        .chain(component.menu_item_trees.iter().map(|tree| {
1321            generate_item_tree(tree, root, Some(&ParentScope::new(&ctx, None)), None, false)
1322        }))
1323        .collect::<Vec<_>>();
1324
1325    let mut declared_property_vars = Vec::new();
1326    let mut declared_property_types = Vec::new();
1327    let mut declared_callbacks = Vec::new();
1328    let mut declared_callbacks_types = Vec::new();
1329    let mut declared_callbacks_ret = Vec::new();
1330
1331    for property in component.properties.iter() {
1332        let prop_ident = ident(&property.name);
1333        let rust_property_type = rust_property_type(&property.ty).unwrap();
1334        declared_property_vars.push(prop_ident.clone());
1335        declared_property_types.push(rust_property_type.clone());
1336    }
1337    let mut callback_tracker_names = Vec::new();
1338
1339    for callback in component.callbacks.iter() {
1340        let cb_ident = ident(&callback.name);
1341        let callback_args =
1342            callback.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
1343        let return_type = rust_primitive_type(&callback.ret_ty).unwrap();
1344        declared_callbacks.push(cb_ident.clone());
1345        declared_callbacks_types.push(callback_args);
1346        declared_callbacks_ret.push(return_type);
1347        if callback.needs_tracker {
1348            callback_tracker_names.push(callback_tracker_ident(&callback.name));
1349        }
1350    }
1351
1352    let change_tracker_names = component
1353        .change_callbacks
1354        .iter()
1355        .enumerate()
1356        .map(|(idx, _)| format_ident!("change_tracker{idx}"));
1357
1358    let declared_functions = generate_functions(component.functions.as_ref(), &ctx);
1359
1360    let mut init = Vec::new();
1361    let mut item_names = Vec::new();
1362    let mut item_types = Vec::new();
1363
1364    #[cfg(slint_debug_property)]
1365    init.push(quote!(
1366        #(self_rc.#declared_property_vars.debug_name.replace(
1367            concat!(stringify!(#inner_component_id), ".", stringify!(#declared_property_vars)).into());)*
1368    ));
1369
1370    for item in &component.items {
1371        item_names.push(ident(&item.name));
1372        item_types.push(ident(&item.ty.class_name));
1373        #[cfg(slint_debug_property)]
1374        {
1375            let mut it = Some(&item.ty);
1376            let elem_name = ident(&item.name);
1377            while let Some(ty) = it {
1378                for (prop, info) in &ty.properties {
1379                    if info.ty.is_property_type() && prop != "commands" {
1380                        let name = format!("{}::{}.{}", component.name, item.name, prop);
1381                        let prop = ident(&prop);
1382                        init.push(
1383                            quote!(self_rc.#elem_name.#prop.debug_name.replace(#name.into());),
1384                        );
1385                    }
1386                }
1387                it = ty.parent.as_ref();
1388            }
1389        }
1390    }
1391
1392    let mut repeated_visit_branch: Vec<TokenStream> = Vec::new();
1393    let mut repeated_element_components: Vec<TokenStream> = Vec::new();
1394    let mut repeated_subtree_ranges: Vec<TokenStream> = Vec::new();
1395    let mut repeated_subtree_components: Vec<TokenStream> = Vec::new();
1396    let mut ensure_instantiated_stmts: Vec<TokenStream> = Vec::new();
1397
1398    for (idx, repeated) in component.repeated.iter_enumerated() {
1399        extra_components.push(generate_repeated_component(
1400            repeated,
1401            root,
1402            &ParentScope::new(&ctx, Some(idx)),
1403        ));
1404
1405        let idx = usize::from(idx) as u32;
1406
1407        if let Some(item_index) = repeated.container_item_index {
1408            let embed_item = access_local_member(
1409                &llr::LocalMemberIndex::Native {
1410                    item_index,
1411                    prop_name: Default::default(),
1412                    kind: llr::NativeMemberKind::Property,
1413                }
1414                .into(),
1415                &ctx,
1416            );
1417
1418            repeated_visit_branch.push(quote!(
1419                #idx => {
1420                    #embed_item.visit_children_item(-1, order, visitor)
1421                }
1422            ));
1423            repeated_subtree_ranges.push(quote!(
1424                #idx => {
1425                    #embed_item.subtree_range()
1426                }
1427            ));
1428            repeated_subtree_components.push(quote!(
1429                #idx => {
1430                    if subtree_index == 0 {
1431                        *result = #embed_item.subtree_component()
1432                    }
1433                }
1434            ));
1435            ensure_instantiated_stmts.push(quote!({
1436                _changed |= #embed_item.ensure_updated();
1437            }));
1438        } else {
1439            let repeater_id = format_ident!("repeater{}", idx);
1440            let rep_inner_component_id =
1441                self::inner_component_id(&root.sub_components[repeated.sub_tree.root]);
1442
1443            let model = compile_expression(&repeated.model.borrow(), &ctx);
1444            init.push(quote! {
1445                _self.#repeater_id.set_model_binding({
1446                    let self_weak = sp::VRcMapped::downgrade(&self_rc);
1447                    move || {
1448                        let self_rc = self_weak.upgrade().unwrap();
1449                        let _self = self_rc.as_pin_ref();
1450                        (#model) as _
1451                    }
1452                });
1453            });
1454            if let Some(listview) = &repeated.listview {
1455                let content_y = access_member(&listview.content_y, &ctx).unwrap();
1456                let lv_h = access_member(&listview.listview_height, &ctx).unwrap();
1457                let lv_w = access_member(&listview.listview_width, &ctx).unwrap();
1458                let content_w = listview.content_width.as_ref().map_or_else(
1459                    || quote!(None),
1460                    |w| {
1461                        let w = access_member(w, &ctx).unwrap();
1462                        quote!(Some(#w))
1463                    },
1464                );
1465                let content_h = listview.content_height.as_ref().map_or_else(
1466                    || quote!(None),
1467                    |h| {
1468                        let h = access_member(h, &ctx).unwrap();
1469                        quote!(Some(#h))
1470                    },
1471                );
1472
1473                repeated_visit_branch.push(quote!(
1474                    #idx => {
1475                        #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).track_changes_listview(
1476                            #content_w, #content_h, #content_y, #lv_w.get(), #lv_h
1477                        );
1478                        #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).visit(order, visitor)
1479                    }
1480                ));
1481                ensure_instantiated_stmts.push(quote!({
1482                    _changed |= #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).ensure_updated_listview(
1483                        || { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into() },
1484                        #content_w, #content_h, #content_y, #lv_w.get(), #lv_h
1485                    );
1486                }));
1487            } else {
1488                repeated_visit_branch.push(quote!(
1489                    #idx => {
1490                        #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).visit(order, visitor)
1491                    }
1492                ));
1493                ensure_instantiated_stmts.push(quote!({
1494                    _changed |= #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).ensure_updated(
1495                        || #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into()
1496                    );
1497                }));
1498            }
1499            repeated_subtree_ranges.push(quote!(
1500                #idx => {
1501                    #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).track_instance_changes();
1502                    sp::IndexRange::from(_self.#repeater_id.range())
1503                }
1504            ));
1505            repeated_subtree_components.push(quote!(
1506                #idx => {
1507                    if let Some(instance) = _self.#repeater_id.instance_at(subtree_index) {
1508                        *result = sp::VRc::downgrade(&sp::VRc::into_dyn(instance));
1509                    }
1510                }
1511            ));
1512            repeated_element_components.push(if repeated.index_prop.is_some() {
1513                quote!(#repeater_id: sp::Repeater<#rep_inner_component_id>)
1514            } else {
1515                quote!(#repeater_id: sp::Conditional<#rep_inner_component_id>)
1516            });
1517        }
1518    }
1519
1520    let mut accessible_role_branch = Vec::new();
1521    let mut accessible_string_property_branch = Vec::new();
1522    let mut accessibility_action_branch = Vec::new();
1523    let mut supported_accessibility_actions = BTreeMap::<u32, BTreeSet<_>>::new();
1524    for ((index, what), expr) in &component.accessible_prop {
1525        let e = compile_expression(&expr.borrow(), &ctx);
1526        if what == "Role" {
1527            accessible_role_branch.push(quote!(#index => #e,));
1528        } else if let Some(what) = what.strip_prefix("Action") {
1529            let arg_count = crate::generator::accessibility_action_argument_count(what);
1530            let what = ident(what);
1531            accessibility_action_branch.push(if arg_count == 0 {
1532                quote!((#index, sp::AccessibilityAction::#what) => { #e })
1533            } else {
1534                let arg = (0..arg_count).map(|i| format_ident!("arg_{i}")).collect::<Vec<_>>();
1535                quote!((#index, sp::AccessibilityAction::#what(#(#arg),*)) => { #[allow(unused_variables)] let args = (#(#arg,)*); #e })
1536            });
1537            supported_accessibility_actions.entry(*index).or_default().insert(what);
1538        } else {
1539            let what = ident(what);
1540            accessible_string_property_branch
1541                .push(quote!((#index, sp::AccessibleStringProperty::#what) => sp::Some(#e),));
1542        }
1543    }
1544    let mut supported_accessibility_actions_branch = supported_accessibility_actions
1545        .into_iter()
1546        .map(|(index, values)| quote!(#index => #(sp::SupportedAccessibilityAction::#values)|*,))
1547        .collect::<Vec<_>>();
1548
1549    let mut item_geometry_branch = component
1550        .geometries
1551        .iter()
1552        .enumerate()
1553        .filter_map(|(i, x)| x.as_ref().map(|x| (i, x)))
1554        .map(|(index, expr)| {
1555            let expr = compile_expression(&expr.borrow(), &ctx);
1556            let index = index as u32;
1557            quote!(#index => #expr,)
1558        })
1559        .collect::<Vec<_>>();
1560
1561    let mut item_element_infos_branch = component
1562        .element_infos
1563        .iter()
1564        .map(|(item_index, ids)| quote!(#item_index => { return sp::Some(#ids.into()); }))
1565        .collect::<Vec<_>>();
1566
1567    let mut user_init_code: Vec<TokenStream> = Vec::new();
1568
1569    let mut sub_component_names: Vec<Ident> = Vec::new();
1570    let mut sub_component_types: Vec<Ident> = Vec::new();
1571
1572    for sub in &component.sub_components {
1573        let field_name = ident(&sub.name);
1574        let sc = &root.sub_components[sub.ty];
1575        let sub_component_id = self::inner_component_id(sc);
1576        let local_tree_index: u32 = sub.index_in_tree as _;
1577        let local_index_of_first_child: u32 = sub.index_of_first_child_in_tree as _;
1578        let global_access = &ctx.generator_state.global_access;
1579
1580        // For children of sub-components, the item index generated by the generate_item_indices pass
1581        // starts at 1 (0 is the root element).
1582        let global_index = if local_tree_index == 0 {
1583            quote!(tree_index)
1584        } else {
1585            quote!(tree_index_of_first_child + #local_tree_index - 1)
1586        };
1587        let global_children = if local_index_of_first_child == 0 {
1588            quote!(0)
1589        } else {
1590            quote!(tree_index_of_first_child + #local_index_of_first_child - 1)
1591        };
1592
1593        let sub_compo_field = access_component_field_offset(&inner_component_id, &field_name);
1594
1595        init.push(quote!(#sub_component_id::init(
1596            sp::VRcMapped::map(self_rc.clone(), |x| #sub_compo_field.apply_pin(x)),
1597            #global_access.clone(), #global_index, #global_children
1598        )?;));
1599        user_init_code.push(quote!(#sub_component_id::user_init(
1600            sp::VRcMapped::map(self_rc.clone(), |x| #sub_compo_field.apply_pin(x)),
1601        );));
1602
1603        let sub_component_repeater_count = sc.repeater_count(root);
1604        if sub_component_repeater_count > 0 {
1605            let repeater_offset = sub.repeater_offset;
1606            let last_repeater = repeater_offset + sub_component_repeater_count - 1;
1607            repeated_visit_branch.push(quote!(
1608                #repeater_offset..=#last_repeater => {
1609                    #sub_compo_field.apply_pin(_self).visit_dynamic_children(dyn_index - #repeater_offset, order, visitor)
1610                }
1611            ));
1612            repeated_subtree_ranges.push(quote!(
1613                #repeater_offset..=#last_repeater => {
1614                    #sub_compo_field.apply_pin(_self).subtree_range(dyn_index - #repeater_offset)
1615                }
1616            ));
1617            repeated_subtree_components.push(quote!(
1618                #repeater_offset..=#last_repeater => {
1619                    #sub_compo_field.apply_pin(_self).subtree_component(dyn_index - #repeater_offset, subtree_index, result)
1620                }
1621            ));
1622            ensure_instantiated_stmts.push(quote!(
1623                _changed |= #sub_compo_field.apply_pin(_self).ensure_instantiated();
1624            ));
1625        }
1626
1627        let sub_items_count = sc.child_item_count(root);
1628        accessible_role_branch.push(quote!(
1629            #local_tree_index => #sub_compo_field.apply_pin(_self).accessible_role(0),
1630        ));
1631        accessible_string_property_branch.push(quote!(
1632            (#local_tree_index, _) => #sub_compo_field.apply_pin(_self).accessible_string_property(0, what),
1633        ));
1634        accessibility_action_branch.push(quote!(
1635            (#local_tree_index, _) => #sub_compo_field.apply_pin(_self).accessibility_action(0, action),
1636        ));
1637        supported_accessibility_actions_branch.push(quote!(
1638            #local_tree_index => #sub_compo_field.apply_pin(_self).supported_accessibility_actions(0),
1639        ));
1640        if sub_items_count > 1 {
1641            let range_begin = local_index_of_first_child;
1642            let range_end = range_begin + sub_items_count - 2 + sc.repeater_count(root);
1643            accessible_role_branch.push(quote!(
1644                #range_begin..=#range_end => #sub_compo_field.apply_pin(_self).accessible_role(index - #range_begin + 1),
1645            ));
1646            accessible_string_property_branch.push(quote!(
1647                (#range_begin..=#range_end, _) => #sub_compo_field.apply_pin(_self).accessible_string_property(index - #range_begin + 1, what),
1648            ));
1649            item_geometry_branch.push(quote!(
1650                #range_begin..=#range_end => return #sub_compo_field.apply_pin(_self).item_geometry(index - #range_begin + 1),
1651            ));
1652            accessibility_action_branch.push(quote!(
1653                (#range_begin..=#range_end, _) => #sub_compo_field.apply_pin(_self).accessibility_action(index - #range_begin + 1, action),
1654            ));
1655            supported_accessibility_actions_branch.push(quote!(
1656                #range_begin..=#range_end => #sub_compo_field.apply_pin(_self).supported_accessibility_actions(index - #range_begin + 1),
1657            ));
1658            item_element_infos_branch.push(quote!(
1659                #range_begin..=#range_end => #sub_compo_field.apply_pin(_self).item_element_infos(index - #range_begin + 1),
1660            ));
1661        }
1662
1663        sub_component_names.push(field_name);
1664        sub_component_types.push(sub_component_id);
1665    }
1666
1667    let popup_id_names =
1668        component.popup_windows.iter().enumerate().map(|(i, _)| internal_popup_id(i));
1669
1670    for twb in &component.two_way_bindings {
1671        let p1 = access_local_member(&twb.prop1, &ctx);
1672        let r = if let Some(info) = twb.resolve_model(&ctx) {
1673            generate_model_two_way_binding(&ctx, &info, &p1, &twb.field_access)
1674        } else {
1675            let p2 = access_member(&twb.prop2, &ctx);
1676            p2.then(|p2| {
1677                if twb.field_access.is_empty() {
1678                    quote!(sp::Property::link_two_way(#p1, #p2))
1679                } else {
1680                    let (access, ty) =
1681                        lower_field_access_chain(ctx.property_ty(&twb.prop2), &twb.field_access);
1682                    let to_property_value =
1683                        set_primitive_property_value(&ty, quote!(s #access .clone()));
1684                    let to_struct_value =
1685                        primitive_value_from_property_value(&ty, quote!((*v).clone()));
1686                    quote!(sp::Property::link_two_way_with_map(#p2, #p1, |s| #to_property_value, |s, v| s #access = #to_struct_value))
1687                }
1688            })
1689        };
1690        init.push(quote!(#r;))
1691    }
1692
1693    // The pre-init code (custom font registration) runs before the property initialization.
1694    let pre_init_code: Vec<TokenStream> = component
1695        .pre_init_code
1696        .iter()
1697        .map(|e| {
1698            let code = compile_expression(&e.borrow(), &ctx);
1699            quote!(#code;)
1700        })
1701        .collect();
1702    init.splice(0..0, pre_init_code);
1703
1704    // Initialize all properties which have an initial value in the slint file
1705    // This sets up also the callback handler and bindings
1706    for (prop, expression) in &component.property_init {
1707        handle_property_init(prop, expression, &mut init, &ctx)
1708    }
1709    for prop in &component.const_properties {
1710        let rust_property = access_local_member(prop, &ctx);
1711        init.push(quote!(#rust_property.set_constant();))
1712    }
1713
1714    let parent_component_type = parent_ctx.iter().map(|parent| {
1715        let parent_component_id =
1716            self::inner_component_id(&ctx.compilation_unit.sub_components[parent.sub_component]);
1717        quote!(sp::VWeakMapped::<sp::ItemTreeVTable, #parent_component_id>)
1718    });
1719
1720    user_init_code.extend(component.init_code.iter().map(|e| {
1721        let code = compile_expression(&e.borrow(), &ctx);
1722        quote!(#code;)
1723    }));
1724
1725    user_init_code.extend(component.change_callbacks.iter().enumerate().map(|(idx, (p, e))| {
1726        let code = compile_expression(&e.borrow(), &ctx);
1727        let prop = compile_expression(&Expression::PropertyReference(p.clone()), &ctx);
1728        let change_tracker = format_ident!("change_tracker{idx}");
1729        quote! {
1730            #[allow(dead_code, unused)]
1731            sp::change_tracker_init_erased(
1732                &_self.#change_tracker,
1733                &self_rc,
1734                move |_self, _: &()| #prop,
1735                move |_self, _| { #code; }
1736            );
1737        }
1738    }));
1739
1740    let layout_info_h = compile_expression_no_parenthesis(&component.layout_info_h.borrow(), &ctx);
1741    let layout_info_v = compile_expression_no_parenthesis(&component.layout_info_v.borrow(), &ctx);
1742    let grid_layout_input_for_repeated_fn =
1743        component.grid_layout_input_for_repeated.as_ref().map(|expr| {
1744            let expr = compile_expression_no_parenthesis(&expr.borrow(), &ctx);
1745            quote! {
1746                fn grid_layout_input_for_repeated(
1747                    self: ::core::pin::Pin<&Self>,
1748                    new_row: bool,
1749                    result: &mut [sp::GridLayoutInputData],
1750                ) {
1751                    #![allow(unused)]
1752                    let _self = self;
1753                    #expr
1754                }
1755            }
1756        });
1757
1758    let flexbox_layout_item_info_for_repeated_fn =
1759        component.flexbox_layout_item_info_for_repeated.as_ref().map(|expr| {
1760            let expr = compile_expression(&expr.borrow(), &ctx);
1761            quote! {
1762                fn flexbox_layout_item_info_for_repeated(
1763                    self: ::core::pin::Pin<&Self>,
1764                ) -> sp::FlexboxLayoutItemInfo {
1765                    #![allow(unused)]
1766                    let _self = self;
1767                    #expr
1768                }
1769            }
1770        });
1771
1772    let cross_axis_self_alignment_for_repeated_fn =
1773        component.cross_axis_self_alignment_for_repeated.as_ref().map(|(_, expr)| {
1774            let expr = compile_expression(&expr.borrow(), &ctx);
1775            quote! {
1776                fn cross_axis_self_alignment_for_repeated(
1777                    self: ::core::pin::Pin<&Self>,
1778                ) -> sp::CrossAxisAlignment {
1779                    #![allow(unused)]
1780                    let _self = self;
1781                    #expr
1782                }
1783            }
1784        });
1785
1786    let layout_order_for_repeated_fn =
1787        component.layout_order_for_repeated.as_ref().map(|(_, expr)| {
1788            let expr = compile_expression(&expr.borrow(), &ctx);
1789            quote! {
1790                fn layout_order_for_repeated(self: ::core::pin::Pin<&Self>) -> i32 {
1791                    #![allow(unused)]
1792                    let _self = self;
1793                    #expr
1794                }
1795            }
1796        });
1797
1798    // FIXME! this is only public because of the ComponentHandle::WeakInner. we should find another way
1799    let visibility = parent_ctx.is_none().then(|| quote!(pub));
1800
1801    let subtree_index_function = if let Some(property_index) = index_property {
1802        let prop = access_local_member(&property_index.into(), &ctx);
1803        quote!(#prop.get() as usize)
1804    } else {
1805        quote!(usize::MAX)
1806    };
1807
1808    let timer_names =
1809        component.timers.iter().enumerate().map(|(idx, _)| format_ident!("timer{idx}"));
1810    let update_timers = (!component.timers.is_empty()).then(|| {
1811        let updt = component.timers.iter().enumerate().map(|(idx, tmr)| {
1812            let ident = format_ident!("timer{idx}");
1813            let interval = compile_expression(&tmr.interval.borrow(), &ctx);
1814            let running = compile_expression(&tmr.running.borrow(), &ctx);
1815            let callback = compile_expression(&tmr.triggered.borrow(), &ctx);
1816            quote!(
1817                let millis = if #running { (#interval) as i64 } else { -1 };
1818                if millis >= 0 {
1819                    let interval = ::core::time::Duration::from_millis(millis as u64);
1820                    if !self.#ident.running() || interval != self.#ident.interval() {
1821                        let self_weak = self.self_weak.get().unwrap().clone();
1822                        self.#ident.start(sp::TimerMode::Repeated, interval, move || {
1823                            if let Some(self_rc) = self_weak.upgrade() {
1824                                let _self = self_rc.as_pin_ref();
1825                                #callback
1826                            }
1827                        });
1828                    }
1829                } else {
1830                    self.#ident.stop();
1831                }
1832            )
1833        });
1834        user_init_code.push(quote!(_self.update_timers();));
1835        quote!(
1836            fn update_timers(self: ::core::pin::Pin<&Self>) {
1837                let _self = self;
1838                #(#updt)*
1839            }
1840        )
1841    });
1842
1843    let mut chunk_fns = Vec::new();
1844    let init = emit_in_chunks(
1845        "init",
1846        init,
1847        &quote!(self_rc: sp::VRcMapped<sp::ItemTreeVTable, Self>, tree_index: u32, tree_index_of_first_child: u32),
1848        &quote!(self_rc.clone(), tree_index, tree_index_of_first_child),
1849        &quote!(let _self = self_rc.as_pin_ref();),
1850        true,
1851        &mut chunk_fns,
1852    );
1853    let user_init_code = emit_in_chunks(
1854        "user_init",
1855        user_init_code,
1856        &quote!(self_rc: sp::VRcMapped<sp::ItemTreeVTable, Self>),
1857        &quote!(self_rc.clone()),
1858        &quote!(let _self = self_rc.as_pin_ref();),
1859        false,
1860        &mut chunk_fns,
1861    );
1862
1863    let pin_macro = if pinned_drop { quote!(#[pin_drop]) } else { quote!(#[pin]) };
1864
1865    quote!(
1866        #[derive(sp::FieldOffsets, Default)]
1867        #[const_field_offset(sp::const_field_offset)]
1868        #[repr(C)]
1869        #pin_macro
1870        #visibility
1871        struct #inner_component_id {
1872            #(#item_names : sp::#item_types,)*
1873            #(#sub_component_names : #sub_component_types,)*
1874            #(#popup_id_names : ::core::cell::Cell<sp::Option<::core::num::NonZeroU32>>,)*
1875            #(#declared_property_vars : sp::Property<#declared_property_types>,)*
1876            #(#declared_callbacks : sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
1877            #(#callback_tracker_names : sp::Property<()>,)*
1878            #(#repeated_element_components,)*
1879            #(#change_tracker_names : sp::ChangeTracker,)*
1880            #(#timer_names : sp::Timer,)*
1881            self_weak : sp::OnceCell<sp::VWeakMapped<sp::ItemTreeVTable, #inner_component_id>>,
1882            #(parent : #parent_component_type,)*
1883            globals: sp::OnceCell<sp::Rc<SharedGlobals>>,
1884            tree_index: ::core::cell::Cell<u32>,
1885            tree_index_of_first_child: ::core::cell::Cell<u32>,
1886        }
1887
1888        impl #inner_component_id {
1889            // Shorthands used by the generated expression code: these accesses are
1890            // emitted many times, so keep the call sites as small as possible.
1891            #[allow(dead_code)]
1892            fn globals(&self) -> &sp::Rc<SharedGlobals> {
1893                self.globals.get().unwrap()
1894            }
1895
1896            #[allow(dead_code)]
1897            fn origin_rc(&self) -> sp::ItemTreeRc {
1898                sp::VRcMapped::origin(&self.self_weak.get().unwrap().upgrade().unwrap())
1899            }
1900
1901            fn init(self_rc: sp::VRcMapped<sp::ItemTreeVTable, Self>,
1902                    globals : sp::Rc<SharedGlobals>,
1903                    tree_index: u32, tree_index_of_first_child: u32)
1904                -> ::core::result::Result<(), slint::PlatformError>
1905            {
1906                #![allow(unused)]
1907                let _self = self_rc.as_pin_ref();
1908                let _ = _self.self_weak.set(sp::VRcMapped::downgrade(&self_rc));
1909                let _ = _self.globals.set(globals);
1910                _self.tree_index.set(tree_index);
1911                _self.tree_index_of_first_child.set(tree_index_of_first_child);
1912                #(#init)*
1913                ::core::result::Result::Ok(())
1914            }
1915
1916            fn user_init(self_rc: sp::VRcMapped<sp::ItemTreeVTable, Self>) {
1917                #![allow(unused)]
1918                let _self = self_rc.as_pin_ref();
1919                #(#user_init_code)*
1920            }
1921
1922            #(#chunk_fns)*
1923
1924            fn visit_dynamic_children(
1925                self: ::core::pin::Pin<&Self>,
1926                dyn_index: u32,
1927                order: sp::TraversalOrder,
1928                visitor: sp::ItemVisitorRefMut<'_>
1929            ) -> sp::VisitChildrenResult {
1930                #![allow(unused)]
1931                let _self = self;
1932                match dyn_index {
1933                    #(#repeated_visit_branch)*
1934                    _ => panic!("invalid dyn_index {}", dyn_index),
1935                }
1936            }
1937
1938            fn ensure_instantiated(self: ::core::pin::Pin<&Self>) -> bool {
1939                #![allow(unused)]
1940                let _self = self;
1941                let mut _changed = false;
1942                #(#ensure_instantiated_stmts)*
1943                _changed
1944            }
1945
1946            fn layout_info(self: ::core::pin::Pin<&Self>, orientation: sp::Orientation) -> sp::LayoutInfo {
1947                #![allow(unused)]
1948                let _self = self;
1949                match orientation {
1950                    sp::Orientation::Horizontal => #layout_info_h,
1951                    sp::Orientation::Vertical => #layout_info_v,
1952                }
1953            }
1954
1955            #grid_layout_input_for_repeated_fn
1956
1957            #flexbox_layout_item_info_for_repeated_fn
1958
1959            #cross_axis_self_alignment_for_repeated_fn
1960
1961            #layout_order_for_repeated_fn
1962
1963            fn subtree_range(self: ::core::pin::Pin<&Self>, dyn_index: u32) -> sp::IndexRange {
1964                #![allow(unused)]
1965                let _self = self;
1966                match dyn_index {
1967                    #(#repeated_subtree_ranges)*
1968                    _ => panic!("invalid dyn_index {}", dyn_index),
1969                }
1970            }
1971
1972            fn subtree_component(self: ::core::pin::Pin<&Self>, dyn_index: u32, subtree_index: usize, result: &mut sp::ItemTreeWeak) {
1973                #![allow(unused)]
1974                let _self = self;
1975                match dyn_index {
1976                    #(#repeated_subtree_components)*
1977                    _ => panic!("invalid dyn_index {}", dyn_index),
1978                };
1979            }
1980
1981            fn index_property(self: ::core::pin::Pin<&Self>) -> usize {
1982                #![allow(unused)]
1983                let _self = self;
1984                #subtree_index_function
1985            }
1986
1987            fn item_geometry(self: ::core::pin::Pin<&Self>, index: u32) -> sp::LogicalRect {
1988                #![allow(unused)]
1989                let _self = self;
1990                // The result of the expression is an anonymous struct, `{height: length, width: length, x: length, y: length}`
1991                // fields are in alphabetical order
1992                let (h, w, x, y) = match index {
1993                    #(#item_geometry_branch)*
1994                    _ => return ::core::default::Default::default()
1995                };
1996                sp::euclid::rect(x, y, w, h)
1997            }
1998
1999            fn accessible_role(self: ::core::pin::Pin<&Self>, index: u32) -> sp::AccessibleRole {
2000                #![allow(unused)]
2001                let _self = self;
2002                match index {
2003                    #(#accessible_role_branch)*
2004                    //#(#forward_sub_ranges => #forward_sub_field.apply_pin(_self).accessible_role())*
2005                    _ => sp::AccessibleRole::default(),
2006                }
2007            }
2008
2009            fn accessible_string_property(
2010                self: ::core::pin::Pin<&Self>,
2011                index: u32,
2012                what: sp::AccessibleStringProperty,
2013            ) -> sp::Option<sp::SharedString> {
2014                #![allow(unused)]
2015                let _self = self;
2016                match (index, what) {
2017                    #(#accessible_string_property_branch)*
2018                    _ => sp::None,
2019                }
2020            }
2021
2022            fn accessibility_action(self: ::core::pin::Pin<&Self>, index: u32, action: &sp::AccessibilityAction) {
2023                #![allow(unused)]
2024                let _self = self;
2025                match (index, action) {
2026                    #(#accessibility_action_branch)*
2027                    _ => (),
2028                }
2029            }
2030
2031            fn supported_accessibility_actions(self: ::core::pin::Pin<&Self>, index: u32) -> sp::SupportedAccessibilityAction {
2032                #![allow(unused)]
2033                let _self = self;
2034                match index {
2035                    #(#supported_accessibility_actions_branch)*
2036                    _ => ::core::default::Default::default(),
2037                }
2038            }
2039
2040            fn item_element_infos(self: ::core::pin::Pin<&Self>, index: u32) -> sp::Option<sp::SharedString> {
2041                #![allow(unused)]
2042                let _self = self;
2043                match index {
2044                    #(#item_element_infos_branch)*
2045                    _ => { ::core::default::Default::default() }
2046                }
2047            }
2048
2049            #update_timers
2050
2051            #(#declared_functions)*
2052        }
2053
2054        #(#extra_components)*
2055    )
2056}
2057
2058fn generate_functions(functions: &[llr::Function], ctx: &EvaluationContext) -> Vec<TokenStream> {
2059    functions
2060        .iter()
2061        .map(|f| {
2062            let mut ctx2 = ctx.clone();
2063            ctx2.argument_types = &f.args;
2064            let tokens_for_expression = compile_expression(&f.code.borrow(), &ctx2);
2065            let as_ = if f.ret_ty == Type::Void {
2066                Some(quote!(;))
2067            } else if f.code.borrow().ty(&ctx2) == Type::Invalid {
2068                // Don't cast if the Rust code is the never type, as with return statements inside a block, the
2069                // type of the return expression is `()` instead of `!`.
2070                None
2071            } else {
2072                Some(quote!(as _))
2073            };
2074            let fn_id = ident(&format!("fn_{}", f.name));
2075            let args_ty =
2076                f.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
2077            let return_type = rust_primitive_type(&f.ret_ty).unwrap();
2078            let args_name =
2079                (0..f.args.len()).map(|i| format_ident!("arg_{}", i)).collect::<Vec<_>>();
2080
2081            quote! {
2082                #[allow(dead_code, unused)]
2083                pub fn #fn_id(self: ::core::pin::Pin<&Self>, #(#args_name : #args_ty,)*) -> #return_type {
2084                    let _self = self;
2085                    let args = (#(#args_name,)*);
2086                    (#tokens_for_expression) #as_
2087                }
2088            }
2089        })
2090        .collect()
2091}
2092
2093fn generate_global(
2094    global_idx: llr::GlobalIdx,
2095    global: &llr::GlobalComponent,
2096    root: &llr::CompilationUnit,
2097    compiler_config: &CompilerConfiguration,
2098    global_exports: &mut Vec<TokenStream>,
2099) -> TokenStream {
2100    let mut declared_property_vars = Vec::new();
2101    let mut declared_property_types = Vec::new();
2102    let mut declared_callbacks = Vec::new();
2103    let mut declared_callbacks_types = Vec::new();
2104    let mut declared_callbacks_ret = Vec::new();
2105
2106    for property in global.properties.iter() {
2107        declared_property_vars.push(ident(&property.name));
2108        declared_property_types.push(rust_property_type(&property.ty).unwrap());
2109    }
2110    let mut callback_tracker_names = Vec::new();
2111
2112    for callback in &global.callbacks {
2113        let callback_args =
2114            callback.args.iter().map(|a| rust_primitive_type(a).unwrap()).collect::<Vec<_>>();
2115        declared_callbacks.push(ident(&callback.name));
2116        declared_callbacks_types.push(callback_args);
2117        declared_callbacks_ret.push(rust_primitive_type(&callback.ret_ty));
2118        if callback.needs_tracker {
2119            callback_tracker_names.push(callback_tracker_ident(&callback.name));
2120        }
2121    }
2122
2123    let mut init = Vec::new();
2124    let inner_component_id = global_inner_name(global);
2125
2126    #[cfg(slint_debug_property)]
2127    init.push(quote!(
2128        #(self_rc.#declared_property_vars.debug_name.replace(
2129            concat!(stringify!(#inner_component_id), ".", stringify!(#declared_property_vars)).into());)*
2130    ));
2131
2132    let ctx = EvaluationContext::new_global(
2133        root,
2134        global_idx,
2135        RustGeneratorContext { global_access: quote!(_self.globals()) },
2136    );
2137
2138    let declared_functions = generate_functions(global.functions.as_ref(), &ctx);
2139
2140    for (property_index, expression) in &global.init_values {
2141        handle_property_init(
2142            &llr::LocalMemberReference::from(property_index.clone()).into(),
2143            expression,
2144            &mut init,
2145            &ctx,
2146        )
2147    }
2148    for (property_index, cst) in global.const_properties.iter_enumerated() {
2149        if *cst {
2150            let rust_property = access_local_member(&property_index.into(), &ctx);
2151            init.push(quote!(#rust_property.set_constant();))
2152        }
2153    }
2154
2155    let public_component_id = ident(&global.name);
2156    let global_id = format_ident!("global_{}", public_component_id);
2157
2158    let change_tracker_names = global
2159        .change_callbacks
2160        .keys()
2161        .map(|idx| format_ident!("change_tracker{}", usize::from(*idx)));
2162    init.extend(global.change_callbacks.iter().map(|(p, e)| {
2163        let code = compile_expression(&e.borrow(), &ctx);
2164        let prop = access_local_member(&(*p).into(), &ctx);
2165        let change_tracker = format_ident!("change_tracker{}", usize::from(*p));
2166        quote! {
2167            #[allow(dead_code, unused)]
2168            _self.#change_tracker.init(
2169                self_rc.globals.get().unwrap().clone(),
2170                move |global_weak| {
2171                    let self_rc = global_weak.upgrade().unwrap().#global_id.clone();
2172                    let _self = self_rc.as_ref();
2173                    #prop.get()
2174                },
2175                move |global_weak, _| {
2176                    let self_rc = global_weak.upgrade().unwrap().#global_id.clone();
2177                    let _self = self_rc.as_ref();
2178                    #code;
2179                }
2180            );
2181        }
2182    }));
2183
2184    let mut chunk_fns = Vec::new();
2185    let init = emit_in_chunks(
2186        "init",
2187        init,
2188        &quote!(self_rc: ::core::pin::Pin<sp::Rc<Self>>),
2189        &quote!(self_rc.clone()),
2190        &quote!(let _self = self_rc.as_ref();),
2191        false,
2192        &mut chunk_fns,
2193    );
2194
2195    let pub_token = if compiler_config.library_name.is_some() && !global.is_builtin {
2196        global_exports.push(quote! (#inner_component_id));
2197        quote!(pub)
2198    } else {
2199        quote!()
2200    };
2201
2202    let public_interface = global.exported.then(|| {
2203        let property_and_callback_accessors = public_api(
2204            &global.public_properties,
2205            &global.private_properties,
2206            quote!(self.0.as_ref()),
2207            &ctx,
2208        );
2209        let aliases = global.aliases.iter().map(|name| ident(name));
2210        let getters = generate_global_getters(global, root);
2211
2212        let strong_handle_impl = quote!(
2213            impl slint::StrongHandle for #public_component_id<'static> {
2214                type WeakInner = sp::Weak<#inner_component_id>;
2215
2216                fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> ::core::option::Option<Self> {
2217                    let inner = ::core::pin::Pin::new(inner.upgrade()?);
2218                    ::core::option::Option::Some(Self(inner, ::core::marker::PhantomData::default()))
2219                }
2220            }
2221        );
2222
2223        quote!(
2224            #[allow(unused)]
2225            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>);
2226
2227            impl<'a> #public_component_id<'a> {
2228                #property_and_callback_accessors
2229            }
2230            #(pub type #aliases<'a> = #public_component_id<'a>;)*
2231            #getters
2232
2233            #strong_handle_impl
2234        )
2235    });
2236
2237    let private_interface = (!global.is_builtin).then(|| {
2238        quote!(
2239            #[derive(sp::FieldOffsets, Default)]
2240            #[const_field_offset(sp::const_field_offset)]
2241            #[repr(C)]
2242            #[pin]
2243            pub struct #inner_component_id {
2244                #(#pub_token  #declared_property_vars: sp::Property<#declared_property_types>,)*
2245                #(#pub_token  #declared_callbacks: sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
2246                #(#pub_token  #callback_tracker_names : sp::Property<()>,)*
2247                #(#pub_token  #change_tracker_names : sp::ChangeTracker,)*
2248                globals : sp::OnceCell<sp::Weak<SharedGlobals>>,
2249            }
2250
2251            impl #inner_component_id {
2252                // Shorthand used by the generated expression code: the member access is
2253                // emitted for every global property access, so keep it as small as possible.
2254                #[allow(dead_code)]
2255                fn globals(&self) -> sp::Rc<SharedGlobals> {
2256                    self.globals.get().unwrap().upgrade().unwrap()
2257                }
2258                fn new() -> ::core::pin::Pin<sp::Rc<Self>> {
2259                    sp::Rc::pin(Self::default())
2260                }
2261                fn init(self: ::core::pin::Pin<sp::Rc<Self>>, globals: &sp::Rc<SharedGlobals>) {
2262                    #![allow(unused)]
2263                    let _ = self.globals.set(sp::Rc::downgrade(globals));
2264                    let self_rc = self;
2265                    let _self = self_rc.as_ref();
2266                    #(#init)*
2267                }
2268
2269                #(#chunk_fns)*
2270
2271                #(#declared_functions)*
2272            }
2273        )
2274    });
2275
2276    quote!(#private_interface #public_interface)
2277}
2278
2279fn generate_global_getters(
2280    global: &llr::GlobalComponent,
2281    root: &llr::CompilationUnit,
2282) -> TokenStream {
2283    let public_component_id = ident(&global.name);
2284    let global_id = format_ident!("global_{}", public_component_id);
2285
2286    let getters = root.public_components.iter().map(|c| {
2287        let root_component_id = ident(&c.name);
2288        quote! {
2289            impl<'a> slint::Global<'a, #root_component_id> for #public_component_id<'a> {
2290                type StaticSelf = #public_component_id<'static>;
2291
2292                fn get(component: &'a #root_component_id) -> Self {
2293                    Self(component.0.globals.get().unwrap().#global_id.clone(), ::core::marker::PhantomData::default())
2294                }
2295
2296                fn as_weak(&self) -> slint::Weak<Self::StaticSelf> {
2297                    let inner = ::core::pin::Pin::into_inner(self.0.clone());
2298                    slint::Weak::new(sp::Rc::downgrade(&inner))
2299                }
2300            }
2301        }
2302    });
2303
2304    quote! (
2305        #(#getters)*
2306    )
2307}
2308
2309fn generate_item_tree(
2310    sub_tree: &llr::ItemTree,
2311    root: &llr::CompilationUnit,
2312    parent_ctx: Option<&ParentScope>,
2313    index_property: Option<llr::PropertyIdx>,
2314    is_popup: bool,
2315) -> TokenStream {
2316    let needs_window_adapter = root.needs_window_adapter();
2317    let sub_comp = generate_sub_component(
2318        sub_tree.root,
2319        root,
2320        parent_ctx,
2321        index_property,
2322        needs_window_adapter,
2323    );
2324    let inner_component_id = self::inner_component_id(&root.sub_components[sub_tree.root]);
2325    let parent_component_type = parent_ctx
2326        .iter()
2327        .map(|parent| {
2328            let parent_component_id =
2329                self::inner_component_id(&root.sub_components[parent.sub_component]);
2330            quote!(sp::VWeakMapped::<sp::ItemTreeVTable, #parent_component_id>)
2331        })
2332        .collect::<Vec<_>>();
2333
2334    let is_root_component = !is_popup && parent_ctx.is_none();
2335    let globals = if is_popup {
2336        quote!(globals)
2337    } else if parent_ctx.is_some() {
2338        quote!(parent.upgrade().unwrap().globals.get().unwrap().clone())
2339    } else {
2340        quote!(SharedGlobals::new(sp::VRc::downgrade(&self_dyn_rc)))
2341    };
2342    // The root component owns the freshly created `SharedGlobals` and is responsible for running
2343    // its eager initialization. The root's own `globals` field must be set *before* that init
2344    // runs, because a global binding (e.g. `Palette.color-scheme`) may resolve the root's window
2345    // adapter through `globals` during evaluation. Popups and sub-components receive an already
2346    // initialized `SharedGlobals`, so they skip this step.
2347    let set_and_init_globals = if is_root_component {
2348        quote!(
2349            let _ = sp::VRc::map(self_rc.clone(), |x| x).as_pin_ref().globals.set(globals.clone());
2350            globals.init_globals();
2351        )
2352    } else {
2353        quote!()
2354    };
2355    let globals_arg = is_popup.then(|| quote!(globals: sp::Rc<SharedGlobals>));
2356
2357    let embedding_function = if parent_ctx.is_some() {
2358        quote!(todo!("Components written in Rust can not get embedded yet."))
2359    } else {
2360        quote!(false)
2361    };
2362
2363    let parent_item_expression = parent_ctx.map(|parent| parent.repeater_index.map_or_else(|| {
2364        // No repeater index, this could be a PopupWindow
2365        quote!{
2366            if let Some(parent_rc) = self.parent.clone().upgrade() {
2367                let parent_origin = sp::VRcMapped::origin(&parent_rc);
2368                // TODO: store popup index in ctx and set it here instead of 0?
2369                *_result = sp::ItemRc::new_root(parent_origin).downgrade();
2370            }
2371        }
2372    }, |idx| {
2373        let current_sub_component = &root.sub_components[parent.sub_component];
2374        let sub_component_offset = current_sub_component.repeated[idx].index_in_tree;
2375
2376        quote!{
2377            if let Some((parent_component, parent_index)) = self
2378                .parent
2379                .clone()
2380                .upgrade()
2381                .map(|sc| (sp::VRcMapped::origin(&sc), sc.tree_index_of_first_child.get()))
2382            {
2383                *_result = sp::ItemRc::new(parent_component, parent_index + #sub_component_offset - 1)
2384                    .downgrade();
2385            }
2386        }
2387    }));
2388    let mut item_tree_array: Vec<TokenStream> = Vec::new();
2389    let mut item_array = Vec::new();
2390    let mut z_sorted_nodes: Vec<(usize, &llr::TreeNode)> = Vec::new();
2391    sub_tree.tree.visit_in_array(&mut |node, children_offset, parent_index| {
2392        let parent_index = parent_index as u32;
2393        let (_, component) =
2394            follow_sub_component_path(root, sub_tree.root, &node.sub_component_path);
2395
2396        if node.z_sort_order_property.is_some() {
2397            z_sorted_nodes.push((item_tree_array.len(), node));
2398        }
2399
2400        match node.item_index {
2401            Either::Right(mut repeater_index) => {
2402                assert_eq!(node.children.len(), 0);
2403                let mut sub_component = &root.sub_components[sub_tree.root];
2404                for i in &node.sub_component_path {
2405                    repeater_index += sub_component.sub_components[*i].repeater_offset;
2406                    sub_component = &root.sub_components[sub_component.sub_components[*i].ty];
2407                }
2408                item_tree_array.push(quote!(
2409                    sp::ItemTreeNode::DynamicTree {
2410                        index: #repeater_index,
2411                        parent_index: #parent_index,
2412                    }
2413                ));
2414            }
2415            Either::Left(item_index) => {
2416                let item = &component.items[item_index];
2417
2418                let children_count = node.children.len() as u32;
2419                let children_index = children_offset as u32;
2420                let item_array_len = item_array.len() as u32;
2421                let is_accessible = node.is_accessible;
2422                item_tree_array.push(quote!(
2423                    sp::ItemTreeNode::Item {
2424                        is_accessible: #is_accessible,
2425                        children_count: #children_count,
2426                        children_index: #children_index,
2427                        parent_index: #parent_index,
2428                        item_array_index: #item_array_len,
2429                    }
2430                ));
2431
2432                // The array is const, so nested offsets are composed with the
2433                // const compose_field_offsets rather than the non-const `+`.
2434                let mut sc = &root.sub_components[sub_tree.root];
2435                let mut offsets = Vec::new();
2436                for i in &node.sub_component_path {
2437                    offsets.push(access_component_field_offset(
2438                        &self::inner_component_id(sc),
2439                        &ident(&sc.sub_components[*i].name),
2440                    ));
2441                    sc = &root.sub_components[sc.sub_components[*i].ty];
2442                }
2443                let offset = offsets.into_iter().rfold(
2444                    access_component_field_offset(
2445                        &self::inner_component_id(component),
2446                        &ident(&item.name),
2447                    ),
2448                    |acc, seg| quote!(sp::compose_field_offsets(#seg, #acc)),
2449                );
2450                item_array.push(quote!(sp::VOffset::new(#offset)));
2451            }
2452        }
2453    });
2454
2455    let item_tree_array_len = item_tree_array.len();
2456    let item_array_len = item_array.len();
2457
2458    let element_info_body = if root.has_debug_info {
2459        quote!(
2460            *_result = self.item_element_infos(_index).unwrap_or_default();
2461            true
2462        )
2463    } else {
2464        quote!(false)
2465    };
2466
2467    // SystemTrayIcon-only compilation units don't have a `WindowAdapter` on
2468    // SharedGlobals, so the per-tree register / unregister / vtable hooks
2469    // skip the adapter-touching paths and bottom out at None. Without a
2470    // `WindowAdapter` there's no renderer to free graphics resources with
2471    // and tray-rooted items allocate none, so the `PinnedDrop` impl is
2472    // omitted entirely (the struct uses `#[pin]` instead of `#[pin_drop]`).
2473    let (register_window_adapter_arg, pinned_drop_impl, window_adapter_vtable_body): (
2474        TokenStream,
2475        Option<TokenStream>,
2476        TokenStream,
2477    ) = if needs_window_adapter {
2478        (
2479            quote!(globals.maybe_window_adapter_impl()),
2480            Some(quote!(
2481                impl sp::PinnedDrop for #inner_component_id {
2482                    fn drop(self: ::core::pin::Pin<&mut #inner_component_id>) {
2483                        sp::vtable::new_vref!(let vref : VRef<sp::ItemTreeVTable> for sp::ItemTree = self.as_ref().get_ref());
2484                        if let Some(wa) = self.globals.get().unwrap().maybe_window_adapter_impl() {
2485                            sp::unregister_item_tree(self.as_ref(), vref, Self::item_array(), &wa);
2486                        }
2487                    }
2488                }
2489            )),
2490            quote!(if do_create {
2491                *result = sp::Some(self.globals.get().unwrap().window_adapter_impl());
2492            } else {
2493                *result = self.globals.get().unwrap().maybe_window_adapter_impl();
2494            }),
2495        )
2496    } else {
2497        (
2498            quote!(::core::option::Option::None),
2499            None,
2500            // Always None for tray-rooted trees: there's no adapter to hand
2501            // out and `do_create=true` must not silently materialize one.
2502            quote!(
2503                let _ = do_create;
2504                *result = sp::None;
2505            ),
2506        )
2507    };
2508
2509    let default_call = quote!(sp::visit_item_tree(
2510        &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()),
2511        self.get_item_tree().as_slice(),
2512        index,
2513        order,
2514        visitor,
2515        &mut |order, visitor, dyn_index| self.visit_dynamic_children(dyn_index, order, visitor),
2516    ));
2517    let z_sorted_visit_body = if z_sorted_nodes.is_empty() {
2518        quote!(return #default_call;)
2519    } else {
2520        let ctx = EvaluationContext::new_sub_component(
2521            root,
2522            sub_tree.root,
2523            RustGeneratorContext { global_access: quote!(_self.globals.get().unwrap()) },
2524            parent_ctx,
2525        );
2526        let z_match_arms = z_sorted_nodes.iter().map(|(node_idx, node)| {
2527            let idx_lit = *node_idx as isize;
2528            let sources = node.z_sort_order_property.as_ref().unwrap();
2529            // The closure pushes one (child_offset, instance, z) entry per child, or one
2530            // per instance for repeated children with per-instance z; the runtime sorts
2531            // the entries and visits them in z order.
2532            let pushes = sources.iter().zip(&node.children).enumerate().map(|(k, (source, child))| {
2533                let k = k as u32;
2534                match source {
2535                    llr::ZSource::Expression(e) => {
2536                        let e = compile_expression(&e.borrow(), &ctx);
2537                        quote!(push(#k, sp::None, #e as f32);)
2538                    }
2539                    llr::ZSource::RepeaterInstances => {
2540                        let itertools::Either::Right(repeater_index) = child.item_index else {
2541                            unreachable!("per-instance z is only set on repeated children")
2542                        };
2543                        let (compo_path, sub_component) =
2544                            follow_sub_component_path(root, sub_tree.root, &child.sub_component_path);
2545                        let rep_field = access_component_field_offset(
2546                            &self::inner_component_id(sub_component),
2547                            &format_ident!("repeater{}", repeater_index),
2548                        );
2549                        quote!((#compo_path #rep_field).apply_pin(_self).for_each_instance_z(&mut |instance, z| push(#k, sp::Some(instance), z));)
2550                    }
2551                }
2552            });
2553            quote! {
2554                #idx_lit => {
2555                    return sp::visit_item_tree_z_sorted(
2556                        &sp::VRcMapped::origin(&self.as_ref().self_weak.get().unwrap().upgrade().unwrap()),
2557                        self.get_item_tree().as_slice(),
2558                        index,
2559                        order,
2560                        visitor,
2561                        &mut |order, visitor, dyn_index| self.visit_dynamic_children(dyn_index, order, visitor),
2562                        &mut |push| { #(#pushes)* },
2563                    );
2564                }
2565            }
2566        });
2567        quote! {
2568            let _self = self;
2569            match index {
2570                #(#z_match_arms)*
2571                _ => return #default_call,
2572            }
2573        }
2574    };
2575
2576    quote!(
2577        #sub_comp
2578
2579        impl #inner_component_id {
2580            fn new(#(parent: #parent_component_type,)* #globals_arg) -> ::core::result::Result<sp::VRc<sp::ItemTreeVTable, Self>, slint::PlatformError> {
2581                #![allow(unused)]
2582                let mut _self = Self::default();
2583                #(_self.parent = parent.clone() as #parent_component_type;)*
2584                let self_rc = sp::VRc::new(_self);
2585                let self_dyn_rc = sp::VRc::into_dyn(self_rc.clone());
2586                let globals = #globals;
2587                #set_and_init_globals
2588                sp::register_item_tree(&self_dyn_rc, #register_window_adapter_arg);
2589                Self::init(sp::VRc::map(self_rc.clone(), |x| x), globals, 0, 1)?;
2590                ::core::result::Result::Ok(self_rc)
2591            }
2592
2593            fn item_tree() -> &'static [sp::ItemTreeNode] {
2594                const ITEM_TREE : [sp::ItemTreeNode; #item_tree_array_len] = [#(#item_tree_array),*];
2595                &ITEM_TREE
2596            }
2597
2598            fn item_array() -> &'static [sp::VOffset<Self, sp::ItemVTable, sp::AllowPin>] {
2599                const ITEM_ARRAY : [sp::VOffset<#inner_component_id, sp::ItemVTable, sp::AllowPin>; #item_array_len]
2600                    = [#(#item_array),*];
2601                &ITEM_ARRAY
2602            }
2603        }
2604
2605        const _ : () = {
2606            use slint::private_unstable_api::re_exports::*;
2607            ItemTreeVTable_static!(static VT for self::#inner_component_id);
2608        };
2609
2610        #pinned_drop_impl
2611
2612        impl sp::ItemTree for #inner_component_id {
2613            fn visit_children_item(self: ::core::pin::Pin<&Self>, index: isize, order: sp::TraversalOrder, visitor: sp::ItemVisitorRefMut<'_>)
2614                -> sp::VisitChildrenResult
2615            {
2616                #z_sorted_visit_body
2617            }
2618
2619            fn get_item_ref(self: ::core::pin::Pin<&Self>, index: u32) -> ::core::pin::Pin<sp::ItemRef<'_>> {
2620                match &self.get_item_tree().as_slice()[index as usize] {
2621                    sp::ItemTreeNode::Item { item_array_index, .. } => {
2622                        Self::item_array()[*item_array_index as usize].apply_pin(self)
2623                    }
2624                    sp::ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
2625
2626                }
2627            }
2628
2629            fn get_item_tree(
2630                self: ::core::pin::Pin<&Self>) -> sp::Slice<'_, sp::ItemTreeNode>
2631            {
2632                Self::item_tree().into()
2633            }
2634
2635            fn get_subtree_range(
2636                self: ::core::pin::Pin<&Self>, index: u32) -> sp::IndexRange
2637            {
2638                self.subtree_range(index)
2639            }
2640
2641            fn get_subtree(
2642                self: ::core::pin::Pin<&Self>, index: u32, subtree_index: usize, result: &mut sp::ItemTreeWeak)
2643            {
2644                self.subtree_component(index, subtree_index, result);
2645            }
2646
2647            fn subtree_index(
2648                self: ::core::pin::Pin<&Self>) -> usize
2649            {
2650                self.index_property()
2651            }
2652
2653            fn parent_node(self: ::core::pin::Pin<&Self>, _result: &mut sp::ItemWeak) {
2654                #parent_item_expression
2655            }
2656
2657            fn embed_component(self: ::core::pin::Pin<&Self>, _parent_component: &sp::ItemTreeWeak, _item_tree_index: u32) -> bool {
2658                #embedding_function
2659            }
2660
2661            fn layout_info(self: ::core::pin::Pin<&Self>, orientation: sp::Orientation) -> sp::LayoutInfo {
2662                self.layout_info(orientation)
2663            }
2664
2665            fn ensure_instantiated(self: ::core::pin::Pin<&Self>) -> bool {
2666                self.ensure_instantiated()
2667            }
2668
2669            fn item_geometry(self: ::core::pin::Pin<&Self>, index: u32) -> sp::LogicalRect {
2670                self.item_geometry(index)
2671            }
2672
2673            fn accessible_role(self: ::core::pin::Pin<&Self>, index: u32) -> sp::AccessibleRole {
2674                self.accessible_role(index)
2675            }
2676
2677            fn accessible_string_property(
2678                self: ::core::pin::Pin<&Self>,
2679                index: u32,
2680                what: sp::AccessibleStringProperty,
2681                result: &mut sp::SharedString,
2682            ) -> bool {
2683                if let Some(r) = self.accessible_string_property(index, what) {
2684                    *result = r;
2685                    true
2686                } else {
2687                    false
2688                }
2689            }
2690
2691            fn accessibility_action(self: ::core::pin::Pin<&Self>, index: u32, action: &sp::AccessibilityAction) {
2692                self.accessibility_action(index, action);
2693            }
2694
2695            fn supported_accessibility_actions(self: ::core::pin::Pin<&Self>, index: u32) -> sp::SupportedAccessibilityAction {
2696                self.supported_accessibility_actions(index)
2697            }
2698
2699            fn item_element_infos(
2700                self: ::core::pin::Pin<&Self>,
2701                _index: u32,
2702                _result: &mut sp::SharedString,
2703            ) -> bool {
2704                #element_info_body
2705            }
2706
2707            fn window_adapter(
2708                self: ::core::pin::Pin<&Self>,
2709                do_create: bool,
2710                result: &mut sp::Option<sp::Rc<dyn sp::WindowAdapter>>,
2711            ) {
2712                #window_adapter_vtable_body
2713            }
2714        }
2715
2716
2717    )
2718}
2719
2720fn generate_repeated_component(
2721    repeated: &llr::RepeatedElement,
2722    unit: &llr::CompilationUnit,
2723    parent_ctx: &ParentScope,
2724) -> TokenStream {
2725    let component =
2726        generate_item_tree(&repeated.sub_tree, unit, Some(parent_ctx), repeated.index_prop, false);
2727
2728    let ctx = EvaluationContext {
2729        compilation_unit: unit,
2730        current_scope: EvaluationScope::SubComponent(repeated.sub_tree.root, Some(parent_ctx)),
2731        generator_state: RustGeneratorContext { global_access: quote!(_self.globals()) },
2732        argument_types: &[],
2733    };
2734
2735    let root_sc = &unit.sub_components[repeated.sub_tree.root];
2736    let inner_component_id = self::inner_component_id(root_sc);
2737
2738    let grid_layout_input_data_fn = root_sc.grid_layout_input_for_repeated.as_ref().map(|_| {
2739        let has_inner_repeaters = llr::has_inner_repeaters(&root_sc.row_child_templates);
2740        if has_inner_repeaters {
2741            let templates = root_sc.row_child_templates.as_ref().unwrap();
2742            let static_count = llr::static_child_count(templates);
2743
2744            // Generate fill code: one snippet per template entry.
2745            // new_row is re-evaluated per slot (write_idx == 0 && new_row) so that only the
2746            // first produced slot is marked as starting a new row.
2747            let fill_code: Vec<TokenStream> = templates
2748                .iter()
2749                .map(|entry| match entry {
2750                    llr::RowChildTemplateInfo::Static { .. } => quote! {
2751                        if write_idx < result.len() {
2752                            let mut data = statics[static_idx].clone();
2753                            data.new_row = write_idx == 0 && new_row;
2754                            result[write_idx] = data;
2755                        }
2756                        write_idx += 1;
2757                        static_idx += 1;
2758                    },
2759                    llr::RowChildTemplateInfo::Repeated { repeater_index, .. } => {
2760                        let inner_rep_id =
2761                            format_ident!("repeater{}", usize::from(*repeater_index));
2762                        quote! {
2763                            #inner_component_id::FIELD_OFFSETS.#inner_rep_id().apply_pin(_self.as_ref()).track_instance_changes();
2764                            let inner_len = _self.as_ref().#inner_rep_id.len();
2765                            for _i in 0..inner_len {
2766                                if write_idx < result.len() {
2767                                    // Let the inner cell report its own col/row/colspan/rowspan.
2768                                    if let Some(inner) = _self.as_ref().#inner_rep_id.instance_at(_i) {
2769                                        inner.as_pin_ref().grid_layout_input_data(write_idx == 0 && new_row, core::slice::from_mut(&mut result[write_idx]));
2770                                    }
2771                                }
2772                                write_idx += 1;
2773                            }
2774                        }
2775                    }
2776                })
2777                .collect();
2778            let static_setup = if static_count > 0 {
2779                quote! {
2780                    let mut statics: [sp::GridLayoutInputData; #static_count] =
2781                        ::core::array::from_fn(|_| Default::default());
2782                    _self.as_ref().grid_layout_input_for_repeated(new_row, &mut statics);
2783                    let mut static_idx: usize = 0;
2784                }
2785            } else {
2786                quote! {}
2787            };
2788            let static_finalize = if static_count > 0 {
2789                quote! {
2790                    let _ = static_idx; // avoid unused_assignments warning
2791                }
2792            } else {
2793                quote! {}
2794            };
2795
2796            quote! {
2797                fn grid_layout_input_data(
2798                    self: ::core::pin::Pin<&Self>,
2799                    new_row: bool,
2800                    result: &mut [sp::GridLayoutInputData],
2801                ) {
2802                    let _self = self;
2803                    #static_setup
2804                    let mut write_idx: usize = 0;
2805                    #(#fill_code)*
2806                    #static_finalize
2807                    // Fill any remaining slots with auto-placed placeholders
2808                    // (Default leads to col=ROW_COL_AUTO, row=ROW_COL_AUTO, colspan=1, rowspan=1).
2809                    result[write_idx..].fill(Default::default());
2810                }
2811            }
2812        } else {
2813            quote! {
2814                fn grid_layout_input_data(
2815                    self: ::core::pin::Pin<&Self>,
2816                    new_row: bool,
2817                    result: &mut [sp::GridLayoutInputData],
2818                ) {
2819                    self.as_ref().grid_layout_input_for_repeated(new_row, result)
2820                }
2821            }
2822        }
2823    });
2824
2825    let extra_fn = if let Some(listview) = &repeated.listview {
2826        let p_y = access_member(&listview.prop_y, &ctx).unwrap();
2827        let p_height = access_member(&listview.prop_height, &ctx).unwrap();
2828        quote! {
2829            fn listview_layout(
2830                self: ::core::pin::Pin<&Self>,
2831                offset_y: &mut sp::LogicalLength,
2832            ) -> sp::LogicalLength {
2833                let _self = self;
2834                #p_y.set(*offset_y);
2835                *offset_y += #p_height.get();
2836                sp::LogicalLength::new(self.as_ref().layout_info(sp::Orientation::Horizontal).min)
2837            }
2838        }
2839    } else {
2840        // The cell's `cross-axis-self-alignment` in a box layout, filled into the
2841        // returned LayoutItemInfo for the cross axis only, so the main-axis cache
2842        // stays independent of it; the remaining literals default it to `auto`.
2843        let align_self_field =
2844            root_sc.cross_axis_self_alignment_for_repeated.as_ref().map(|(cross_o, _)| {
2845                let cross_o = match cross_o {
2846                    Orientation::Horizontal => quote!(sp::Orientation::Horizontal),
2847                    Orientation::Vertical => quote!(sp::Orientation::Vertical),
2848                };
2849                quote!(cross_axis_self_alignment: if o == #cross_o {
2850                    self.as_ref().cross_axis_self_alignment_for_repeated()
2851                } else {
2852                    ::core::default::Default::default()
2853                },)
2854            });
2855        // Likewise `layout-order`, for the main axis only: it just reorders that
2856        // solve.
2857        let order_field = root_sc.layout_order_for_repeated.as_ref().map(|(main_o, _)| {
2858            let main_o = match main_o {
2859                Orientation::Horizontal => quote!(sp::Orientation::Horizontal),
2860                Orientation::Vertical => quote!(sp::Orientation::Vertical),
2861            };
2862            quote!(layout_order: if o == #main_o {
2863                self.as_ref().layout_order_for_repeated()
2864            } else {
2865                0
2866            },)
2867        });
2868        let layout_item_info_fn = root_sc.child_of_layout.then(|| {
2869            // Generate layout_item_info (from the RepeatedItemTree trait) in terms of ItemTree::layout_info
2870            if root_sc.is_repeated_row {
2871                // Repeated grid Rows cannot carry per-item box layout properties;
2872                // the row-scan literals below default those fields.
2873                debug_assert!(root_sc.cross_axis_self_alignment_for_repeated.is_none());
2874                debug_assert!(root_sc.layout_order_for_repeated.is_none());
2875
2876                // A GridLayout measures an inner repeated child at the column
2877                // width it assigns it, like the static children measure at
2878                // their own (lazily pulled) width.
2879                let inner_constraint = |measure_at_cross_width: bool| {
2880                    let Some(e) =
2881                        root_sc.grid_row_child_cross_width.as_ref().filter(|_| measure_at_cross_width)
2882                    else {
2883                        return quote!(inner.as_pin_ref().layout_info(o));
2884                    };
2885                    let idx = ident(GRID_MEASURE_CHILD_INDEX_LOCAL);
2886                    let w = compile_expression(&e.borrow(), &ctx);
2887                    quote!(match o {
2888                        sp::Orientation::Vertical => inner
2889                            .as_pin_ref()
2890                            .layout_item_info_at_cross_width(({ let #idx = index; #w }) as f32)
2891                            .constraint,
2892                        sp::Orientation::Horizontal => inner.as_pin_ref().layout_info(o),
2893                    })
2894                };
2895                let body = if let Some(templates) = &root_sc.row_child_templates {
2896                    // Generate a sequential scan through all templates in declaration order.
2897                    // For each Static: check if count == index and return the precomputed info.
2898                    // For each Repeated: check if index falls within [count, count+len), and return the inner instance's info.
2899                    let n = templates.len();
2900                    let scan_steps: Vec<TokenStream> = templates
2901                        .iter()
2902                        .enumerate()
2903                        .map(|(i, entry)| {
2904                            let is_last = i + 1 == n;
2905                            match entry {
2906                            llr::RowChildTemplateInfo::Static { child_index } => {
2907                                let child = &root_sc.grid_layout_children[*child_index];
2908                                let layout_info_h_code =
2909                                    compile_expression(&child.layout_info_h.borrow(), &ctx);
2910                                let layout_info_v_code =
2911                                    compile_expression(&child.layout_info_v.borrow(), &ctx);
2912                                let advance = (!is_last).then(|| quote! { count += 1; });
2913                                quote! {
2914                                    if count == index {
2915                                        return sp::LayoutItemInfo {
2916                                            constraint: match o {
2917                                                sp::Orientation::Horizontal => #layout_info_h_code,
2918                                                sp::Orientation::Vertical => #layout_info_v_code,
2919                                            },
2920                                            ..::core::default::Default::default()
2921                                        };
2922                                    }
2923                                    #advance
2924                                }
2925                            }
2926                            llr::RowChildTemplateInfo::Repeated {
2927                                repeater_index,
2928                                measure_at_cross_width,
2929                            } => {
2930                                let inner_rep_id =
2931                                    format_ident!("repeater{}", usize::from(*repeater_index));
2932                                let inner_constraint = inner_constraint(*measure_at_cross_width);
2933                                let advance = (!is_last).then(|| quote! { count += inner_len; });
2934                                quote! {
2935                                    {
2936                                        #inner_component_id::FIELD_OFFSETS.#inner_rep_id().apply_pin(_self).track_instance_changes();
2937                                        let inner_len = _self.#inner_rep_id.len();
2938                                        if index >= count && index - count < inner_len {
2939                                            if let Some(inner) = _self.#inner_rep_id.instance_at(index - count) {
2940                                                return sp::LayoutItemInfo {
2941                                                    constraint: #inner_constraint,
2942                                                    ..::core::default::Default::default()
2943                                                };
2944                                            }
2945                                        }
2946                                        #advance
2947                                    }
2948                                }
2949                            }
2950                        }})
2951                        .collect();
2952
2953                    quote! {
2954                        #[allow(unused)]
2955                        if let Some(index) = child_index {
2956                            let _self = self.as_ref();
2957                            let mut count = 0usize;
2958                            #(#scan_steps)*
2959                            sp::LayoutItemInfo::default()
2960                        } else {
2961                            sp::LayoutItemInfo { constraint: self.as_ref().layout_info(o), #align_self_field #order_field ..::core::default::Default::default() }
2962                        }
2963                    }
2964                } else {
2965                    quote! {
2966                        sp::LayoutItemInfo { constraint: self.as_ref().layout_info(o), #align_self_field #order_field ..::core::default::Default::default() }
2967                    }
2968                };
2969
2970                quote! {
2971                    fn layout_item_info(
2972                        self: ::core::pin::Pin<&Self>,
2973                        o: sp::Orientation,
2974                        child_index: sp::Option<usize>,
2975                    ) -> sp::LayoutItemInfo {
2976                        #body
2977                    }
2978                }
2979            } else { // not a repeated row
2980                quote! {
2981                    fn layout_item_info(
2982                        self: ::core::pin::Pin<&Self>,
2983                        o: sp::Orientation,
2984                        _child_index: sp::Option<usize>,
2985                    ) -> sp::LayoutItemInfo {
2986                        sp::LayoutItemInfo { constraint: self.as_ref().layout_info(o), #align_self_field #order_field ..::core::default::Default::default() }
2987                    }
2988                }
2989            }
2990        });
2991        let flexbox_layout_item_info_fn =
2992            root_sc.flexbox_layout_item_info_for_repeated.as_ref().map(|_| {
2993                // Break the height-for-width recursion for a repeated instance
2994                // in a column FlexboxLayout: its vertical info must not read
2995                // self.width (set by the parent flex cache it is feeding).
2996                // Use the constrained vertical info (computed at the instance's
2997                // own preferred width via layoutinfo-v-with-constraint) instead.
2998                let v_constrained =
2999                    root_sc.layout_info_v_constrained_for_repeated.as_ref().map(|e| {
3000                        let v_info = compile_expression(&e.borrow(), &ctx);
3001                        quote! {
3002                            if matches!(o, sp::Orientation::Vertical) && child_index.is_none() {
3003                                info.constraint = #v_info;
3004                                return info;
3005                            }
3006                        }
3007                    });
3008                // A column FlexboxLayout calls this with its real container width
3009                // so a height-for-width instance wraps to the same height as an
3010                // equivalent static cell (instead of the preferred-width single
3011                // line that `flexbox_layout_item_info` returns). The expression
3012                // reads the `cross_width` local (matches CROSS_WIDTH_LOCAL).
3013                let at_cross_width_body = root_sc
3014                    .layout_info_v_at_cross_width_for_repeated
3015                    .as_ref()
3016                    .map(|e| {
3017                        let v_info = compile_expression(&e.borrow(), &ctx);
3018                        quote! { info.constraint = #v_info; }
3019                    })
3020                    .unwrap_or_else(|| {
3021                        quote! {
3022                            info.constraint =
3023                                self.layout_item_info(sp::Orientation::Vertical, sp::None).constraint;
3024                        }
3025                    });
3026                let cross_width_param = ident(CROSS_WIDTH_LOCAL);
3027                quote! {
3028                    fn flexbox_layout_item_info(
3029                        self: ::core::pin::Pin<&Self>,
3030                        o: sp::Orientation,
3031                        child_index: sp::Option<usize>,
3032                    ) -> sp::FlexboxLayoutItemInfo {
3033                        #[allow(unused)]
3034                        let _self = self.as_ref();
3035                        let mut info = self.as_ref().flexbox_layout_item_info_for_repeated();
3036                        #v_constrained
3037                        info.constraint = self.layout_item_info(o, child_index).constraint;
3038                        info
3039                    }
3040                    #[allow(unused_variables)]
3041                    fn flexbox_layout_item_info_at_cross_width(
3042                        self: ::core::pin::Pin<&Self>,
3043                        #cross_width_param: f32,
3044                    ) -> sp::FlexboxLayoutItemInfo {
3045                        #[allow(unused)]
3046                        let _self = self.as_ref();
3047                        let mut info = self.as_ref().flexbox_layout_item_info_for_repeated();
3048                        #at_cross_width_body
3049                        info
3050                    }
3051                }
3052            });
3053        // A box layout calls this with the width it lays the instance out at,
3054        // so a height-for-width instance measures like an equivalent static
3055        // cell. Mirrors the flexbox `flexbox_layout_item_info_at_cross_width`;
3056        // the trait default (the plain `layout_item_info`) covers the other
3057        // repeated components. The per-item fields are the same as in
3058        // `layout_item_info`; `o` is fixed, so bind it locally and reuse those
3059        // guards. Don't delegate to `layout_item_info` for them: it measures
3060        // the constraint through `layout_info`, which is what this accessor
3061        // exists to avoid.
3062        let layout_item_info_at_cross_width_fn = root_sc
3063            .layout_info_v_at_cross_width_for_repeated
3064            .as_ref()
3065            .filter(|_| root_sc.flexbox_layout_item_info_for_repeated.is_none())
3066            .map(|e| {
3067                let info = compile_expression(&e.borrow(), &ctx);
3068                let param = ident(CROSS_WIDTH_LOCAL);
3069                quote! {
3070                    fn layout_item_info_at_cross_width(
3071                        self: ::core::pin::Pin<&Self>,
3072                        #param: f32,
3073                    ) -> sp::LayoutItemInfo {
3074                        #[allow(unused)]
3075                        let _self = self.as_ref();
3076                        #[allow(unused)]
3077                        let o = sp::Orientation::Vertical;
3078                        sp::LayoutItemInfo { constraint: #info, #align_self_field #order_field ..::core::default::Default::default() }
3079                    }
3080                }
3081            });
3082        quote! {
3083            #layout_item_info_fn
3084            #flexbox_layout_item_info_fn
3085            #layout_item_info_at_cross_width_fn
3086            #grid_layout_input_data_fn
3087        }
3088    };
3089
3090    let data_type = if let Some(data_prop) = repeated.data_prop {
3091        rust_primitive_type(&root_sc.properties[data_prop].ty).unwrap()
3092    } else {
3093        quote!(())
3094    };
3095
3096    let access_prop =
3097        |property_index: llr::PropertyIdx| access_local_member(&property_index.into(), &ctx);
3098    let index_prop = repeated.index_prop.into_iter().map(access_prop);
3099    let set_data_expr = repeated.data_prop.into_iter().map(|property_index| {
3100        let prop_type = ctx.relative_property_ty(&property_index.into(), 0);
3101        let data_prop = access_prop(property_index);
3102        let value_tokens = set_primitive_property_value(prop_type, quote!(_data));
3103        quote!(#data_prop.set(#value_tokens);)
3104    });
3105    let z_order_fn = repeated.dynamic_z.as_ref().map(|z_ref| {
3106        let ctx = EvaluationContext::new_sub_component(
3107            unit,
3108            repeated.sub_tree.root,
3109            RustGeneratorContext { global_access: quote!(_self.globals.get().unwrap()) },
3110            Some(parent_ctx),
3111        );
3112        let z_prop = access_member(z_ref, &ctx).get_property();
3113        quote! {
3114            fn z_order(self: ::core::pin::Pin<&Self>) -> sp::Option<f32> {
3115                let _self = self;
3116                sp::Some(#z_prop as f32)
3117            }
3118        }
3119    });
3120
3121    quote!(
3122        #component
3123
3124        impl sp::RepeatedItemTree for #inner_component_id {
3125            type Data = #data_type;
3126            fn update(&self, _index: usize, _data: Self::Data) {
3127                let self_rc = self.self_weak.get().unwrap().upgrade().unwrap();
3128                let _self = self_rc.as_pin_ref();
3129                #(#index_prop.set(_index as _);)*
3130                #(#set_data_expr)*
3131            }
3132            fn init(&self) {
3133                let self_rc = self.self_weak.get().unwrap().upgrade().unwrap();
3134                #inner_component_id::user_init(
3135                    sp::VRcMapped::map(self_rc, |x| x),
3136                );
3137            }
3138            #z_order_fn
3139            #extra_fn
3140        }
3141    )
3142}
3143
3144/// Return an identifier suitable for this component for internal use
3145fn inner_component_id(component: &llr::SubComponent) -> proc_macro2::Ident {
3146    format_ident!("Inner{}", ident(&component.name))
3147}
3148
3149fn internal_popup_id(index: usize) -> proc_macro2::Ident {
3150    let mut name = index.to_string();
3151    name.insert_str(0, "popup_id_");
3152    ident(&name)
3153}
3154
3155fn global_inner_name(g: &llr::GlobalComponent) -> TokenStream {
3156    if g.is_builtin {
3157        let i = ident(&g.name);
3158        quote!(sp::#i)
3159    } else {
3160        let i = format_ident!("Inner{}", ident(&g.name));
3161        quote!(#i)
3162    }
3163}
3164
3165fn property_set_value_tokens(
3166    property: &llr::MemberReference,
3167    value_tokens: TokenStream,
3168    ctx: &EvaluationContext,
3169) -> TokenStream {
3170    let prop = access_member(property, ctx);
3171    let prop_type = ctx.property_ty(property);
3172    let value_tokens = set_primitive_property_value(prop_type, value_tokens);
3173    if let Some((animation, map)) = &ctx.property_info(property).animation {
3174        let mut animation = (*animation).clone();
3175        map.map_expression(&mut animation);
3176        let animation_tokens = compile_expression(&animation, ctx);
3177        return prop
3178            .then(|prop| quote!(#prop.set_animated_value(#value_tokens as _, #animation_tokens)));
3179    }
3180    prop.then(|prop| quote!(#prop.set(#value_tokens as _)))
3181}
3182
3183/// Returns the code that can access the given property or callback
3184fn access_member(reference: &llr::MemberReference, ctx: &EvaluationContext) -> MemberAccess {
3185    fn in_global(
3186        g: &llr::GlobalComponent,
3187        index: &llr::LocalMemberIndex,
3188        _self: TokenStream,
3189    ) -> MemberAccess {
3190        let global_name = global_inner_name(g);
3191        match index {
3192            llr::LocalMemberIndex::Property(property_idx) => {
3193                let property_name = ident(&g.properties[*property_idx].name);
3194                let property_field = quote!({ *&#global_name::FIELD_OFFSETS.#property_name() });
3195                MemberAccess::Direct(quote!(#property_field.apply_pin(#_self)))
3196            }
3197            llr::LocalMemberIndex::Callback(callback_idx) => {
3198                let callback_name = ident(&g.callbacks[*callback_idx].name);
3199                let callback_field = quote!({ *&#global_name::FIELD_OFFSETS.#callback_name() });
3200                MemberAccess::Direct(quote!(#callback_field.apply_pin(#_self)))
3201            }
3202            llr::LocalMemberIndex::Function(function_idx) => {
3203                let fn_id = ident(&format!("fn_{}", g.functions[*function_idx].name));
3204                MemberAccess::Direct(quote!(#_self.#fn_id))
3205            }
3206            llr::LocalMemberIndex::Native { .. } | llr::LocalMemberIndex::Timer(_) => {
3207                unreachable!()
3208            }
3209        }
3210    }
3211
3212    match reference {
3213        llr::MemberReference::Relative { parent_level, local_reference } => {
3214            if let Some(current_global) = ctx.current_global() {
3215                return in_global(current_global, &local_reference.reference, quote!(_self));
3216            }
3217
3218            let parent_path = parent_access_path(*parent_level);
3219
3220            match &local_reference.reference {
3221                llr::LocalMemberIndex::Property(property_index) => {
3222                    let (compo_path, sub_component) = follow_sub_component_path(
3223                        ctx.compilation_unit,
3224                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
3225                        &local_reference.sub_component_path,
3226                    );
3227                    let component_id = inner_component_id(sub_component);
3228                    let property_name = ident(&sub_component.properties[*property_index].name);
3229                    let property_field =
3230                        access_component_field_offset(&component_id, &property_name);
3231                    parent_path.map_or_else(
3232                        || MemberAccess::Direct(quote!((#compo_path #property_field).apply_pin(_self))),
3233                        |parent_path| {
3234                            MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #property_field).apply_pin(x.as_pin_ref()))))
3235                        },
3236                    )
3237                }
3238                llr::LocalMemberIndex::Callback(callback_index) => {
3239                    let (compo_path, sub_component) = follow_sub_component_path(
3240                        ctx.compilation_unit,
3241                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
3242                        &local_reference.sub_component_path,
3243                    );
3244                    let component_id = inner_component_id(sub_component);
3245                    let callback_name = ident(&sub_component.callbacks[*callback_index].name);
3246                    let callback_field =
3247                        access_component_field_offset(&component_id, &callback_name);
3248                    parent_path.map_or_else(
3249                        || MemberAccess::Direct(quote!((#compo_path #callback_field).apply_pin(_self))),
3250                        |parent_path| {
3251                            MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #callback_field).apply_pin(x.as_pin_ref()))))
3252                        },
3253                    )
3254                }
3255                llr::LocalMemberIndex::Function(function_index) => {
3256                    let mut sub_component = &ctx.compilation_unit.sub_components
3257                        [ctx.parent_sub_component_idx(*parent_level).unwrap()];
3258                    let mut compo_path = parent_path
3259                        .as_ref()
3260                        .map_or_else(|| quote!(_self), |_| quote!(x.as_pin_ref()));
3261                    for i in &local_reference.sub_component_path {
3262                        let component_id = inner_component_id(sub_component);
3263                        let sub_component_name = ident(&sub_component.sub_components[*i].name);
3264                        let field =
3265                            access_component_field_offset(&component_id, &sub_component_name);
3266                        compo_path = quote!(#field.apply_pin(#compo_path));
3267                        sub_component = &ctx.compilation_unit.sub_components
3268                            [sub_component.sub_components[*i].ty];
3269                    }
3270                    let fn_id =
3271                        ident(&format!("fn_{}", sub_component.functions[*function_index].name));
3272                    parent_path.map_or_else(
3273                        || MemberAccess::Direct(quote!(#compo_path.#fn_id)),
3274                        |parent_path| {
3275                            MemberAccess::OptionFn(parent_path, quote!(|x| #compo_path.#fn_id))
3276                        },
3277                    )
3278                }
3279                llr::LocalMemberIndex::Timer(timer_index) => {
3280                    let (compo_path, sub_component) = follow_sub_component_path(
3281                        ctx.compilation_unit,
3282                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
3283                        &local_reference.sub_component_path,
3284                    );
3285                    let component_id = inner_component_id(sub_component);
3286                    let timer_ident = format_ident!("timer{}", usize::from(*timer_index));
3287                    let timer_field = access_component_field_offset(&component_id, &timer_ident);
3288                    parent_path.map_or_else(
3289                        || MemberAccess::Direct(quote!((#compo_path #timer_field).apply_pin(_self))),
3290                        |parent_path| {
3291                            MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #timer_field).apply_pin(x.as_pin_ref()))))
3292                        },
3293                    )
3294                }
3295                llr::LocalMemberIndex::Native { item_index, prop_name, .. } => {
3296                    let (compo_path, sub_component) = follow_sub_component_path(
3297                        ctx.compilation_unit,
3298                        ctx.parent_sub_component_idx(*parent_level).unwrap(),
3299                        &local_reference.sub_component_path,
3300                    );
3301                    let component_id = inner_component_id(sub_component);
3302                    let item_name = ident(&sub_component.items[*item_index].name);
3303                    let item_field = access_component_field_offset(&component_id, &item_name);
3304                    if prop_name.is_empty() {
3305                        // then this is actually a reference to the element itself
3306                        parent_path.map_or_else(
3307                            || MemberAccess::Direct(quote!((#compo_path #item_field).apply_pin(_self))),
3308                            |parent_path| {
3309                                MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| (#compo_path #item_field).apply_pin(x.as_pin_ref()))))
3310                            }
3311                        )
3312                    } else if matches!(
3313                        sub_component.items[*item_index].ty.lookup_property(prop_name),
3314                        Some(&Type::Function(..))
3315                    ) {
3316                        let property_name = ident(prop_name);
3317                        parent_path.map_or_else(
3318                            || MemberAccess::Direct(quote!((#compo_path #item_field).apply_pin(_self).#property_name)),
3319                            |parent_path| {
3320                                MemberAccess::OptionFn(quote!(#parent_path.as_ref().map(|x| (#compo_path #item_field).apply_pin(x.as_pin_ref()))), quote!(|x| x .#property_name))
3321                            }
3322                        )
3323                    } else {
3324                        let property_name = ident(prop_name);
3325                        let item_ty = ident(&sub_component.items[*item_index].ty.class_name);
3326                        let prop_offset = quote!((#compo_path #item_field + sp::#item_ty::FIELD_OFFSETS.#property_name()));
3327                        parent_path.map_or_else(
3328                            || MemberAccess::Direct(quote!(#prop_offset.apply_pin(_self))),
3329                            |parent_path| {
3330                                MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| #prop_offset.apply_pin(x.as_pin_ref()))))
3331                            }
3332                        )
3333                    }
3334                }
3335            }
3336        }
3337        llr::MemberReference::Global { global_index, member } => {
3338            let global = &ctx.compilation_unit.globals[*global_index];
3339            let s = if matches!(ctx.current_scope, EvaluationScope::Global(i) if i == *global_index)
3340            {
3341                quote!(_self)
3342            } else {
3343                let global_access = &ctx.generator_state.global_access;
3344                let global_id = format_ident!("global_{}", ident(&global.name));
3345                quote!(#global_access.#global_id.as_ref())
3346            };
3347            in_global(global, member, s)
3348        }
3349    }
3350}
3351
3352fn access_local_member(
3353    reference: &llr::LocalMemberReference,
3354    ctx: &EvaluationContext,
3355) -> TokenStream {
3356    access_member(&reference.clone().into(), ctx).unwrap()
3357}
3358
3359/// Helper to access a member property/callback of a component.
3360///
3361/// Because the parent can be deleted (issue #3464), this might be an option when accessing the parent
3362#[derive(Clone)]
3363enum MemberAccess {
3364    /// The token stream is just an expression to the member
3365    Direct(TokenStream),
3366    /// The token stream is a an expression to an option of the member
3367    Option(TokenStream),
3368    /// the first token stream is an option, and the second is a path to the function in a `.map` and it must be called
3369    OptionFn(TokenStream, TokenStream),
3370}
3371
3372impl MemberAccess {
3373    /// Used for code that is meant to return `()`
3374    fn then(self, f: impl FnOnce(TokenStream) -> TokenStream) -> TokenStream {
3375        self.then_named("x", f)
3376    }
3377
3378    /// Like [`Self::then`], but names the binding the member is spliced from, so that this access
3379    /// can be nested inside another one without shadowing it.
3380    fn then_named(self, binding: &str, f: impl FnOnce(TokenStream) -> TokenStream) -> TokenStream {
3381        let binding = format_ident!("{binding}");
3382        match self {
3383            MemberAccess::Direct(t) => f(t),
3384            MemberAccess::Option(t) => {
3385                let r = f(quote!(#binding));
3386                quote!({ let _ = #t.map(|#binding| #r); })
3387            }
3388            MemberAccess::OptionFn(opt, inner) => {
3389                let r = f(inner);
3390                quote!({ let _ = #opt.as_ref().map(#r); })
3391            }
3392        }
3393    }
3394
3395    fn map_or_default(self, f: impl FnOnce(TokenStream) -> TokenStream) -> TokenStream {
3396        match self {
3397            MemberAccess::Direct(t) => f(t),
3398            MemberAccess::Option(t) => {
3399                let r = f(quote!(x));
3400                quote!(#t.map(|x| #r).unwrap_or_default())
3401            }
3402            MemberAccess::OptionFn(opt, inner) => {
3403                let r = f(inner);
3404                quote!(#opt.as_ref().map(#r).unwrap_or_default())
3405            }
3406        }
3407    }
3408
3409    fn get_property(self) -> TokenStream {
3410        match self {
3411            MemberAccess::Direct(t) => quote!(#t.get()),
3412            MemberAccess::Option(t) => {
3413                quote!(#t.map(|x| x.get()).unwrap_or_default())
3414            }
3415            MemberAccess::OptionFn(..) => panic!("function is not a property"),
3416        }
3417    }
3418
3419    /// To be used when we know that the reference was local
3420    #[track_caller]
3421    fn unwrap(&self) -> TokenStream {
3422        match self {
3423            MemberAccess::Direct(t) => quote!(#t),
3424            _ => panic!("not a local property?"),
3425        }
3426    }
3427}
3428
3429fn follow_sub_component_path<'a>(
3430    compilation_unit: &'a llr::CompilationUnit,
3431    root: llr::SubComponentIdx,
3432    sub_component_path: &[llr::SubComponentInstanceIdx],
3433) -> (TokenStream, &'a llr::SubComponent) {
3434    let mut compo_path = quote!();
3435    let mut sub_component = &compilation_unit.sub_components[root];
3436    for i in sub_component_path {
3437        let component_id = inner_component_id(sub_component);
3438        let sub_component_name = ident(&sub_component.sub_components[*i].name);
3439        let field = access_component_field_offset(&component_id, &sub_component_name);
3440        compo_path = quote!(#compo_path #field +);
3441        sub_component = &compilation_unit.sub_components[sub_component.sub_components[*i].ty];
3442    }
3443    (compo_path, sub_component)
3444}
3445
3446/// Like [`follow_sub_component_path`],
3447/// but returns a plain field access suffix (`.sub1.sub2`) instead of a field offset expression.
3448fn follow_sub_component_path_fields<'a>(
3449    compilation_unit: &'a llr::CompilationUnit,
3450    root: llr::SubComponentIdx,
3451    sub_component_path: &[llr::SubComponentInstanceIdx],
3452) -> (TokenStream, &'a llr::SubComponent) {
3453    let mut compo_path = quote!();
3454    let mut sub_component = &compilation_unit.sub_components[root];
3455    for i in sub_component_path {
3456        let sub_component_name = ident(&sub_component.sub_components[*i].name);
3457        compo_path = quote!(#compo_path.#sub_component_name);
3458        sub_component = &compilation_unit.sub_components[sub_component.sub_components[*i].ty];
3459    }
3460    (compo_path, sub_component)
3461}
3462
3463fn access_window_adapter_field(ctx: &EvaluationContext) -> TokenStream {
3464    let global_access = &ctx.generator_state.global_access;
3465    quote!(&#global_access.window_adapter_impl())
3466}
3467
3468/// The component instance that holds the native item `pr` refers to.
3469///
3470/// This is an `Option` when the item lives in an ancestor component: that chain can die while a
3471/// callback of a repeated element is still running — the enclosing popup closed itself, or the
3472/// model dropped the row the element belongs to — and then nothing must be emitted at all.
3473fn item_owner(pr: &llr::MemberReference) -> MemberAccess {
3474    let llr::MemberReference::Relative { parent_level, .. } = pr else { unreachable!() };
3475    match parent_access_path(*parent_level) {
3476        None => MemberAccess::Direct(quote!(_self)),
3477        Some(parent_path) => {
3478            MemberAccess::Option(quote!(#parent_path.as_ref().map(|x| x.as_pin_ref())))
3479        }
3480    }
3481}
3482
3483/// Tokens for the native item `pr` is about, relative to the component instance holding it (see
3484/// [`item_owner`]): the member `pr` designates — the item itself or one of its functions — and the
3485/// item's `ItemRc`.
3486///
3487/// Taking the owner as a parameter is what lets a caller that needs both get them out of a single
3488/// walk of the parent chain.
3489fn native_item_from_owner(
3490    pr: &llr::MemberReference,
3491    ctx: &EvaluationContext,
3492    owner: &TokenStream,
3493) -> (TokenStream, TokenStream) {
3494    let llr::MemberReference::Relative { parent_level, local_reference } = pr else {
3495        unreachable!()
3496    };
3497    let llr::LocalMemberIndex::Native { item_index, prop_name, .. } = &local_reference.reference
3498    else {
3499        unreachable!()
3500    };
3501    let root = ctx.parent_sub_component_idx(*parent_level).unwrap();
3502    let (compo_path, sub_component) =
3503        follow_sub_component_path(ctx.compilation_unit, root, &local_reference.sub_component_path);
3504    let component_id = inner_component_id(sub_component);
3505    let item_name = ident(&sub_component.items[*item_index].name);
3506    let item_field = access_component_field_offset(&component_id, &item_name);
3507    let mut member = quote!((#compo_path #item_field).apply_pin(#owner));
3508    if !prop_name.is_empty() {
3509        // a builtin member function of the item, like `TextInput::select-all`
3510        let property_name = ident(prop_name);
3511        member = quote!(#member.#property_name);
3512    }
3513
3514    let (suffix, _) = follow_sub_component_path_fields(
3515        ctx.compilation_unit,
3516        root,
3517        &local_reference.sub_component_path,
3518    );
3519    let compo = quote!(#owner #suffix);
3520    let item_index_in_tree = sub_component.items[*item_index].index_in_tree;
3521    let item_index_tokens = if item_index_in_tree == 0 {
3522        quote!(#compo.tree_index.get())
3523    } else {
3524        quote!(#compo.tree_index_of_first_child.get() + #item_index_in_tree - 1)
3525    };
3526    (member, quote!(sp::ItemRc::new(#compo.origin_rc(), #item_index_tokens)))
3527}
3528
3529/// Compile `expr` to a Rust expression returning an owned value.
3530fn compile_expression_to_value(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
3531    /// Whether the code generated by [`compile_expression`] always evaluates to a fresh owned
3532    /// value, so that no `.clone()` is needed to use it as a value.
3533    fn produces_owned_value(expr: &Expression) -> bool {
3534        match expr {
3535            Expression::StringLiteral(..)
3536            | Expression::NumberLiteral(..)
3537            | Expression::BoolLiteral(..)
3538            | Expression::KeysLiteral(..)
3539            // compiles to a `.get()` (or `.map(...).unwrap_or_default()`) call
3540            | Expression::PropertyReference(..)
3541            // compiles to `args.N.clone()`
3542            | Expression::FunctionParameterReference { .. }
3543            // compiles to `x.row_data_tracked(i).unwrap_or_default()`
3544            | Expression::ArrayIndex { .. }
3545            | Expression::Cast { .. }
3546            | Expression::BuiltinFunctionCall { .. }
3547            | Expression::CallBackCall { .. }
3548            | Expression::FunctionCall { .. }
3549            | Expression::ItemMemberFunctionCall { .. }
3550            | Expression::ExtraBuiltinFunctionCall { .. }
3551            | Expression::BinaryExpression { .. }
3552            | Expression::UnaryOp { .. }
3553            | Expression::ImageReference { .. }
3554            | Expression::Array { .. }
3555            | Expression::Struct { .. }
3556            | Expression::EasingCurve(..)
3557            | Expression::LinearGradient { .. }
3558            | Expression::RadialGradient { .. }
3559            | Expression::ConicGradient { .. }
3560            | Expression::EnumerationValue(..)
3561            | Expression::Closure { .. } => true,
3562            Expression::Condition { true_expr, false_expr, .. } => {
3563                produces_owned_value(true_expr) && produces_owned_value(false_expr)
3564            }
3565            // an empty code block evaluates to `()`, which is owned
3566            Expression::CodeBlock(b) => b.last().is_none_or(produces_owned_value),
3567            _ => false,
3568        }
3569    }
3570
3571    let compiled_expr = compile_expression(expr, ctx);
3572    if produces_owned_value(expr) { compiled_expr } else { quote!((#compiled_expr).clone()) }
3573}
3574
3575impl quote::ToTokens for crate::expression_tree::ImageReference {
3576    fn to_tokens(&self, tokens: &mut TokenStream) {
3577        let tks = match self {
3578            crate::expression_tree::ImageReference::None => {
3579                quote!(sp::Image::default())
3580            }
3581            crate::expression_tree::ImageReference::Path(path) => {
3582                let path = path.as_str();
3583                quote!(sp::Image::load_from_path(::std::path::Path::new(#path)).unwrap_or_default())
3584            }
3585            crate::expression_tree::ImageReference::Url(url) => {
3586                let url = url.as_str();
3587                // URL image references only work on the web, where the browser fetches them.
3588                quote!({
3589                    #[cfg(target_arch = "wasm32")]
3590                    { sp::load_as_html_image(#url).unwrap_or_default() }
3591                    #[cfg(not(target_arch = "wasm32"))]
3592                    { sp::Image::default() }
3593                })
3594            }
3595            crate::expression_tree::ImageReference::DataUri(_) => {
3596                unreachable!("data: URIs are embedded before code generation")
3597            }
3598            crate::expression_tree::ImageReference::EmbeddedData { resource_id, extension } => {
3599                let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id.0);
3600                let format = proc_macro2::Literal::byte_string(extension.as_bytes());
3601                quote!(sp::load_image_from_embedded_data(#symbol.into(), sp::Slice::from_slice(#format)))
3602            }
3603            crate::expression_tree::ImageReference::EmbeddedTexture { resource_id } => {
3604                let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id.0);
3605                quote!(
3606                    sp::Image::from(sp::ImageInner::StaticTextures(&#symbol))
3607                )
3608            }
3609        };
3610        tokens.extend(tks);
3611    }
3612}
3613
3614/// Compile `expr` to a Rust expression which may potentially return a reference.
3615///
3616/// The body of every non-trivial match arm lives in its own `#[inline(never)]`
3617/// helper function: this function recurses for nested expressions, and with all
3618/// arm bodies inlined, its stack frame in unoptimized builds becomes so large
3619/// that deeply nested expressions overflow the stack.
3620fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
3621    match expr {
3622        Expression::StringLiteral(s) => {
3623            let s = s.as_str();
3624            quote!(sp::SharedString::from(#s))
3625        }
3626        Expression::KeysLiteral(..) => compile_keys_literal(expr),
3627        Expression::NumberLiteral(n) => {
3628            if n.is_nan() {
3629                quote!(f64::NAN)
3630            } else if n.is_infinite() {
3631                if *n > 0. { quote!(f64::INFINITY) } else { quote!(f64::NEG_INFINITY) }
3632            } else {
3633                quote!(#n)
3634            }
3635        }
3636        Expression::BoolLiteral(b) => quote!(#b),
3637        Expression::Cast { .. } => compile_cast(expr, ctx),
3638        Expression::PropertyReference(nr) => {
3639            let access = access_member(nr, ctx);
3640            let prop_type = ctx.property_ty(nr);
3641            primitive_property_value(prop_type, access)
3642        }
3643        Expression::BuiltinFunctionCall { function, arguments, .. } => {
3644            compile_builtin_function_call(function.clone(), arguments, ctx)
3645        }
3646        Expression::CallBackCall { .. } => compile_callback_call(expr, ctx),
3647        Expression::FunctionCall { .. } => compile_function_call(expr, ctx),
3648        Expression::ItemMemberFunctionCall { .. } => compile_item_member_function_call(expr, ctx),
3649        Expression::ExtraBuiltinFunctionCall { .. } => {
3650            compile_extra_builtin_function_call(expr, ctx)
3651        }
3652        Expression::FunctionParameterReference { index } => {
3653            let i = proc_macro2::Literal::usize_unsuffixed(*index);
3654            quote! {args.#i.clone()}
3655        }
3656        Expression::StructFieldAccess { base, name } => match base.ty(ctx) {
3657            Type::Struct(s) => {
3658                let base_e = compile_expression_no_parenthesis(base, ctx);
3659                let f = struct_field_access(&s, name);
3660                quote!((#base_e).#f)
3661            }
3662            _ => panic!("Expression::StructFieldAccess's base expression is not an Object type"),
3663        },
3664        Expression::ArrayIndex { .. } => compile_array_index(expr, ctx),
3665        Expression::CodeBlock(..) => compile_code_block(expr, ctx),
3666        Expression::PropertyAssignment { property, value } => {
3667            let value = compile_expression(value, ctx);
3668            property_set_value_tokens(property, value, ctx)
3669        }
3670        Expression::ModelDataAssignment { .. } => compile_model_data_assignment(expr, ctx),
3671        Expression::ArrayIndexAssignment { .. } => compile_array_index_assignment(expr, ctx),
3672        Expression::SliceIndexAssignment { slice_name, index, value } => {
3673            let slice_ident = ident(slice_name);
3674            let value_e = compile_expression(value, ctx);
3675            quote!(#slice_ident[#index] = #value_e)
3676        }
3677        Expression::BinaryExpression { .. } => compile_binary_expression(expr, ctx),
3678        Expression::UnaryOp { sub, op } => {
3679            let sub = compile_expression(sub, ctx);
3680            if *op == '+' {
3681                // there is no unary '+' in rust
3682                return sub;
3683            }
3684            let op = proc_macro2::Punct::new(*op, proc_macro2::Spacing::Alone);
3685            quote!( (#op #sub) )
3686        }
3687        Expression::ImageReference { .. } => compile_image_reference(expr),
3688        Expression::Condition { .. } => compile_condition(expr, ctx),
3689        Expression::Array { .. } => compile_array(expr, ctx),
3690        Expression::Struct { .. } => compile_struct(expr, ctx),
3691
3692        Expression::StoreLocalVariable { name, value } => {
3693            let value = compile_expression_to_value_no_parenthesis(value, ctx);
3694            let name = ident(name);
3695            quote!(let #name = #value;)
3696        }
3697        Expression::ReadLocalVariable { name, .. } => {
3698            let name = ident(name);
3699            quote!(#name)
3700        }
3701        Expression::MouseCursor(cursor) => match cursor {
3702            llr::MouseCursorInner::BuiltIn(expression) => {
3703                let expression = compile_expression(expression, ctx);
3704                quote!(sp::MouseCursorInner::BuiltIn(#expression.clone()))
3705            }
3706            llr::MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
3707                let image = compile_expression(image, ctx);
3708                let hotspot_x = compile_expression(hotspot_x, ctx);
3709                let hotspot_y = compile_expression(hotspot_y, ctx);
3710
3711                quote!(sp::MouseCursorInner::CustomMouseCursor { image: #image.clone(), hotspot_x: #hotspot_x.clone() as i32, hotspot_y: #hotspot_y.clone() as i32 })
3712            }
3713        },
3714        Expression::EasingCurve(EasingCurve::CubicBezier(a, b, c, d)) => {
3715            quote!(sp::EasingCurve::CubicBezier([#a, #b, #c, #d]))
3716        }
3717        Expression::EasingCurve(EasingCurve::Spring(a)) => {
3718            quote!(sp::EasingCurve::Spring(#a))
3719        }
3720        // The other curves have no parameters and map to a runtime variant with the same name.
3721        Expression::EasingCurve(e) => {
3722            let ident = format_ident!("{e:?}");
3723            quote!(sp::EasingCurve::#ident)
3724        }
3725        Expression::LinearGradient { .. } => compile_linear_gradient(expr, ctx),
3726        Expression::RadialGradient { .. } => compile_radial_gradient(expr, ctx),
3727        Expression::ConicGradient { .. } => compile_conic_gradient(expr, ctx),
3728        Expression::EnumerationValue(value) => {
3729            let base_ident = ident(&value.enumeration.name);
3730            let value_ident = ident(&value.to_pascal_case());
3731            if value.enumeration.node.is_some() {
3732                quote!(#base_ident::#value_ident)
3733            } else {
3734                quote!(sp::#base_ident::#value_ident)
3735            }
3736        }
3737        Expression::LayoutCacheAccess { .. } => compile_layout_cache_access(expr, ctx),
3738        Expression::GridRepeaterCacheAccess { .. } => compile_grid_repeater_cache_access(expr, ctx),
3739        Expression::WithLayoutItemInfo {
3740            cells_variable,
3741            repeater_indices_var_name,
3742            repeater_steps_var_name,
3743            elements,
3744            orientation,
3745            repeated_cross_size,
3746            sub_expression,
3747        } => generate_with_layout_item_info(
3748            cells_variable,
3749            repeater_indices_var_name.as_ref().map(SmolStr::as_str),
3750            repeater_steps_var_name.as_ref().map(SmolStr::as_str),
3751            elements.as_ref(),
3752            *orientation,
3753            repeated_cross_size.as_deref(),
3754            sub_expression,
3755            ctx,
3756        ),
3757
3758        Expression::WithFlexboxLayoutItemInfo {
3759            cells_h_variable,
3760            cells_v_variable,
3761            flex_props_variable,
3762            repeater_indices_var_name,
3763            elements,
3764            repeated_cross_width,
3765            sub_expression,
3766        } => generate_with_flexbox_layout_item_info(
3767            cells_h_variable,
3768            cells_v_variable,
3769            flex_props_variable.as_deref(),
3770            repeater_indices_var_name.as_ref().map(SmolStr::as_str),
3771            elements.as_ref(),
3772            repeated_cross_width.as_deref(),
3773            sub_expression,
3774            ctx,
3775        ),
3776
3777        Expression::SolveFlexboxLayoutWithMeasure { data, repeater_indices, measure_cells } => {
3778            let data = compile_expression(data, ctx);
3779            let repeater_indices = compile_expression(repeater_indices, ctx);
3780            let closure = generate_flexbox_measure_closure(measure_cells, ctx);
3781            quote! { {
3782                #closure
3783                sp::solve_flexbox_layout_with_measure(&#data, #repeater_indices, Some(&mut measure))
3784            } }
3785        }
3786
3787        Expression::FlexboxLayoutInfoCrossAxisWithMeasure { arguments, measure_cells } => {
3788            let a = compile_builtin_arguments(arguments, ctx);
3789            let closure = generate_flexbox_measure_closure(measure_cells, ctx);
3790            quote! { {
3791                #closure
3792                sp::flexbox_layout_info_cross_axis_with_measure(#(#a as _,)* Some(&mut measure))
3793            } }
3794        }
3795
3796        Expression::BoxLayoutInfoOrthoWithMeasure { solve_data, padding_ortho, measure_cells } => {
3797            let data = compile_expression(solve_data, ctx);
3798            let padding = compile_expression(padding_ortho, ctx);
3799            let known_size_ident = ident(MEASURE_KNOWN_W_LOCAL);
3800            let steps = measure_cells.iter().map(|cell| match cell {
3801                llr::BoxMeasureCell::Static { info } => {
3802                    let info = compile_expression(info, ctx);
3803                    quote!(
3804                        {
3805                            let #known_size_ident = box_ortho_solved.as_slice()[cursor * 2 + 1] as f32;
3806                            let _ = #known_size_ident;
3807                            cells_vec.push(sp::LayoutItemInfo { constraint: { #info }, ..::core::default::Default::default() });
3808                            cursor += 1;
3809                        }
3810                    )
3811                }
3812                llr::BoxMeasureCell::Repeated(repeater) => {
3813                    let repeater_id =
3814                        format_ident!("repeater{}", usize::from(repeater.repeater_index));
3815                    quote!(
3816                        for i in 0.._self.#repeater_id.len() {
3817                            if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
3818                                cells_vec.push(sub_comp.as_pin_ref().layout_item_info_at_cross_width(
3819                                    box_ortho_solved.as_slice()[cursor * 2 + 1] as f32,
3820                                ));
3821                            } else {
3822                                cells_vec.push(::core::default::Default::default());
3823                            }
3824                            cursor += 1;
3825                        }
3826                    )
3827                }
3828            });
3829            let min_cell_count = measure_cells.len();
3830            quote! { {
3831                let box_ortho_solved = sp::solve_box_layout(&#data, sp::Slice::from_slice(&[]));
3832                let mut cells_vec = sp::Vec::with_capacity(#min_cell_count);
3833                let mut cursor = 0usize;
3834                #(#steps)*
3835                let _ = cursor;
3836                sp::box_layout_info_ortho(sp::Slice::from_slice(&cells_vec), &#padding)
3837            } }
3838        }
3839
3840        Expression::WithGridInputData {
3841            cells_variable,
3842            repeater_indices_var_name,
3843            repeater_steps_var_name,
3844            elements,
3845            sub_expression,
3846        } => generate_with_grid_input_data(
3847            cells_variable,
3848            repeater_indices_var_name,
3849            repeater_steps_var_name,
3850            elements.as_ref(),
3851            sub_expression,
3852            ctx,
3853        ),
3854
3855        Expression::MinMax { .. } => compile_min_max(expr, ctx),
3856        Expression::EmptyComponentFactory => quote!(slint::ComponentFactory::default()),
3857        Expression::EmptyDataTransfer => quote!(slint::DataTransfer::default()),
3858        Expression::TranslationReference { .. } => compile_translation_reference(expr, ctx),
3859        Expression::Closure { arg_name, expression } => {
3860            let arg_name = ident(arg_name);
3861            let expression = compile_expression(expression, ctx);
3862            quote! {
3863                |#arg_name| {#expression}
3864            }
3865        }
3866        // Generated code has no debug hooks; use the wrapped expression.
3867        Expression::DebugHook { expression, .. } => compile_expression(expression, ctx),
3868    }
3869}
3870
3871#[inline(never)]
3872fn compile_keys_literal(expr: &Expression) -> TokenStream {
3873    let Expression::KeysLiteral(keys) = expr else { unreachable!() };
3874    let key = &*keys.key;
3875    let alt = keys.modifiers.alt;
3876    let control = keys.modifiers.control;
3877    let shift = keys.modifiers.shift;
3878    let meta = keys.modifiers.meta;
3879    let ignore_shift = keys.ignore_shift;
3880    let ignore_alt = keys.ignore_alt;
3881
3882    quote!(
3883        sp::make_keys(
3884            #key.into(),
3885            {
3886                let mut modifiers = sp::KeyboardModifiers::default();
3887                modifiers.alt = #alt;
3888                modifiers.control = #control;
3889                modifiers.shift = #shift;
3890                modifiers.meta = #meta;
3891                modifiers
3892            },
3893            #ignore_shift,
3894            #ignore_alt))
3895}
3896
3897#[inline(never)]
3898fn compile_cast(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
3899    let Expression::Cast { from, to } = expr else { unreachable!() };
3900    let f = compile_expression(from, ctx);
3901    match (from.ty(ctx), to) {
3902        (Type::Float32, Type::Int32) => {
3903            quote!(((#f) as i32))
3904        }
3905        (from, Type::String) if from.as_unit_product().is_some() => {
3906            quote!(sp::shared_string_from_number((#f) as f64))
3907        }
3908        (Type::Float32, Type::Model) | (Type::Int32, Type::Model) => {
3909            quote!(sp::ModelRc::new(#f.max(::core::default::Default::default()) as usize))
3910        }
3911        (Type::Float32, Type::Color) => {
3912            quote!(sp::Color::from_argb_encoded((#f) as u32))
3913        }
3914        (Type::Color, Type::Brush) => {
3915            quote!(slint::Brush::SolidColor(#f))
3916        }
3917        (Type::Brush, Type::Color) => {
3918            quote!(#f.color())
3919        }
3920        (Type::Struct(lhs), Type::Struct(rhs)) => {
3921            debug_assert_eq!(
3922                lhs.fields, rhs.fields,
3923                "cast of struct with deferent fields should be handled before llr"
3924            );
3925            match (&lhs.name, &rhs.name) {
3926                (StructName::None, targetstruct) if targetstruct.is_some() => {
3927                    // Convert from an anonymous struct to a named one
3928                    let fields = lhs.fields.iter().enumerate().map(|(index, (name, _))| {
3929                        let index = proc_macro2::Literal::usize_unsuffixed(index);
3930                        let name = ident(name);
3931                        quote!(the_struct.#name = (obj.#index).clone() as _;)
3932                    });
3933                    let id = struct_name_to_tokens(targetstruct).unwrap();
3934                    quote!({ let obj = #f; let mut the_struct = #id::default(); #(#fields)* the_struct })
3935                }
3936                (sourcestruct, StructName::None) if sourcestruct.is_some() => {
3937                    // Convert from a named struct to an anonymous one
3938                    let fields = lhs.fields.keys().map(|name| ident(name));
3939                    quote!({ let obj = #f; (#(obj.#fields,)*) })
3940                }
3941                _ => f,
3942            }
3943        }
3944        (Type::Array(..), Type::PathData)
3945            if matches!(
3946                from.as_ref(),
3947                Expression::Array { element_ty: Type::Struct { .. }, .. }
3948            ) =>
3949        {
3950            let path_elements = match from.as_ref() {
3951                Expression::Array { element_ty: _, values, output: _ } => values
3952                    .iter()
3953                    .map(|path_elem_expr|
3954                        // Close{} is a struct with no fields in markup, and PathElement::Close has no fields
3955                        if matches!(path_elem_expr, Expression::Struct { ty, .. } if ty.fields.is_empty()) {
3956                            quote!(sp::PathElement::Close)
3957                        } else {
3958                            compile_expression(path_elem_expr, ctx)
3959                        }
3960                    ),
3961                _ => {
3962                    unreachable!()
3963                }
3964            };
3965            quote!(sp::PathData::Elements(sp::SharedVector::<_>::from_slice(&[#((#path_elements).into()),*])))
3966        }
3967        (Type::Struct { .. }, Type::PathData)
3968            if matches!(from.as_ref(), Expression::Struct { .. }) =>
3969        {
3970            let (events, points) = match from.as_ref() {
3971                Expression::Struct { ty: _, values } => (
3972                    compile_expression(&values["events"], ctx),
3973                    compile_expression(&values["points"], ctx),
3974                ),
3975                _ => {
3976                    unreachable!()
3977                }
3978            };
3979            quote!(sp::PathData::Events(sp::SharedVector::<_>::from_slice(&#events), sp::SharedVector::<_>::from_slice(&#points)))
3980        }
3981        (Type::String, Type::PathData) => {
3982            quote!(sp::PathData::Commands(#f))
3983        }
3984        (Type::Enumeration(e), Type::String) => {
3985            let cases = e.values.iter().enumerate().map(|(idx, v)| {
3986                let c = compile_expression(
3987                    &Expression::EnumerationValue(EnumerationValue {
3988                        value: idx,
3989                        enumeration: e.clone(),
3990                    }),
3991                    ctx,
3992                );
3993                let v = v.as_str();
3994                quote!(#c => sp::SharedString::from(#v))
3995            });
3996            quote!(match #f { #(#cases,)*  _ => sp::SharedString::default() })
3997        }
3998        (_, Type::Void) => {
3999            quote!({#f;})
4000        }
4001        _ => f,
4002    }
4003}
4004
4005#[inline(never)]
4006fn compile_callback_call(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4007    let Expression::CallBackCall { callback, arguments } = expr else { unreachable!() };
4008    let f = access_member(callback, ctx);
4009    let register_dep =
4010        access_callback_tracker(callback, ctx).map(|t| t.then(|t| quote!({ #t.get(); })));
4011    let a = arguments.iter().map(|a| compile_expression_to_value(a, ctx));
4012    if expr.ty(ctx) == Type::Void {
4013        f.then(|f| quote!({ #register_dep #f.call(&(#(#a as _,)*)); }))
4014    } else {
4015        f.map_or_default(|f| quote!({ #register_dep #f.call(&(#(#a as _,)*)) }))
4016    }
4017}
4018
4019#[inline(never)]
4020fn compile_function_call(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4021    let Expression::FunctionCall { function, arguments } = expr else { unreachable!() };
4022    let a = arguments.iter().map(|a| compile_expression_to_value(a, ctx));
4023    let f = access_member(function, ctx);
4024    if expr.ty(ctx) == Type::Void {
4025        f.then(|f| quote!(#f( #(#a as _),*)))
4026    } else {
4027        f.map_or_default(|f| quote!(#f( #(#a as _),*)))
4028    }
4029}
4030
4031#[inline(never)]
4032fn compile_item_member_function_call(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4033    let Expression::ItemMemberFunctionCall { function } = expr else { unreachable!() };
4034    let window_adapter_tokens = access_window_adapter_field(ctx);
4035    item_owner(function).map_or_default(|owner| {
4036        let (fun, item_rc) = native_item_from_owner(function, ctx, &owner);
4037        quote!(#fun(#window_adapter_tokens, &#item_rc))
4038    })
4039}
4040
4041/// Compile builtin-call arguments: structs are passed by reference.
4042fn compile_builtin_arguments(
4043    arguments: &[Expression],
4044    ctx: &EvaluationContext,
4045) -> Vec<TokenStream> {
4046    arguments
4047        .iter()
4048        .map(|a| {
4049            let arg = compile_expression(a, ctx);
4050            if matches!(a.ty(ctx), Type::Struct { .. }) { quote!(&#arg) } else { arg }
4051        })
4052        .collect()
4053}
4054
4055#[inline(never)]
4056fn compile_extra_builtin_function_call(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4057    let Expression::ExtraBuiltinFunctionCall { function, arguments, return_ty: _ } = expr else {
4058        unreachable!()
4059    };
4060    let f = ident(function);
4061    let a = compile_builtin_arguments(arguments, ctx);
4062    quote! { sp::#f(#(#a as _),*) }
4063}
4064
4065#[inline(never)]
4066fn compile_array_index(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4067    let Expression::ArrayIndex { array, index } = expr else { unreachable!() };
4068    debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
4069    let base_e = compile_expression(array, ctx);
4070    let index_e = compile_expression(index, ctx);
4071    quote!(match &#base_e { x => {
4072        let index = (#index_e) as usize;
4073        x.row_data_tracked(index).unwrap_or_default()
4074    }})
4075}
4076
4077#[inline(never)]
4078fn compile_code_block(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4079    let Expression::CodeBlock(sub) = expr else { unreachable!() };
4080    let mut body = TokenStream::new();
4081    for (i, e) in sub.iter().enumerate() {
4082        body.extend(compile_expression_no_parenthesis(e, ctx));
4083        if i + 1 < sub.len() && !matches!(e, Expression::StoreLocalVariable { .. }) {
4084            body.extend(quote!(;));
4085        }
4086    }
4087    quote!({ #body })
4088}
4089
4090#[inline(never)]
4091fn compile_model_data_assignment(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4092    let Expression::ModelDataAssignment { level, value } = expr else { unreachable!() };
4093    let value = compile_expression(value, ctx);
4094    let mut path = quote!(_self);
4095    let EvaluationScope::SubComponent(mut sc, mut par) = ctx.current_scope else { unreachable!() };
4096    let mut repeater_index = None;
4097    for _ in 0..=*level {
4098        let x = par.unwrap();
4099        par = x.parent;
4100        repeater_index = x.repeater_index;
4101        sc = x.sub_component;
4102        path = quote!(#path.parent.upgrade().unwrap());
4103    }
4104    let repeater_index = repeater_index.unwrap();
4105    let sub_component = &ctx.compilation_unit.sub_components[sc];
4106    let local_reference = sub_component.repeated[repeater_index].index_prop.unwrap().into();
4107    let index_prop = llr::MemberReference::Relative { parent_level: *level, local_reference };
4108    let index_access = access_member(&index_prop, ctx).get_property();
4109    let repeater = access_component_field_offset(
4110        &inner_component_id(sub_component),
4111        &format_ident!("repeater{}", usize::from(repeater_index)),
4112    );
4113    quote!(#repeater.apply_pin(#path.as_pin_ref()).model_set_row_data(#index_access as _, #value as _))
4114}
4115
4116#[inline(never)]
4117fn compile_array_index_assignment(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4118    let Expression::ArrayIndexAssignment { array, index, value } = expr else { unreachable!() };
4119    debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
4120    let base_e = compile_expression(array, ctx);
4121    let index_e = compile_expression(index, ctx);
4122    let value_e = compile_expression(value, ctx);
4123    quote!((#base_e).set_row_data(#index_e as isize as usize, #value_e as _))
4124}
4125
4126#[inline(never)]
4127fn compile_binary_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4128    // Long chains of binary operators, such as `a && b && c && ...`, nest on the
4129    // left side. Iterate that spine instead of recursing into it, so that the
4130    // stack depth stays bounded no matter how long the chain is.
4131    let mut spine = Vec::new();
4132    let mut node = expr;
4133    while let Expression::BinaryExpression { lhs, rhs, op } = node {
4134        spine.push((rhs, *op));
4135        node = lhs;
4136    }
4137    let mut result = compile_expression_to_value_no_parenthesis(node, ctx);
4138    let mut result_ty = node.ty(ctx);
4139    for (rhs, op) in spine.into_iter().rev() {
4140        result = compile_binary_operator(result, &result_ty, rhs, op, ctx);
4141        result_ty = llr::binary_expression_ty(op, || result_ty);
4142    }
4143    result
4144}
4145
4146fn compile_binary_operator(
4147    lhs: TokenStream,
4148    lhs_ty: &Type,
4149    rhs: &Expression,
4150    op: char,
4151    ctx: &EvaluationContext,
4152) -> TokenStream {
4153    let rhs = compile_expression_to_value_no_parenthesis(rhs, ctx);
4154
4155    if lhs_ty.as_unit_product().is_some() && (op == '=' || op == '!') {
4156        let maybe_negate = if op == '!' { quote!(!) } else { quote!() };
4157        quote!(#maybe_negate sp::ApproxEq::<f64>::approx_eq(&(#lhs as f64), &(#rhs as f64)))
4158    } else {
4159        let (conv1, conv2) = match crate::expression_tree::operator_class(op) {
4160            OperatorClass::ArithmeticOp => match lhs_ty {
4161                Type::String => (None, Some(quote!(.as_str()))),
4162                Type::Struct { .. } => (None, None),
4163                _ => (Some(quote!(as f64)), Some(quote!(as f64))),
4164            },
4165            OperatorClass::ComparisonOp
4166                if matches!(
4167                    lhs_ty,
4168                    Type::Int32
4169                        | Type::Float32
4170                        | Type::Duration
4171                        | Type::PhysicalLength
4172                        | Type::LogicalLength
4173                        | Type::Angle
4174                        | Type::Percent
4175                        | Type::Rem
4176                ) =>
4177            {
4178                (Some(quote!(as f64)), Some(quote!(as f64)))
4179            }
4180            _ => (None, None),
4181        };
4182
4183        let op = match op {
4184            '=' => quote!(==),
4185            '!' => quote!(!=),
4186            '≤' => quote!(<=),
4187            '≥' => quote!(>=),
4188            '&' => quote!(&&),
4189            '|' => quote!(||),
4190            _ => proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
4191                op,
4192                proc_macro2::Spacing::Alone,
4193            ))
4194            .into(),
4195        };
4196        quote!( (((#lhs) #conv1 ) #op ((#rhs) #conv2)) )
4197    }
4198}
4199
4200#[inline(never)]
4201fn compile_image_reference(expr: &Expression) -> TokenStream {
4202    let Expression::ImageReference { resource_ref, nine_slice } = expr else { unreachable!() };
4203    match &nine_slice {
4204        Some([a, b, c, d]) => {
4205            quote! {{ let mut image = #resource_ref; image.set_nine_slice_edges(#a, #b, #c, #d); image }}
4206        }
4207        None => quote!(#resource_ref),
4208    }
4209}
4210
4211#[inline(never)]
4212fn compile_condition(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4213    let Expression::Condition { condition, true_expr, false_expr } = expr else { unreachable!() };
4214    let condition_code = compile_expression_no_parenthesis(condition, ctx);
4215    let true_code = compile_expression(true_expr, ctx);
4216    let false_code = compile_expression_no_parenthesis(false_expr, ctx);
4217    let semi = if false_expr.ty(ctx) == Type::Void { quote!(;) } else { quote!(as _) };
4218    quote!(
4219        if #condition_code {
4220            (#true_code) #semi
4221        } else {
4222            #false_code
4223        }
4224    )
4225}
4226
4227/// Maximum number of elements an array literal constructs in one function.
4228/// Like [`INIT_CHUNK_SIZE`], this keeps a function body from getting too big:
4229/// the build time is flat up to 64 elements per chunk and explodes at 128.
4230const ARRAY_CHUNK_SIZE: usize = 32;
4231
4232#[inline(never)]
4233fn compile_array(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4234    let Expression::Array { values, element_ty, output } = expr else { unreachable!() };
4235    let val = values.iter().map(|e| compile_expression_to_value(e, ctx));
4236    match output {
4237        ArrayOutput::Model => {
4238            let rust_element_ty = rust_primitive_type(element_ty).unwrap();
4239            let vec = if values.len() > ARRAY_CHUNK_SIZE && !is_plain_value(element_ty) {
4240                let len = values.len();
4241                let chunks = values.chunks(ARRAY_CHUNK_SIZE).map(|chunk| {
4242                    let val = chunk.iter().map(|e| compile_expression_to_value(e, ctx));
4243                    // Pushing one by one also keeps the elements out of a big stack temporary.
4244                    quote!(slint::private_unstable_api::build_array_chunk(|| {
4245                        #(_array.push(#val as _);)*
4246                    });)
4247                });
4248                quote!({
4249                    let mut _array = sp::Vec::<#rust_element_ty>::with_capacity(#len);
4250                    #(#chunks)*
4251                    _array
4252                })
4253            } else {
4254                quote!(sp::vec![#(#val as _),*])
4255            };
4256            quote!(sp::ModelRc::new(sp::VecModel::<#rust_element_ty>::from(#vec)))
4257        }
4258        ArrayOutput::Slice => quote!(sp::Slice::from_slice(&[#(#val),*])),
4259        ArrayOutput::Vector => quote!(sp::vec![#(#val as _),*]),
4260    }
4261}
4262
4263/// Whether a literal array of `ty` compiles to a constant blob, which beats
4264/// storing the elements one by one.
4265/// Types that aren't listed are chunked, which is the safe direction.
4266fn is_plain_value(ty: &Type) -> bool {
4267    matches!(
4268        ty,
4269        Type::Int32
4270            | Type::Float32
4271            | Type::Bool
4272            | Type::Color
4273            | Type::Duration
4274            | Type::Angle
4275            | Type::PhysicalLength
4276            | Type::LogicalLength
4277            | Type::Rem
4278            | Type::Percent
4279            | Type::Enumeration(_)
4280    )
4281}
4282
4283#[inline(never)]
4284fn compile_struct(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4285    let Expression::Struct { ty, values } = expr else { unreachable!() };
4286    if ty.name.is_some() {
4287        let name_tokens = struct_name_to_tokens(&ty.name).unwrap();
4288        // A struct literal can only be used when all the fields are public, there
4289        // is no hidden field (like euclid's `_unit`), and the struct is not
4290        // `#[non_exhaustive]`. That holds for the generated user structs and for
4291        // the listed builtin structs.
4292        use crate::langtype::BuiltinStruct as BS;
4293        let supports_struct_literal = match &ty.name {
4294            StructName::User { .. } => true,
4295            StructName::Builtin(b) => {
4296                b.is_layout_data()
4297                    || matches!(
4298                        b,
4299                        BS::LayoutInfo
4300                            | BS::LayoutItemInfo
4301                            | BS::FlexboxLayoutItemInfo
4302                            | BS::FlexItemProps
4303                            | BS::Padding
4304                            | BS::PropertyAnimation
4305                            | BS::StateInfo
4306                    )
4307            }
4308            StructName::None => false,
4309        };
4310        if supports_struct_literal {
4311            let (keys, elem): (Vec<_>, Vec<_>) = ty
4312                .fields
4313                .keys()
4314                .filter(|k| values.contains_key(*k))
4315                .map(|k| (ident(k), compile_expression_to_value(&values[k], ctx)))
4316                .unzip();
4317            let default_rest = (keys.len() != ty.fields.len())
4318                .then(|| quote!(..::core::default::Default::default()));
4319            quote!(#name_tokens{#(#keys: #elem as _,)* #default_rest})
4320        } else {
4321            let elem = ty
4322                .fields
4323                .keys()
4324                .map(|k| values.get(k).map(|e| compile_expression_to_value(e, ctx)));
4325            let keys = ty.fields.keys().map(|k| ident(k));
4326            quote!({ let mut the_struct = #name_tokens::default(); #(the_struct.#keys = #elem as _;)* the_struct})
4327        }
4328    } else {
4329        let elem =
4330            ty.fields.keys().map(|k| values.get(k).map(|e| compile_expression_to_value(e, ctx)));
4331        let as_ = ty.fields.values().map(|t| {
4332            if t.as_unit_product().is_some() {
4333                // number needs to be converted to the right things because intermediate
4334                // result might be f64 and that's usually not what the type of the tuple is in the end
4335                let t = rust_primitive_type(t).unwrap();
4336                quote!(as #t)
4337            } else {
4338                quote!()
4339            }
4340        });
4341        // This will produce a tuple
4342        quote!((#((#elem).clone() #as_,)*))
4343    }
4344}
4345
4346#[inline(never)]
4347fn compile_linear_gradient(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4348    let Expression::LinearGradient { angle, stops } = expr else { unreachable!() };
4349    let angle = compile_expression(angle, ctx);
4350    let stops = stops.iter().map(|(color, stop)| {
4351        let color = compile_expression(color, ctx);
4352        let position = compile_expression(stop, ctx);
4353        quote!(sp::GradientStop{ color: #color, position: #position as _ })
4354    });
4355    quote!(slint::Brush::LinearGradient(
4356        sp::LinearGradientBrush::new(#angle as _, [#(#stops),*])
4357    ))
4358}
4359
4360#[inline(never)]
4361fn compile_radial_gradient(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4362    let Expression::RadialGradient { center, radius, stops } = expr else { unreachable!() };
4363    let stops = stops.iter().map(|(color, stop)| {
4364        let color = compile_expression(color, ctx);
4365        let position = compile_expression(stop, ctx);
4366        quote!(sp::GradientStop{ color: #color, position: #position as _ })
4367    });
4368    let brush_expr = quote!(sp::RadialGradientBrush::new_circle([#(#stops),*]));
4369    let brush_expr = if let Some((cx, cy)) = center {
4370        let cx = compile_expression(cx, ctx);
4371        let cy = compile_expression(cy, ctx);
4372        quote!(#brush_expr.with_center(#cx as f32, #cy as f32))
4373    } else {
4374        brush_expr
4375    };
4376    let brush_expr = if let Some(r) = radius {
4377        let r = compile_expression(r, ctx);
4378        quote!(#brush_expr.with_radius(#r as f32))
4379    } else {
4380        brush_expr
4381    };
4382    quote!(slint::Brush::RadialGradient(#brush_expr))
4383}
4384
4385#[inline(never)]
4386fn compile_conic_gradient(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4387    let Expression::ConicGradient { from_angle, center, stops } = expr else { unreachable!() };
4388    let from_angle = compile_expression(from_angle, ctx);
4389    let stops = stops.iter().map(|(color, stop)| {
4390        let color = compile_expression(color, ctx);
4391        let position = compile_expression(stop, ctx);
4392        quote!(sp::GradientStop{ color: #color, position: #position as _ })
4393    });
4394    let brush_expr = quote!(sp::ConicGradientBrush::new(#from_angle as _, [#(#stops),*]));
4395    let brush_expr = if let Some((cx, cy)) = center {
4396        let cx = compile_expression(cx, ctx);
4397        let cy = compile_expression(cy, ctx);
4398        quote!(#brush_expr.with_center(#cx as f32, #cy as f32))
4399    } else {
4400        brush_expr
4401    };
4402    quote!(slint::Brush::ConicGradient(#brush_expr))
4403}
4404
4405#[inline(never)]
4406fn compile_layout_cache_access(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4407    let Expression::LayoutCacheAccess {
4408        layout_cache_prop,
4409        index,
4410        repeater_index,
4411        entries_per_item,
4412    } = expr
4413    else {
4414        unreachable!()
4415    };
4416    access_member(layout_cache_prop, ctx).map_or_default(|cache| {
4417        if let Some(ri) = repeater_index {
4418            let offset = compile_expression(ri, ctx);
4419            quote!({
4420                let cache = #cache.get();
4421                *cache.get((cache[#index] as usize) + #offset as usize * #entries_per_item).unwrap_or(&(0 as _))
4422            })
4423        } else {
4424            quote!(#cache.get()[#index])
4425        }
4426    })
4427}
4428
4429#[inline(never)]
4430fn compile_grid_repeater_cache_access(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4431    let Expression::GridRepeaterCacheAccess {
4432        layout_cache_prop,
4433        index,
4434        repeater_index,
4435        stride,
4436        child_offset,
4437        inner_repeater_index,
4438        entries_per_item,
4439    } = expr
4440    else {
4441        unreachable!()
4442    };
4443    access_member(layout_cache_prop, ctx).map_or_default(|cache| {
4444        let offset = compile_expression(repeater_index, ctx);
4445        let stride_val = compile_expression(stride, ctx);
4446        let inner_offset = inner_repeater_index.as_ref().map(|inner_ri| {
4447            let inner_offset = compile_expression(inner_ri, ctx);
4448            quote!(+ #inner_offset as usize * #entries_per_item)
4449        });
4450
4451        quote!({
4452            let cache = #cache.get();
4453            cache.get(#index)
4454                .and_then(|base| cache.get(*base as usize + #offset as usize * (#stride_val as usize) + #child_offset #inner_offset))
4455                .copied()
4456                .unwrap_or(0 as _)
4457        })
4458    })
4459}
4460
4461#[inline(never)]
4462fn compile_min_max(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4463    let Expression::MinMax { ty, op, lhs, rhs } = expr else { unreachable!() };
4464    let lhs = compile_expression(lhs, ctx);
4465    let t = rust_primitive_type(ty);
4466    let (lhs, rhs) = match t {
4467        Some(t) => {
4468            let rhs = compile_expression(rhs, ctx);
4469            (quote!((#lhs as #t)), quote!(#rhs as #t))
4470        }
4471        None => {
4472            let rhs = compile_expression_no_parenthesis(rhs, ctx);
4473            (lhs, rhs)
4474        }
4475    };
4476    match op {
4477        MinMaxOp::Min => {
4478            quote!(#lhs.min(#rhs))
4479        }
4480        MinMaxOp::Max => {
4481            quote!(#lhs.max(#rhs))
4482        }
4483    }
4484}
4485
4486#[inline(never)]
4487fn compile_translation_reference(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4488    let Expression::TranslationReference { format_args, string_index, plural } = expr else {
4489        unreachable!()
4490    };
4491    let args = compile_expression(format_args, ctx);
4492    match plural {
4493        Some(plural) => {
4494            let plural = compile_expression(plural, ctx);
4495            quote!(sp::translate_from_bundle_with_plural(
4496                &self::_SLINT_TRANSLATED_STRINGS_PLURALS[#string_index],
4497                &self::_SLINT_TRANSLATED_PLURAL_RULES,
4498                sp::Slice::<sp::SharedString>::from(#args).as_slice(),
4499                #plural as _
4500            ))
4501        }
4502        None => {
4503            quote!(sp::translate_from_bundle(&self::_SLINT_TRANSLATED_STRINGS[#string_index], sp::Slice::<sp::SharedString>::from(#args).as_slice()))
4504        }
4505    }
4506}
4507
4508fn struct_field_access(s: &Struct, name: &str) -> proc_macro2::TokenTree {
4509    if s.name.is_none() {
4510        let index = s
4511            .fields
4512            .keys()
4513            .position(|k| k == name)
4514            .expect("Expression::StructFieldAccess: Cannot find a key in an object");
4515        proc_macro2::Literal::usize_unsuffixed(index).into()
4516    } else {
4517        ident(name).into()
4518    }
4519}
4520
4521fn compile_builtin_function_call(
4522    function: BuiltinFunction,
4523    arguments: &[Expression],
4524    ctx: &EvaluationContext,
4525) -> TokenStream {
4526    let mut a = arguments.iter().map(|a| compile_expression_to_value(a, ctx));
4527    match function {
4528        BuiltinFunction::SetFocusItem => {
4529            if let [Expression::PropertyReference(pr)] = arguments {
4530                let window_tokens = access_window_adapter_field(ctx);
4531                item_owner(pr).then(|owner| {
4532                    let (_, focus_item) = native_item_from_owner(pr, ctx, &owner);
4533                    quote!(sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(&#focus_item, true, sp::FocusReason::Programmatic))
4534                })
4535            } else {
4536                panic!("internal error: invalid args to SetFocusItem {arguments:?}")
4537            }
4538        }
4539        BuiltinFunction::ClearFocusItem => {
4540            if let [Expression::PropertyReference(pr)] = arguments {
4541                let window_tokens = access_window_adapter_field(ctx);
4542                item_owner(pr).then(|owner| {
4543                    let (_, focus_item) = native_item_from_owner(pr, ctx, &owner);
4544                    quote!(sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(&#focus_item, false, sp::FocusReason::Programmatic))
4545                })
4546            } else {
4547                panic!("internal error: invalid args to ClearFocusItem {arguments:?}")
4548            }
4549        }
4550        BuiltinFunction::ShowPopupWindow => {
4551            // `owner_ref` is the popup's declaring component (its `popup_id` and scope);
4552            // `anchor_ref` is the parent item for positioning.
4553            if let [
4554                Expression::NumberLiteral(popup_index),
4555                close_policy,
4556                Expression::PropertyReference(owner_ref),
4557                Expression::PropertyReference(anchor_ref),
4558                is_open_args @ ..,
4559            ] = arguments
4560            {
4561                let mut component_access_tokens = MemberAccess::Direct(quote!(_self));
4562                let llr::MemberReference::Relative { parent_level, local_reference } = owner_ref
4563                else {
4564                    unreachable!()
4565                };
4566                for _ in 0..*parent_level {
4567                    component_access_tokens = match component_access_tokens {
4568                        MemberAccess::Option(token_stream) => MemberAccess::Option(
4569                            quote!(#token_stream.and_then(|a| a.as_pin_ref().parent.upgrade())),
4570                        ),
4571                        MemberAccess::Direct(token_stream) => {
4572                            MemberAccess::Option(quote!(#token_stream.parent.upgrade()))
4573                        }
4574                        _ => unreachable!(),
4575                    };
4576                }
4577                let (suffix, _) = follow_sub_component_path_fields(
4578                    ctx.compilation_unit,
4579                    ctx.parent_sub_component_idx(*parent_level).unwrap(),
4580                    &local_reference.sub_component_path,
4581                );
4582                ctx.with_reference_scope(
4583                    *parent_level,
4584                    &local_reference.sub_component_path,
4585                    |parent_ctx| {
4586                let popup = &ctx.compilation_unit.sub_components[parent_ctx.sub_component]
4587                    .popup_windows[*popup_index as usize];
4588                let popup_window_id =
4589                    inner_component_id(&ctx.compilation_unit.sub_components[popup.item_tree.root]);
4590                let popup_ctx = EvaluationContext::new_sub_component(
4591                    ctx.compilation_unit,
4592                    popup.item_tree.root,
4593                    RustGeneratorContext { global_access: quote!(_self.globals()) },
4594                    Some(&parent_ctx),
4595                );
4596                let position = compile_expression(&popup.position.borrow(), &popup_ctx);
4597                let close_policy = compile_expression(close_policy, ctx);
4598                let popup_id_name = internal_popup_id(*popup_index as usize);
4599                let window_kind = if popup.is_tooltip {
4600                    quote!(sp::WindowKind::ToolTip)
4601                } else {
4602                    quote!(sp::WindowKind::Popup)
4603                };
4604                let globals_init = quote! {
4605                    if let Some(popup_window_adapter) = window.create_child_window_adapter(#window_kind) {
4606                        shared_global.clone_with_window_adapter(popup_window_adapter)
4607                    } else {
4608                        shared_global.clone()
4609                    }
4610                };
4611                // The optional 4th argument is a property reference to the synthesized `is-open`,
4612                // mapped in this show call's own frame (see lower_show_popup_window), so it resolves
4613                // directly against `ctx`/`_self`, exactly like `parent_ref`.
4614                let is_open_set_expr = is_open_args.first().map(|arg| {
4615                    let Expression::PropertyReference(is_open_ref) = arg else {
4616                        unreachable!(
4617                            "ShowPopupWindow is-open argument must be a property reference"
4618                        )
4619                    };
4620                    access_member(is_open_ref, ctx).then(|p| quote!(#p.set(value)))
4621                });
4622                item_owner(anchor_ref).then_named("anchor_owner", |owner| {
4623                    let (_, parent_item) = native_item_from_owner(anchor_ref, ctx, &owner);
4624                    component_access_tokens.then(|component_access_tokens| {
4625                    let compo = quote!(#component_access_tokens #suffix);
4626                    // Keep the parent's `is-open` in sync: `show_popup` invokes this setter with `true`
4627                    // immediately and with `false` from every close path (see window.rs). Passing it
4628                    // directly into `show_popup` avoids an extra registration call and a second popup
4629                    // lookup. Menus and `is-open`-less popups get a no-op setter.
4630                    let (is_open_self_weak_decl, is_open_setter) = match &is_open_set_expr {
4631                        Some(set_expr) => (
4632                            quote!(let is_open_self_weak = _self.self_weak.get().unwrap().clone();),
4633                            quote! {
4634                                sp::Box::new(move |value: bool| {
4635                                    if let Some(is_open_self) = is_open_self_weak.upgrade() {
4636                                        let _self = is_open_self.as_pin_ref();
4637                                        #set_expr
4638                                    }
4639                                })
4640                            },
4641                        ),
4642                        None => (quote!(), quote!(sp::Box::new(|_| {}))),
4643                    };
4644                    quote!({
4645                        let parent_item = &#parent_item;
4646                        // Use the newly created window adapter if we are able to create one. Otherwise use the parent's one
4647                        let shared_global = #compo.globals.get().unwrap();
4648                        let window_adapter = shared_global.window_adapter_impl();
4649                        let window = sp::WindowInner::from_pub(window_adapter.window());
4650                        let globals = #globals_init;
4651
4652                        let popup_instance = #popup_window_id::new(#compo.self_weak.get().unwrap().clone(), globals).unwrap();
4653                        let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
4654                        if let Some(current_id) = #compo.#popup_id_name.take() {
4655                            window.close_popup(current_id);
4656                        }
4657
4658                        let popup_instance_vrc_for_position = popup_instance_vrc.clone();
4659                        let access_position = sp::Box::new(move || {
4660                            let _self = popup_instance_vrc_for_position.as_pin_ref(); #position
4661                        });
4662
4663                        #is_open_self_weak_decl
4664                        let popup_id = window.show_popup(
4665                            &sp::VRc::into_dyn(popup_instance.into()),
4666                            access_position,
4667                            #close_policy,
4668                            parent_item,
4669                            #window_kind,
4670                            #is_open_setter,
4671                        );
4672                        #compo.#popup_id_name.set(Some(popup_id));
4673                        #popup_window_id::user_init(popup_instance_vrc.clone());
4674                    })
4675                    })
4676                })
4677                    },
4678                )
4679            } else {
4680                panic!("internal error: invalid args to ShowPopupWindow {arguments:?}")
4681            }
4682        }
4683        BuiltinFunction::ClosePopupWindow => {
4684            if let [
4685                Expression::NumberLiteral(popup_index),
4686                Expression::PropertyReference(parent_ref),
4687            ] = arguments
4688            {
4689                let mut component_access_tokens = MemberAccess::Direct(quote!(_self));
4690                let llr::MemberReference::Relative { parent_level, local_reference } = parent_ref
4691                else {
4692                    unreachable!()
4693                };
4694                for _ in 0..*parent_level {
4695                    component_access_tokens = match component_access_tokens {
4696                        MemberAccess::Option(token_stream) => MemberAccess::Option(
4697                            quote!(#token_stream.and_then(|a| a.parent.upgrade())),
4698                        ),
4699                        MemberAccess::Direct(token_stream) => {
4700                            MemberAccess::Option(quote!(#token_stream.parent.upgrade()))
4701                        }
4702                        _ => unreachable!(),
4703                    };
4704                }
4705                let (suffix, _) = follow_sub_component_path_fields(
4706                    ctx.compilation_unit,
4707                    ctx.parent_sub_component_idx(*parent_level).unwrap(),
4708                    &local_reference.sub_component_path,
4709                );
4710                let popup_id_name = internal_popup_id(*popup_index as usize);
4711                let current_id_tokens = match component_access_tokens {
4712                    MemberAccess::Option(token_stream) => quote!(
4713                        #token_stream.and_then(|a| a.as_pin_ref() #suffix.#popup_id_name.take().map(|id| (a.as_pin_ref() #suffix.globals.get().unwrap().clone(), id)))
4714                    ),
4715                    MemberAccess::Direct(token_stream) => {
4716                        quote!(#token_stream.as_ref() #suffix.#popup_id_name.take().map(|id|(#token_stream.as_ref() #suffix.globals.get().unwrap().clone(), id)))
4717                    }
4718                    _ => unreachable!(),
4719                };
4720                quote!(
4721                    if let Some((globals, current_id)) = #current_id_tokens {
4722                        sp::WindowInner::from_pub(globals.window_adapter_impl().window()).close_popup(current_id);
4723                    }
4724                )
4725            } else {
4726                panic!("internal error: invalid args to ClosePopupWindow {arguments:?}")
4727            }
4728        }
4729        BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
4730            let [Expression::PropertyReference(context_menu_ref), entries, position] = arguments
4731            else {
4732                panic!("internal error: invalid args to ShowPopupMenu {arguments:?}")
4733            };
4734
4735            let context_menu = access_member(context_menu_ref, ctx);
4736            let position = compile_expression(position, ctx);
4737
4738            let popup = ctx
4739                .compilation_unit
4740                .popup_menu
4741                .as_ref()
4742                .expect("there should be a popup menu if we want to show it");
4743            let popup_id =
4744                inner_component_id(&ctx.compilation_unit.sub_components[popup.item_tree.root]);
4745            let window_adapter_tokens = access_window_adapter_field(ctx);
4746
4747            let popup_ctx = EvaluationContext::new_sub_component(
4748                ctx.compilation_unit,
4749                popup.item_tree.root,
4750                RustGeneratorContext { global_access: quote!(_self.globals()) },
4751                None,
4752            );
4753            let access_entries = access_member(&popup.entries, &popup_ctx).unwrap();
4754            let access_sub_menu = access_member(&popup.sub_menu, &popup_ctx).unwrap();
4755            let access_activated = access_member(&popup.activated, &popup_ctx).unwrap();
4756            let access_close = access_member(&popup.close, &popup_ctx).unwrap();
4757
4758            let close_popup = context_menu.clone().then(|context_menu| quote!{
4759                if let Some(current_id) = #context_menu.popup_id.take() {
4760                    sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
4761                }
4762            });
4763
4764            let set_id = context_menu
4765                .clone()
4766                .then(|context_menu| quote!(#context_menu.popup_id.set(Some(id))));
4767            item_owner(context_menu_ref).then_named("context_menu_owner", |owner| {
4768                let (_, context_menu_rc) = native_item_from_owner(context_menu_ref, ctx, &owner);
4769                let slint_show = quote! {
4770                    #close_popup
4771                    let access_position = sp::Box::new(move || position);
4772                    let id = sp::WindowInner::from_pub(window_adapter.window()).show_popup(
4773                        &sp::VRc::into_dyn(popup_instance.into()),
4774                        access_position,
4775                        sp::PopupClosePolicy::CloseOnClickOutside,
4776                        &#context_menu_rc,
4777                        sp::WindowKind::Menu,
4778                        sp::Box::new(|_| {}),
4779                    );
4780                    #set_id;
4781                    #popup_id::user_init(popup_instance_vrc);
4782                };
4783
4784                let common_init = quote! {
4785                    let position = #position;
4786                    let popup_instance = #popup_id::new(_self.globals.get().unwrap().clone()).unwrap();
4787                    let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
4788                    let parent_weak = _self.self_weak.get().unwrap().clone();
4789                    let window_adapter = #window_adapter_tokens;
4790                };
4791
4792                if let Expression::NumberLiteral(tree_index) = entries {
4793                    // We have an MenuItem tree
4794                    let current_sub_component = ctx.current_sub_component().unwrap();
4795                    let item_tree_id = inner_component_id(
4796                        &ctx.compilation_unit.sub_components
4797                            [current_sub_component.menu_item_trees[*tree_index as usize].root],
4798                    );
4799                    quote! {{
4800                        #common_init
4801                        let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
4802                        let context_menu_item_tree = sp::VRc::new(sp::MenuFromItemTree::new(sp::VRc::into_dyn(menu_item_tree_instance)));
4803                        let context_menu_item_tree_ = context_menu_item_tree.clone();
4804                        {
4805                            let mut entries = sp::SharedVector::default();
4806                            sp::Menu::sub_menu(&*context_menu_item_tree, sp::Option::None, &mut entries);
4807                            let _self = popup_instance_vrc.as_pin_ref();
4808                            #access_entries.set(sp::ModelRc::new(sp::SharedVectorModel::from(entries)));
4809                            let context_menu_item_tree = context_menu_item_tree_.clone();
4810                            #access_sub_menu.set_handler(move |entry| {
4811                                let mut entries = sp::SharedVector::default();
4812                                sp::Menu::sub_menu(&*context_menu_item_tree, sp::Option::Some(&entry.0), &mut entries);
4813                                sp::ModelRc::new(sp::SharedVectorModel::from(entries))
4814                            });
4815                            let context_menu_item_tree = context_menu_item_tree_.clone();
4816                            #access_activated.set_handler(move |entry| {
4817                                sp::Menu::activate(&*context_menu_item_tree_, &entry.0);
4818                            });
4819                            let self_weak = parent_weak.clone();
4820                            #access_close.set_handler(move |()| {
4821                                let Some(self_rc) = self_weak.upgrade() else { return };
4822                                let _self = self_rc.as_pin_ref();
4823                                #close_popup
4824                            });
4825                        }
4826                        let context_menu_item_tree = sp::VRc::into_dyn(context_menu_item_tree);
4827                        if !sp::WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(context_menu_item_tree, position, &#context_menu_rc) {
4828                            #slint_show
4829                        }
4830                    }}
4831                } else {
4832                    // ShowPopupMenuInternal: entries should be an expression of type array of MenuEntry
4833                    debug_assert!(
4834                        matches!(entries.ty(ctx), Type::Array(ty) if matches!(&*ty, Type::Struct{..}))
4835                    );
4836                    let entries = compile_expression(entries, ctx);
4837                    let forward_callback = |access, cb| {
4838                        let call = context_menu
4839                            .clone()
4840                            .map_or_default(|context_menu| quote!(#context_menu.#cb.call(entry)));
4841                        quote!(
4842                            let self_weak = parent_weak.clone();
4843                            #access.set_handler(move |entry| {
4844                                if let Some(self_rc) = self_weak.upgrade() {
4845                                    let _self = self_rc.as_pin_ref();
4846                                    #call
4847                                } else { ::core::default::Default::default() }
4848                            });
4849                        )
4850                    };
4851                    let fw_sub_menu = forward_callback(access_sub_menu.clone(), quote!(sub_menu));
4852                    let fw_activated =
4853                        forward_callback(access_activated.clone(), quote!(activated));
4854                    quote! {{
4855                        #common_init
4856                        let entries = #entries;
4857                        {
4858                            let _self = popup_instance_vrc.as_pin_ref();
4859                            #access_entries.set(entries.clone());
4860                            #fw_sub_menu
4861                            #fw_activated
4862                            let self_weak = parent_weak.clone();
4863                            #access_close.set_handler(move |()| {
4864                                let Some(self_rc) = self_weak.upgrade() else { return };
4865                                let _self = self_rc.as_pin_ref();
4866                                #close_popup
4867                            });
4868                        }
4869                        #slint_show
4870                    }}
4871                }
4872            })
4873        }
4874        BuiltinFunction::SetSelectionOffsets => {
4875            if let [llr::Expression::PropertyReference(pr), anchor_expr, focus_expr] = arguments {
4876                let window_adapter_tokens = access_window_adapter_field(ctx);
4877                let anchor = compile_expression(anchor_expr, ctx);
4878                let focus = compile_expression(focus_expr, ctx);
4879
4880                item_owner(pr).then(|owner| {
4881                    let (item, item_rc) = native_item_from_owner(pr, ctx, &owner);
4882                    quote!(
4883                        #item.set_selection_offsets(#window_adapter_tokens, &#item_rc, #anchor as i32, #focus as i32)
4884                    )
4885                })
4886            } else {
4887                panic!("internal error: invalid args to set-selection-offsets {arguments:?}")
4888            }
4889        }
4890        BuiltinFunction::ItemFontMetrics => {
4891            if let [Expression::PropertyReference(pr)] = arguments {
4892                let window_adapter_tokens = access_window_adapter_field(ctx);
4893                item_owner(pr).map_or_default(|owner| {
4894                    let (item, item_rc) = native_item_from_owner(pr, ctx, &owner);
4895                    quote!(
4896                        #item.font_metrics(#window_adapter_tokens, &#item_rc)
4897                    )
4898                })
4899            } else {
4900                panic!("internal error: invalid args to ItemMemberFunction {arguments:?}")
4901            }
4902        }
4903        BuiltinFunction::ImplicitLayoutInfo(orient) => {
4904            if let [Expression::PropertyReference(pr), constraint_expr] = arguments {
4905                let window_adapter_tokens = access_window_adapter_field(ctx);
4906                let constraint = compile_expression(constraint_expr, ctx);
4907                item_owner(pr).map_or_default(|owner| {
4908                    let (item, item_rc) = native_item_from_owner(pr, ctx, &owner);
4909                    quote!(
4910                        sp::Item::layout_info(#item, #orient, #constraint as _, #window_adapter_tokens, &#item_rc)
4911                    )
4912                })
4913            } else {
4914                panic!("internal error: invalid args to ImplicitLayoutInfo {arguments:?}")
4915            }
4916        }
4917        BuiltinFunction::RegisterCustomFontByPath => {
4918            if let [Expression::StringLiteral(path)] = arguments {
4919                let global_access = &ctx.generator_state.global_access;
4920                let path = path.as_str();
4921                // The `?` requires the enclosing generated function to return `Result`: font
4922                // registration is only emitted in `init()`, which does.
4923                quote!(#global_access.window_adapter_ref()?.renderer().register_font_from_path(&std::path::PathBuf::from(#path)).unwrap())
4924            } else {
4925                panic!("internal error: invalid args to RegisterCustomFontByPath {arguments:?}")
4926            }
4927        }
4928        BuiltinFunction::RegisterCustomFontByMemory => {
4929            if let [Expression::NumberLiteral(resource_id)] = &arguments {
4930                let global_access = &ctx.generator_state.global_access;
4931                let resource_id: usize = *resource_id as _;
4932                let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
4933                quote!(#global_access.window_adapter_ref()?.renderer().register_font_from_memory(#symbol.into()).unwrap())
4934            } else {
4935                panic!("internal error: invalid args to RegisterCustomFontByMemory {arguments:?}")
4936            }
4937        }
4938        BuiltinFunction::RegisterBitmapFont => {
4939            if let [Expression::NumberLiteral(resource_id)] = &arguments {
4940                let global_access = &ctx.generator_state.global_access;
4941                let resource_id: usize = *resource_id as _;
4942                let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
4943                quote!(#global_access.window_adapter_ref()?.renderer().register_bitmap_font(&#symbol))
4944            } else {
4945                panic!("internal error: invalid args to RegisterBitmapFont must be a number")
4946            }
4947        }
4948        BuiltinFunction::GetWindowScaleFactor => {
4949            let window_adapter_tokens = access_window_adapter_field(ctx);
4950            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).scale_factor())
4951        }
4952        BuiltinFunction::GetWindowDefaultFontSize => {
4953            quote!(
4954                sp::WindowItem::resolved_default_font_size(sp::VRcMapped::origin(
4955                    &_self.self_weak.get().unwrap().upgrade().unwrap()
4956                ))
4957                .get()
4958            )
4959        }
4960        BuiltinFunction::AnimationTick => {
4961            quote!(sp::animation_tick())
4962        }
4963        BuiltinFunction::Debug => quote!(slint::private_unstable_api::debug(#(#a)*)),
4964        BuiltinFunction::DefaultWindowTitle => quote!(sp::default_window_title()),
4965        BuiltinFunction::DecimalSeparator => {
4966            let window_adapter_tokens = access_window_adapter_field(ctx);
4967            quote!(sp::SharedString::from(
4968                sp::WindowInner::from_pub(#window_adapter_tokens.window())
4969                    .context()
4970                    .locale_decimal_separator()
4971            ))
4972        }
4973        BuiltinFunction::Mod => {
4974            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4975            quote!(sp::Euclid::rem_euclid(&(#a1 as f64), &(#a2 as f64)))
4976        }
4977        BuiltinFunction::Round => quote!((#(#a)* as f64).round()),
4978        BuiltinFunction::Ceil => quote!((#(#a)* as f64).ceil()),
4979        BuiltinFunction::Floor => quote!((#(#a)* as f64).floor()),
4980        BuiltinFunction::Sqrt => quote!((#(#a)* as f64).sqrt()),
4981        BuiltinFunction::Abs => quote!((#(#a)* as f64).abs()),
4982        BuiltinFunction::Sin => quote!((#(#a)* as f64).to_radians().sin()),
4983        BuiltinFunction::Cos => quote!((#(#a)* as f64).to_radians().cos()),
4984        BuiltinFunction::Tan => quote!((#(#a)* as f64).to_radians().tan()),
4985        BuiltinFunction::ASin => quote!((#(#a)* as f64).asin().to_degrees()),
4986        BuiltinFunction::ACos => quote!((#(#a)* as f64).acos().to_degrees()),
4987        BuiltinFunction::ATan => quote!((#(#a)* as f64).atan().to_degrees()),
4988        BuiltinFunction::ATan2 => {
4989            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4990            quote!((#a1 as f64).atan2(#a2 as f64).to_degrees())
4991        }
4992        BuiltinFunction::Log => {
4993            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4994            quote!((#a1 as f64).log(#a2 as f64))
4995        }
4996        BuiltinFunction::Ln => quote!((#(#a)* as f64).ln()),
4997        BuiltinFunction::Pow => {
4998            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4999            quote!((#a1 as f64).powf(#a2 as f64))
5000        }
5001        BuiltinFunction::Exp => quote!((#(#a)* as f64).exp()),
5002        BuiltinFunction::ToFixed => {
5003            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
5004            quote!(sp::shared_string_from_number_fixed(#a1 as f64, (#a2 as i32).max(0) as usize))
5005        }
5006        BuiltinFunction::ToPrecision => {
5007            let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
5008            quote!(sp::shared_string_from_number_precision(#a1 as f64, (#a2 as i32).max(0) as usize))
5009        }
5010        BuiltinFunction::ToStringUnlocalized => {
5011            let a1 = a.next().unwrap();
5012            quote!(sp::shared_string_from_number_unlocalized(#a1 as f64))
5013        }
5014        BuiltinFunction::StringToFloat => {
5015            quote!(sp::string_to_float(#(#a)*.as_str()).unwrap_or_default())
5016        }
5017        BuiltinFunction::StringIsFloat => quote!(sp::string_to_float(#(#a)*.as_str()).is_some()),
5018        BuiltinFunction::StringIsEmpty => quote!(#(#a)*.is_empty()),
5019        BuiltinFunction::StringCharacterCount => {
5020            quote!( sp::UnicodeSegmentation::graphemes(#(#a)*.as_str(), true).count() as i32 )
5021        }
5022        BuiltinFunction::StringToLowercase => quote!(sp::SharedString::from(#(#a)*.to_lowercase())),
5023        BuiltinFunction::StringToUppercase => quote!(sp::SharedString::from(#(#a)*.to_uppercase())),
5024        BuiltinFunction::StringStartsWith => {
5025            let (s, pat) = (a.next().unwrap(), a.next().unwrap());
5026            quote!(#s.starts_with(#pat.as_str()))
5027        }
5028        BuiltinFunction::StringEndsWith => {
5029            let (s, pat) = (a.next().unwrap(), a.next().unwrap());
5030            quote!(#s.ends_with(#pat.as_str()))
5031        }
5032        BuiltinFunction::StringReplaceAll => {
5033            let (s, from, to) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5034            quote!(sp::shared_string_replace_all(&#s, #from.as_str(), #to.as_str()))
5035        }
5036        BuiltinFunction::KeysToString => quote!(sp::ToSharedString::to_shared_string(&#(#a)*)),
5037        BuiltinFunction::ColorRgbaStruct => quote!( #(#a)*.to_argb_u8()),
5038        BuiltinFunction::ColorHsvaStruct => quote!( #(#a)*.to_hsva()),
5039        BuiltinFunction::ColorOklchStruct => quote!( #(#a)*.to_oklch()),
5040        BuiltinFunction::ColorBrighter => {
5041            let x = a.next().unwrap();
5042            let factor = a.next().unwrap();
5043            quote!(#x.brighter(#factor as f32))
5044        }
5045        BuiltinFunction::ColorDarker => {
5046            let x = a.next().unwrap();
5047            let factor = a.next().unwrap();
5048            quote!(#x.darker(#factor as f32))
5049        }
5050        BuiltinFunction::ColorTransparentize => {
5051            let x = a.next().unwrap();
5052            let factor = a.next().unwrap();
5053            quote!(#x.transparentize(#factor as f32))
5054        }
5055        BuiltinFunction::ColorMix => {
5056            let x = a.next().unwrap();
5057            let y = a.next().unwrap();
5058            let factor = a.next().unwrap();
5059            quote!(#x.mix(&#y.into(), #factor as f32))
5060        }
5061        BuiltinFunction::ColorWithAlpha => {
5062            let x = a.next().unwrap();
5063            let alpha = a.next().unwrap();
5064            quote!(#x.with_alpha(#alpha as f32))
5065        }
5066        BuiltinFunction::ImageSize => quote!( #(#a)*.size()),
5067        BuiltinFunction::ArrayLength => {
5068            quote!(match &#(#a)* { x => {
5069                x.model_tracker().track_row_count_changes();
5070                x.row_count() as i32
5071            }})
5072        }
5073        BuiltinFunction::ArrayPush => {
5074            let model = a.next().unwrap();
5075            let value = a.next().unwrap();
5076            quote!({
5077                let model = &#model;
5078                let value = #value;
5079                sp::report_model_error("push", None, model.push_row(value));
5080            })
5081        }
5082        BuiltinFunction::ArrayRemove => {
5083            let model = a.next().unwrap();
5084            let index = a.next().unwrap();
5085            quote!({
5086                let model = &#model;
5087                let result = match usize::try_from(#index) {
5088                    Ok(index) => model.remove_row(index),
5089                    Err(_) => Err(sp::ModelError::out_of_bounds(model.row_count())),
5090                };
5091                sp::report_model_error("remove", None, result);
5092            })
5093        }
5094        BuiltinFunction::ArrayInsert => {
5095            let model = a.next().unwrap();
5096            let index = a.next().unwrap();
5097            let value = a.next().unwrap();
5098            quote!({
5099                let model = &#model;
5100                let index = #index;
5101                let value = #value;
5102                let result = match usize::try_from(index) {
5103                    Ok(index) => model.insert_row(index, value),
5104                    Err(_) => Err(sp::ModelError::out_of_bounds(model.row_count())),
5105                };
5106                sp::report_model_error("insert", None, result);
5107            })
5108        }
5109        BuiltinFunction::Rgb => {
5110            let (r, g, b, a) =
5111                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5112            quote!({
5113                let r: u8 = (#r as u32).min(255) as u8;
5114                let g: u8 = (#g as u32).min(255) as u8;
5115                let b: u8 = (#b as u32).min(255) as u8;
5116                let a: u8 = (255. * (#a as f32)).max(0.).min(255.) as u8;
5117                sp::Color::from_argb_u8(a, r, g, b)
5118            })
5119        }
5120        BuiltinFunction::Hsv => {
5121            let (h, s, v, a) =
5122                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5123            quote!({
5124                let s: f32 = (#s as f32).max(0.).min(1.) as f32;
5125                let v: f32 = (#v as f32).max(0.).min(1.) as f32;
5126                let a: f32 = (1. * (#a as f32)).max(0.).min(1.) as f32;
5127                sp::Color::from_hsva(#h as f32, s, v, a)
5128            })
5129        }
5130        BuiltinFunction::Oklch => {
5131            let (l, c, h, alpha) =
5132                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5133            quote!({
5134                let l: f32 = (#l as f32).max(0.).min(1.) as f32;
5135                let c: f32 = (#c as f32).max(0.) as f32;
5136                let alpha: f32 = (#alpha as f32).max(0.).min(1.) as f32;
5137                sp::Color::from_oklch(l, c, #h as f32, alpha)
5138            })
5139        }
5140        BuiltinFunction::ColorScheme => {
5141            // A `Palette.color-scheme` binding inside a SystemTrayIcon-rooted component
5142            // resolves against the tray's own scheme; everything else falls back to the
5143            // process-wide value held by the SlintContext.
5144            let global_access = &ctx.generator_state.global_access;
5145            quote!({
5146                let _root = #global_access.root_item_tree_weak.upgrade().unwrap();
5147                sp::context_for_root(&_root)
5148                    .map_or(sp::ColorScheme::Unknown, |c| c.color_scheme(Some(&_root)))
5149            })
5150        }
5151        BuiltinFunction::AccentColor => {
5152            let global_access = &ctx.generator_state.global_access;
5153            quote!(sp::accent_color(&#global_access.root_item_tree_weak.upgrade().unwrap()))
5154        }
5155        BuiltinFunction::SupportsNativeMenuBar => {
5156            let window_adapter_tokens = access_window_adapter_field(ctx);
5157            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar())
5158        }
5159        BuiltinFunction::SetupMenuBar => {
5160            let window_adapter_tokens = access_window_adapter_field(ctx);
5161            let [
5162                Expression::PropertyReference(entries_r),
5163                Expression::PropertyReference(sub_menu_r),
5164                Expression::PropertyReference(activated_r),
5165                Expression::NumberLiteral(tree_index),
5166                Expression::BoolLiteral(no_native),
5167                condition,
5168                visible,
5169                ..,
5170            ] = arguments
5171            else {
5172                panic!("internal error: incorrect arguments to SetupMenuBar")
5173            };
5174
5175            // We have an MenuItem tree
5176            let current_sub_component = ctx.current_sub_component().unwrap();
5177            let item_tree_id = inner_component_id(
5178                &ctx.compilation_unit.sub_components
5179                    [current_sub_component.menu_item_trees[*tree_index as usize].root],
5180            );
5181
5182            let access_entries = access_member(entries_r, ctx).unwrap();
5183            let access_sub_menu = access_member(sub_menu_r, ctx).unwrap();
5184            let access_activated = access_member(activated_r, ctx).unwrap();
5185
5186            let compile_prop = |prop_expr: &Expression| {
5187                let binding = compile_expression(prop_expr, ctx);
5188                quote!({
5189                    let self_weak = _self.self_weak.get().unwrap().clone();
5190                    move || {
5191                        let Some(self_rc) = self_weak.upgrade() else { return false };
5192                        let _self = self_rc.as_pin_ref();
5193                        #binding
5194                    }
5195                })
5196            };
5197
5198            let condition_tokens = compile_prop(condition);
5199            let visible_tokens = compile_prop(visible);
5200
5201            let native_impl = {
5202                let menu_from_item_tree = quote!(sp::VRc::new(sp::MenuFromItemTree::new_with_condition_and_visible(sp::VRc::into_dyn(menu_item_tree_instance), #condition_tokens, #visible_tokens)));
5203                if *no_native {
5204                    quote!(let menu_item_tree = #menu_from_item_tree;)
5205                } else {
5206                    quote! {
5207                        let menu_item_tree = #menu_from_item_tree;
5208                        if sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar() {
5209                            let menu_item_tree_dyn = sp::VRc::into_dyn(sp::VRc::clone(&menu_item_tree));
5210                            sp::WindowInner::from_pub(#window_adapter_tokens.window()).setup_menubar(menu_item_tree_dyn);
5211                        }
5212                    }
5213                }
5214            };
5215
5216            quote!({
5217                let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
5218                #native_impl
5219                // These handlers keep the menu item tree alive on the component; the native menu bar
5220                // holds only a weak reference to it.
5221                {
5222                    let menu_item_tree_ = sp::VRc::clone(&menu_item_tree);
5223                    #access_entries.set_binding(move || {
5224                        let mut entries = sp::SharedVector::default();
5225                        sp::VRc::borrow(&menu_item_tree_).sub_menu(sp::Option::None, &mut entries);
5226                        sp::ModelRc::new(sp::SharedVectorModel::from(entries))
5227                    });
5228                    let menu_item_tree_ = sp::VRc::clone(&menu_item_tree);
5229                    #access_sub_menu.set_handler(move |entry| {
5230                        let mut entries = sp::SharedVector::default();
5231                        sp::VRc::borrow(&menu_item_tree_).sub_menu(sp::Option::Some(&entry.0), &mut entries);
5232                        sp::ModelRc::new(sp::SharedVectorModel::from(entries))
5233                    });
5234                    let menu_item_tree_ = menu_item_tree.clone();
5235                    #access_activated.set_handler(move |entry| {
5236                        sp::VRc::borrow(&menu_item_tree_).activate(&entry.0);
5237                    });
5238                }
5239                sp::WindowInner::from_pub(#window_adapter_tokens.window())
5240                    .setup_menubar_shortcuts(sp::VRc::into_dyn(menu_item_tree));
5241            })
5242        }
5243        BuiltinFunction::SetupSystemTrayIcon => {
5244            let [
5245                Expression::PropertyReference(system_tray_ref),
5246                Expression::NumberLiteral(tree_index),
5247                rest @ ..,
5248            ] = arguments
5249            else {
5250                panic!("internal error: incorrect arguments to SetupSystemTrayIcon")
5251            };
5252
5253            let current_sub_component = ctx.current_sub_component().unwrap();
5254            let item_tree_id = inner_component_id(
5255                &ctx.compilation_unit.sub_components
5256                    [current_sub_component.menu_item_trees[*tree_index as usize].root],
5257            );
5258
5259            let system_tray = access_member(system_tray_ref, ctx).unwrap();
5260            let (_, system_tray_rc) = native_item_from_owner(system_tray_ref, ctx, &quote!(_self));
5261
5262            // `if cond : Menu { ... }` lowers the condition into a closure that
5263            // gates the menu's shadow tree.
5264            let condition_tokens = if let Some(condition) = rest.first() {
5265                let binding = compile_expression(condition, ctx);
5266                quote!({
5267                    let self_weak = _self.self_weak.get().unwrap().clone();
5268                    move || {
5269                        let Some(self_rc) = self_weak.upgrade() else { return false };
5270                        let _self = self_rc.as_pin_ref();
5271                        #binding
5272                    }
5273                })
5274            } else {
5275                quote!(|| true)
5276            };
5277
5278            let menu_from_item_tree = quote!(sp::MenuFromItemTree::new_with_condition_and_visible(
5279                sp::VRc::into_dyn(menu_item_tree_instance),
5280                #condition_tokens,
5281                || true
5282            ));
5283
5284            quote!({
5285                let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
5286                let menu_vrc = sp::VRc::into_dyn(sp::VRc::new(#menu_from_item_tree));
5287                #system_tray.set_menu(&#system_tray_rc, menu_vrc);
5288            })
5289        }
5290        BuiltinFunction::MonthDayCount => {
5291            let (m, y) = (a.next().unwrap(), a.next().unwrap());
5292            quote!(sp::month_day_count(#m as u32, #y as i32).unwrap_or(0))
5293        }
5294        BuiltinFunction::MonthOffset => {
5295            let (m, y) = (a.next().unwrap(), a.next().unwrap());
5296            quote!(sp::month_offset(#m as u32, #y as i32))
5297        }
5298        BuiltinFunction::FormatDate => {
5299            let (f, d, m, y) =
5300                (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5301            quote!(sp::format_date(&#f, #d as u32, #m as u32, #y as i32))
5302        }
5303        BuiltinFunction::ValidDate => {
5304            let (d, f) = (a.next().unwrap(), a.next().unwrap());
5305            quote!(sp::parse_date(#d.as_str(), #f.as_str()).is_some())
5306        }
5307        BuiltinFunction::ParseDate => {
5308            let (d, f) = (a.next().unwrap(), a.next().unwrap());
5309            quote!(sp::ModelRc::new(sp::parse_date(#d.as_str(), #f.as_str()).map(|d| sp::VecModel::from_slice(&d)).unwrap_or_default()))
5310        }
5311        BuiltinFunction::DateNow => {
5312            quote!(sp::ModelRc::new(sp::VecModel::from_slice(&sp::date_now())))
5313        }
5314        BuiltinFunction::TextInputFocused => {
5315            let window_adapter_tokens = access_window_adapter_field(ctx);
5316            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).text_input_focused())
5317        }
5318        BuiltinFunction::SetTextInputFocused => {
5319            let window_adapter_tokens = access_window_adapter_field(ctx);
5320            quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).set_text_input_focused(#(#a)*))
5321        }
5322        BuiltinFunction::Translate => {
5323            quote!(slint::private_unstable_api::translate(#((#a) as _),*))
5324        }
5325        BuiltinFunction::Use24HourFormat => {
5326            quote!(slint::private_unstable_api::use_24_hour_format())
5327        }
5328        BuiltinFunction::ItemAbsolutePosition => {
5329            if let [Expression::PropertyReference(pr)] = arguments {
5330                item_owner(pr).map_or_default(|owner| {
5331                    let (_, item_rc) = native_item_from_owner(pr, ctx, &owner);
5332                    quote!({
5333                        let item_rc = #item_rc;
5334                        sp::logical_position_to_api(item_rc.map_to_window(item_rc.geometry().origin))
5335                    })
5336                })
5337            } else {
5338                panic!("internal error: invalid args to MapPointToWindow {arguments:?}")
5339            }
5340        }
5341        BuiltinFunction::UpdateTimers => {
5342            quote!(_self.update_timers())
5343        }
5344        BuiltinFunction::DetectOperatingSystem => {
5345            quote!(sp::detect_operating_system())
5346        }
5347        // start and stop are unreachable because they are lowered to simple assignment of running
5348        BuiltinFunction::StartTimer => unreachable!(),
5349        BuiltinFunction::StopTimer => unreachable!(),
5350        BuiltinFunction::RestartTimer => {
5351            if let [Expression::PropertyReference(pr)] = arguments {
5352                access_member(pr, ctx).then(|timer| quote!(#timer.restart()))
5353            } else {
5354                panic!("internal error: invalid args to RestartTimer {arguments:?}")
5355            }
5356        }
5357        BuiltinFunction::OpenUrl => {
5358            let url = a.next().unwrap();
5359            let window_adapter_tokens = access_window_adapter_field(ctx);
5360            quote!(sp::open_url(&#url, #window_adapter_tokens.window()).is_ok())
5361        }
5362        BuiltinFunction::MacosBringAllWindowsToFront => {
5363            quote!(sp::macos_bring_all_windows_to_front())
5364        }
5365        BuiltinFunction::ParseMarkdown => {
5366            let format_string = a.next().unwrap();
5367            let args = a.next().unwrap();
5368            quote!(sp::parse_markdown(&#format_string, &#args))
5369        }
5370        BuiltinFunction::StringToStyledText => {
5371            let string = a.next().unwrap();
5372            quote!(sp::string_to_styled_text(#string.to_string()))
5373        }
5374        BuiltinFunction::ColorToStyledText => {
5375            let color = a.next().unwrap();
5376            quote!(sp::color_to_styled_text(#color))
5377        }
5378        BuiltinFunction::PathPointAt => {
5379            if let [Expression::PropertyReference(pr), t] = arguments {
5380                let t = compile_expression(t, ctx);
5381                item_owner(pr).map_or_default(|owner| {
5382                    let (_, item_rc) = native_item_from_owner(pr, ctx, &owner);
5383                    quote!({
5384                        let item_rc = #item_rc;
5385                        sp::logical_position_to_api(
5386                            item_rc
5387                                .downcast::<sp::Path>()
5388                                .unwrap()
5389                                .as_pin_ref()
5390                                .point_at(&item_rc, #t as f32),
5391                        )
5392                    })
5393                })
5394            } else {
5395                panic!("internal error: invalid args to PathPointAt {arguments:?}")
5396            }
5397        }
5398        BuiltinFunction::PathAngleAt => {
5399            if let [Expression::PropertyReference(pr), t] = arguments {
5400                let t = compile_expression(t, ctx);
5401                item_owner(pr).map_or_default(|owner| {
5402                    let (_, item_rc) = native_item_from_owner(pr, ctx, &owner);
5403                    quote!({
5404                        let item_rc = #item_rc;
5405                        item_rc
5406                            .downcast::<sp::Path>()
5407                            .unwrap()
5408                            .as_pin_ref()
5409                            .angle_at(&item_rc, #t as f32)
5410                    })
5411                })
5412            } else {
5413                panic!("internal error: invalid args to PathAngleAt {arguments:?}")
5414            }
5415        }
5416        BuiltinFunction::ArrayAny => {
5417            let arr_expression = compile_expression_to_value(&arguments[0], ctx);
5418            let Expression::Closure { arg_name, expression } = &arguments[1] else {
5419                panic!("internal error: ArrayAny expects a closure as second argument")
5420            };
5421            let arg_name = ident(arg_name);
5422            let closure_expression = compile_expression(expression, ctx);
5423            quote!({
5424                let arr = #arr_expression;
5425                sp::model_any(&arr, |#arg_name| -> bool { #closure_expression })
5426            })
5427        }
5428        BuiltinFunction::ArrayAll => {
5429            let arr_expression = compile_expression_to_value(&arguments[0], ctx);
5430            let Expression::Closure { arg_name, expression } = &arguments[1] else {
5431                panic!("internal error: ArrayAll expects a closure as second argument")
5432            };
5433            let arg_name = ident(arg_name);
5434            let closure_expression = compile_expression(expression, ctx);
5435            quote!({
5436                let arr = #arr_expression;
5437                sp::model_all(&arr, |#arg_name| -> bool { #closure_expression })
5438            })
5439        }
5440        BuiltinFunction::ArrayFindIndex => {
5441            let arr_expression = compile_expression_to_value(&arguments[0], ctx);
5442            let Expression::Closure { arg_name, expression } = &arguments[1] else {
5443                panic!("internal error: ArrayFindIndex expects a closure as second argument")
5444            };
5445            let arg_name = ident(arg_name);
5446            let closure_expression = compile_expression(expression, ctx);
5447            quote!({
5448                let arr = #arr_expression;
5449                sp::model_find_index(&arr, |#arg_name| -> bool { #closure_expression })
5450            })
5451        }
5452    }
5453}
5454
5455fn struct_name_to_tokens(name: &StructName) -> Option<proc_macro2::TokenStream> {
5456    match name {
5457        StructName::None => None,
5458        StructName::User { name, .. } => Some(proc_macro2::TokenTree::from(ident(name)).into()),
5459        StructName::Builtin(builtin_struct) => {
5460            let name: &'static str = builtin_struct.into();
5461            let name = format_ident!("{}", name);
5462            match builtin_struct {
5463                crate::langtype::BuiltinStruct::Color
5464                | crate::langtype::BuiltinStruct::LogicalPosition
5465                | crate::langtype::BuiltinStruct::LogicalSize => Some(quote!(slint::#name)),
5466                s if s.is_public() => Some(quote!(slint::language::#name)),
5467                _ => Some(quote!(sp::#name)),
5468            }
5469        }
5470    }
5471}
5472
5473fn generate_common_repeater_code(
5474    // Which repeater this is about
5475    repeater_index: llr::RepeatedElementIdx,
5476    // If not set, we're only going over repeaters and calling a function on repeated items
5477    // If set, we're also generating code to fill in this "repeated indices" array
5478    repeated_indices_var_name: &Option<Ident>,
5479    // ... which currently has this size, so this is where we'll write
5480    repeated_indices_size: &mut usize,
5481    // ... and this "repeater steps" array (number of items in repeated rows)
5482    repeater_steps_var_name: &Option<Ident>,
5483    repeater_count_code: &mut TokenStream,
5484    // The name of the items vector to use for measuring length (e.g., "items_vec" or "items_vec_h")
5485    items_vec_name: &str,
5486    ctx: &EvaluationContext,
5487) -> (TokenStream, Option<usize>) {
5488    let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
5489    let inner_component_id = self::inner_component_id(ctx.current_sub_component().unwrap());
5490    *repeater_count_code = quote!(#repeater_count_code + _self.#repeater_id.len());
5491
5492    let items_vec_ident = ident(items_vec_name);
5493    let mut repeater_code = quote!(
5494        #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).track_instance_changes();
5495    );
5496    let mut rs_idx_for_init = None;
5497    if let Some(ri) = repeated_indices_var_name {
5498        let ri_idx = *repeated_indices_size;
5499        repeater_code = quote!(
5500            #repeater_code
5501            #ri[#ri_idx] = #items_vec_ident.len() as u32;
5502            #ri[#ri_idx + 1] = _self.#repeater_id.len() as u32;
5503        );
5504        *repeated_indices_size += 2;
5505        if repeater_steps_var_name.is_some() {
5506            rs_idx_for_init = Some(ri_idx / 2);
5507        }
5508    }
5509
5510    (repeater_code, rs_idx_for_init)
5511}
5512
5513fn generate_common_repeater_indices_init_code(
5514    repeated_indices_var_name: &Option<Ident>,
5515    repeated_indices_size: usize,
5516    repeater_steps_var_name: &Option<Ident>,
5517) -> TokenStream {
5518    if let Some(ri) = repeated_indices_var_name {
5519        let rs_init = if let Some(rs) = repeater_steps_var_name {
5520            quote!(let mut #rs = [0u32; #repeated_indices_size / 2];)
5521        } else {
5522            quote!()
5523        };
5524        quote!(
5525            let mut #ri = [0u32; #repeated_indices_size];
5526            #rs_init
5527        )
5528    } else {
5529        quote!()
5530    }
5531}
5532
5533/// For each inner repeater in `templates`, generates code to track instance
5534/// changes and add its length to `total`.  `row_inner_component_id` is the
5535/// repeating Row sub-component's identifier.
5536fn build_inner_track_and_len(
5537    templates: &[llr::RowChildTemplateInfo],
5538    row_inner_component_id: &proc_macro2::Ident,
5539) -> Vec<TokenStream> {
5540    templates
5541        .iter()
5542        .filter_map(|e| match e {
5543            llr::RowChildTemplateInfo::Repeated { repeater_index, .. } => {
5544                let inner_rep_id = format_ident!("repeater{}", usize::from(*repeater_index));
5545                Some(quote! {
5546                    #row_inner_component_id::FIELD_OFFSETS.#inner_rep_id().apply_pin(pin).track_instance_changes();
5547                    total += pin.#inner_rep_id.len();
5548                })
5549            }
5550            _ => None,
5551        })
5552        .collect()
5553}
5554
5555fn generate_repeater_push_code(
5556    repeater_index: llr::RepeatedElementIdx,
5557    row_child_templates: &Option<Vec<llr::RowChildTemplateInfo>>,
5558    repeated_indices_var_name: &Option<proc_macro2::Ident>,
5559    repeated_indices_size: &mut usize,
5560    repeater_steps_var_name: &Option<proc_macro2::Ident>,
5561    repeated_count_code: &mut TokenStream,
5562    ctx: &EvaluationContext,
5563    dynamic_loop_code: impl FnOnce(
5564        proc_macro2::Ident,
5565        usize,
5566        Vec<TokenStream>,
5567        Option<TokenStream>,
5568    ) -> TokenStream,
5569    static_loop_code: impl FnOnce(proc_macro2::Ident, usize, bool) -> TokenStream,
5570) -> TokenStream {
5571    let row_templates = row_child_templates.as_deref();
5572    if llr::has_inner_repeaters(row_child_templates) {
5573        let templates = row_templates.unwrap();
5574        let static_count = llr::static_child_count(templates);
5575        let parent_sc = ctx.current_sub_component().unwrap();
5576        let row_sc_idx = parent_sc.repeated[repeater_index].sub_tree.root;
5577        let row_sc = &ctx.compilation_unit.sub_components[row_sc_idx];
5578        let row_inner_component_id = self::inner_component_id(row_sc);
5579        let inner_ensure_and_len = build_inner_track_and_len(templates, &row_inner_component_id);
5580
5581        let (common_push_code, rs_idx) = self::generate_common_repeater_code(
5582            repeater_index,
5583            repeated_indices_var_name,
5584            repeated_indices_size,
5585            repeater_steps_var_name,
5586            repeated_count_code,
5587            "items_vec",
5588            ctx,
5589        );
5590        let rs_init = rs_idx.and_then(|idx| {
5591            repeater_steps_var_name.as_ref().map(|rs| quote!(#rs[#idx] = total_item_count as u32;))
5592        });
5593
5594        let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
5595        let loop_code = dynamic_loop_code(repeater_id, static_count, inner_ensure_and_len, rs_init);
5596        quote!(
5597            #common_push_code
5598            #loop_code
5599        )
5600    } else {
5601        let step = row_templates.map_or(1, |t| t.len());
5602        let (common_push_code, rs_idx) = self::generate_common_repeater_code(
5603            repeater_index,
5604            repeated_indices_var_name,
5605            repeated_indices_size,
5606            repeater_steps_var_name,
5607            repeated_count_code,
5608            "items_vec",
5609            ctx,
5610        );
5611        let rs_init = rs_idx.and_then(|idx| {
5612            repeater_steps_var_name.as_ref().map(|rs| quote!(#rs[#idx] = #step as u32;))
5613        });
5614        let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
5615        let loop_code = static_loop_code(repeater_id, step, row_templates.is_none());
5616        quote!(
5617            #common_push_code
5618            #rs_init
5619            #loop_code
5620        )
5621    }
5622}
5623
5624fn generate_with_grid_input_data(
5625    cells_variable: &str,
5626    repeated_indices_var_name: &SmolStr,
5627    repeater_steps_var_name: &SmolStr,
5628    elements: &[Either<Expression, llr::GridLayoutRepeatedElement>],
5629    sub_expression: &Expression,
5630    ctx: &EvaluationContext,
5631) -> TokenStream {
5632    let repeated_indices_var_name = Some(ident(repeated_indices_var_name));
5633    let repeater_steps_var_name = Some(ident(repeater_steps_var_name));
5634    let mut fixed_count = 0usize;
5635    let mut repeated_count_code = quote!();
5636    let mut push_code = Vec::new();
5637    let mut repeated_indices_size = 0usize;
5638    for item in elements {
5639        match item {
5640            Either::Left(value) => {
5641                let value = compile_expression(value, ctx);
5642                fixed_count += 1;
5643                push_code.push(quote!(items_vec.push(#value);))
5644            }
5645            Either::Right(repeater) => {
5646                let repeater_push_code = generate_repeater_push_code(
5647                    repeater.repeater_index,
5648                    &repeater.row_child_templates,
5649                    &repeated_indices_var_name,
5650                    &mut repeated_indices_size,
5651                    &repeater_steps_var_name,
5652                    &mut repeated_count_code,
5653                    ctx,
5654                    |repeater_id, static_count, inner_ensure_and_len, rs_init| {
5655                        quote!({
5656                            let len = _self.#repeater_id.len();
5657                            let max_total = (0..len).filter_map(|i| {
5658                                _self.#repeater_id.instance_at(i).map(|rc| {
5659                                    let pin = rc.as_pin_ref();
5660                                    let mut total = #static_count;
5661                                    #(#inner_ensure_and_len)*
5662                                    total
5663                                })
5664                            }).max().unwrap_or(#static_count);
5665                            let total_item_count = max_total;
5666                            #rs_init
5667                            let start_offset = items_vec.len();
5668                            items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(len * total_item_count));
5669                            for i in 0..len {
5670                                if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5671                                    let offset = start_offset + i * total_item_count;
5672                                    sub_comp.as_pin_ref().grid_layout_input_data(new_row, &mut items_vec[offset..offset + total_item_count]);
5673                                }
5674                            }
5675                        })
5676                    },
5677                    |repeater_id, step, is_column_repeater| {
5678                        // Only reset new_row for column-repeaters. For repeated rows, each sub-comp
5679                        // is its own row so new_row stays true.
5680                        let reset_new_row_code =
5681                            if is_column_repeater { quote!(new_row = false;) } else { quote!() };
5682                        quote!({
5683                            let len = _self.#repeater_id.len();
5684                            let start_offset = items_vec.len();
5685                            items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(len * #step));
5686                            for i in 0..len {
5687                                if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5688                                    let offset = start_offset + i * #step;
5689                                    sub_comp.as_pin_ref().grid_layout_input_data(new_row, &mut items_vec[offset..offset + #step]);
5690                                    #reset_new_row_code
5691                                }
5692                            }
5693                        })
5694                    },
5695                );
5696                let new_row = repeater.new_row;
5697                push_code.push(quote!(
5698                    let mut new_row = #new_row;
5699                    #repeater_push_code
5700                ));
5701            }
5702        }
5703    }
5704    let ri_init_code = generate_common_repeater_indices_init_code(
5705        &repeated_indices_var_name,
5706        repeated_indices_size,
5707        &repeater_steps_var_name,
5708    );
5709    let ri_from_slice =
5710        repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
5711    let rs_from_slice =
5712        repeater_steps_var_name.map(|rs| quote!(let #rs = sp::Slice::from_slice(&#rs);));
5713    let cells_variable = ident(cells_variable);
5714    let sub_expression = compile_expression(sub_expression, ctx);
5715
5716    quote! { {
5717        #ri_init_code
5718        let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
5719        #(#push_code)*
5720        let #cells_variable = sp::Slice::from_slice(&items_vec);
5721        #ri_from_slice
5722        #rs_from_slice
5723        #sub_expression
5724    } }
5725}
5726
5727fn generate_with_layout_item_info(
5728    cells_variable: &str,
5729    repeated_indices_var_name: Option<&str>,
5730    repeater_steps_var_name: Option<&str>,
5731    elements: &[Either<Expression, llr::LayoutRepeatedElement>],
5732    orientation: Orientation,
5733    repeated_cross_size: Option<&Expression>,
5734    sub_expression: &Expression,
5735    ctx: &EvaluationContext,
5736) -> TokenStream {
5737    let repeated_indices_var_name = repeated_indices_var_name.map(ident);
5738    let repeater_steps_var_name = repeater_steps_var_name.map(ident);
5739    // Content width forwarded to repeated cells on a vertical box layout's
5740    // main-axis pass, so a height-for-width instance measures at the width it
5741    // is laid out at, like a static cell. Evaluated once, not per instance.
5742    let cross_size_init = repeated_cross_size.map(|e| {
5743        let cs = compile_expression(e, ctx);
5744        quote!(let box_cross_size = (#cs) as f32;)
5745    });
5746    let mut fixed_count = 0usize;
5747    let mut repeated_count_code = quote!();
5748    let mut push_code = Vec::new();
5749    let mut repeated_indices_size = 0usize;
5750    for item in elements {
5751        match item {
5752            Either::Left(value) => {
5753                let value = compile_expression(value, ctx);
5754                fixed_count += 1;
5755                push_code.push(quote!(items_vec.push(#value);))
5756            }
5757            Either::Right(repeater) => {
5758                // A grid measures each instance at its own solved column width,
5759                // read from the horizontal cache with the loop counter bound to
5760                // `GRID_MEASURE_REPEATER_INDEX_LOCAL`.
5761                let grid_cross_width = repeater.cross_width.as_ref().map(|e| {
5762                    let idx = ident(GRID_MEASURE_REPEATER_INDEX_LOCAL);
5763                    let w = compile_expression(e, ctx);
5764                    quote!({ let #idx = i; #w })
5765                });
5766                let repeater_push_code = generate_repeater_push_code(
5767                    repeater.repeater_index,
5768                    &repeater.row_child_templates,
5769                    &repeated_indices_var_name,
5770                    &mut repeated_indices_size,
5771                    &repeater_steps_var_name,
5772                    &mut repeated_count_code,
5773                    ctx,
5774                    |repeater_id, static_count, inner_ensure_and_len, rs_init| {
5775                        // Only box layouts set a cross size, and their repeaters
5776                        // never have row templates.
5777                        debug_assert!(cross_size_init.is_none());
5778                        quote!(
5779                            {
5780                                let len = _self.#repeater_id.len();
5781                                let max_total = (0..len).filter_map(|i| {
5782                                    _self.#repeater_id.instance_at(i).map(|rc| {
5783                                        let pin = rc.as_pin_ref();
5784                                        let mut total = #static_count;
5785                                        #(#inner_ensure_and_len)*
5786                                        total
5787                                    })
5788                                }).max().unwrap_or(#static_count);
5789                                let total_item_count = max_total;
5790                                #rs_init
5791                                for i in 0..len {
5792                                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5793                                        for child_idx in 0..total_item_count {
5794                                            items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, Some(child_idx)));
5795                                        }
5796                                    } else {
5797                                        // Not-yet-instantiated slot: push placeholder cells so the cell
5798                                        // count stays in sync with the repeater length written above.
5799                                        items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(total_item_count));
5800                                    }
5801                                }
5802                            }
5803                        )
5804                    },
5805                    |repeater_id, step, is_column_repeater| {
5806                        if step == 0 {
5807                            quote!()
5808                        } else if step == 1 && is_column_repeater {
5809                            // Column-repeater: each sub-component IS a cell; None returns its own layout_info
5810                            let item_info = match (&cross_size_init, &grid_cross_width, orientation)
5811                            {
5812                                (Some(_), _, Orientation::Vertical) => quote!(
5813                                    sub_comp
5814                                        .as_pin_ref()
5815                                        .layout_item_info_at_cross_width(box_cross_size)
5816                                ),
5817                                (Some(_), _, Orientation::Horizontal) => {
5818                                    unreachable!("a horizontal main pass forwards no cross size")
5819                                }
5820                                (None, Some(w), _) => quote!(
5821                                    sub_comp
5822                                        .as_pin_ref()
5823                                        .layout_item_info_at_cross_width((#w) as f32)
5824                                ),
5825                                (None, None, _) => quote!(
5826                                    sub_comp.as_pin_ref().layout_item_info(#orientation, None)
5827                                ),
5828                            };
5829                            quote!(
5830                                for i in 0.._self.#repeater_id.len() {
5831                                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5832                                       items_vec.push(#item_info);
5833                                    } else {
5834                                        items_vec.push(::core::default::Default::default());
5835                                    }
5836                                }
5837                            )
5838                        } else {
5839                            // Multi-step repeaters only exist in grids, which
5840                            // never set a cross size.
5841                            debug_assert!(cross_size_init.is_none());
5842                            quote!(
5843                                for i in 0.._self.#repeater_id.len() {
5844                                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5845                                        for child_idx in 0..#step {
5846                                            items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, Some(child_idx)));
5847                                        }
5848                                    } else {
5849                                        items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(#step));
5850                                    }
5851                                }
5852                            )
5853                        }
5854                    },
5855                );
5856                push_code.push(repeater_push_code);
5857            }
5858        }
5859    }
5860    let ri_init_code = generate_common_repeater_indices_init_code(
5861        &repeated_indices_var_name,
5862        repeated_indices_size,
5863        &repeater_steps_var_name,
5864    );
5865
5866    let ri_from_slice =
5867        repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
5868    let rs_from_slice =
5869        repeater_steps_var_name.map(|rs| quote!(let #rs = sp::Slice::from_slice(&#rs);));
5870    let cells_variable = ident(cells_variable);
5871    let sub_expression = compile_expression(sub_expression, ctx);
5872
5873    quote! { {
5874        #ri_init_code
5875        #cross_size_init
5876        let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
5877        #(#push_code)*
5878        let #cells_variable = sp::Slice::from_slice(&items_vec);
5879        #ri_from_slice
5880        #rs_from_slice
5881        #sub_expression
5882    } }
5883}
5884
5885fn generate_with_flexbox_layout_item_info(
5886    cells_h_variable: &str,
5887    cells_v_variable: &str,
5888    flex_props_variable: Option<&str>,
5889    repeated_indices_var_name: Option<&str>,
5890    elements: &[Either<(Expression, Expression, Expression), llr::LayoutRepeatedElement>],
5891    repeated_cross_width: Option<&Expression>,
5892    sub_expression: &Expression,
5893    ctx: &EvaluationContext,
5894) -> TokenStream {
5895    // With no flex-props variable the sub-expression only reads the cells, so
5896    // don't evaluate (and thus depend on) a static cell's flex properties. A
5897    // repeated cell still computes its props inside the bundled item-info call,
5898    // whose constraint half is needed either way.
5899    let wants_flex_props = flex_props_variable.is_some();
5900    let repeated_indices_var_name = repeated_indices_var_name.map(ident);
5901    // Container width forwarded to repeated cells' vertical query (column flex),
5902    // so a height-for-width instance wraps to the real width like a static cell.
5903    let cross_width = repeated_cross_width.map(|w| compile_expression(w, ctx));
5904    let mut fixed_count = 0usize;
5905    let mut repeated_count_code = quote!();
5906    let mut push_code = Vec::new();
5907    let mut repeated_indices_size = 0usize;
5908
5909    for item in elements {
5910        match item {
5911            Either::Left((value_h, value_v, value_flex)) => {
5912                let value_h = compile_expression(value_h, ctx);
5913                let value_v = compile_expression(value_v, ctx);
5914                let flex_push = wants_flex_props.then(|| {
5915                    let value_flex = compile_expression(value_flex, ctx);
5916                    quote!(items_vec_flex.push(#value_flex);)
5917                });
5918                fixed_count += 1;
5919                push_code.push(quote!(
5920                    items_vec_h.push(#value_h);
5921                    items_vec_v.push(#value_v);
5922                    #flex_push
5923                ))
5924            }
5925            Either::Right(repeater) => {
5926                let (common_push_code, _rs_idx) = self::generate_common_repeater_code(
5927                    repeater.repeater_index,
5928                    &repeated_indices_var_name,
5929                    &mut repeated_indices_size,
5930                    &None, // No repeater_steps for flexbox
5931                    &mut repeated_count_code,
5932                    "items_vec_h", // Use items_vec_h for length tracking (same as items_vec_v)
5933                    ctx,
5934                );
5935                let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
5936                // For a column flex, measure each instance's vertical info at the
5937                // container width; otherwise use its preferred-width default.
5938                let v_query = if let Some(w) = &cross_width {
5939                    quote!(sub_comp.as_pin_ref().flexbox_layout_item_info_at_cross_width((#w) as f32))
5940                } else {
5941                    quote!(
5942                        sub_comp
5943                            .as_pin_ref()
5944                            .flexbox_layout_item_info(sp::Orientation::Vertical, None)
5945                    )
5946                };
5947                // The instance vtable returns the bundled `FlexboxLayoutItemInfo`;
5948                // split it into the constraint cell and the flex props.
5949                let flex_push =
5950                    wants_flex_props.then(|| quote!(items_vec_flex.push(info_h.props);));
5951                let flex_placeholder = wants_flex_props
5952                    .then(|| quote!(items_vec_flex.push(::core::default::Default::default());));
5953                let loop_code = quote!(for i in 0.._self.#repeater_id.len() {
5954                    if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5955                        let info_h = sub_comp.as_pin_ref().flexbox_layout_item_info(sp::Orientation::Horizontal, None);
5956                        let info_v = #v_query;
5957                        #flex_push
5958                        items_vec_h.push(sp::LayoutItemInfo { constraint: info_h.constraint, ..::core::default::Default::default() });
5959                        items_vec_v.push(sp::LayoutItemInfo { constraint: info_v.constraint, ..::core::default::Default::default() });
5960                    } else {
5961                        // Not-yet-instantiated slot: push placeholder cells so the cell
5962                        // count stays in sync with the repeater length written above.
5963                        items_vec_h.push(::core::default::Default::default());
5964                        items_vec_v.push(::core::default::Default::default());
5965                        #flex_placeholder
5966                    }
5967                });
5968                push_code.push(quote!(
5969                    #common_push_code
5970                    #loop_code
5971                ));
5972            }
5973        }
5974    }
5975
5976    let ri_init_code = generate_common_repeater_indices_init_code(
5977        &repeated_indices_var_name,
5978        repeated_indices_size,
5979        &None,
5980    );
5981
5982    let ri_from_slice =
5983        repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
5984    let cells_h_variable = ident(cells_h_variable);
5985    let cells_v_variable = ident(cells_v_variable);
5986    let (flex_decl, flex_slice) = flex_props_variable
5987        .map(|v| {
5988            let v = ident(v);
5989            (
5990                quote!(let mut items_vec_flex = sp::Vec::with_capacity(#fixed_count #repeated_count_code);),
5991                quote!(let #v = sp::Slice::from_slice(&items_vec_flex);),
5992            )
5993        })
5994        .unzip();
5995    let sub_expression = compile_expression(sub_expression, ctx);
5996
5997    quote! { {
5998        #ri_init_code
5999        let mut items_vec_h = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
6000        let mut items_vec_v = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
6001        #flex_decl
6002        #(#push_code)*
6003        let #cells_h_variable = sp::Slice::from_slice(&items_vec_h);
6004        let #cells_v_variable = sp::Slice::from_slice(&items_vec_v);
6005        #flex_slice
6006        #ri_from_slice
6007        #sub_expression
6008    } }
6009}
6010
6011/// Emit the measure callback shared by `solve_flexbox_layout_with_measure` and
6012/// `flexbox_layout_info_cross_axis_with_measure` calls, bound to a `measure`
6013/// local. For each static height-for-width cell, `measure_cells[i]` carries
6014/// its vertical `LayoutInfo` expression, which reads the `measure_known_w`
6015/// local. taffy calls the callback with at most one of width/height known
6016/// (the cross axis): with the width known we recompute that cell's height at
6017/// it, with the height known no dimension changes. A call with neither
6018/// dimension known is a content-size probe (see `FlexboxMeasureFn` in
6019/// i-slint-core): it measures the height at the default width.
6020fn generate_flexbox_measure_closure(
6021    measure_cells: &[llr::FlexboxMeasureCell],
6022    ctx: &EvaluationContext,
6023) -> TokenStream {
6024    let known_w_ident = ident(MEASURE_KNOWN_W_LOCAL);
6025    let has_repeater =
6026        measure_cells.iter().any(|item| matches!(item, llr::FlexboxMeasureCell::Repeated(_)));
6027
6028    // Without a repeater the cell index is known at compile time, so match on
6029    // it (O(1) dispatch). With a repeater the count is only known at runtime:
6030    // walk the elements, advancing `cursor` by 1 per static cell and by the
6031    // repeater's instance count per repeater, until `index`'s range is found.
6032    let v_body = if !has_repeater {
6033        let arms = measure_cells.iter().enumerate().filter_map(|(i, item)| {
6034            let llr::FlexboxMeasureCell::Static { v_info } = item else { return None };
6035            let idx = proc_macro2::Literal::usize_unsuffixed(i);
6036            let v = compile_expression(v_info, ctx);
6037            Some(quote!(#idx => return (w, ({ #v }).preferred_bounded()),))
6038        });
6039        quote!(match index { #(#arms)* _ => {} })
6040    } else {
6041        let steps = measure_cells.iter().map(|item| match item {
6042            llr::FlexboxMeasureCell::Static { v_info } => {
6043                let v = compile_expression(v_info, ctx);
6044                quote!(
6045                    if index == cursor { return (w, ({ #v }).preferred_bounded()); }
6046                    cursor += 1;
6047                )
6048            }
6049            llr::FlexboxMeasureCell::Repeated(repeater) => {
6050                let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
6051                quote!(
6052                    {
6053                        let len = _self.#repeater_id.len();
6054                        if index >= cursor && index < cursor + len {
6055                            if let Some(sub_comp) = _self.#repeater_id.instance_at(index - cursor) {
6056                                return (w, sub_comp
6057                                    .as_pin_ref()
6058                                    .flexbox_layout_item_info_at_cross_width(w)
6059                                    .constraint
6060                                    .preferred_bounded());
6061                            }
6062                            return (w, h);
6063                        }
6064                        cursor += len;
6065                    }
6066                )
6067            }
6068            llr::FlexboxMeasureCell::Fixed => quote!(cursor += 1;),
6069        });
6070        // The final `cursor += …` is a dead write; `let _ = cursor;` consumes it
6071        // to avoid an `unused_assignments` warning in the generated code.
6072        quote!(let mut cursor = 0usize; #(#steps)* let _ = cursor;)
6073    };
6074
6075    // A dimension taffy didn't assign (`known_* == false`) arrives pre-resolved
6076    // to the cell's preferred size by resolve_measure_defaults in i-slint-core.
6077    quote! {
6078        let mut measure = |index: usize, w: f32, h: f32, _known_w: bool, known_h: bool| -> (f32, f32) {
6079            if known_h {
6080                return (w, h);
6081            }
6082            let #known_w_ident = w;
6083            let _ = #known_w_ident;
6084            #v_body
6085            (w, h)
6086        };
6087    }
6088}
6089
6090/// Access a field offset via `FIELD_OFFSETS.field()`. The `FIELD_OFFSETS`
6091/// constant is a ZST with const fn methods, so this does not create a MIR
6092/// local of any aggregate type.
6093fn access_component_field_offset(component_id: &Ident, field: &Ident) -> TokenStream {
6094    quote!(#component_id::FIELD_OFFSETS.#field())
6095}
6096
6097fn embedded_file_tokens(path: &str) -> TokenStream {
6098    let file = crate::fileaccess::load_file(std::path::Path::new(path)).unwrap(); // embedding pass ensured that the file exists
6099    match file.builtin_contents {
6100        Some(static_data) => {
6101            let literal = proc_macro2::Literal::byte_string(static_data);
6102            quote!(#literal)
6103        }
6104        None => quote!(::core::include_bytes!(#path)),
6105    }
6106}
6107
6108fn generate_resources(doc: &Document) -> Vec<TokenStream> {
6109    #[cfg(feature = "renderer-software")]
6110    let link_section = std::env::var("SLINT_ASSET_SECTION")
6111        .ok()
6112        .map(|section| quote!(#[unsafe(link_section = #section)]));
6113
6114    doc.embedded_file_resources
6115        .borrow()
6116        .iter_enumerated()
6117        .map(|(resource_id, er)| {
6118            let resource_id = resource_id.0;
6119            let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
6120            match &er.kind {
6121                &crate::embedded_resources::EmbeddedResourcesKind::ListOnly => {
6122                    quote!()
6123                },
6124                // Only the slint-sc generator produces these resources.
6125                #[cfg(feature = "slint-sc")]
6126                crate::embedded_resources::EmbeddedResourcesKind::StaticPixels { .. } => {
6127                    unreachable!("slint-sc resources in the Rust generator")
6128                },
6129                crate::embedded_resources::EmbeddedResourcesKind::FileData => {
6130                    let data = embedded_file_tokens(er.path.as_deref().unwrap());
6131                    quote!(static #symbol: &'static [u8] = #data;)
6132                }
6133                crate::embedded_resources::EmbeddedResourcesKind::DataUriPayload(bytes, _) => {
6134                    quote!(static #symbol: &'static [u8] = &[#(#bytes),*];)
6135                }
6136                #[cfg(feature = "renderer-software")]
6137                crate::embedded_resources::EmbeddedResourcesKind::TextureData(crate::embedded_resources::Texture {
6138                    data, format, rect,
6139                    total_size: crate::embedded_resources::Size{width, height},
6140                    original_size: crate::embedded_resources::Size{width: unscaled_width, height: unscaled_height},
6141                }) => {
6142                    let (r_x, r_y, r_w, r_h) = (rect.x(), rect.y(), rect.width(), rect.height());
6143                    let color = if let crate::embedded_resources::PixelFormat::AlphaMap([r, g, b]) = format {
6144                        quote!(sp::Color::from_rgb_u8(#r, #g, #b))
6145                    } else {
6146                        quote!(sp::Color::from_argb_encoded(0))
6147                    };
6148                    let symbol_data = format_ident!("SLINT_EMBEDDED_RESOURCE_{}_DATA", resource_id);
6149                    let data_size = data.len();
6150                    quote!(
6151                        #link_section
6152                        // (the second array is to ensure alignment)
6153                        static #symbol_data : ([u8; #data_size], [u32;0])= ([#(#data),*], []);
6154                        #link_section
6155                        static #symbol: sp::StaticTextures = sp::StaticTextures{
6156                            size: sp::IntSize::new(#width as _, #height as _),
6157                            original_size: sp::IntSize::new(#unscaled_width as _, #unscaled_height as _),
6158                            data: sp::Slice::from_slice(&#symbol_data.0),
6159                            textures: sp::Slice::from_slice(&[
6160                                sp::StaticTexture {
6161                                    rect: sp::euclid::rect(#r_x as _, #r_y as _, #r_w as _, #r_h as _),
6162                                    format: #format,
6163                                    color: #color,
6164                                    index: 0,
6165                                }
6166                            ])
6167                        };
6168                    )
6169                },
6170                #[cfg(feature = "renderer-software")]
6171                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 }) => {
6172
6173                    let character_map_size = character_map.len();
6174
6175                    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 }));
6176
6177                    let glyphs_size = glyphs.len();
6178
6179                    let glyphs = glyphs.iter().map(|crate::embedded_resources::BitmapGlyphs{pixel_size, glyph_data}| {
6180                        let glyph_data_size = glyph_data.len();
6181                        let glyph_data = glyph_data.iter().map(|crate::embedded_resources::BitmapGlyph{x, y, width, height, x_advance, data}|{
6182                            let data_size = data.len();
6183                            quote!(
6184                                sp::BitmapGlyph {
6185                                    x: #x,
6186                                    y: #y,
6187                                    width: #width,
6188                                    height: #height,
6189                                    x_advance: #x_advance,
6190                                    data: sp::Slice::from_slice({
6191                                        #link_section
6192                                        static DATA : [u8; #data_size] = [#(#data),*];
6193                                        &DATA
6194                                    }),
6195                                }
6196                            )
6197                        });
6198
6199                        quote!(
6200                            sp::BitmapGlyphs {
6201                                pixel_size: #pixel_size,
6202                                glyph_data: sp::Slice::from_slice({
6203                                    #link_section
6204                                    static GDATA : [sp::BitmapGlyph; #glyph_data_size] = [#(#glyph_data),*];
6205                                    &GDATA
6206                                }),
6207                            }
6208                        )
6209                    });
6210
6211                    quote!(
6212                        #link_section
6213                        static #symbol: sp::BitmapFont = sp::BitmapFont {
6214                            family_name: sp::Slice::from_slice(#family_name.as_bytes()),
6215                            character_map: sp::Slice::from_slice({
6216                                #link_section
6217                                static CM : [sp::CharacterMapEntry; #character_map_size] = [#(#character_map),*];
6218                                &CM
6219                            }),
6220                            units_per_em: #units_per_em,
6221                            ascent: #ascent,
6222                            descent: #descent,
6223                            x_height: #x_height,
6224                            cap_height: #cap_height,
6225                            glyphs: sp::Slice::from_slice({
6226                                #link_section
6227                                static GLYPHS : [sp::BitmapGlyphs; #glyphs_size] = [#(#glyphs),*];
6228                                &GLYPHS
6229                            }),
6230                            weight: #weight,
6231                            italic: #italic,
6232                            sdf: #sdf,
6233                        };
6234                    )
6235                },
6236            }
6237        })
6238        .collect()
6239}
6240
6241fn remove_parenthesis(
6242    expr: &Expression,
6243    ctx: &EvaluationContext,
6244    compile: impl FnOnce(&Expression, &EvaluationContext) -> TokenStream,
6245) -> TokenStream {
6246    fn extract_single_group(stream: &TokenStream) -> Option<TokenStream> {
6247        let mut iter = stream.clone().into_iter();
6248        let elem = iter.next()?;
6249        let TokenTree::Group(elem) = elem else { return None };
6250        if elem.delimiter() != proc_macro2::Delimiter::Parenthesis {
6251            return None;
6252        }
6253        if iter.next().is_some() {
6254            return None;
6255        }
6256        Some(elem.stream())
6257    }
6258
6259    let mut stream = compile(expr, ctx);
6260    if !matches!(expr, Expression::Struct { .. }) {
6261        while let Some(s) = extract_single_group(&stream) {
6262            stream = s;
6263        }
6264    }
6265    stream
6266}
6267
6268fn compile_expression_no_parenthesis(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
6269    remove_parenthesis(expr, ctx, compile_expression)
6270}
6271
6272fn compile_expression_to_value_no_parenthesis(
6273    expr: &Expression,
6274    ctx: &EvaluationContext,
6275) -> TokenStream {
6276    remove_parenthesis(expr, ctx, compile_expression_to_value)
6277}
6278
6279#[cfg(feature = "bundle-translations")]
6280fn generate_translations(
6281    translations: &crate::translations::Translations,
6282    compilation_unit: &llr::CompilationUnit,
6283) -> TokenStream {
6284    let strings = translations.strings.iter().map(|strings| {
6285        let array = strings.iter().map(|s| match s.as_ref().map(SmolStr::as_str) {
6286            Some(s) => quote!(Some(#s)),
6287            None => quote!(None),
6288        });
6289        quote!(&[#(#array),*])
6290    });
6291    let plurals = translations.plurals.iter().map(|plurals| {
6292        let array = plurals.iter().map(|p| match p {
6293            Some(p) => {
6294                let p = p.iter().map(SmolStr::as_str);
6295                quote!(Some(&[#(#p),*]))
6296            }
6297            None => quote!(None),
6298        });
6299        quote!(&[#(#array),*])
6300    });
6301
6302    let ctx = EvaluationContext {
6303        compilation_unit,
6304        current_scope: EvaluationScope::Global(0.into()),
6305        generator_state: RustGeneratorContext {
6306            global_access: quote!(compile_error!("language rule can't access state")),
6307        },
6308        argument_types: &[Type::Int32],
6309    };
6310    let rules = translations.plural_rules.iter().map(|rule| {
6311        let rule = match rule {
6312            Some(rule) => {
6313                let rule = compile_expression(rule, &ctx);
6314                quote!(Some(|arg: i32| { let args = (arg,); (#rule) as usize } ))
6315            }
6316            None => quote!(None),
6317        };
6318        quote!(#rule)
6319    });
6320
6321    let lang = translations.languages.iter().map(|(lang, separator)| {
6322        let lang = lang.as_str();
6323        quote!(
6324            sp::TranslationsBundled {
6325                language: #lang,
6326                decimal_separator: #separator
6327            }
6328        )
6329    });
6330
6331    quote!(
6332        const _SLINT_TRANSLATED_STRINGS: &[&[sp::Option<&str>]] = &[#(#strings),*];
6333        const _SLINT_TRANSLATED_STRINGS_PLURALS: &[&[sp::Option<&[&str]>]] = &[#(#plurals),*];
6334        #[allow(unused)]
6335        const _SLINT_TRANSLATED_PLURAL_RULES: &[sp::Option<fn(i32) -> usize>] = &[#(#rules),*];
6336        const _SLINT_BUNDLED_TRANSLATIONS: &[sp::TranslationsBundled] = &[#(#lang),*];
6337    )
6338}