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