Skip to main content

i_slint_compiler/generator/
rust.rs

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