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