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