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 path = 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 path = quote!(#path.parent.upgrade().unwrap());
4103 }
4104 let repeater_index = repeater_index.unwrap();
4105 let sub_component = &ctx.compilation_unit.sub_components[sc];
4106 let local_reference = sub_component.repeated[repeater_index].index_prop.unwrap().into();
4107 let index_prop = llr::MemberReference::Relative { parent_level: *level, local_reference };
4108 let index_access = access_member(&index_prop, ctx).get_property();
4109 let repeater = access_component_field_offset(
4110 &inner_component_id(sub_component),
4111 &format_ident!("repeater{}", usize::from(repeater_index)),
4112 );
4113 quote!(#repeater.apply_pin(#path.as_pin_ref()).model_set_row_data(#index_access as _, #value as _))
4114}
4115
4116#[inline(never)]
4117fn compile_array_index_assignment(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4118 let Expression::ArrayIndexAssignment { array, index, value } = expr else { unreachable!() };
4119 debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
4120 let base_e = compile_expression(array, ctx);
4121 let index_e = compile_expression(index, ctx);
4122 let value_e = compile_expression(value, ctx);
4123 quote!((#base_e).set_row_data(#index_e as isize as usize, #value_e as _))
4124}
4125
4126#[inline(never)]
4127fn compile_binary_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4128 let mut spine = Vec::new();
4132 let mut node = expr;
4133 while let Expression::BinaryExpression { lhs, rhs, op } = node {
4134 spine.push((rhs, *op));
4135 node = lhs;
4136 }
4137 let mut result = compile_expression_to_value_no_parenthesis(node, ctx);
4138 let mut result_ty = node.ty(ctx);
4139 for (rhs, op) in spine.into_iter().rev() {
4140 result = compile_binary_operator(result, &result_ty, rhs, op, ctx);
4141 result_ty = llr::binary_expression_ty(op, || result_ty);
4142 }
4143 result
4144}
4145
4146fn compile_binary_operator(
4147 lhs: TokenStream,
4148 lhs_ty: &Type,
4149 rhs: &Expression,
4150 op: char,
4151 ctx: &EvaluationContext,
4152) -> TokenStream {
4153 let rhs = compile_expression_to_value_no_parenthesis(rhs, ctx);
4154
4155 if lhs_ty.as_unit_product().is_some() && (op == '=' || op == '!') {
4156 let maybe_negate = if op == '!' { quote!(!) } else { quote!() };
4157 quote!(#maybe_negate sp::ApproxEq::<f64>::approx_eq(&(#lhs as f64), &(#rhs as f64)))
4158 } else {
4159 let (conv1, conv2) = match crate::expression_tree::operator_class(op) {
4160 OperatorClass::ArithmeticOp => match lhs_ty {
4161 Type::String => (None, Some(quote!(.as_str()))),
4162 Type::Struct { .. } => (None, None),
4163 _ => (Some(quote!(as f64)), Some(quote!(as f64))),
4164 },
4165 OperatorClass::ComparisonOp
4166 if matches!(
4167 lhs_ty,
4168 Type::Int32
4169 | Type::Float32
4170 | Type::Duration
4171 | Type::PhysicalLength
4172 | Type::LogicalLength
4173 | Type::Angle
4174 | Type::Percent
4175 | Type::Rem
4176 ) =>
4177 {
4178 (Some(quote!(as f64)), Some(quote!(as f64)))
4179 }
4180 _ => (None, None),
4181 };
4182
4183 let op = match op {
4184 '=' => quote!(==),
4185 '!' => quote!(!=),
4186 '≤' => quote!(<=),
4187 '≥' => quote!(>=),
4188 '&' => quote!(&&),
4189 '|' => quote!(||),
4190 _ => proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
4191 op,
4192 proc_macro2::Spacing::Alone,
4193 ))
4194 .into(),
4195 };
4196 quote!( (((#lhs) #conv1 ) #op ((#rhs) #conv2)) )
4197 }
4198}
4199
4200#[inline(never)]
4201fn compile_image_reference(expr: &Expression) -> TokenStream {
4202 let Expression::ImageReference { resource_ref, nine_slice } = expr else { unreachable!() };
4203 match &nine_slice {
4204 Some([a, b, c, d]) => {
4205 quote! {{ let mut image = #resource_ref; image.set_nine_slice_edges(#a, #b, #c, #d); image }}
4206 }
4207 None => quote!(#resource_ref),
4208 }
4209}
4210
4211#[inline(never)]
4212fn compile_condition(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4213 let Expression::Condition { condition, true_expr, false_expr } = expr else { unreachable!() };
4214 let condition_code = compile_expression_no_parenthesis(condition, ctx);
4215 let true_code = compile_expression(true_expr, ctx);
4216 let false_code = compile_expression_no_parenthesis(false_expr, ctx);
4217 let semi = if false_expr.ty(ctx) == Type::Void { quote!(;) } else { quote!(as _) };
4218 quote!(
4219 if #condition_code {
4220 (#true_code) #semi
4221 } else {
4222 #false_code
4223 }
4224 )
4225}
4226
4227const ARRAY_CHUNK_SIZE: usize = 32;
4231
4232#[inline(never)]
4233fn compile_array(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4234 let Expression::Array { values, element_ty, output } = expr else { unreachable!() };
4235 let val = values.iter().map(|e| compile_expression_to_value(e, ctx));
4236 match output {
4237 ArrayOutput::Model => {
4238 let rust_element_ty = rust_primitive_type(element_ty).unwrap();
4239 let vec = if values.len() > ARRAY_CHUNK_SIZE && !is_plain_value(element_ty) {
4240 let len = values.len();
4241 let chunks = values.chunks(ARRAY_CHUNK_SIZE).map(|chunk| {
4242 let val = chunk.iter().map(|e| compile_expression_to_value(e, ctx));
4243 quote!(slint::private_unstable_api::build_array_chunk(|| {
4245 #(_array.push(#val as _);)*
4246 });)
4247 });
4248 quote!({
4249 let mut _array = sp::Vec::<#rust_element_ty>::with_capacity(#len);
4250 #(#chunks)*
4251 _array
4252 })
4253 } else {
4254 quote!(sp::vec![#(#val as _),*])
4255 };
4256 quote!(sp::ModelRc::new(sp::VecModel::<#rust_element_ty>::from(#vec)))
4257 }
4258 ArrayOutput::Slice => quote!(sp::Slice::from_slice(&[#(#val),*])),
4259 ArrayOutput::Vector => quote!(sp::vec![#(#val as _),*]),
4260 }
4261}
4262
4263fn is_plain_value(ty: &Type) -> bool {
4267 matches!(
4268 ty,
4269 Type::Int32
4270 | Type::Float32
4271 | Type::Bool
4272 | Type::Color
4273 | Type::Duration
4274 | Type::Angle
4275 | Type::PhysicalLength
4276 | Type::LogicalLength
4277 | Type::Rem
4278 | Type::Percent
4279 | Type::Enumeration(_)
4280 )
4281}
4282
4283#[inline(never)]
4284fn compile_struct(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4285 let Expression::Struct { ty, values } = expr else { unreachable!() };
4286 if ty.name.is_some() {
4287 let name_tokens = struct_name_to_tokens(&ty.name).unwrap();
4288 use crate::langtype::BuiltinStruct as BS;
4293 let supports_struct_literal = match &ty.name {
4294 StructName::User { .. } => true,
4295 StructName::Builtin(b) => {
4296 b.is_layout_data()
4297 || matches!(
4298 b,
4299 BS::LayoutInfo
4300 | BS::LayoutItemInfo
4301 | BS::FlexboxLayoutItemInfo
4302 | BS::FlexItemProps
4303 | BS::Padding
4304 | BS::PropertyAnimation
4305 | BS::StateInfo
4306 )
4307 }
4308 StructName::None => false,
4309 };
4310 if supports_struct_literal {
4311 let (keys, elem): (Vec<_>, Vec<_>) = ty
4312 .fields
4313 .keys()
4314 .filter(|k| values.contains_key(*k))
4315 .map(|k| (ident(k), compile_expression_to_value(&values[k], ctx)))
4316 .unzip();
4317 let default_rest = (keys.len() != ty.fields.len())
4318 .then(|| quote!(..::core::default::Default::default()));
4319 quote!(#name_tokens{#(#keys: #elem as _,)* #default_rest})
4320 } else {
4321 let elem = ty
4322 .fields
4323 .keys()
4324 .map(|k| values.get(k).map(|e| compile_expression_to_value(e, ctx)));
4325 let keys = ty.fields.keys().map(|k| ident(k));
4326 quote!({ let mut the_struct = #name_tokens::default(); #(the_struct.#keys = #elem as _;)* the_struct})
4327 }
4328 } else {
4329 let elem =
4330 ty.fields.keys().map(|k| values.get(k).map(|e| compile_expression_to_value(e, ctx)));
4331 let as_ = ty.fields.values().map(|t| {
4332 if t.as_unit_product().is_some() {
4333 let t = rust_primitive_type(t).unwrap();
4336 quote!(as #t)
4337 } else {
4338 quote!()
4339 }
4340 });
4341 quote!((#((#elem).clone() #as_,)*))
4343 }
4344}
4345
4346#[inline(never)]
4347fn compile_linear_gradient(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4348 let Expression::LinearGradient { angle, stops } = expr else { unreachable!() };
4349 let angle = compile_expression(angle, ctx);
4350 let stops = stops.iter().map(|(color, stop)| {
4351 let color = compile_expression(color, ctx);
4352 let position = compile_expression(stop, ctx);
4353 quote!(sp::GradientStop{ color: #color, position: #position as _ })
4354 });
4355 quote!(slint::Brush::LinearGradient(
4356 sp::LinearGradientBrush::new(#angle as _, [#(#stops),*])
4357 ))
4358}
4359
4360#[inline(never)]
4361fn compile_radial_gradient(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4362 let Expression::RadialGradient { center, radius, stops } = expr else { unreachable!() };
4363 let stops = stops.iter().map(|(color, stop)| {
4364 let color = compile_expression(color, ctx);
4365 let position = compile_expression(stop, ctx);
4366 quote!(sp::GradientStop{ color: #color, position: #position as _ })
4367 });
4368 let brush_expr = quote!(sp::RadialGradientBrush::new_circle([#(#stops),*]));
4369 let brush_expr = if let Some((cx, cy)) = center {
4370 let cx = compile_expression(cx, ctx);
4371 let cy = compile_expression(cy, ctx);
4372 quote!(#brush_expr.with_center(#cx as f32, #cy as f32))
4373 } else {
4374 brush_expr
4375 };
4376 let brush_expr = if let Some(r) = radius {
4377 let r = compile_expression(r, ctx);
4378 quote!(#brush_expr.with_radius(#r as f32))
4379 } else {
4380 brush_expr
4381 };
4382 quote!(slint::Brush::RadialGradient(#brush_expr))
4383}
4384
4385#[inline(never)]
4386fn compile_conic_gradient(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4387 let Expression::ConicGradient { from_angle, center, stops } = expr else { unreachable!() };
4388 let from_angle = compile_expression(from_angle, ctx);
4389 let stops = stops.iter().map(|(color, stop)| {
4390 let color = compile_expression(color, ctx);
4391 let position = compile_expression(stop, ctx);
4392 quote!(sp::GradientStop{ color: #color, position: #position as _ })
4393 });
4394 let brush_expr = quote!(sp::ConicGradientBrush::new(#from_angle as _, [#(#stops),*]));
4395 let brush_expr = if let Some((cx, cy)) = center {
4396 let cx = compile_expression(cx, ctx);
4397 let cy = compile_expression(cy, ctx);
4398 quote!(#brush_expr.with_center(#cx as f32, #cy as f32))
4399 } else {
4400 brush_expr
4401 };
4402 quote!(slint::Brush::ConicGradient(#brush_expr))
4403}
4404
4405#[inline(never)]
4406fn compile_layout_cache_access(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4407 let Expression::LayoutCacheAccess {
4408 layout_cache_prop,
4409 index,
4410 repeater_index,
4411 entries_per_item,
4412 } = expr
4413 else {
4414 unreachable!()
4415 };
4416 access_member(layout_cache_prop, ctx).map_or_default(|cache| {
4417 if let Some(ri) = repeater_index {
4418 let offset = compile_expression(ri, ctx);
4419 quote!({
4420 let cache = #cache.get();
4421 *cache.get((cache[#index] as usize) + #offset as usize * #entries_per_item).unwrap_or(&(0 as _))
4422 })
4423 } else {
4424 quote!(#cache.get()[#index])
4425 }
4426 })
4427}
4428
4429#[inline(never)]
4430fn compile_grid_repeater_cache_access(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4431 let Expression::GridRepeaterCacheAccess {
4432 layout_cache_prop,
4433 index,
4434 repeater_index,
4435 stride,
4436 child_offset,
4437 inner_repeater_index,
4438 entries_per_item,
4439 } = expr
4440 else {
4441 unreachable!()
4442 };
4443 access_member(layout_cache_prop, ctx).map_or_default(|cache| {
4444 let offset = compile_expression(repeater_index, ctx);
4445 let stride_val = compile_expression(stride, ctx);
4446 let inner_offset = inner_repeater_index.as_ref().map(|inner_ri| {
4447 let inner_offset = compile_expression(inner_ri, ctx);
4448 quote!(+ #inner_offset as usize * #entries_per_item)
4449 });
4450
4451 quote!({
4452 let cache = #cache.get();
4453 cache.get(#index)
4454 .and_then(|base| cache.get(*base as usize + #offset as usize * (#stride_val as usize) + #child_offset #inner_offset))
4455 .copied()
4456 .unwrap_or(0 as _)
4457 })
4458 })
4459}
4460
4461#[inline(never)]
4462fn compile_min_max(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4463 let Expression::MinMax { ty, op, lhs, rhs } = expr else { unreachable!() };
4464 let lhs = compile_expression(lhs, ctx);
4465 let t = rust_primitive_type(ty);
4466 let (lhs, rhs) = match t {
4467 Some(t) => {
4468 let rhs = compile_expression(rhs, ctx);
4469 (quote!((#lhs as #t)), quote!(#rhs as #t))
4470 }
4471 None => {
4472 let rhs = compile_expression_no_parenthesis(rhs, ctx);
4473 (lhs, rhs)
4474 }
4475 };
4476 match op {
4477 MinMaxOp::Min => {
4478 quote!(#lhs.min(#rhs))
4479 }
4480 MinMaxOp::Max => {
4481 quote!(#lhs.max(#rhs))
4482 }
4483 }
4484}
4485
4486#[inline(never)]
4487fn compile_translation_reference(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
4488 let Expression::TranslationReference { format_args, string_index, plural } = expr else {
4489 unreachable!()
4490 };
4491 let args = compile_expression(format_args, ctx);
4492 match plural {
4493 Some(plural) => {
4494 let plural = compile_expression(plural, ctx);
4495 quote!(sp::translate_from_bundle_with_plural(
4496 &self::_SLINT_TRANSLATED_STRINGS_PLURALS[#string_index],
4497 &self::_SLINT_TRANSLATED_PLURAL_RULES,
4498 sp::Slice::<sp::SharedString>::from(#args).as_slice(),
4499 #plural as _
4500 ))
4501 }
4502 None => {
4503 quote!(sp::translate_from_bundle(&self::_SLINT_TRANSLATED_STRINGS[#string_index], sp::Slice::<sp::SharedString>::from(#args).as_slice()))
4504 }
4505 }
4506}
4507
4508fn struct_field_access(s: &Struct, name: &str) -> proc_macro2::TokenTree {
4509 if s.name.is_none() {
4510 let index = s
4511 .fields
4512 .keys()
4513 .position(|k| k == name)
4514 .expect("Expression::StructFieldAccess: Cannot find a key in an object");
4515 proc_macro2::Literal::usize_unsuffixed(index).into()
4516 } else {
4517 ident(name).into()
4518 }
4519}
4520
4521fn compile_builtin_function_call(
4522 function: BuiltinFunction,
4523 arguments: &[Expression],
4524 ctx: &EvaluationContext,
4525) -> TokenStream {
4526 let mut a = arguments.iter().map(|a| compile_expression_to_value(a, ctx));
4527 match function {
4528 BuiltinFunction::SetFocusItem => {
4529 if let [Expression::PropertyReference(pr)] = arguments {
4530 let window_tokens = access_window_adapter_field(ctx);
4531 item_owner(pr).then(|owner| {
4532 let (_, focus_item) = native_item_from_owner(pr, ctx, &owner);
4533 quote!(sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(&#focus_item, true, sp::FocusReason::Programmatic))
4534 })
4535 } else {
4536 panic!("internal error: invalid args to SetFocusItem {arguments:?}")
4537 }
4538 }
4539 BuiltinFunction::ClearFocusItem => {
4540 if let [Expression::PropertyReference(pr)] = arguments {
4541 let window_tokens = access_window_adapter_field(ctx);
4542 item_owner(pr).then(|owner| {
4543 let (_, focus_item) = native_item_from_owner(pr, ctx, &owner);
4544 quote!(sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(&#focus_item, false, sp::FocusReason::Programmatic))
4545 })
4546 } else {
4547 panic!("internal error: invalid args to ClearFocusItem {arguments:?}")
4548 }
4549 }
4550 BuiltinFunction::ShowPopupWindow => {
4551 if let [
4554 Expression::NumberLiteral(popup_index),
4555 close_policy,
4556 Expression::PropertyReference(owner_ref),
4557 Expression::PropertyReference(anchor_ref),
4558 is_open_args @ ..,
4559 ] = arguments
4560 {
4561 let mut component_access_tokens = MemberAccess::Direct(quote!(_self));
4562 let llr::MemberReference::Relative { parent_level, local_reference } = owner_ref
4563 else {
4564 unreachable!()
4565 };
4566 for _ in 0..*parent_level {
4567 component_access_tokens = match component_access_tokens {
4568 MemberAccess::Option(token_stream) => MemberAccess::Option(
4569 quote!(#token_stream.and_then(|a| a.as_pin_ref().parent.upgrade())),
4570 ),
4571 MemberAccess::Direct(token_stream) => {
4572 MemberAccess::Option(quote!(#token_stream.parent.upgrade()))
4573 }
4574 _ => unreachable!(),
4575 };
4576 }
4577 let (suffix, _) = follow_sub_component_path_fields(
4578 ctx.compilation_unit,
4579 ctx.parent_sub_component_idx(*parent_level).unwrap(),
4580 &local_reference.sub_component_path,
4581 );
4582 ctx.with_reference_scope(
4583 *parent_level,
4584 &local_reference.sub_component_path,
4585 |parent_ctx| {
4586 let popup = &ctx.compilation_unit.sub_components[parent_ctx.sub_component]
4587 .popup_windows[*popup_index as usize];
4588 let popup_window_id =
4589 inner_component_id(&ctx.compilation_unit.sub_components[popup.item_tree.root]);
4590 let popup_ctx = EvaluationContext::new_sub_component(
4591 ctx.compilation_unit,
4592 popup.item_tree.root,
4593 RustGeneratorContext { global_access: quote!(_self.globals()) },
4594 Some(&parent_ctx),
4595 );
4596 let position = compile_expression(&popup.position.borrow(), &popup_ctx);
4597 let close_policy = compile_expression(close_policy, ctx);
4598 let popup_id_name = internal_popup_id(*popup_index as usize);
4599 let window_kind = if popup.is_tooltip {
4600 quote!(sp::WindowKind::ToolTip)
4601 } else {
4602 quote!(sp::WindowKind::Popup)
4603 };
4604 let globals_init = quote! {
4605 if let Some(popup_window_adapter) = window.create_child_window_adapter(#window_kind) {
4606 shared_global.clone_with_window_adapter(popup_window_adapter)
4607 } else {
4608 shared_global.clone()
4609 }
4610 };
4611 let is_open_set_expr = is_open_args.first().map(|arg| {
4615 let Expression::PropertyReference(is_open_ref) = arg else {
4616 unreachable!(
4617 "ShowPopupWindow is-open argument must be a property reference"
4618 )
4619 };
4620 access_member(is_open_ref, ctx).then(|p| quote!(#p.set(value)))
4621 });
4622 item_owner(anchor_ref).then_named("anchor_owner", |owner| {
4623 let (_, parent_item) = native_item_from_owner(anchor_ref, ctx, &owner);
4624 component_access_tokens.then(|component_access_tokens| {
4625 let compo = quote!(#component_access_tokens #suffix);
4626 let (is_open_self_weak_decl, is_open_setter) = match &is_open_set_expr {
4631 Some(set_expr) => (
4632 quote!(let is_open_self_weak = _self.self_weak.get().unwrap().clone();),
4633 quote! {
4634 sp::Box::new(move |value: bool| {
4635 if let Some(is_open_self) = is_open_self_weak.upgrade() {
4636 let _self = is_open_self.as_pin_ref();
4637 #set_expr
4638 }
4639 })
4640 },
4641 ),
4642 None => (quote!(), quote!(sp::Box::new(|_| {}))),
4643 };
4644 quote!({
4645 let parent_item = &#parent_item;
4646 let shared_global = #compo.globals.get().unwrap();
4648 let window_adapter = shared_global.window_adapter_impl();
4649 let window = sp::WindowInner::from_pub(window_adapter.window());
4650 let globals = #globals_init;
4651
4652 let popup_instance = #popup_window_id::new(#compo.self_weak.get().unwrap().clone(), globals).unwrap();
4653 let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
4654 if let Some(current_id) = #compo.#popup_id_name.take() {
4655 window.close_popup(current_id);
4656 }
4657
4658 let popup_instance_vrc_for_position = popup_instance_vrc.clone();
4659 let access_position = sp::Box::new(move || {
4660 let _self = popup_instance_vrc_for_position.as_pin_ref(); #position
4661 });
4662
4663 #is_open_self_weak_decl
4664 let popup_id = window.show_popup(
4665 &sp::VRc::into_dyn(popup_instance.into()),
4666 access_position,
4667 #close_policy,
4668 parent_item,
4669 #window_kind,
4670 #is_open_setter,
4671 );
4672 #compo.#popup_id_name.set(Some(popup_id));
4673 #popup_window_id::user_init(popup_instance_vrc.clone());
4674 })
4675 })
4676 })
4677 },
4678 )
4679 } else {
4680 panic!("internal error: invalid args to ShowPopupWindow {arguments:?}")
4681 }
4682 }
4683 BuiltinFunction::ClosePopupWindow => {
4684 if let [
4685 Expression::NumberLiteral(popup_index),
4686 Expression::PropertyReference(parent_ref),
4687 ] = arguments
4688 {
4689 let mut component_access_tokens = MemberAccess::Direct(quote!(_self));
4690 let llr::MemberReference::Relative { parent_level, local_reference } = parent_ref
4691 else {
4692 unreachable!()
4693 };
4694 for _ in 0..*parent_level {
4695 component_access_tokens = match component_access_tokens {
4696 MemberAccess::Option(token_stream) => MemberAccess::Option(
4697 quote!(#token_stream.and_then(|a| a.parent.upgrade())),
4698 ),
4699 MemberAccess::Direct(token_stream) => {
4700 MemberAccess::Option(quote!(#token_stream.parent.upgrade()))
4701 }
4702 _ => unreachable!(),
4703 };
4704 }
4705 let (suffix, _) = follow_sub_component_path_fields(
4706 ctx.compilation_unit,
4707 ctx.parent_sub_component_idx(*parent_level).unwrap(),
4708 &local_reference.sub_component_path,
4709 );
4710 let popup_id_name = internal_popup_id(*popup_index as usize);
4711 let current_id_tokens = match component_access_tokens {
4712 MemberAccess::Option(token_stream) => quote!(
4713 #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)))
4714 ),
4715 MemberAccess::Direct(token_stream) => {
4716 quote!(#token_stream.as_ref() #suffix.#popup_id_name.take().map(|id|(#token_stream.as_ref() #suffix.globals.get().unwrap().clone(), id)))
4717 }
4718 _ => unreachable!(),
4719 };
4720 quote!(
4721 if let Some((globals, current_id)) = #current_id_tokens {
4722 sp::WindowInner::from_pub(globals.window_adapter_impl().window()).close_popup(current_id);
4723 }
4724 )
4725 } else {
4726 panic!("internal error: invalid args to ClosePopupWindow {arguments:?}")
4727 }
4728 }
4729 BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
4730 let [Expression::PropertyReference(context_menu_ref), entries, position] = arguments
4731 else {
4732 panic!("internal error: invalid args to ShowPopupMenu {arguments:?}")
4733 };
4734
4735 let context_menu = access_member(context_menu_ref, ctx);
4736 let position = compile_expression(position, ctx);
4737
4738 let popup = ctx
4739 .compilation_unit
4740 .popup_menu
4741 .as_ref()
4742 .expect("there should be a popup menu if we want to show it");
4743 let popup_id =
4744 inner_component_id(&ctx.compilation_unit.sub_components[popup.item_tree.root]);
4745 let window_adapter_tokens = access_window_adapter_field(ctx);
4746
4747 let popup_ctx = EvaluationContext::new_sub_component(
4748 ctx.compilation_unit,
4749 popup.item_tree.root,
4750 RustGeneratorContext { global_access: quote!(_self.globals()) },
4751 None,
4752 );
4753 let access_entries = access_member(&popup.entries, &popup_ctx).unwrap();
4754 let access_sub_menu = access_member(&popup.sub_menu, &popup_ctx).unwrap();
4755 let access_activated = access_member(&popup.activated, &popup_ctx).unwrap();
4756 let access_close = access_member(&popup.close, &popup_ctx).unwrap();
4757
4758 let close_popup = context_menu.clone().then(|context_menu| quote!{
4759 if let Some(current_id) = #context_menu.popup_id.take() {
4760 sp::WindowInner::from_pub(#window_adapter_tokens.window()).close_popup(current_id);
4761 }
4762 });
4763
4764 let set_id = context_menu
4765 .clone()
4766 .then(|context_menu| quote!(#context_menu.popup_id.set(Some(id))));
4767 item_owner(context_menu_ref).then_named("context_menu_owner", |owner| {
4768 let (_, context_menu_rc) = native_item_from_owner(context_menu_ref, ctx, &owner);
4769 let slint_show = quote! {
4770 #close_popup
4771 let access_position = sp::Box::new(move || position);
4772 let id = sp::WindowInner::from_pub(window_adapter.window()).show_popup(
4773 &sp::VRc::into_dyn(popup_instance.into()),
4774 access_position,
4775 sp::PopupClosePolicy::CloseOnClickOutside,
4776 &#context_menu_rc,
4777 sp::WindowKind::Menu,
4778 sp::Box::new(|_| {}),
4779 );
4780 #set_id;
4781 #popup_id::user_init(popup_instance_vrc);
4782 };
4783
4784 let common_init = quote! {
4785 let position = #position;
4786 let popup_instance = #popup_id::new(_self.globals.get().unwrap().clone()).unwrap();
4787 let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x);
4788 let parent_weak = _self.self_weak.get().unwrap().clone();
4789 let window_adapter = #window_adapter_tokens;
4790 };
4791
4792 if let Expression::NumberLiteral(tree_index) = entries {
4793 let current_sub_component = ctx.current_sub_component().unwrap();
4795 let item_tree_id = inner_component_id(
4796 &ctx.compilation_unit.sub_components
4797 [current_sub_component.menu_item_trees[*tree_index as usize].root],
4798 );
4799 quote! {{
4800 #common_init
4801 let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
4802 let context_menu_item_tree = sp::VRc::new(sp::MenuFromItemTree::new(sp::VRc::into_dyn(menu_item_tree_instance)));
4803 let context_menu_item_tree_ = context_menu_item_tree.clone();
4804 {
4805 let mut entries = sp::SharedVector::default();
4806 sp::Menu::sub_menu(&*context_menu_item_tree, sp::Option::None, &mut entries);
4807 let _self = popup_instance_vrc.as_pin_ref();
4808 #access_entries.set(sp::ModelRc::new(sp::SharedVectorModel::from(entries)));
4809 let context_menu_item_tree = context_menu_item_tree_.clone();
4810 #access_sub_menu.set_handler(move |entry| {
4811 let mut entries = sp::SharedVector::default();
4812 sp::Menu::sub_menu(&*context_menu_item_tree, sp::Option::Some(&entry.0), &mut entries);
4813 sp::ModelRc::new(sp::SharedVectorModel::from(entries))
4814 });
4815 let context_menu_item_tree = context_menu_item_tree_.clone();
4816 #access_activated.set_handler(move |entry| {
4817 sp::Menu::activate(&*context_menu_item_tree_, &entry.0);
4818 });
4819 let self_weak = parent_weak.clone();
4820 #access_close.set_handler(move |()| {
4821 let Some(self_rc) = self_weak.upgrade() else { return };
4822 let _self = self_rc.as_pin_ref();
4823 #close_popup
4824 });
4825 }
4826 let context_menu_item_tree = sp::VRc::into_dyn(context_menu_item_tree);
4827 if !sp::WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(context_menu_item_tree, position, &#context_menu_rc) {
4828 #slint_show
4829 }
4830 }}
4831 } else {
4832 debug_assert!(
4834 matches!(entries.ty(ctx), Type::Array(ty) if matches!(&*ty, Type::Struct{..}))
4835 );
4836 let entries = compile_expression(entries, ctx);
4837 let forward_callback = |access, cb| {
4838 let call = context_menu
4839 .clone()
4840 .map_or_default(|context_menu| quote!(#context_menu.#cb.call(entry)));
4841 quote!(
4842 let self_weak = parent_weak.clone();
4843 #access.set_handler(move |entry| {
4844 if let Some(self_rc) = self_weak.upgrade() {
4845 let _self = self_rc.as_pin_ref();
4846 #call
4847 } else { ::core::default::Default::default() }
4848 });
4849 )
4850 };
4851 let fw_sub_menu = forward_callback(access_sub_menu.clone(), quote!(sub_menu));
4852 let fw_activated =
4853 forward_callback(access_activated.clone(), quote!(activated));
4854 quote! {{
4855 #common_init
4856 let entries = #entries;
4857 {
4858 let _self = popup_instance_vrc.as_pin_ref();
4859 #access_entries.set(entries.clone());
4860 #fw_sub_menu
4861 #fw_activated
4862 let self_weak = parent_weak.clone();
4863 #access_close.set_handler(move |()| {
4864 let Some(self_rc) = self_weak.upgrade() else { return };
4865 let _self = self_rc.as_pin_ref();
4866 #close_popup
4867 });
4868 }
4869 #slint_show
4870 }}
4871 }
4872 })
4873 }
4874 BuiltinFunction::SetSelectionOffsets => {
4875 if let [llr::Expression::PropertyReference(pr), anchor_expr, focus_expr] = arguments {
4876 let window_adapter_tokens = access_window_adapter_field(ctx);
4877 let anchor = compile_expression(anchor_expr, ctx);
4878 let focus = compile_expression(focus_expr, ctx);
4879
4880 item_owner(pr).then(|owner| {
4881 let (item, item_rc) = native_item_from_owner(pr, ctx, &owner);
4882 quote!(
4883 #item.set_selection_offsets(#window_adapter_tokens, &#item_rc, #anchor as i32, #focus as i32)
4884 )
4885 })
4886 } else {
4887 panic!("internal error: invalid args to set-selection-offsets {arguments:?}")
4888 }
4889 }
4890 BuiltinFunction::ItemFontMetrics => {
4891 if let [Expression::PropertyReference(pr)] = arguments {
4892 let window_adapter_tokens = access_window_adapter_field(ctx);
4893 item_owner(pr).map_or_default(|owner| {
4894 let (item, item_rc) = native_item_from_owner(pr, ctx, &owner);
4895 quote!(
4896 #item.font_metrics(#window_adapter_tokens, &#item_rc)
4897 )
4898 })
4899 } else {
4900 panic!("internal error: invalid args to ItemMemberFunction {arguments:?}")
4901 }
4902 }
4903 BuiltinFunction::ImplicitLayoutInfo(orient) => {
4904 if let [Expression::PropertyReference(pr), constraint_expr] = arguments {
4905 let window_adapter_tokens = access_window_adapter_field(ctx);
4906 let constraint = compile_expression(constraint_expr, ctx);
4907 item_owner(pr).map_or_default(|owner| {
4908 let (item, item_rc) = native_item_from_owner(pr, ctx, &owner);
4909 quote!(
4910 sp::Item::layout_info(#item, #orient, #constraint as _, #window_adapter_tokens, &#item_rc)
4911 )
4912 })
4913 } else {
4914 panic!("internal error: invalid args to ImplicitLayoutInfo {arguments:?}")
4915 }
4916 }
4917 BuiltinFunction::RegisterCustomFontByPath => {
4918 if let [Expression::StringLiteral(path)] = arguments {
4919 let global_access = &ctx.generator_state.global_access;
4920 let path = path.as_str();
4921 quote!(#global_access.window_adapter_ref()?.renderer().register_font_from_path(&std::path::PathBuf::from(#path)).unwrap())
4924 } else {
4925 panic!("internal error: invalid args to RegisterCustomFontByPath {arguments:?}")
4926 }
4927 }
4928 BuiltinFunction::RegisterCustomFontByMemory => {
4929 if let [Expression::NumberLiteral(resource_id)] = &arguments {
4930 let global_access = &ctx.generator_state.global_access;
4931 let resource_id: usize = *resource_id as _;
4932 let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
4933 quote!(#global_access.window_adapter_ref()?.renderer().register_font_from_memory(#symbol.into()).unwrap())
4934 } else {
4935 panic!("internal error: invalid args to RegisterCustomFontByMemory {arguments:?}")
4936 }
4937 }
4938 BuiltinFunction::RegisterBitmapFont => {
4939 if let [Expression::NumberLiteral(resource_id)] = &arguments {
4940 let global_access = &ctx.generator_state.global_access;
4941 let resource_id: usize = *resource_id as _;
4942 let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
4943 quote!(#global_access.window_adapter_ref()?.renderer().register_bitmap_font(&#symbol))
4944 } else {
4945 panic!("internal error: invalid args to RegisterBitmapFont must be a number")
4946 }
4947 }
4948 BuiltinFunction::GetWindowScaleFactor => {
4949 let window_adapter_tokens = access_window_adapter_field(ctx);
4950 quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).scale_factor())
4951 }
4952 BuiltinFunction::GetWindowDefaultFontSize => {
4953 quote!(
4954 sp::WindowItem::resolved_default_font_size(sp::VRcMapped::origin(
4955 &_self.self_weak.get().unwrap().upgrade().unwrap()
4956 ))
4957 .get()
4958 )
4959 }
4960 BuiltinFunction::AnimationTick => {
4961 quote!(sp::animation_tick())
4962 }
4963 BuiltinFunction::Debug => quote!(slint::private_unstable_api::debug(#(#a)*)),
4964 BuiltinFunction::DefaultWindowTitle => quote!(sp::default_window_title()),
4965 BuiltinFunction::DecimalSeparator => {
4966 let window_adapter_tokens = access_window_adapter_field(ctx);
4967 quote!(sp::SharedString::from(
4968 sp::WindowInner::from_pub(#window_adapter_tokens.window())
4969 .context()
4970 .locale_decimal_separator()
4971 ))
4972 }
4973 BuiltinFunction::Mod => {
4974 let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4975 quote!(sp::Euclid::rem_euclid(&(#a1 as f64), &(#a2 as f64)))
4976 }
4977 BuiltinFunction::Round => quote!((#(#a)* as f64).round()),
4978 BuiltinFunction::Ceil => quote!((#(#a)* as f64).ceil()),
4979 BuiltinFunction::Floor => quote!((#(#a)* as f64).floor()),
4980 BuiltinFunction::Sqrt => quote!((#(#a)* as f64).sqrt()),
4981 BuiltinFunction::Abs => quote!((#(#a)* as f64).abs()),
4982 BuiltinFunction::Sin => quote!((#(#a)* as f64).to_radians().sin()),
4983 BuiltinFunction::Cos => quote!((#(#a)* as f64).to_radians().cos()),
4984 BuiltinFunction::Tan => quote!((#(#a)* as f64).to_radians().tan()),
4985 BuiltinFunction::ASin => quote!((#(#a)* as f64).asin().to_degrees()),
4986 BuiltinFunction::ACos => quote!((#(#a)* as f64).acos().to_degrees()),
4987 BuiltinFunction::ATan => quote!((#(#a)* as f64).atan().to_degrees()),
4988 BuiltinFunction::ATan2 => {
4989 let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4990 quote!((#a1 as f64).atan2(#a2 as f64).to_degrees())
4991 }
4992 BuiltinFunction::Log => {
4993 let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4994 quote!((#a1 as f64).log(#a2 as f64))
4995 }
4996 BuiltinFunction::Ln => quote!((#(#a)* as f64).ln()),
4997 BuiltinFunction::Pow => {
4998 let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
4999 quote!((#a1 as f64).powf(#a2 as f64))
5000 }
5001 BuiltinFunction::Exp => quote!((#(#a)* as f64).exp()),
5002 BuiltinFunction::ToFixed => {
5003 let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
5004 quote!(sp::shared_string_from_number_fixed(#a1 as f64, (#a2 as i32).max(0) as usize))
5005 }
5006 BuiltinFunction::ToPrecision => {
5007 let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
5008 quote!(sp::shared_string_from_number_precision(#a1 as f64, (#a2 as i32).max(0) as usize))
5009 }
5010 BuiltinFunction::ToStringUnlocalized => {
5011 let a1 = a.next().unwrap();
5012 quote!(sp::shared_string_from_number_unlocalized(#a1 as f64))
5013 }
5014 BuiltinFunction::StringToFloat => {
5015 quote!(sp::string_to_float(#(#a)*.as_str()).unwrap_or_default())
5016 }
5017 BuiltinFunction::StringIsFloat => quote!(sp::string_to_float(#(#a)*.as_str()).is_some()),
5018 BuiltinFunction::StringIsEmpty => quote!(#(#a)*.is_empty()),
5019 BuiltinFunction::StringCharacterCount => {
5020 quote!( sp::UnicodeSegmentation::graphemes(#(#a)*.as_str(), true).count() as i32 )
5021 }
5022 BuiltinFunction::StringToLowercase => quote!(sp::SharedString::from(#(#a)*.to_lowercase())),
5023 BuiltinFunction::StringToUppercase => quote!(sp::SharedString::from(#(#a)*.to_uppercase())),
5024 BuiltinFunction::StringStartsWith => {
5025 let (s, pat) = (a.next().unwrap(), a.next().unwrap());
5026 quote!(#s.starts_with(#pat.as_str()))
5027 }
5028 BuiltinFunction::StringEndsWith => {
5029 let (s, pat) = (a.next().unwrap(), a.next().unwrap());
5030 quote!(#s.ends_with(#pat.as_str()))
5031 }
5032 BuiltinFunction::StringReplaceAll => {
5033 let (s, from, to) = (a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5034 quote!(sp::shared_string_replace_all(&#s, #from.as_str(), #to.as_str()))
5035 }
5036 BuiltinFunction::KeysToString => quote!(sp::ToSharedString::to_shared_string(&#(#a)*)),
5037 BuiltinFunction::ColorRgbaStruct => quote!( #(#a)*.to_argb_u8()),
5038 BuiltinFunction::ColorHsvaStruct => quote!( #(#a)*.to_hsva()),
5039 BuiltinFunction::ColorOklchStruct => quote!( #(#a)*.to_oklch()),
5040 BuiltinFunction::ColorBrighter => {
5041 let x = a.next().unwrap();
5042 let factor = a.next().unwrap();
5043 quote!(#x.brighter(#factor as f32))
5044 }
5045 BuiltinFunction::ColorDarker => {
5046 let x = a.next().unwrap();
5047 let factor = a.next().unwrap();
5048 quote!(#x.darker(#factor as f32))
5049 }
5050 BuiltinFunction::ColorTransparentize => {
5051 let x = a.next().unwrap();
5052 let factor = a.next().unwrap();
5053 quote!(#x.transparentize(#factor as f32))
5054 }
5055 BuiltinFunction::ColorMix => {
5056 let x = a.next().unwrap();
5057 let y = a.next().unwrap();
5058 let factor = a.next().unwrap();
5059 quote!(#x.mix(&#y.into(), #factor as f32))
5060 }
5061 BuiltinFunction::ColorWithAlpha => {
5062 let x = a.next().unwrap();
5063 let alpha = a.next().unwrap();
5064 quote!(#x.with_alpha(#alpha as f32))
5065 }
5066 BuiltinFunction::ImageSize => quote!( #(#a)*.size()),
5067 BuiltinFunction::ArrayLength => {
5068 quote!(match &#(#a)* { x => {
5069 x.model_tracker().track_row_count_changes();
5070 x.row_count() as i32
5071 }})
5072 }
5073 BuiltinFunction::ArrayPush => {
5074 let model = a.next().unwrap();
5075 let value = a.next().unwrap();
5076 quote!({
5077 let model = &#model;
5078 let value = #value;
5079 sp::report_model_error("push", None, model.push_row(value));
5080 })
5081 }
5082 BuiltinFunction::ArrayRemove => {
5083 let model = a.next().unwrap();
5084 let index = a.next().unwrap();
5085 quote!({
5086 let model = &#model;
5087 let result = match usize::try_from(#index) {
5088 Ok(index) => model.remove_row(index),
5089 Err(_) => Err(sp::ModelError::out_of_bounds(model.row_count())),
5090 };
5091 sp::report_model_error("remove", None, result);
5092 })
5093 }
5094 BuiltinFunction::ArrayInsert => {
5095 let model = a.next().unwrap();
5096 let index = a.next().unwrap();
5097 let value = a.next().unwrap();
5098 quote!({
5099 let model = &#model;
5100 let index = #index;
5101 let value = #value;
5102 let result = match usize::try_from(index) {
5103 Ok(index) => model.insert_row(index, value),
5104 Err(_) => Err(sp::ModelError::out_of_bounds(model.row_count())),
5105 };
5106 sp::report_model_error("insert", None, result);
5107 })
5108 }
5109 BuiltinFunction::Rgb => {
5110 let (r, g, b, a) =
5111 (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5112 quote!({
5113 let r: u8 = (#r as u32).min(255) as u8;
5114 let g: u8 = (#g as u32).min(255) as u8;
5115 let b: u8 = (#b as u32).min(255) as u8;
5116 let a: u8 = (255. * (#a as f32)).max(0.).min(255.) as u8;
5117 sp::Color::from_argb_u8(a, r, g, b)
5118 })
5119 }
5120 BuiltinFunction::Hsv => {
5121 let (h, s, v, a) =
5122 (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5123 quote!({
5124 let s: f32 = (#s as f32).max(0.).min(1.) as f32;
5125 let v: f32 = (#v as f32).max(0.).min(1.) as f32;
5126 let a: f32 = (1. * (#a as f32)).max(0.).min(1.) as f32;
5127 sp::Color::from_hsva(#h as f32, s, v, a)
5128 })
5129 }
5130 BuiltinFunction::Oklch => {
5131 let (l, c, h, alpha) =
5132 (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5133 quote!({
5134 let l: f32 = (#l as f32).max(0.).min(1.) as f32;
5135 let c: f32 = (#c as f32).max(0.) as f32;
5136 let alpha: f32 = (#alpha as f32).max(0.).min(1.) as f32;
5137 sp::Color::from_oklch(l, c, #h as f32, alpha)
5138 })
5139 }
5140 BuiltinFunction::ColorScheme => {
5141 let global_access = &ctx.generator_state.global_access;
5145 quote!({
5146 let _root = #global_access.root_item_tree_weak.upgrade().unwrap();
5147 sp::context_for_root(&_root)
5148 .map_or(sp::ColorScheme::Unknown, |c| c.color_scheme(Some(&_root)))
5149 })
5150 }
5151 BuiltinFunction::AccentColor => {
5152 let global_access = &ctx.generator_state.global_access;
5153 quote!(sp::accent_color(&#global_access.root_item_tree_weak.upgrade().unwrap()))
5154 }
5155 BuiltinFunction::SupportsNativeMenuBar => {
5156 let window_adapter_tokens = access_window_adapter_field(ctx);
5157 quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar())
5158 }
5159 BuiltinFunction::SetupMenuBar => {
5160 let window_adapter_tokens = access_window_adapter_field(ctx);
5161 let [
5162 Expression::PropertyReference(entries_r),
5163 Expression::PropertyReference(sub_menu_r),
5164 Expression::PropertyReference(activated_r),
5165 Expression::NumberLiteral(tree_index),
5166 Expression::BoolLiteral(no_native),
5167 condition,
5168 visible,
5169 ..,
5170 ] = arguments
5171 else {
5172 panic!("internal error: incorrect arguments to SetupMenuBar")
5173 };
5174
5175 let current_sub_component = ctx.current_sub_component().unwrap();
5177 let item_tree_id = inner_component_id(
5178 &ctx.compilation_unit.sub_components
5179 [current_sub_component.menu_item_trees[*tree_index as usize].root],
5180 );
5181
5182 let access_entries = access_member(entries_r, ctx).unwrap();
5183 let access_sub_menu = access_member(sub_menu_r, ctx).unwrap();
5184 let access_activated = access_member(activated_r, ctx).unwrap();
5185
5186 let compile_prop = |prop_expr: &Expression| {
5187 let binding = compile_expression(prop_expr, ctx);
5188 quote!({
5189 let self_weak = _self.self_weak.get().unwrap().clone();
5190 move || {
5191 let Some(self_rc) = self_weak.upgrade() else { return false };
5192 let _self = self_rc.as_pin_ref();
5193 #binding
5194 }
5195 })
5196 };
5197
5198 let condition_tokens = compile_prop(condition);
5199 let visible_tokens = compile_prop(visible);
5200
5201 let native_impl = {
5202 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)));
5203 if *no_native {
5204 quote!(let menu_item_tree = #menu_from_item_tree;)
5205 } else {
5206 quote! {
5207 let menu_item_tree = #menu_from_item_tree;
5208 if sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar() {
5209 let menu_item_tree_dyn = sp::VRc::into_dyn(sp::VRc::clone(&menu_item_tree));
5210 sp::WindowInner::from_pub(#window_adapter_tokens.window()).setup_menubar(menu_item_tree_dyn);
5211 }
5212 }
5213 }
5214 };
5215
5216 quote!({
5217 let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
5218 #native_impl
5219 {
5222 let menu_item_tree_ = sp::VRc::clone(&menu_item_tree);
5223 #access_entries.set_binding(move || {
5224 let mut entries = sp::SharedVector::default();
5225 sp::VRc::borrow(&menu_item_tree_).sub_menu(sp::Option::None, &mut entries);
5226 sp::ModelRc::new(sp::SharedVectorModel::from(entries))
5227 });
5228 let menu_item_tree_ = sp::VRc::clone(&menu_item_tree);
5229 #access_sub_menu.set_handler(move |entry| {
5230 let mut entries = sp::SharedVector::default();
5231 sp::VRc::borrow(&menu_item_tree_).sub_menu(sp::Option::Some(&entry.0), &mut entries);
5232 sp::ModelRc::new(sp::SharedVectorModel::from(entries))
5233 });
5234 let menu_item_tree_ = menu_item_tree.clone();
5235 #access_activated.set_handler(move |entry| {
5236 sp::VRc::borrow(&menu_item_tree_).activate(&entry.0);
5237 });
5238 }
5239 sp::WindowInner::from_pub(#window_adapter_tokens.window())
5240 .setup_menubar_shortcuts(sp::VRc::into_dyn(menu_item_tree));
5241 })
5242 }
5243 BuiltinFunction::SetupSystemTrayIcon => {
5244 let [
5245 Expression::PropertyReference(system_tray_ref),
5246 Expression::NumberLiteral(tree_index),
5247 rest @ ..,
5248 ] = arguments
5249 else {
5250 panic!("internal error: incorrect arguments to SetupSystemTrayIcon")
5251 };
5252
5253 let current_sub_component = ctx.current_sub_component().unwrap();
5254 let item_tree_id = inner_component_id(
5255 &ctx.compilation_unit.sub_components
5256 [current_sub_component.menu_item_trees[*tree_index as usize].root],
5257 );
5258
5259 let system_tray = access_member(system_tray_ref, ctx).unwrap();
5260 let (_, system_tray_rc) = native_item_from_owner(system_tray_ref, ctx, "e!(_self));
5261
5262 let condition_tokens = if let Some(condition) = rest.first() {
5265 let binding = compile_expression(condition, ctx);
5266 quote!({
5267 let self_weak = _self.self_weak.get().unwrap().clone();
5268 move || {
5269 let Some(self_rc) = self_weak.upgrade() else { return false };
5270 let _self = self_rc.as_pin_ref();
5271 #binding
5272 }
5273 })
5274 } else {
5275 quote!(|| true)
5276 };
5277
5278 let menu_from_item_tree = quote!(sp::MenuFromItemTree::new_with_condition_and_visible(
5279 sp::VRc::into_dyn(menu_item_tree_instance),
5280 #condition_tokens,
5281 || true
5282 ));
5283
5284 quote!({
5285 let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap();
5286 let menu_vrc = sp::VRc::into_dyn(sp::VRc::new(#menu_from_item_tree));
5287 #system_tray.set_menu(&#system_tray_rc, menu_vrc);
5288 })
5289 }
5290 BuiltinFunction::MonthDayCount => {
5291 let (m, y) = (a.next().unwrap(), a.next().unwrap());
5292 quote!(sp::month_day_count(#m as u32, #y as i32).unwrap_or(0))
5293 }
5294 BuiltinFunction::MonthOffset => {
5295 let (m, y) = (a.next().unwrap(), a.next().unwrap());
5296 quote!(sp::month_offset(#m as u32, #y as i32))
5297 }
5298 BuiltinFunction::FormatDate => {
5299 let (f, d, m, y) =
5300 (a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
5301 quote!(sp::format_date(&#f, #d as u32, #m as u32, #y as i32))
5302 }
5303 BuiltinFunction::ValidDate => {
5304 let (d, f) = (a.next().unwrap(), a.next().unwrap());
5305 quote!(sp::parse_date(#d.as_str(), #f.as_str()).is_some())
5306 }
5307 BuiltinFunction::ParseDate => {
5308 let (d, f) = (a.next().unwrap(), a.next().unwrap());
5309 quote!(sp::ModelRc::new(sp::parse_date(#d.as_str(), #f.as_str()).map(|d| sp::VecModel::from_slice(&d)).unwrap_or_default()))
5310 }
5311 BuiltinFunction::DateNow => {
5312 quote!(sp::ModelRc::new(sp::VecModel::from_slice(&sp::date_now())))
5313 }
5314 BuiltinFunction::TextInputFocused => {
5315 let window_adapter_tokens = access_window_adapter_field(ctx);
5316 quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).text_input_focused())
5317 }
5318 BuiltinFunction::SetTextInputFocused => {
5319 let window_adapter_tokens = access_window_adapter_field(ctx);
5320 quote!(sp::WindowInner::from_pub(#window_adapter_tokens.window()).set_text_input_focused(#(#a)*))
5321 }
5322 BuiltinFunction::Translate => {
5323 quote!(slint::private_unstable_api::translate(#((#a) as _),*))
5324 }
5325 BuiltinFunction::Use24HourFormat => {
5326 quote!(slint::private_unstable_api::use_24_hour_format())
5327 }
5328 BuiltinFunction::ItemAbsolutePosition => {
5329 if let [Expression::PropertyReference(pr)] = arguments {
5330 item_owner(pr).map_or_default(|owner| {
5331 let (_, item_rc) = native_item_from_owner(pr, ctx, &owner);
5332 quote!({
5333 let item_rc = #item_rc;
5334 sp::logical_position_to_api(item_rc.map_to_window(item_rc.geometry().origin))
5335 })
5336 })
5337 } else {
5338 panic!("internal error: invalid args to MapPointToWindow {arguments:?}")
5339 }
5340 }
5341 BuiltinFunction::UpdateTimers => {
5342 quote!(_self.update_timers())
5343 }
5344 BuiltinFunction::DetectOperatingSystem => {
5345 quote!(sp::detect_operating_system())
5346 }
5347 BuiltinFunction::StartTimer => unreachable!(),
5349 BuiltinFunction::StopTimer => unreachable!(),
5350 BuiltinFunction::RestartTimer => {
5351 if let [Expression::PropertyReference(pr)] = arguments {
5352 access_member(pr, ctx).then(|timer| quote!(#timer.restart()))
5353 } else {
5354 panic!("internal error: invalid args to RestartTimer {arguments:?}")
5355 }
5356 }
5357 BuiltinFunction::OpenUrl => {
5358 let url = a.next().unwrap();
5359 let window_adapter_tokens = access_window_adapter_field(ctx);
5360 quote!(sp::open_url(&#url, #window_adapter_tokens.window()).is_ok())
5361 }
5362 BuiltinFunction::MacosBringAllWindowsToFront => {
5363 quote!(sp::macos_bring_all_windows_to_front())
5364 }
5365 BuiltinFunction::ParseMarkdown => {
5366 let format_string = a.next().unwrap();
5367 let args = a.next().unwrap();
5368 quote!(sp::parse_markdown(&#format_string, &#args))
5369 }
5370 BuiltinFunction::StringToStyledText => {
5371 let string = a.next().unwrap();
5372 quote!(sp::string_to_styled_text(#string.to_string()))
5373 }
5374 BuiltinFunction::ColorToStyledText => {
5375 let color = a.next().unwrap();
5376 quote!(sp::color_to_styled_text(#color))
5377 }
5378 BuiltinFunction::PathPointAt => {
5379 if let [Expression::PropertyReference(pr), t] = arguments {
5380 let t = compile_expression(t, ctx);
5381 item_owner(pr).map_or_default(|owner| {
5382 let (_, item_rc) = native_item_from_owner(pr, ctx, &owner);
5383 quote!({
5384 let item_rc = #item_rc;
5385 sp::logical_position_to_api(
5386 item_rc
5387 .downcast::<sp::Path>()
5388 .unwrap()
5389 .as_pin_ref()
5390 .point_at(&item_rc, #t as f32),
5391 )
5392 })
5393 })
5394 } else {
5395 panic!("internal error: invalid args to PathPointAt {arguments:?}")
5396 }
5397 }
5398 BuiltinFunction::PathAngleAt => {
5399 if let [Expression::PropertyReference(pr), t] = arguments {
5400 let t = compile_expression(t, ctx);
5401 item_owner(pr).map_or_default(|owner| {
5402 let (_, item_rc) = native_item_from_owner(pr, ctx, &owner);
5403 quote!({
5404 let item_rc = #item_rc;
5405 item_rc
5406 .downcast::<sp::Path>()
5407 .unwrap()
5408 .as_pin_ref()
5409 .angle_at(&item_rc, #t as f32)
5410 })
5411 })
5412 } else {
5413 panic!("internal error: invalid args to PathAngleAt {arguments:?}")
5414 }
5415 }
5416 BuiltinFunction::ArrayAny => {
5417 let arr_expression = compile_expression_to_value(&arguments[0], ctx);
5418 let Expression::Closure { arg_name, expression } = &arguments[1] else {
5419 panic!("internal error: ArrayAny expects a closure as second argument")
5420 };
5421 let arg_name = ident(arg_name);
5422 let closure_expression = compile_expression(expression, ctx);
5423 quote!({
5424 let arr = #arr_expression;
5425 sp::model_any(&arr, |#arg_name| -> bool { #closure_expression })
5426 })
5427 }
5428 BuiltinFunction::ArrayAll => {
5429 let arr_expression = compile_expression_to_value(&arguments[0], ctx);
5430 let Expression::Closure { arg_name, expression } = &arguments[1] else {
5431 panic!("internal error: ArrayAll expects a closure as second argument")
5432 };
5433 let arg_name = ident(arg_name);
5434 let closure_expression = compile_expression(expression, ctx);
5435 quote!({
5436 let arr = #arr_expression;
5437 sp::model_all(&arr, |#arg_name| -> bool { #closure_expression })
5438 })
5439 }
5440 BuiltinFunction::ArrayFindIndex => {
5441 let arr_expression = compile_expression_to_value(&arguments[0], ctx);
5442 let Expression::Closure { arg_name, expression } = &arguments[1] else {
5443 panic!("internal error: ArrayFindIndex expects a closure as second argument")
5444 };
5445 let arg_name = ident(arg_name);
5446 let closure_expression = compile_expression(expression, ctx);
5447 quote!({
5448 let arr = #arr_expression;
5449 sp::model_find_index(&arr, |#arg_name| -> bool { #closure_expression })
5450 })
5451 }
5452 }
5453}
5454
5455fn struct_name_to_tokens(name: &StructName) -> Option<proc_macro2::TokenStream> {
5456 match name {
5457 StructName::None => None,
5458 StructName::User { name, .. } => Some(proc_macro2::TokenTree::from(ident(name)).into()),
5459 StructName::Builtin(builtin_struct) => {
5460 let name: &'static str = builtin_struct.into();
5461 let name = format_ident!("{}", name);
5462 match builtin_struct {
5463 crate::langtype::BuiltinStruct::Color
5464 | crate::langtype::BuiltinStruct::LogicalPosition
5465 | crate::langtype::BuiltinStruct::LogicalSize => Some(quote!(slint::#name)),
5466 s if s.is_public() => Some(quote!(slint::language::#name)),
5467 _ => Some(quote!(sp::#name)),
5468 }
5469 }
5470 }
5471}
5472
5473fn generate_common_repeater_code(
5474 repeater_index: llr::RepeatedElementIdx,
5476 repeated_indices_var_name: &Option<Ident>,
5479 repeated_indices_size: &mut usize,
5481 repeater_steps_var_name: &Option<Ident>,
5483 repeater_count_code: &mut TokenStream,
5484 items_vec_name: &str,
5486 ctx: &EvaluationContext,
5487) -> (TokenStream, Option<usize>) {
5488 let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
5489 let inner_component_id = self::inner_component_id(ctx.current_sub_component().unwrap());
5490 *repeater_count_code = quote!(#repeater_count_code + _self.#repeater_id.len());
5491
5492 let items_vec_ident = ident(items_vec_name);
5493 let mut repeater_code = quote!(
5494 #inner_component_id::FIELD_OFFSETS.#repeater_id().apply_pin(_self).track_instance_changes();
5495 );
5496 let mut rs_idx_for_init = None;
5497 if let Some(ri) = repeated_indices_var_name {
5498 let ri_idx = *repeated_indices_size;
5499 repeater_code = quote!(
5500 #repeater_code
5501 #ri[#ri_idx] = #items_vec_ident.len() as u32;
5502 #ri[#ri_idx + 1] = _self.#repeater_id.len() as u32;
5503 );
5504 *repeated_indices_size += 2;
5505 if repeater_steps_var_name.is_some() {
5506 rs_idx_for_init = Some(ri_idx / 2);
5507 }
5508 }
5509
5510 (repeater_code, rs_idx_for_init)
5511}
5512
5513fn generate_common_repeater_indices_init_code(
5514 repeated_indices_var_name: &Option<Ident>,
5515 repeated_indices_size: usize,
5516 repeater_steps_var_name: &Option<Ident>,
5517) -> TokenStream {
5518 if let Some(ri) = repeated_indices_var_name {
5519 let rs_init = if let Some(rs) = repeater_steps_var_name {
5520 quote!(let mut #rs = [0u32; #repeated_indices_size / 2];)
5521 } else {
5522 quote!()
5523 };
5524 quote!(
5525 let mut #ri = [0u32; #repeated_indices_size];
5526 #rs_init
5527 )
5528 } else {
5529 quote!()
5530 }
5531}
5532
5533fn build_inner_track_and_len(
5537 templates: &[llr::RowChildTemplateInfo],
5538 row_inner_component_id: &proc_macro2::Ident,
5539) -> Vec<TokenStream> {
5540 templates
5541 .iter()
5542 .filter_map(|e| match e {
5543 llr::RowChildTemplateInfo::Repeated { repeater_index, .. } => {
5544 let inner_rep_id = format_ident!("repeater{}", usize::from(*repeater_index));
5545 Some(quote! {
5546 #row_inner_component_id::FIELD_OFFSETS.#inner_rep_id().apply_pin(pin).track_instance_changes();
5547 total += pin.#inner_rep_id.len();
5548 })
5549 }
5550 _ => None,
5551 })
5552 .collect()
5553}
5554
5555fn generate_repeater_push_code(
5556 repeater_index: llr::RepeatedElementIdx,
5557 row_child_templates: &Option<Vec<llr::RowChildTemplateInfo>>,
5558 repeated_indices_var_name: &Option<proc_macro2::Ident>,
5559 repeated_indices_size: &mut usize,
5560 repeater_steps_var_name: &Option<proc_macro2::Ident>,
5561 repeated_count_code: &mut TokenStream,
5562 ctx: &EvaluationContext,
5563 dynamic_loop_code: impl FnOnce(
5564 proc_macro2::Ident,
5565 usize,
5566 Vec<TokenStream>,
5567 Option<TokenStream>,
5568 ) -> TokenStream,
5569 static_loop_code: impl FnOnce(proc_macro2::Ident, usize, bool) -> TokenStream,
5570) -> TokenStream {
5571 let row_templates = row_child_templates.as_deref();
5572 if llr::has_inner_repeaters(row_child_templates) {
5573 let templates = row_templates.unwrap();
5574 let static_count = llr::static_child_count(templates);
5575 let parent_sc = ctx.current_sub_component().unwrap();
5576 let row_sc_idx = parent_sc.repeated[repeater_index].sub_tree.root;
5577 let row_sc = &ctx.compilation_unit.sub_components[row_sc_idx];
5578 let row_inner_component_id = self::inner_component_id(row_sc);
5579 let inner_ensure_and_len = build_inner_track_and_len(templates, &row_inner_component_id);
5580
5581 let (common_push_code, rs_idx) = self::generate_common_repeater_code(
5582 repeater_index,
5583 repeated_indices_var_name,
5584 repeated_indices_size,
5585 repeater_steps_var_name,
5586 repeated_count_code,
5587 "items_vec",
5588 ctx,
5589 );
5590 let rs_init = rs_idx.and_then(|idx| {
5591 repeater_steps_var_name.as_ref().map(|rs| quote!(#rs[#idx] = total_item_count as u32;))
5592 });
5593
5594 let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
5595 let loop_code = dynamic_loop_code(repeater_id, static_count, inner_ensure_and_len, rs_init);
5596 quote!(
5597 #common_push_code
5598 #loop_code
5599 )
5600 } else {
5601 let step = row_templates.map_or(1, |t| t.len());
5602 let (common_push_code, rs_idx) = self::generate_common_repeater_code(
5603 repeater_index,
5604 repeated_indices_var_name,
5605 repeated_indices_size,
5606 repeater_steps_var_name,
5607 repeated_count_code,
5608 "items_vec",
5609 ctx,
5610 );
5611 let rs_init = rs_idx.and_then(|idx| {
5612 repeater_steps_var_name.as_ref().map(|rs| quote!(#rs[#idx] = #step as u32;))
5613 });
5614 let repeater_id = format_ident!("repeater{}", usize::from(repeater_index));
5615 let loop_code = static_loop_code(repeater_id, step, row_templates.is_none());
5616 quote!(
5617 #common_push_code
5618 #rs_init
5619 #loop_code
5620 )
5621 }
5622}
5623
5624fn generate_with_grid_input_data(
5625 cells_variable: &str,
5626 repeated_indices_var_name: &SmolStr,
5627 repeater_steps_var_name: &SmolStr,
5628 elements: &[Either<Expression, llr::GridLayoutRepeatedElement>],
5629 sub_expression: &Expression,
5630 ctx: &EvaluationContext,
5631) -> TokenStream {
5632 let repeated_indices_var_name = Some(ident(repeated_indices_var_name));
5633 let repeater_steps_var_name = Some(ident(repeater_steps_var_name));
5634 let mut fixed_count = 0usize;
5635 let mut repeated_count_code = quote!();
5636 let mut push_code = Vec::new();
5637 let mut repeated_indices_size = 0usize;
5638 for item in elements {
5639 match item {
5640 Either::Left(value) => {
5641 let value = compile_expression(value, ctx);
5642 fixed_count += 1;
5643 push_code.push(quote!(items_vec.push(#value);))
5644 }
5645 Either::Right(repeater) => {
5646 let repeater_push_code = generate_repeater_push_code(
5647 repeater.repeater_index,
5648 &repeater.row_child_templates,
5649 &repeated_indices_var_name,
5650 &mut repeated_indices_size,
5651 &repeater_steps_var_name,
5652 &mut repeated_count_code,
5653 ctx,
5654 |repeater_id, static_count, inner_ensure_and_len, rs_init| {
5655 quote!({
5656 let len = _self.#repeater_id.len();
5657 let max_total = (0..len).filter_map(|i| {
5658 _self.#repeater_id.instance_at(i).map(|rc| {
5659 let pin = rc.as_pin_ref();
5660 let mut total = #static_count;
5661 #(#inner_ensure_and_len)*
5662 total
5663 })
5664 }).max().unwrap_or(#static_count);
5665 let total_item_count = max_total;
5666 #rs_init
5667 let start_offset = items_vec.len();
5668 items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(len * total_item_count));
5669 for i in 0..len {
5670 if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5671 let offset = start_offset + i * total_item_count;
5672 sub_comp.as_pin_ref().grid_layout_input_data(new_row, &mut items_vec[offset..offset + total_item_count]);
5673 }
5674 }
5675 })
5676 },
5677 |repeater_id, step, is_column_repeater| {
5678 let reset_new_row_code =
5681 if is_column_repeater { quote!(new_row = false;) } else { quote!() };
5682 quote!({
5683 let len = _self.#repeater_id.len();
5684 let start_offset = items_vec.len();
5685 items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(len * #step));
5686 for i in 0..len {
5687 if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5688 let offset = start_offset + i * #step;
5689 sub_comp.as_pin_ref().grid_layout_input_data(new_row, &mut items_vec[offset..offset + #step]);
5690 #reset_new_row_code
5691 }
5692 }
5693 })
5694 },
5695 );
5696 let new_row = repeater.new_row;
5697 push_code.push(quote!(
5698 let mut new_row = #new_row;
5699 #repeater_push_code
5700 ));
5701 }
5702 }
5703 }
5704 let ri_init_code = generate_common_repeater_indices_init_code(
5705 &repeated_indices_var_name,
5706 repeated_indices_size,
5707 &repeater_steps_var_name,
5708 );
5709 let ri_from_slice =
5710 repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
5711 let rs_from_slice =
5712 repeater_steps_var_name.map(|rs| quote!(let #rs = sp::Slice::from_slice(&#rs);));
5713 let cells_variable = ident(cells_variable);
5714 let sub_expression = compile_expression(sub_expression, ctx);
5715
5716 quote! { {
5717 #ri_init_code
5718 let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
5719 #(#push_code)*
5720 let #cells_variable = sp::Slice::from_slice(&items_vec);
5721 #ri_from_slice
5722 #rs_from_slice
5723 #sub_expression
5724 } }
5725}
5726
5727fn generate_with_layout_item_info(
5728 cells_variable: &str,
5729 repeated_indices_var_name: Option<&str>,
5730 repeater_steps_var_name: Option<&str>,
5731 elements: &[Either<Expression, llr::LayoutRepeatedElement>],
5732 orientation: Orientation,
5733 repeated_cross_size: Option<&Expression>,
5734 sub_expression: &Expression,
5735 ctx: &EvaluationContext,
5736) -> TokenStream {
5737 let repeated_indices_var_name = repeated_indices_var_name.map(ident);
5738 let repeater_steps_var_name = repeater_steps_var_name.map(ident);
5739 let cross_size_init = repeated_cross_size.map(|e| {
5743 let cs = compile_expression(e, ctx);
5744 quote!(let box_cross_size = (#cs) as f32;)
5745 });
5746 let mut fixed_count = 0usize;
5747 let mut repeated_count_code = quote!();
5748 let mut push_code = Vec::new();
5749 let mut repeated_indices_size = 0usize;
5750 for item in elements {
5751 match item {
5752 Either::Left(value) => {
5753 let value = compile_expression(value, ctx);
5754 fixed_count += 1;
5755 push_code.push(quote!(items_vec.push(#value);))
5756 }
5757 Either::Right(repeater) => {
5758 let grid_cross_width = repeater.cross_width.as_ref().map(|e| {
5762 let idx = ident(GRID_MEASURE_REPEATER_INDEX_LOCAL);
5763 let w = compile_expression(e, ctx);
5764 quote!({ let #idx = i; #w })
5765 });
5766 let repeater_push_code = generate_repeater_push_code(
5767 repeater.repeater_index,
5768 &repeater.row_child_templates,
5769 &repeated_indices_var_name,
5770 &mut repeated_indices_size,
5771 &repeater_steps_var_name,
5772 &mut repeated_count_code,
5773 ctx,
5774 |repeater_id, static_count, inner_ensure_and_len, rs_init| {
5775 debug_assert!(cross_size_init.is_none());
5778 quote!(
5779 {
5780 let len = _self.#repeater_id.len();
5781 let max_total = (0..len).filter_map(|i| {
5782 _self.#repeater_id.instance_at(i).map(|rc| {
5783 let pin = rc.as_pin_ref();
5784 let mut total = #static_count;
5785 #(#inner_ensure_and_len)*
5786 total
5787 })
5788 }).max().unwrap_or(#static_count);
5789 let total_item_count = max_total;
5790 #rs_init
5791 for i in 0..len {
5792 if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5793 for child_idx in 0..total_item_count {
5794 items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, Some(child_idx)));
5795 }
5796 } else {
5797 items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(total_item_count));
5800 }
5801 }
5802 }
5803 )
5804 },
5805 |repeater_id, step, is_column_repeater| {
5806 if step == 0 {
5807 quote!()
5808 } else if step == 1 && is_column_repeater {
5809 let item_info = match (&cross_size_init, &grid_cross_width, orientation)
5811 {
5812 (Some(_), _, Orientation::Vertical) => quote!(
5813 sub_comp
5814 .as_pin_ref()
5815 .layout_item_info_at_cross_width(box_cross_size)
5816 ),
5817 (Some(_), _, Orientation::Horizontal) => {
5818 unreachable!("a horizontal main pass forwards no cross size")
5819 }
5820 (None, Some(w), _) => quote!(
5821 sub_comp
5822 .as_pin_ref()
5823 .layout_item_info_at_cross_width((#w) as f32)
5824 ),
5825 (None, None, _) => quote!(
5826 sub_comp.as_pin_ref().layout_item_info(#orientation, None)
5827 ),
5828 };
5829 quote!(
5830 for i in 0.._self.#repeater_id.len() {
5831 if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5832 items_vec.push(#item_info);
5833 } else {
5834 items_vec.push(::core::default::Default::default());
5835 }
5836 }
5837 )
5838 } else {
5839 debug_assert!(cross_size_init.is_none());
5842 quote!(
5843 for i in 0.._self.#repeater_id.len() {
5844 if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5845 for child_idx in 0..#step {
5846 items_vec.push(sub_comp.as_pin_ref().layout_item_info(#orientation, Some(child_idx)));
5847 }
5848 } else {
5849 items_vec.extend(::core::iter::repeat_with(::core::default::Default::default).take(#step));
5850 }
5851 }
5852 )
5853 }
5854 },
5855 );
5856 push_code.push(repeater_push_code);
5857 }
5858 }
5859 }
5860 let ri_init_code = generate_common_repeater_indices_init_code(
5861 &repeated_indices_var_name,
5862 repeated_indices_size,
5863 &repeater_steps_var_name,
5864 );
5865
5866 let ri_from_slice =
5867 repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
5868 let rs_from_slice =
5869 repeater_steps_var_name.map(|rs| quote!(let #rs = sp::Slice::from_slice(&#rs);));
5870 let cells_variable = ident(cells_variable);
5871 let sub_expression = compile_expression(sub_expression, ctx);
5872
5873 quote! { {
5874 #ri_init_code
5875 #cross_size_init
5876 let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
5877 #(#push_code)*
5878 let #cells_variable = sp::Slice::from_slice(&items_vec);
5879 #ri_from_slice
5880 #rs_from_slice
5881 #sub_expression
5882 } }
5883}
5884
5885fn generate_with_flexbox_layout_item_info(
5886 cells_h_variable: &str,
5887 cells_v_variable: &str,
5888 flex_props_variable: Option<&str>,
5889 repeated_indices_var_name: Option<&str>,
5890 elements: &[Either<(Expression, Expression, Expression), llr::LayoutRepeatedElement>],
5891 repeated_cross_width: Option<&Expression>,
5892 sub_expression: &Expression,
5893 ctx: &EvaluationContext,
5894) -> TokenStream {
5895 let wants_flex_props = flex_props_variable.is_some();
5900 let repeated_indices_var_name = repeated_indices_var_name.map(ident);
5901 let cross_width = repeated_cross_width.map(|w| compile_expression(w, ctx));
5904 let mut fixed_count = 0usize;
5905 let mut repeated_count_code = quote!();
5906 let mut push_code = Vec::new();
5907 let mut repeated_indices_size = 0usize;
5908
5909 for item in elements {
5910 match item {
5911 Either::Left((value_h, value_v, value_flex)) => {
5912 let value_h = compile_expression(value_h, ctx);
5913 let value_v = compile_expression(value_v, ctx);
5914 let flex_push = wants_flex_props.then(|| {
5915 let value_flex = compile_expression(value_flex, ctx);
5916 quote!(items_vec_flex.push(#value_flex);)
5917 });
5918 fixed_count += 1;
5919 push_code.push(quote!(
5920 items_vec_h.push(#value_h);
5921 items_vec_v.push(#value_v);
5922 #flex_push
5923 ))
5924 }
5925 Either::Right(repeater) => {
5926 let (common_push_code, _rs_idx) = self::generate_common_repeater_code(
5927 repeater.repeater_index,
5928 &repeated_indices_var_name,
5929 &mut repeated_indices_size,
5930 &None, &mut repeated_count_code,
5932 "items_vec_h", ctx,
5934 );
5935 let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
5936 let v_query = if let Some(w) = &cross_width {
5939 quote!(sub_comp.as_pin_ref().flexbox_layout_item_info_at_cross_width((#w) as f32))
5940 } else {
5941 quote!(
5942 sub_comp
5943 .as_pin_ref()
5944 .flexbox_layout_item_info(sp::Orientation::Vertical, None)
5945 )
5946 };
5947 let flex_push =
5950 wants_flex_props.then(|| quote!(items_vec_flex.push(info_h.props);));
5951 let flex_placeholder = wants_flex_props
5952 .then(|| quote!(items_vec_flex.push(::core::default::Default::default());));
5953 let loop_code = quote!(for i in 0.._self.#repeater_id.len() {
5954 if let Some(sub_comp) = _self.#repeater_id.instance_at(i) {
5955 let info_h = sub_comp.as_pin_ref().flexbox_layout_item_info(sp::Orientation::Horizontal, None);
5956 let info_v = #v_query;
5957 #flex_push
5958 items_vec_h.push(sp::LayoutItemInfo { constraint: info_h.constraint, ..::core::default::Default::default() });
5959 items_vec_v.push(sp::LayoutItemInfo { constraint: info_v.constraint, ..::core::default::Default::default() });
5960 } else {
5961 items_vec_h.push(::core::default::Default::default());
5964 items_vec_v.push(::core::default::Default::default());
5965 #flex_placeholder
5966 }
5967 });
5968 push_code.push(quote!(
5969 #common_push_code
5970 #loop_code
5971 ));
5972 }
5973 }
5974 }
5975
5976 let ri_init_code = generate_common_repeater_indices_init_code(
5977 &repeated_indices_var_name,
5978 repeated_indices_size,
5979 &None,
5980 );
5981
5982 let ri_from_slice =
5983 repeated_indices_var_name.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
5984 let cells_h_variable = ident(cells_h_variable);
5985 let cells_v_variable = ident(cells_v_variable);
5986 let (flex_decl, flex_slice) = flex_props_variable
5987 .map(|v| {
5988 let v = ident(v);
5989 (
5990 quote!(let mut items_vec_flex = sp::Vec::with_capacity(#fixed_count #repeated_count_code);),
5991 quote!(let #v = sp::Slice::from_slice(&items_vec_flex);),
5992 )
5993 })
5994 .unzip();
5995 let sub_expression = compile_expression(sub_expression, ctx);
5996
5997 quote! { {
5998 #ri_init_code
5999 let mut items_vec_h = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
6000 let mut items_vec_v = sp::Vec::with_capacity(#fixed_count #repeated_count_code);
6001 #flex_decl
6002 #(#push_code)*
6003 let #cells_h_variable = sp::Slice::from_slice(&items_vec_h);
6004 let #cells_v_variable = sp::Slice::from_slice(&items_vec_v);
6005 #flex_slice
6006 #ri_from_slice
6007 #sub_expression
6008 } }
6009}
6010
6011fn generate_flexbox_measure_closure(
6021 measure_cells: &[llr::FlexboxMeasureCell],
6022 ctx: &EvaluationContext,
6023) -> TokenStream {
6024 let known_w_ident = ident(MEASURE_KNOWN_W_LOCAL);
6025 let has_repeater =
6026 measure_cells.iter().any(|item| matches!(item, llr::FlexboxMeasureCell::Repeated(_)));
6027
6028 let v_body = if !has_repeater {
6033 let arms = measure_cells.iter().enumerate().filter_map(|(i, item)| {
6034 let llr::FlexboxMeasureCell::Static { v_info } = item else { return None };
6035 let idx = proc_macro2::Literal::usize_unsuffixed(i);
6036 let v = compile_expression(v_info, ctx);
6037 Some(quote!(#idx => return (w, ({ #v }).preferred_bounded()),))
6038 });
6039 quote!(match index { #(#arms)* _ => {} })
6040 } else {
6041 let steps = measure_cells.iter().map(|item| match item {
6042 llr::FlexboxMeasureCell::Static { v_info } => {
6043 let v = compile_expression(v_info, ctx);
6044 quote!(
6045 if index == cursor { return (w, ({ #v }).preferred_bounded()); }
6046 cursor += 1;
6047 )
6048 }
6049 llr::FlexboxMeasureCell::Repeated(repeater) => {
6050 let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
6051 quote!(
6052 {
6053 let len = _self.#repeater_id.len();
6054 if index >= cursor && index < cursor + len {
6055 if let Some(sub_comp) = _self.#repeater_id.instance_at(index - cursor) {
6056 return (w, sub_comp
6057 .as_pin_ref()
6058 .flexbox_layout_item_info_at_cross_width(w)
6059 .constraint
6060 .preferred_bounded());
6061 }
6062 return (w, h);
6063 }
6064 cursor += len;
6065 }
6066 )
6067 }
6068 llr::FlexboxMeasureCell::Fixed => quote!(cursor += 1;),
6069 });
6070 quote!(let mut cursor = 0usize; #(#steps)* let _ = cursor;)
6073 };
6074
6075 quote! {
6078 let mut measure = |index: usize, w: f32, h: f32, _known_w: bool, known_h: bool| -> (f32, f32) {
6079 if known_h {
6080 return (w, h);
6081 }
6082 let #known_w_ident = w;
6083 let _ = #known_w_ident;
6084 #v_body
6085 (w, h)
6086 };
6087 }
6088}
6089
6090fn access_component_field_offset(component_id: &Ident, field: &Ident) -> TokenStream {
6094 quote!(#component_id::FIELD_OFFSETS.#field())
6095}
6096
6097fn embedded_file_tokens(path: &str) -> TokenStream {
6098 let file = crate::fileaccess::load_file(std::path::Path::new(path)).unwrap(); match file.builtin_contents {
6100 Some(static_data) => {
6101 let literal = proc_macro2::Literal::byte_string(static_data);
6102 quote!(#literal)
6103 }
6104 None => quote!(::core::include_bytes!(#path)),
6105 }
6106}
6107
6108fn generate_resources(doc: &Document) -> Vec<TokenStream> {
6109 #[cfg(feature = "renderer-software")]
6110 let link_section = std::env::var("SLINT_ASSET_SECTION")
6111 .ok()
6112 .map(|section| quote!(#[unsafe(link_section = #section)]));
6113
6114 doc.embedded_file_resources
6115 .borrow()
6116 .iter_enumerated()
6117 .map(|(resource_id, er)| {
6118 let resource_id = resource_id.0;
6119 let symbol = format_ident!("SLINT_EMBEDDED_RESOURCE_{}", resource_id);
6120 match &er.kind {
6121 &crate::embedded_resources::EmbeddedResourcesKind::ListOnly => {
6122 quote!()
6123 },
6124 #[cfg(feature = "slint-sc")]
6126 crate::embedded_resources::EmbeddedResourcesKind::StaticPixels { .. } => {
6127 unreachable!("slint-sc resources in the Rust generator")
6128 },
6129 crate::embedded_resources::EmbeddedResourcesKind::FileData => {
6130 let data = embedded_file_tokens(er.path.as_deref().unwrap());
6131 quote!(static #symbol: &'static [u8] = #data;)
6132 }
6133 crate::embedded_resources::EmbeddedResourcesKind::DataUriPayload(bytes, _) => {
6134 quote!(static #symbol: &'static [u8] = &[#(#bytes),*];)
6135 }
6136 #[cfg(feature = "renderer-software")]
6137 crate::embedded_resources::EmbeddedResourcesKind::TextureData(crate::embedded_resources::Texture {
6138 data, format, rect,
6139 total_size: crate::embedded_resources::Size{width, height},
6140 original_size: crate::embedded_resources::Size{width: unscaled_width, height: unscaled_height},
6141 }) => {
6142 let (r_x, r_y, r_w, r_h) = (rect.x(), rect.y(), rect.width(), rect.height());
6143 let color = if let crate::embedded_resources::PixelFormat::AlphaMap([r, g, b]) = format {
6144 quote!(sp::Color::from_rgb_u8(#r, #g, #b))
6145 } else {
6146 quote!(sp::Color::from_argb_encoded(0))
6147 };
6148 let symbol_data = format_ident!("SLINT_EMBEDDED_RESOURCE_{}_DATA", resource_id);
6149 let data_size = data.len();
6150 quote!(
6151 #link_section
6152 static #symbol_data : ([u8; #data_size], [u32;0])= ([#(#data),*], []);
6154 #link_section
6155 static #symbol: sp::StaticTextures = sp::StaticTextures{
6156 size: sp::IntSize::new(#width as _, #height as _),
6157 original_size: sp::IntSize::new(#unscaled_width as _, #unscaled_height as _),
6158 data: sp::Slice::from_slice(&#symbol_data.0),
6159 textures: sp::Slice::from_slice(&[
6160 sp::StaticTexture {
6161 rect: sp::euclid::rect(#r_x as _, #r_y as _, #r_w as _, #r_h as _),
6162 format: #format,
6163 color: #color,
6164 index: 0,
6165 }
6166 ])
6167 };
6168 )
6169 },
6170 #[cfg(feature = "renderer-software")]
6171 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 }) => {
6172
6173 let character_map_size = character_map.len();
6174
6175 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 }));
6176
6177 let glyphs_size = glyphs.len();
6178
6179 let glyphs = glyphs.iter().map(|crate::embedded_resources::BitmapGlyphs{pixel_size, glyph_data}| {
6180 let glyph_data_size = glyph_data.len();
6181 let glyph_data = glyph_data.iter().map(|crate::embedded_resources::BitmapGlyph{x, y, width, height, x_advance, data}|{
6182 let data_size = data.len();
6183 quote!(
6184 sp::BitmapGlyph {
6185 x: #x,
6186 y: #y,
6187 width: #width,
6188 height: #height,
6189 x_advance: #x_advance,
6190 data: sp::Slice::from_slice({
6191 #link_section
6192 static DATA : [u8; #data_size] = [#(#data),*];
6193 &DATA
6194 }),
6195 }
6196 )
6197 });
6198
6199 quote!(
6200 sp::BitmapGlyphs {
6201 pixel_size: #pixel_size,
6202 glyph_data: sp::Slice::from_slice({
6203 #link_section
6204 static GDATA : [sp::BitmapGlyph; #glyph_data_size] = [#(#glyph_data),*];
6205 &GDATA
6206 }),
6207 }
6208 )
6209 });
6210
6211 quote!(
6212 #link_section
6213 static #symbol: sp::BitmapFont = sp::BitmapFont {
6214 family_name: sp::Slice::from_slice(#family_name.as_bytes()),
6215 character_map: sp::Slice::from_slice({
6216 #link_section
6217 static CM : [sp::CharacterMapEntry; #character_map_size] = [#(#character_map),*];
6218 &CM
6219 }),
6220 units_per_em: #units_per_em,
6221 ascent: #ascent,
6222 descent: #descent,
6223 x_height: #x_height,
6224 cap_height: #cap_height,
6225 glyphs: sp::Slice::from_slice({
6226 #link_section
6227 static GLYPHS : [sp::BitmapGlyphs; #glyphs_size] = [#(#glyphs),*];
6228 &GLYPHS
6229 }),
6230 weight: #weight,
6231 italic: #italic,
6232 sdf: #sdf,
6233 };
6234 )
6235 },
6236 }
6237 })
6238 .collect()
6239}
6240
6241fn remove_parenthesis(
6242 expr: &Expression,
6243 ctx: &EvaluationContext,
6244 compile: impl FnOnce(&Expression, &EvaluationContext) -> TokenStream,
6245) -> TokenStream {
6246 fn extract_single_group(stream: &TokenStream) -> Option<TokenStream> {
6247 let mut iter = stream.clone().into_iter();
6248 let elem = iter.next()?;
6249 let TokenTree::Group(elem) = elem else { return None };
6250 if elem.delimiter() != proc_macro2::Delimiter::Parenthesis {
6251 return None;
6252 }
6253 if iter.next().is_some() {
6254 return None;
6255 }
6256 Some(elem.stream())
6257 }
6258
6259 let mut stream = compile(expr, ctx);
6260 if !matches!(expr, Expression::Struct { .. }) {
6261 while let Some(s) = extract_single_group(&stream) {
6262 stream = s;
6263 }
6264 }
6265 stream
6266}
6267
6268fn compile_expression_no_parenthesis(expr: &Expression, ctx: &EvaluationContext) -> TokenStream {
6269 remove_parenthesis(expr, ctx, compile_expression)
6270}
6271
6272fn compile_expression_to_value_no_parenthesis(
6273 expr: &Expression,
6274 ctx: &EvaluationContext,
6275) -> TokenStream {
6276 remove_parenthesis(expr, ctx, compile_expression_to_value)
6277}
6278
6279#[cfg(feature = "bundle-translations")]
6280fn generate_translations(
6281 translations: &crate::translations::Translations,
6282 compilation_unit: &llr::CompilationUnit,
6283) -> TokenStream {
6284 let strings = translations.strings.iter().map(|strings| {
6285 let array = strings.iter().map(|s| match s.as_ref().map(SmolStr::as_str) {
6286 Some(s) => quote!(Some(#s)),
6287 None => quote!(None),
6288 });
6289 quote!(&[#(#array),*])
6290 });
6291 let plurals = translations.plurals.iter().map(|plurals| {
6292 let array = plurals.iter().map(|p| match p {
6293 Some(p) => {
6294 let p = p.iter().map(SmolStr::as_str);
6295 quote!(Some(&[#(#p),*]))
6296 }
6297 None => quote!(None),
6298 });
6299 quote!(&[#(#array),*])
6300 });
6301
6302 let ctx = EvaluationContext {
6303 compilation_unit,
6304 current_scope: EvaluationScope::Global(0.into()),
6305 generator_state: RustGeneratorContext {
6306 global_access: quote!(compile_error!("language rule can't access state")),
6307 },
6308 argument_types: &[Type::Int32],
6309 };
6310 let rules = translations.plural_rules.iter().map(|rule| {
6311 let rule = match rule {
6312 Some(rule) => {
6313 let rule = compile_expression(rule, &ctx);
6314 quote!(Some(|arg: i32| { let args = (arg,); (#rule) as usize } ))
6315 }
6316 None => quote!(None),
6317 };
6318 quote!(#rule)
6319 });
6320
6321 let lang = translations.languages.iter().map(|(lang, separator)| {
6322 let lang = lang.as_str();
6323 quote!(
6324 sp::TranslationsBundled {
6325 language: #lang,
6326 decimal_separator: #separator
6327 }
6328 )
6329 });
6330
6331 quote!(
6332 const _SLINT_TRANSLATED_STRINGS: &[&[sp::Option<&str>]] = &[#(#strings),*];
6333 const _SLINT_TRANSLATED_STRINGS_PLURALS: &[&[sp::Option<&[&str]>]] = &[#(#plurals),*];
6334 #[allow(unused)]
6335 const _SLINT_TRANSLATED_PLURAL_RULES: &[sp::Option<fn(i32) -> usize>] = &[#(#rules),*];
6336 const _SLINT_BUNDLED_TRANSLATIONS: &[sp::TranslationsBundled] = &[#(#lang),*];
6337 )
6338}