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