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