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