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