1use crate::fileaccess;
10use std::collections::HashSet;
11use std::fmt::Write;
12use std::sync::OnceLock;
13
14use smol_str::{SmolStr, StrExt, format_smolstr};
15
16#[derive(Clone, Debug, Default, PartialEq)]
18pub struct Config {
19 pub namespace: Option<String>,
20 pub cpp_files: Vec<std::path::PathBuf>,
21 pub header_include: String,
22}
23
24fn is_cpp_keyword(word: &str) -> bool {
26 static CPP_KEYWORDS: OnceLock<HashSet<&'static str>> = OnceLock::new();
27 let keywords = CPP_KEYWORDS.get_or_init(|| {
28 #[rustfmt::skip]
29 let keywords: HashSet<&str> = HashSet::from([
30 "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit",
31 "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch",
32 "char", "char8_t", "char16_t", "char32_t", "class", "compl", "concept", "const",
33 "consteval", "constexpr", "constinit", "const_cast", "continue", "co_await",
34 "co_return", "co_yield", "decltype", "default", "delete", "do", "double",
35 "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float",
36 "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace",
37 "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private",
38 "protected", "public", "reflexpr", "register", "reinterpret_cast", "requires",
39 "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast",
40 "struct", "switch", "synchronized", "template", "this", "thread_local", "throw",
41 "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using",
42 "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq",
43 ]);
44 keywords
45 });
46 keywords.contains(word)
47}
48
49pub fn ident(ident: &str) -> SmolStr {
50 let mut new_ident = SmolStr::from(ident);
51 if ident.contains('-') {
52 new_ident = ident.replace_smolstr("-", "_");
53 }
54 if is_cpp_keyword(new_ident.as_str()) {
55 new_ident = format_smolstr!("{}_", new_ident);
56 }
57 new_ident
58}
59
60pub fn concatenate_ident(ident: &str) -> SmolStr {
61 if ident.contains('-') { ident.replace_smolstr("-", "_") } else { ident.into() }
62}
63
64fn access_item_rc(pr: &llr::MemberReference, ctx: &EvaluationContext) -> String {
67 let mut component_access = "self->".into();
68
69 let llr::MemberReference::Relative { parent_level, local_reference } = pr else {
70 unreachable!()
71 };
72 let llr::LocalMemberIndex::Native { item_index, prop_name: _ } = &local_reference.reference
73 else {
74 unreachable!()
75 };
76
77 for _ in 0..*parent_level {
78 component_access = format!("{component_access}parent.lock().value()->");
79 }
80
81 let (sub_compo_path, sub_component) = follow_sub_component_path(
82 ctx.compilation_unit,
83 ctx.parent_sub_component_idx(*parent_level).unwrap(),
84 &local_reference.sub_component_path,
85 );
86 if !local_reference.sub_component_path.is_empty() {
87 component_access += &sub_compo_path;
88 }
89 let component_rc = format!("{component_access}self_weak.lock()->into_dyn()");
90 let item_index_in_tree = sub_component.items[*item_index].index_in_tree;
91 let item_index = if item_index_in_tree == 0 {
92 format!("{component_access}tree_index")
93 } else {
94 format!("{component_access}tree_index_of_first_child + {item_index_in_tree} - 1")
95 };
96
97 format!("{}, {}", &component_rc, item_index)
98}
99
100pub mod cpp_ast {
103
104 use std::cell::Cell;
105 use std::fmt::{Display, Error, Formatter};
106
107 use smol_str::{SmolStr, format_smolstr};
108
109 thread_local!(static INDENTATION : Cell<u32> = const { Cell::new(0) });
110 fn indent(f: &mut Formatter<'_>) -> Result<(), Error> {
111 INDENTATION.with(|i| {
112 for _ in 0..(i.get()) {
113 write!(f, " ")?;
114 }
115 Ok(())
116 })
117 }
118
119 #[derive(Default, Debug)]
121 pub struct File {
122 pub is_cpp_file: bool,
123 pub includes: Vec<SmolStr>,
124 pub after_includes: String,
125 pub namespace: Option<String>,
126 pub declarations: Vec<Declaration>,
127 pub resources: Vec<Declaration>,
128 pub definitions: Vec<Declaration>,
129 }
130
131 impl File {
132 #[allow(clippy::manual_checked_ops)]
133 pub fn split_off_cpp_files(&mut self, header_file_name: String, count: usize) -> Vec<File> {
134 let mut cpp_files = Vec::with_capacity(count);
135 if count > 0 {
136 let mut definitions = Vec::new();
137
138 let mut i = 0;
139 while i < self.definitions.len() {
140 if matches!(
141 &self.definitions[i],
142 Declaration::Function(Function { template_parameters: Some(..), .. })
143 | Declaration::TypeAlias(..)
144 ) {
145 i += 1;
146 continue;
147 }
148
149 definitions.push(self.definitions.remove(i));
150 }
151
152 let mut cpp_resources = self
153 .resources
154 .iter_mut()
155 .filter_map(|header_resource| match header_resource {
156 Declaration::Var(var) => {
157 var.is_extern = true;
158 Some(Declaration::Var(Var {
159 ty: var.ty.clone(),
160 name: var.name.clone(),
161 array_size: var.array_size,
162 init: std::mem::take(&mut var.init),
163 is_extern: false,
164 ..Default::default()
165 }))
166 }
167 _ => None,
168 })
169 .collect::<Vec<_>>();
170
171 let cpp_includes = vec![format_smolstr!("\"{header_file_name}\"")];
172
173 let def_chunk_size = definitions.len() / count;
174 let res_chunk_size = cpp_resources.len() / count;
175 cpp_files.extend((0..count - 1).map(|_| File {
176 is_cpp_file: true,
177 includes: cpp_includes.clone(),
178 after_includes: String::new(),
179 namespace: self.namespace.clone(),
180 declarations: Default::default(),
181 resources: cpp_resources.drain(0..res_chunk_size).collect(),
182 definitions: definitions.drain(0..def_chunk_size).collect(),
183 }));
184
185 cpp_files.push(File {
186 is_cpp_file: true,
187 includes: cpp_includes,
188 after_includes: String::new(),
189 namespace: self.namespace.clone(),
190 declarations: Default::default(),
191 resources: cpp_resources,
192 definitions,
193 });
194
195 cpp_files.resize_with(count, Default::default);
196 }
197
198 self.definitions.iter_mut().for_each(|def| match def {
200 Declaration::Function(f) => f.is_inline = true,
201 Declaration::Var(v) => v.is_inline = true,
202 _ => {}
203 });
204
205 cpp_files
206 }
207 }
208
209 impl Display for File {
210 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
211 writeln!(f, "// This file is auto-generated")?;
212 if !self.is_cpp_file {
213 writeln!(f, "#pragma once")?;
214 }
215 for i in &self.includes {
216 writeln!(f, "#include {i}")?;
217 }
218 if let Some(namespace) = &self.namespace {
219 writeln!(f, "namespace {namespace} {{")?;
220 INDENTATION.with(|x| x.set(x.get() + 1));
221 }
222
223 write!(f, "{}", self.after_includes)?;
224 for d in self.declarations.iter().chain(self.resources.iter()) {
225 write!(f, "\n{d}")?;
226 }
227 for d in &self.definitions {
228 write!(f, "\n{d}")?;
229 }
230 if let Some(namespace) = &self.namespace {
231 writeln!(f, "}} // namespace {namespace}")?;
232 INDENTATION.with(|x| x.set(x.get() - 1));
233 }
234
235 Ok(())
236 }
237 }
238
239 #[derive(Debug, derive_more::Display)]
241 pub enum Declaration {
242 Struct(Struct),
243 Function(Function),
244 Var(Var),
245 TypeAlias(TypeAlias),
246 Enum(Enum),
247 }
248
249 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
250 pub enum Access {
251 Public,
252 Private,
253 }
255
256 #[derive(Default, Debug)]
257 pub struct Struct {
258 pub name: SmolStr,
259 pub members: Vec<(Access, Declaration)>,
260 pub friends: Vec<SmolStr>,
261 }
262
263 impl Display for Struct {
264 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
265 indent(f)?;
266 if self.members.is_empty() && self.friends.is_empty() {
267 writeln!(f, "class {};", self.name)
268 } else {
269 writeln!(f, "class {} {{", self.name)?;
270 INDENTATION.with(|x| x.set(x.get() + 1));
271 let mut access = Access::Private;
272 for m in &self.members {
273 if m.0 != access {
274 access = m.0;
275 indent(f)?;
276 match access {
277 Access::Public => writeln!(f, "public:")?,
278 Access::Private => writeln!(f, "private:")?,
279 }
280 }
281 write!(f, "{}", m.1)?;
282 }
283 for friend in &self.friends {
284 indent(f)?;
285 writeln!(f, "friend class {friend};")?;
286 }
287 INDENTATION.with(|x| x.set(x.get() - 1));
288 indent(f)?;
289 writeln!(f, "}};")
290 }
291 }
292 }
293
294 impl Struct {
295 pub fn extract_definitions(&mut self) -> impl Iterator<Item = Declaration> + '_ {
296 let struct_name = self.name.clone();
297 self.members.iter_mut().filter_map(move |x| match &mut x.1 {
298 Declaration::Function(f) if f.statements.is_some() => {
299 Some(Declaration::Function(Function {
300 name: format_smolstr!("{}::{}", struct_name, f.name),
301 signature: f.signature.clone(),
302 is_constructor_or_destructor: f.is_constructor_or_destructor,
303 is_static: false,
304 is_friend: false,
305 statements: f.statements.take(),
306 template_parameters: f.template_parameters.clone(),
307 constructor_member_initializers: f.constructor_member_initializers.clone(),
308 ..Default::default()
309 }))
310 }
311 _ => None,
312 })
313 }
314 }
315
316 #[derive(Default, Debug)]
317 pub struct Enum {
318 pub name: SmolStr,
319 pub values: Vec<SmolStr>,
320 }
321
322 impl Display for Enum {
323 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
324 indent(f)?;
325 writeln!(f, "enum class {} {{", self.name)?;
326 INDENTATION.with(|x| x.set(x.get() + 1));
327 for value in &self.values {
328 write!(f, "{value},")?;
329 }
330 INDENTATION.with(|x| x.set(x.get() - 1));
331 indent(f)?;
332 writeln!(f, "}};")
333 }
334 }
335
336 #[derive(Default, Debug)]
338 pub struct Function {
339 pub name: SmolStr,
340 pub signature: String,
342 pub is_constructor_or_destructor: bool,
344 pub is_static: bool,
345 pub is_friend: bool,
346 pub is_inline: bool,
347 pub statements: Option<Vec<String>>,
350 pub template_parameters: Option<String>,
352 pub constructor_member_initializers: Vec<String>,
354 }
355
356 impl Display for Function {
357 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
358 indent(f)?;
359 if let Some(tpl) = &self.template_parameters {
360 write!(f, "template<{tpl}> ")?;
361 }
362 if self.is_static {
363 write!(f, "static ")?;
364 }
365 if self.is_friend {
366 write!(f, "friend ")?;
367 }
368 if self.is_inline {
369 write!(f, "inline ")?;
370 }
371 if !self.is_constructor_or_destructor {
372 write!(f, "auto ")?;
373 }
374 write!(f, "{} {}", self.name, self.signature)?;
375 if let Some(st) = &self.statements {
376 if !self.constructor_member_initializers.is_empty() {
377 writeln!(f, "\n : {}", self.constructor_member_initializers.join(","))?;
378 }
379 writeln!(f, "{{")?;
380 for s in st {
381 indent(f)?;
382 writeln!(f, " {s}")?;
383 }
384 indent(f)?;
385 writeln!(f, "}}")
386 } else {
387 writeln!(f, ";")
388 }
389 }
390 }
391
392 #[derive(Default, Debug)]
394 pub struct Var {
395 pub is_inline: bool,
396 pub is_extern: bool,
397 pub ty: SmolStr,
398 pub name: SmolStr,
399 pub array_size: Option<usize>,
400 pub init: Option<String>,
401 }
402
403 impl Display for Var {
404 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
405 indent(f)?;
406 if self.is_extern {
407 write!(f, "extern ")?;
408 }
409 if self.is_inline {
410 write!(f, "inline ")?;
411 }
412 write!(f, "{} {}", self.ty, self.name)?;
413 if let Some(size) = self.array_size {
414 write!(f, "[{size}]")?;
415 }
416 if let Some(i) = &self.init {
417 write!(f, " = {i}")?;
418 }
419 writeln!(f, ";")
420 }
421 }
422
423 #[derive(Default, Debug)]
424 pub struct TypeAlias {
425 pub new_name: SmolStr,
426 pub old_name: SmolStr,
427 }
428
429 impl Display for TypeAlias {
430 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
431 indent(f)?;
432 writeln!(f, "using {} = {};", self.new_name, self.old_name)
433 }
434 }
435
436 pub trait CppType {
437 fn cpp_type(&self) -> Option<SmolStr>;
438 }
439
440 pub fn escape_string(str: &str) -> String {
441 let mut result = String::with_capacity(str.len());
442 for x in str.chars() {
443 match x {
444 '\n' => result.push_str("\\n"),
445 '\\' => result.push_str("\\\\"),
446 '\"' => result.push_str("\\\""),
447 '\t' => result.push_str("\\t"),
448 '\r' => result.push_str("\\r"),
449 _ if !x.is_ascii() || (x as u32) < 32 => {
450 use std::fmt::Write;
451 write!(result, "\\U{:0>8x}", x as u32).unwrap();
452 }
453 _ => result.push(x),
454 }
455 }
456 result
457 }
458}
459
460use crate::CompilerConfiguration;
461use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp};
462use crate::langtype::{
463 BuiltinPrivateStruct, BuiltinPublicStruct, Enumeration, EnumerationValue, NativeClass,
464 StructName, Type,
465};
466use crate::layout::Orientation;
467use crate::llr::{
468 self, EvaluationContext as llr_EvaluationContext, EvaluationScope, ParentScope,
469 TypeResolutionContext as _,
470};
471use crate::object_tree::Document;
472use cpp_ast::*;
473use itertools::{Either, Itertools};
474use std::cell::Cell;
475use std::collections::{BTreeMap, BTreeSet};
476
477const SHARED_GLOBAL_CLASS: &str = "SharedGlobals";
478
479#[derive(Default)]
480struct ConditionalIncludes {
481 iostream: Cell<bool>,
482 cstdlib: Cell<bool>,
483 cmath: Cell<bool>,
484}
485
486#[derive(Clone)]
487struct CppGeneratorContext<'a> {
488 global_access: String,
489 conditional_includes: &'a ConditionalIncludes,
490}
491
492type EvaluationContext<'a> = llr_EvaluationContext<'a, CppGeneratorContext<'a>>;
493
494impl CppType for StructName {
495 fn cpp_type(&self) -> Option<SmolStr> {
496 match self {
497 StructName::None => None,
498 StructName::User { name, .. } => Some(ident(name)),
499 StructName::BuiltinPrivate(builtin_private) => builtin_private.cpp_type(),
500 StructName::BuiltinPublic(builtin_public) => builtin_public.cpp_type(),
501 }
502 }
503}
504
505impl CppType for BuiltinPrivateStruct {
506 fn cpp_type(&self) -> Option<SmolStr> {
507 let name: &'static str = self.into();
508 match self {
509 Self::PathMoveTo
510 | Self::PathLineTo
511 | Self::PathArcTo
512 | Self::PathCubicTo
513 | Self::PathQuadraticTo
514 | Self::PathClose => Some(format_smolstr!("slint::private_api::{}", name)),
515 _ => Some(format_smolstr!("slint::cbindgen_private::{}", name)),
516 }
517 }
518}
519
520impl CppType for BuiltinPublicStruct {
521 fn cpp_type(&self) -> Option<SmolStr> {
522 let name: &'static str = self.into();
523 match self {
524 Self::Color | Self::LogicalPosition | Self::LogicalSize => {
525 Some(format_smolstr!("slint::{}", name))
526 }
527 _ => Some(format_smolstr!("slint::language::{}", name)),
528 }
529 }
530}
531
532impl CppType for Type {
533 fn cpp_type(&self) -> Option<SmolStr> {
534 match self {
535 Type::Void => Some("void".into()),
536 Type::Float32 => Some("float".into()),
537 Type::Int32 => Some("int".into()),
538 Type::String => Some("slint::SharedString".into()),
539 Type::Keys => Some("slint::Keys".into()),
540 Type::Color => Some("slint::Color".into()),
541 Type::Duration => Some("std::int64_t".into()),
542 Type::Angle => Some("float".into()),
543 Type::PhysicalLength => Some("float".into()),
544 Type::LogicalLength => Some("float".into()),
545 Type::Rem => Some("float".into()),
546 Type::Percent => Some("float".into()),
547 Type::Bool => Some("bool".into()),
548 Type::Struct(s) => s.name.cpp_type().or_else(|| {
549 let elem = s.fields.values().map(|v| v.cpp_type()).collect::<Option<Vec<_>>>()?;
550
551 Some(format_smolstr!("std::tuple<{}>", elem.join(", ")))
552 }),
553 Type::Array(i) => {
554 Some(format_smolstr!("std::shared_ptr<slint::Model<{}>>", i.cpp_type()?))
555 }
556 Type::Image => Some("slint::Image".into()),
557 Type::Enumeration(enumeration) => {
558 if enumeration.node.is_some() {
559 Some(ident(&enumeration.name))
560 } else {
561 Some(format_smolstr!("slint::cbindgen_private::{}", ident(&enumeration.name)))
562 }
563 }
564 Type::Brush => Some("slint::Brush".into()),
565 Type::LayoutCache => Some("slint::SharedVector<float>".into()),
566 Type::ArrayOfU16 => Some("slint::SharedVector<uint16_t>".into()),
567 Type::Easing => Some("slint::cbindgen_private::EasingCurve".into()),
568 Type::StyledText => Some("slint::private_api::StyledText".into()),
569 _ => None,
570 }
571 }
572}
573
574fn to_cpp_orientation(o: Orientation) -> &'static str {
575 match o {
576 Orientation::Horizontal => "slint::cbindgen_private::Orientation::Horizontal",
577 Orientation::Vertical => "slint::cbindgen_private::Orientation::Vertical",
578 }
579}
580
581fn remove_parentheses(expr: &str) -> &str {
583 if expr.starts_with('(') && expr.ends_with(')') {
584 let mut level = 0;
585 for byte in &expr.as_bytes()[1..expr.len() - 1] {
587 match byte {
588 b')' if level == 0 => return expr,
589 b')' => level -= 1,
590 b'(' => level += 1,
591 _ => (),
592 }
593 }
594 &expr[1..expr.len() - 1]
595 } else {
596 expr
597 }
598}
599
600#[test]
601fn remove_parentheses_test() {
602 assert_eq!(remove_parentheses("(foo(bar))"), "foo(bar)");
603 assert_eq!(remove_parentheses("(foo).bar"), "(foo).bar");
604 assert_eq!(remove_parentheses("(foo(bar))"), "foo(bar)");
605 assert_eq!(remove_parentheses("(foo)(bar)"), "(foo)(bar)");
606 assert_eq!(remove_parentheses("(foo).get()"), "(foo).get()");
607 assert_eq!(remove_parentheses("((foo).get())"), "(foo).get()");
608 assert_eq!(remove_parentheses("(((()())()))"), "((()())())");
609 assert_eq!(remove_parentheses("((()())())"), "(()())()");
610 assert_eq!(remove_parentheses("(()())()"), "(()())()");
611 assert_eq!(remove_parentheses("()())("), "()())(");
612}
613
614fn property_set_value_code(
615 property: &llr::MemberReference,
616 value_expr: &str,
617 ctx: &EvaluationContext,
618) -> String {
619 let prop = access_member(property, ctx);
620 if let Some((animation, map)) = &ctx.property_info(property).animation {
621 let mut animation = (*animation).clone();
622 map.map_expression(&mut animation);
623 let animation_code = compile_expression(&animation, ctx);
624 return prop
625 .then(|prop| format!("{prop}.set_animated_value({value_expr}, {animation_code})"));
626 }
627 prop.then(|prop| format!("{prop}.set({value_expr})"))
628}
629
630fn handle_property_init(
631 prop: &llr::MemberReference,
632 binding_expression: &llr::BindingExpression,
633 init: &mut Vec<String>,
634 ctx: &EvaluationContext,
635) {
636 let prop_access = access_member(prop, ctx).unwrap();
637 let prop_type = ctx.property_ty(prop);
638 if let Type::Callback(callback) = &prop_type {
639 let mut ctx2 = ctx.clone();
640 ctx2.argument_types = &callback.args;
641
642 let mut params = callback.args.iter().enumerate().map(|(i, ty)| {
643 format!("[[maybe_unused]] {} arg_{}", ty.cpp_type().unwrap_or_default(), i)
644 });
645
646 init.push(format!(
647 "{prop_access}.set_handler(
648 [this]({params}) {{
649 [[maybe_unused]] auto self = this;
650 {code};
651 }});",
652 prop_access = prop_access,
653 params = params.join(", "),
654 code = return_compile_expression(
655 &binding_expression.expression.borrow(),
656 &ctx2,
657 Some(&callback.return_type)
658 )
659 ));
660 } else {
661 let init_expr = compile_expression(&binding_expression.expression.borrow(), ctx);
662
663 init.push(if binding_expression.is_constant && !binding_expression.is_state_info {
664 format!("{prop_access}.set({init_expr});")
665 } else {
666 let binding_code = format!(
667 "[this]() {{
668 [[maybe_unused]] auto self = this;
669 return {init_expr};
670 }}"
671 );
672
673 if binding_expression.is_state_info {
674 format!("slint::private_api::set_state_binding({prop_access}, {binding_code});")
675 } else {
676 match &binding_expression.animation {
677 Some(llr::Animation::Static(anim)) => {
678 let anim = compile_expression(anim, ctx);
679 format!("{prop_access}.set_animated_binding({binding_code},
682 [this](uint64_t **start_time) -> slint::cbindgen_private::PropertyAnimation {{
683 [[maybe_unused]] auto self = this;
684 auto anim = {anim};
685 *start_time = nullptr;
686 return anim;
687 }});",
688 )
689 }
690 Some(llr::Animation::Transition(animation)) => {
691 let animation = compile_expression(animation, ctx);
692 format!(
693 "{prop_access}.set_animated_binding({binding_code},
694 [this](uint64_t **start_time) -> slint::cbindgen_private::PropertyAnimation {{
695 [[maybe_unused]] auto self = this;
696 auto [animation, change_time] = {animation};
697 **start_time = change_time;
698 return animation;
699 }});",
700 )
701 }
702 None => format!("{prop_access}.set_binding({binding_code});"),
703 }
704 }
705 });
706 }
707}
708
709pub fn generate(
711 doc: &Document,
712 config: Config,
713 compiler_config: &CompilerConfiguration,
714) -> std::io::Result<impl std::fmt::Display> {
715 if std::env::var("SLINT_LIVE_PREVIEW").is_ok() {
716 return super::cpp_live_preview::generate(doc, config, compiler_config);
717 }
718
719 let mut file = generate_types(&doc.used_types.borrow().structs_and_enums, &config);
720
721 for (resource_id, er) in doc.embedded_file_resources.borrow().iter_enumerated() {
722 embed_resource(er, resource_id, &mut file.resources);
723 }
724
725 let llr = llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config);
726
727 #[cfg(feature = "bundle-translations")]
728 if let Some(translations) = &llr.translations {
729 generate_translation(translations, &llr, &mut file.resources);
730 }
731
732 file.declarations.extend(
734 llr.public_components
735 .iter()
736 .map(|c| Declaration::Struct(Struct { name: ident(&c.name), ..Default::default() })),
737 );
738
739 file.declarations.push(Declaration::Struct(Struct {
741 name: SmolStr::new_static(SHARED_GLOBAL_CLASS),
742 ..Default::default()
743 }));
744
745 file.declarations.extend(llr.used_sub_components.iter().map(|sub_compo| {
747 Declaration::Struct(Struct {
748 name: ident(&llr.sub_components[*sub_compo].name),
749 ..Default::default()
750 })
751 }));
752
753 let conditional_includes = ConditionalIncludes::default();
754
755 for sub_compo in &llr.used_sub_components {
756 let sub_compo_id = ident(&llr.sub_components[*sub_compo].name);
757 let mut sub_compo_struct = Struct { name: sub_compo_id.clone(), ..Default::default() };
758 generate_sub_component(
759 &mut sub_compo_struct,
760 *sub_compo,
761 &llr,
762 None,
763 Access::Public,
764 &mut file,
765 &conditional_includes,
766 );
767 file.definitions.extend(sub_compo_struct.extract_definitions().collect::<Vec<_>>());
768 file.declarations.push(Declaration::Struct(sub_compo_struct));
769 }
770
771 let mut globals_struct =
772 Struct { name: SmolStr::new_static(SHARED_GLOBAL_CLASS), ..Default::default() };
773
774 globals_struct.members.push((
776 Access::Public,
778 Declaration::Var(Var {
779 ty: "std::optional<slint::Window>".into(),
780 name: "m_window".into(),
781 ..Default::default()
782 }),
783 ));
784
785 globals_struct.members.push((
786 Access::Public,
787 Declaration::Var(Var {
788 ty: "slint::cbindgen_private::ItemTreeWeak".into(),
789 name: "root_weak".into(),
790 ..Default::default()
791 }),
792 ));
793
794 let mut window_creation_code = vec![
795 format!("auto self = const_cast<{SHARED_GLOBAL_CLASS} *>(this);"),
796 "if (!self->m_window.has_value()) {".into(),
797 " auto &window = self->m_window.emplace(slint::private_api::WindowAdapterRc());".into(),
798 ];
799
800 if let Some(scale_factor) = compiler_config.const_scale_factor {
801 window_creation_code
802 .push(format!("window.window_handle().set_const_scale_factor({scale_factor});"));
803 }
804
805 window_creation_code.extend([
806 " window.window_handle().set_component(self->root_weak);".into(),
807 "}".into(),
808 "return *self->m_window;".into(),
809 ]);
810
811 globals_struct.members.push((
812 Access::Public,
813 Declaration::Function(Function {
814 name: "window".into(),
815 signature: "() const -> slint::Window&".into(),
816 statements: Some(window_creation_code),
817 ..Default::default()
818 }),
819 ));
820
821 let mut init_global = Vec::new();
822
823 for (idx, glob) in llr.globals.iter_enumerated() {
824 if !glob.must_generate() {
825 continue;
826 }
827 let name = format_smolstr!("global_{}", concatenate_ident(&glob.name));
828 let ty = if glob.is_builtin {
829 generate_global_builtin(&mut file, &conditional_includes, idx, glob, &llr);
830 format_smolstr!("slint::cbindgen_private::{}", glob.name)
831 } else {
832 init_global.push(format!("{name}->init();"));
833 generate_global(&mut file, &conditional_includes, idx, glob, &llr);
834 ident(&glob.name)
835 };
836
837 file.definitions.extend(glob.aliases.iter().map(|name| {
838 Declaration::TypeAlias(TypeAlias { old_name: ident(&glob.name), new_name: ident(name) })
839 }));
840
841 globals_struct.members.push((
842 Access::Public,
843 Declaration::Var(Var {
844 ty: format_smolstr!("std::shared_ptr<{}>", ty),
845 name,
846 init: Some(format!("std::make_shared<{ty}>(this)")),
847 ..Default::default()
848 }),
849 ));
850 }
851
852 globals_struct.members.push((
853 Access::Public,
854 Declaration::Function(Function {
855 name: globals_struct.name.clone(),
856 is_constructor_or_destructor: true,
857 signature: "()".into(),
858 statements: Some(init_global),
859 ..Default::default()
860 }),
861 ));
862
863 file.declarations.push(Declaration::Struct(globals_struct));
864
865 if let Some(popup_menu) = &llr.popup_menu {
866 let component_id = ident(&llr.sub_components[popup_menu.item_tree.root].name);
867 let mut popup_struct = Struct { name: component_id.clone(), ..Default::default() };
868 generate_item_tree(
869 &mut popup_struct,
870 &popup_menu.item_tree,
871 &llr,
872 None,
873 true,
874 component_id,
875 Access::Public,
876 &mut file,
877 &conditional_includes,
878 );
879 file.definitions.extend(popup_struct.extract_definitions().collect::<Vec<_>>());
880 file.declarations.push(Declaration::Struct(popup_struct));
881 };
882
883 for p in &llr.public_components {
884 generate_public_component(&mut file, &conditional_includes, p, &llr);
885 }
886
887 generate_type_aliases(&mut file, doc);
888
889 if conditional_includes.iostream.get() {
890 file.includes.push("<iostream>".into());
891 }
892
893 if conditional_includes.cstdlib.get() {
894 file.includes.push("<cstdlib>".into());
895 }
896
897 if conditional_includes.cmath.get() {
898 file.includes.push("<cmath>".into());
899 }
900
901 let cpp_files = file.split_off_cpp_files(config.header_include, config.cpp_files.len());
902
903 for (cpp_file_name, cpp_file) in config.cpp_files.iter().zip(cpp_files) {
904 fileaccess::write_file_if_changed(cpp_file_name, cpp_file.to_string().as_bytes())?;
907 }
908
909 Ok(file)
910}
911
912pub fn generate_types(used_types: &[Type], config: &Config) -> File {
913 let mut file = File { namespace: config.namespace.clone(), ..Default::default() };
914
915 file.includes.push("<array>".into());
916 file.includes.push("<limits>".into());
917 file.includes.push("<slint.h>".into());
918
919 file.after_includes = format!(
920 "static_assert({x} == SLINT_VERSION_MAJOR && {y} == SLINT_VERSION_MINOR && {z} == SLINT_VERSION_PATCH, \
921 \"This file was generated with Slint compiler version {x}.{y}.{z}, but the Slint library used is \" \
922 SLINT_VERSION_STRING \". The version numbers must match exactly.\");",
923 x = env!("CARGO_PKG_VERSION_MAJOR"),
924 y = env!("CARGO_PKG_VERSION_MINOR"),
925 z = env!("CARGO_PKG_VERSION_PATCH")
926 );
927
928 for ty in used_types {
929 match ty {
930 Type::Struct(s) if s.node().is_some() => {
931 generate_struct(&mut file, &s.name, &s.fields);
932 }
933 Type::Enumeration(en) => {
934 generate_enum(&mut file, en);
935 }
936 _ => (),
937 }
938 }
939
940 file
941}
942
943fn expand_data_to_cpp_u8_array(data: &[u8]) -> String {
944 let mut init = "{ ".to_string();
945
946 for (index, byte) in data.iter().enumerate() {
947 if index > 0 {
948 init.push(',');
949 }
950 write!(&mut init, "0x{byte:x}").unwrap();
951 if index % 16 == 0 {
952 init.push('\n');
953 }
954 }
955
956 init.push('}');
957 init
958}
959
960fn embed_resource(
961 resource: &crate::embedded_resources::EmbeddedResources,
962 resource_id: crate::embedded_resources::EmbeddedResourcesIdx,
963 declarations: &mut Vec<Declaration>,
964) {
965 match &resource.kind {
966 crate::embedded_resources::EmbeddedResourcesKind::ListOnly => {}
967 crate::embedded_resources::EmbeddedResourcesKind::FileData => {
968 let resource_file = crate::fileaccess::load_file(std::path::Path::new(
969 resource.path.as_deref().unwrap(),
970 ))
971 .unwrap(); let data = resource_file.read();
973
974 declarations.push(Declaration::Var(Var {
975 ty: "const uint8_t".into(),
976 name: format_smolstr!("slint_embedded_resource_{}", resource_id),
977 array_size: Some(data.len()),
978 init: Some(expand_data_to_cpp_u8_array(data.as_ref())),
979 ..Default::default()
980 }));
981 }
982 crate::embedded_resources::EmbeddedResourcesKind::DataUriPayload(data, _) => {
983 declarations.push(Declaration::Var(Var {
984 ty: "const uint8_t".into(),
985 name: format_smolstr!("slint_embedded_resource_{}", resource_id),
986 array_size: Some(data.len()),
987 init: Some(expand_data_to_cpp_u8_array(data)),
988 ..Default::default()
989 }));
990 }
991 #[cfg(feature = "software-renderer")]
992 crate::embedded_resources::EmbeddedResourcesKind::TextureData(
993 crate::embedded_resources::Texture {
994 data,
995 format,
996 rect,
997 total_size: crate::embedded_resources::Size { width, height },
998 original_size:
999 crate::embedded_resources::Size { width: unscaled_width, height: unscaled_height },
1000 },
1001 ) => {
1002 let (r_x, r_y, r_w, r_h) = (rect.x(), rect.y(), rect.width(), rect.height());
1003 let color = if let crate::embedded_resources::PixelFormat::AlphaMap([r, g, b]) = format
1004 {
1005 format!("slint::Color::from_rgb_uint8({r}, {g}, {b})")
1006 } else {
1007 "slint::Color{}".to_string()
1008 };
1009 let count = data.len();
1010 let data = data.iter().map(ToString::to_string).join(", ");
1011 let data_name = format_smolstr!("slint_embedded_resource_{}_data", resource_id);
1012 declarations.push(Declaration::Var(Var {
1013 ty: "const uint8_t".into(),
1014 name: data_name.clone(),
1015 array_size: Some(count),
1016 init: Some(format!("{{ {data} }}")),
1017 ..Default::default()
1018 }));
1019 let texture_name = format_smolstr!("slint_embedded_resource_{}_texture", resource_id);
1020 declarations.push(Declaration::Var(Var {
1021 ty: "const slint::cbindgen_private::types::StaticTexture".into(),
1022 name: texture_name.clone(),
1023 array_size: None,
1024 init: Some(format!(
1025 "{{
1026 .rect = {{ {r_x}, {r_y}, {r_w}, {r_h} }},
1027 .format = slint::cbindgen_private::types::TexturePixelFormat::{format},
1028 .color = {color},
1029 .index = 0,
1030 }}"
1031 )),
1032 ..Default::default()
1033 }));
1034 let init = format!(
1035 "slint::cbindgen_private::types::StaticTextures {{
1036 .size = {{ {width}, {height} }},
1037 .original_size = {{ {unscaled_width}, {unscaled_height} }},
1038 .data = slint::private_api::make_slice({data_name} , {count} ),
1039 .textures = slint::private_api::make_slice(&{texture_name}, 1)
1040 }}"
1041 );
1042 declarations.push(Declaration::Var(Var {
1043 ty: "const slint::cbindgen_private::types::StaticTextures".into(),
1044 name: format_smolstr!("slint_embedded_resource_{}", resource_id),
1045 array_size: None,
1046 init: Some(init),
1047 ..Default::default()
1048 }))
1049 }
1050 #[cfg(feature = "software-renderer")]
1051 crate::embedded_resources::EmbeddedResourcesKind::BitmapFontData(
1052 crate::embedded_resources::BitmapFont {
1053 family_name,
1054 character_map,
1055 units_per_em,
1056 ascent,
1057 descent,
1058 x_height,
1059 cap_height,
1060 glyphs,
1061 weight,
1062 italic,
1063 sdf,
1064 },
1065 ) => {
1066 let family_name_var =
1067 format_smolstr!("slint_embedded_resource_{}_family_name", resource_id);
1068 let family_name_size = family_name.len();
1069 declarations.push(Declaration::Var(Var {
1070 ty: "const uint8_t".into(),
1071 name: family_name_var.clone(),
1072 array_size: Some(family_name_size),
1073 init: Some(format!(
1074 "{{ {} }}",
1075 family_name.as_bytes().iter().map(ToString::to_string).join(", ")
1076 )),
1077 ..Default::default()
1078 }));
1079
1080 let charmap_var = format_smolstr!("slint_embedded_resource_{}_charmap", resource_id);
1081 let charmap_size = character_map.len();
1082 declarations.push(Declaration::Var(Var {
1083 ty: "const slint::cbindgen_private::CharacterMapEntry".into(),
1084 name: charmap_var.clone(),
1085 array_size: Some(charmap_size),
1086 init: Some(format!(
1087 "{{ {} }}",
1088 character_map
1089 .iter()
1090 .map(|entry| format!(
1091 "{{ .code_point = {}, .glyph_index = {} }}",
1092 entry.code_point as u32, entry.glyph_index
1093 ))
1094 .join(", ")
1095 )),
1096 ..Default::default()
1097 }));
1098
1099 for (glyphset_index, glyphset) in glyphs.iter().enumerate() {
1100 for (glyph_index, glyph) in glyphset.glyph_data.iter().enumerate() {
1101 declarations.push(Declaration::Var(Var {
1102 ty: "const uint8_t".into(),
1103 name: format_smolstr!(
1104 "slint_embedded_resource_{}_gs_{}_gd_{}",
1105 resource_id,
1106 glyphset_index,
1107 glyph_index
1108 ),
1109 array_size: Some(glyph.data.len()),
1110 init: Some(format!(
1111 "{{ {} }}",
1112 glyph.data.iter().map(ToString::to_string).join(", ")
1113 )),
1114 ..Default::default()
1115 }));
1116 }
1117
1118 declarations.push(Declaration::Var(Var{
1119 ty: "const slint::cbindgen_private::BitmapGlyph".into(),
1120 name: format_smolstr!("slint_embedded_resource_{}_glyphset_{}", resource_id, glyphset_index),
1121 array_size: Some(glyphset.glyph_data.len()),
1122 init: Some(format!("{{ {} }}", glyphset.glyph_data.iter().enumerate().map(|(glyph_index, glyph)| {
1123 format!("{{ .x = {}, .y = {}, .width = {}, .height = {}, .x_advance = {}, .data = slint::private_api::make_slice({}, {}) }}",
1124 glyph.x, glyph.y, glyph.width, glyph.height, glyph.x_advance,
1125 format_args!("slint_embedded_resource_{}_gs_{}_gd_{}", resource_id, glyphset_index, glyph_index),
1126 glyph.data.len()
1127 )
1128 }).join(", \n"))),
1129 ..Default::default()
1130 }));
1131 }
1132
1133 let glyphsets_var =
1134 format_smolstr!("slint_embedded_resource_{}_glyphsets", resource_id);
1135 let glyphsets_size = glyphs.len();
1136 declarations.push(Declaration::Var(Var {
1137 ty: "const slint::cbindgen_private::BitmapGlyphs".into(),
1138 name: glyphsets_var.clone(),
1139 array_size: Some(glyphsets_size),
1140 init: Some(format!(
1141 "{{ {} }}",
1142 glyphs
1143 .iter()
1144 .enumerate()
1145 .map(|(glyphset_index, glyphset)| format!(
1146 "{{ .pixel_size = {}, .glyph_data = slint::private_api::make_slice({}, {}) }}",
1147 glyphset.pixel_size, format_args!("slint_embedded_resource_{}_glyphset_{}", resource_id, glyphset_index), glyphset.glyph_data.len()
1148 ))
1149 .join(", \n")
1150 )),
1151 ..Default::default()
1152 }));
1153
1154 let init = format!(
1155 "slint::cbindgen_private::BitmapFont {{
1156 .family_name = slint::private_api::make_slice({family_name_var} , {family_name_size}),
1157 .character_map = slint::private_api::make_slice({charmap_var}, {charmap_size}),
1158 .units_per_em = {units_per_em},
1159 .ascent = {ascent},
1160 .descent = {descent},
1161 .x_height = {x_height},
1162 .cap_height = {cap_height},
1163 .glyphs = slint::private_api::make_slice({glyphsets_var}, {glyphsets_size}),
1164 .weight = {weight},
1165 .italic = {italic},
1166 .sdf = {sdf},
1167 }}"
1168 );
1169
1170 declarations.push(Declaration::Var(Var {
1171 ty: "const slint::cbindgen_private::BitmapFont".into(),
1172 name: format_smolstr!("slint_embedded_resource_{}", resource_id),
1173 array_size: None,
1174 init: Some(init),
1175 ..Default::default()
1176 }))
1177 }
1178 }
1179}
1180
1181fn generate_struct(file: &mut File, name: &StructName, fields: &BTreeMap<SmolStr, Type>) {
1182 let StructName::User { name: user_name, node } = name else {
1183 panic!("internal error: Cannot generate anonymous struct");
1184 };
1185 let name = ident(user_name);
1186 let mut members = node
1187 .ObjectTypeMember()
1188 .map(|n| crate::parser::identifier_text(&n).unwrap())
1189 .map(|name| {
1190 (
1191 Access::Public,
1192 Declaration::Var(Var {
1193 ty: fields.get(&name).unwrap().cpp_type().unwrap(),
1194 name: ident(&name),
1195 ..Default::default()
1196 }),
1197 )
1198 })
1199 .collect::<Vec<_>>();
1200
1201 members.push((
1202 Access::Public,
1203 Declaration::Function(Function {
1204 name: "operator==".into(),
1205 signature: format!("(const class {name} &a, const class {name} &b) -> bool = default"),
1206 is_friend: true,
1207 statements: None,
1208 ..Function::default()
1209 }),
1210 ));
1211
1212 file.declarations.push(Declaration::Struct(Struct { name, members, ..Default::default() }))
1213}
1214
1215fn generate_enum(file: &mut File, en: &std::rc::Rc<Enumeration>) {
1216 file.declarations.push(Declaration::Enum(Enum {
1217 name: ident(&en.name),
1218 values: (0..en.values.len())
1219 .map(|value| {
1220 ident(&EnumerationValue { value, enumeration: en.clone() }.to_pascal_case())
1221 })
1222 .collect(),
1223 }))
1224}
1225
1226fn generate_public_component(
1230 file: &mut File,
1231 conditional_includes: &ConditionalIncludes,
1232 component: &llr::PublicComponent,
1233 unit: &llr::CompilationUnit,
1234) {
1235 let component_id = ident(&component.name);
1236
1237 let mut component_struct = Struct { name: component_id.clone(), ..Default::default() };
1238
1239 component_struct.members.push((
1241 Access::Private,
1242 Declaration::Var(Var {
1243 ty: SmolStr::new_static(SHARED_GLOBAL_CLASS),
1244 name: "m_globals".into(),
1245 ..Default::default()
1246 }),
1247 ));
1248
1249 for glob in unit.globals.iter().filter(|glob| glob.must_generate() && !glob.is_builtin) {
1250 component_struct.friends.push(ident(&glob.name));
1251 }
1252
1253 let mut global_accessor_function_body = Vec::new();
1254 let mut builtin_globals = Vec::new();
1255 for glob in unit.globals.iter().filter(|glob| glob.exported && glob.must_generate()) {
1256 let accessor_statement = if glob.is_builtin {
1257 builtin_globals.push(format!("std::is_same_v<T, {}>", ident(&glob.name)));
1258 format!(
1259 "{0}if constexpr(std::is_same_v<T, {1}>) {{ return {1}(m_globals.global_{1}); }}",
1260 if global_accessor_function_body.is_empty() { "" } else { "else " },
1261 concatenate_ident(&glob.name),
1262 )
1263 } else {
1264 format!(
1265 "{0}if constexpr(std::is_same_v<T, {1}>) {{ return *m_globals.global_{1}.get(); }}",
1266 if global_accessor_function_body.is_empty() { "" } else { "else " },
1267 concatenate_ident(&glob.name),
1268 )
1269 };
1270 global_accessor_function_body.push(accessor_statement);
1271 }
1272 if !global_accessor_function_body.is_empty() {
1273 global_accessor_function_body.push(
1274 "else { static_assert(!sizeof(T*), \"The type is not global/or exported\"); }".into(),
1275 );
1276
1277 component_struct.members.push((
1278 Access::Public,
1279 Declaration::Function(Function {
1280 name: "global".into(),
1281 signature: if builtin_globals.is_empty() {
1282 "() const -> const T&".into()
1283 } else {
1284 format!(
1285 "() const -> std::conditional_t<{} , T, const T&>",
1286 builtin_globals.iter().join(" || ")
1287 )
1288 },
1289 statements: Some(global_accessor_function_body),
1290 template_parameters: Some("typename T".into()),
1291 ..Default::default()
1292 }),
1293 ));
1294 }
1295
1296 let ctx = EvaluationContext {
1297 compilation_unit: unit,
1298 current_scope: EvaluationScope::SubComponent(component.item_tree.root, None),
1299 generator_state: CppGeneratorContext {
1300 global_access: "(&this->m_globals)".to_string(),
1301 conditional_includes,
1302 },
1303 argument_types: &[],
1304 };
1305
1306 let old_declarations = file.declarations.len();
1307
1308 generate_item_tree(
1309 &mut component_struct,
1310 &component.item_tree,
1311 unit,
1312 None,
1313 false,
1314 component_id,
1315 Access::Private, file,
1317 conditional_includes,
1318 );
1319
1320 for new_decl in file.declarations.iter().skip(old_declarations) {
1323 if let Declaration::Struct(struc @ Struct { .. }) = new_decl {
1324 component_struct.friends.push(struc.name.clone());
1325 };
1326 }
1327
1328 generate_public_api_for_properties(
1329 &mut component_struct.members,
1330 &component.public_properties,
1331 &component.private_properties,
1332 &ctx,
1333 );
1334
1335 component_struct.members.push((
1336 Access::Public,
1337 Declaration::Function(Function {
1338 name: "show".into(),
1339 signature: "() -> void".into(),
1340 statements: Some(vec!["window().show();".into()]),
1341 ..Default::default()
1342 }),
1343 ));
1344
1345 component_struct.members.push((
1346 Access::Public,
1347 Declaration::Function(Function {
1348 name: "hide".into(),
1349 signature: "() -> void".into(),
1350 statements: Some(vec!["window().hide();".into()]),
1351 ..Default::default()
1352 }),
1353 ));
1354
1355 component_struct.members.push((
1356 Access::Public,
1357 Declaration::Function(Function {
1358 name: "window".into(),
1359 signature: "() const -> slint::Window&".into(),
1360 statements: Some(vec!["return m_globals.window();".into()]),
1361 ..Default::default()
1362 }),
1363 ));
1364
1365 component_struct.members.push((
1366 Access::Public,
1367 Declaration::Function(Function {
1368 name: "run".into(),
1369 signature: "() -> void".into(),
1370 statements: Some(vec![
1371 "show();".into(),
1372 "slint::run_event_loop();".into(),
1373 "hide();".into(),
1374 ]),
1375 ..Default::default()
1376 }),
1377 ));
1378
1379 component_struct.friends.push("slint::private_api::WindowAdapterRc".into());
1380
1381 add_friends(&mut component_struct.friends, unit, component.item_tree.root, true);
1382
1383 fn add_friends(
1384 friends: &mut Vec<SmolStr>,
1385 unit: &llr::CompilationUnit,
1386 c: llr::SubComponentIdx,
1387 is_root: bool,
1388 ) {
1389 let sc = &unit.sub_components[c];
1390 if !is_root {
1391 friends.push(ident(&sc.name));
1392 }
1393 for repeater in &sc.repeated {
1394 add_friends(friends, unit, repeater.sub_tree.root, false)
1395 }
1396 for popup in &sc.popup_windows {
1397 add_friends(friends, unit, popup.item_tree.root, false)
1398 }
1399 for menu in &sc.menu_item_trees {
1400 add_friends(friends, unit, menu.root, false)
1401 }
1402 }
1403
1404 file.definitions.extend(component_struct.extract_definitions().collect::<Vec<_>>());
1405 file.declarations.push(Declaration::Struct(component_struct));
1406}
1407
1408fn generate_item_tree(
1409 target_struct: &mut Struct,
1410 sub_tree: &llr::ItemTree,
1411 root: &llr::CompilationUnit,
1412 parent_ctx: Option<&ParentScope>,
1413 is_popup_menu: bool,
1414 item_tree_class_name: SmolStr,
1415 field_access: Access,
1416 file: &mut File,
1417 conditional_includes: &ConditionalIncludes,
1418) {
1419 target_struct.friends.push(format_smolstr!(
1420 "vtable::VRc<slint::private_api::ItemTreeVTable, {}>",
1421 item_tree_class_name
1422 ));
1423
1424 generate_sub_component(
1425 target_struct,
1426 sub_tree.root,
1427 root,
1428 parent_ctx,
1429 field_access,
1430 file,
1431 conditional_includes,
1432 );
1433
1434 let mut item_tree_array: Vec<String> = Default::default();
1435 let mut item_array: Vec<String> = Default::default();
1436
1437 sub_tree.tree.visit_in_array(&mut |node, children_offset, parent_index| {
1438 let parent_index = parent_index as u32;
1439
1440 match node.item_index {
1441 Either::Right(mut repeater_index) => {
1442 assert_eq!(node.children.len(), 0);
1443 let mut sub_component = &root.sub_components[sub_tree.root];
1444 for i in &node.sub_component_path {
1445 repeater_index += sub_component.sub_components[*i].repeater_offset;
1446 sub_component = &root.sub_components[sub_component.sub_components[*i].ty];
1447 }
1448 item_tree_array.push(format!(
1449 "slint::private_api::make_dyn_node({repeater_index}, {parent_index})"
1450 ));
1451 }
1452 Either::Left(item_index) => {
1453 let mut compo_offset = String::new();
1454 let mut sub_component = &root.sub_components[sub_tree.root];
1455 for i in &node.sub_component_path {
1456 let next_sub_component_name = ident(&sub_component.sub_components[*i].name);
1457 write!(
1458 compo_offset,
1459 "offsetof({}, {}) + ",
1460 ident(&sub_component.name),
1461 next_sub_component_name
1462 )
1463 .unwrap();
1464 sub_component = &root.sub_components[sub_component.sub_components[*i].ty];
1465 }
1466
1467 let item = &sub_component.items[item_index];
1468 let children_count = node.children.len() as u32;
1469 let children_index = children_offset as u32;
1470 let item_array_index = item_array.len() as u32;
1471
1472 item_tree_array.push(format!(
1473 "slint::private_api::make_item_node({}, {}, {}, {}, {})",
1474 children_count,
1475 children_index,
1476 parent_index,
1477 item_array_index,
1478 node.is_accessible
1479 ));
1480 item_array.push(format!(
1481 "{{ {}, {} offsetof({}, {}) }}",
1482 item.ty.cpp_vtable_getter,
1483 compo_offset,
1484 &ident(&sub_component.name),
1485 ident(&item.name),
1486 ));
1487 }
1488 }
1489 });
1490
1491 let mut visit_children_statements = vec![
1492 "static const auto dyn_visit = [] (const void *base, [[maybe_unused]] slint::private_api::TraversalOrder order, [[maybe_unused]] slint::private_api::ItemVisitorRefMut visitor, [[maybe_unused]] uint32_t dyn_index) -> uint64_t {".to_owned(),
1493 format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", item_tree_class_name)];
1494 let mut subtree_range_statement = vec![" std::abort();".into()];
1495 let mut subtree_component_statement = vec![" std::abort();".into()];
1496
1497 if target_struct.members.iter().any(|(_, declaration)| {
1498 matches!(&declaration, Declaration::Function(func @ Function { .. }) if func.name == "visit_dynamic_children")
1499 }) {
1500 visit_children_statements
1501 .push(" return self->visit_dynamic_children(dyn_index, order, visitor);".into());
1502 subtree_range_statement = vec![
1503 format!("auto self = reinterpret_cast<const {}*>(component.instance);", item_tree_class_name),
1504 "return self->subtree_range(dyn_index);".to_owned(),
1505 ];
1506 subtree_component_statement = vec![
1507 format!("auto self = reinterpret_cast<const {}*>(component.instance);", item_tree_class_name),
1508 "self->subtree_component(dyn_index, subtree_index, result);".to_owned(),
1509 ];
1510 } else {
1511 visit_children_statements.push(" std::abort();".into());
1512 }
1513
1514 visit_children_statements.extend([
1515 "};".into(),
1516 format!("auto self_rc = reinterpret_cast<const {item_tree_class_name}*>(component.instance)->self_weak.lock()->into_dyn();"),
1517 "return slint::cbindgen_private::slint_visit_item_tree(&self_rc, get_item_tree(component) , index, order, visitor, dyn_visit);".to_owned(),
1518 ]);
1519
1520 target_struct.members.push((
1521 Access::Private,
1522 Declaration::Function(Function {
1523 name: "visit_children".into(),
1524 signature: "(slint::private_api::ItemTreeRef component, intptr_t index, slint::private_api::TraversalOrder order, slint::private_api::ItemVisitorRefMut visitor) -> uint64_t".into(),
1525 is_static: true,
1526 statements: Some(visit_children_statements),
1527 ..Default::default()
1528 }),
1529 ));
1530
1531 target_struct.members.push((
1532 Access::Private,
1533 Declaration::Function(Function {
1534 name: "get_item_ref".into(),
1535 signature: "(slint::private_api::ItemTreeRef component, uint32_t index) -> slint::private_api::ItemRef".into(),
1536 is_static: true,
1537 statements: Some(vec![
1538 "return slint::private_api::get_item_ref(component, get_item_tree(component), item_array(), index);".to_owned(),
1539 ]),
1540 ..Default::default()
1541 }),
1542 ));
1543
1544 target_struct.members.push((
1545 Access::Private,
1546 Declaration::Function(Function {
1547 name: "get_subtree_range".into(),
1548 signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] uint32_t dyn_index) -> slint::private_api::IndexRange".into(),
1549 is_static: true,
1550 statements: Some(subtree_range_statement),
1551 ..Default::default()
1552 }),
1553 ));
1554
1555 target_struct.members.push((
1556 Access::Private,
1557 Declaration::Function(Function {
1558 name: "get_subtree".into(),
1559 signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] uint32_t dyn_index, [[maybe_unused]] uintptr_t subtree_index, [[maybe_unused]] slint::private_api::ItemTreeWeak *result) -> void".into(),
1560 is_static: true,
1561 statements: Some(subtree_component_statement),
1562 ..Default::default()
1563 }),
1564 ));
1565
1566 target_struct.members.push((
1567 Access::Private,
1568 Declaration::Function(Function {
1569 name: "get_item_tree".into(),
1570 signature: "(slint::private_api::ItemTreeRef) -> slint::cbindgen_private::Slice<slint::private_api::ItemTreeNode>".into(),
1571 is_static: true,
1572 statements: Some(vec![
1573 "return item_tree();".to_owned(),
1574 ]),
1575 ..Default::default()
1576 }),
1577 ));
1578
1579 let parent_item_from_parent_component = parent_ctx.as_ref()
1580 .map(|parent| {
1581 parent.repeater_index.map_or_else(|| {
1582 vec![
1584 format!("auto self = reinterpret_cast<const {item_tree_class_name}*>(component.instance);"),
1585 format!("auto parent = self->parent.lock().value();"),
1586 format!("*result = {{ parent->self_weak, 0 }};"),
1588 ]
1589 }, |idx| {
1590 let current_sub_component = &root.sub_components[parent.sub_component];
1591 let parent_index = current_sub_component.repeated[idx].index_in_tree;
1592 vec![
1593 format!("auto self = reinterpret_cast<const {item_tree_class_name}*>(component.instance);"),
1594 format!("auto parent = self->parent.lock().value();"),
1595 format!("*result = {{ parent->self_weak, parent->tree_index_of_first_child + {} }};", parent_index - 1),
1596 ]
1597 })
1598 })
1599 .unwrap_or_default();
1600 target_struct.members.push((
1601 Access::Private,
1602 Declaration::Function(Function {
1603 name: "parent_node".into(),
1604 signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] slint::private_api::ItemWeak *result) -> void".into(),
1605 is_static: true,
1606 statements: Some(parent_item_from_parent_component,),
1607 ..Default::default()
1608 }),
1609 ));
1610
1611 target_struct.members.push((
1612 Access::Private,
1613 Declaration::Function(Function {
1614 name: "embed_component".into(),
1615 signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] const slint::private_api::ItemTreeWeak *parent_component, [[maybe_unused]] const uint32_t parent_index) -> bool".into(),
1616 is_static: true,
1617 statements: Some(vec!["return false; /* todo! */".into()]),
1618 ..Default::default()
1619 }),
1620 ));
1621
1622 target_struct.members.push((
1624 Access::Private,
1625 Declaration::Function(Function {
1626 name: "subtree_index".into(),
1627 signature: "([[maybe_unused]] slint::private_api::ItemTreeRef component) -> uintptr_t"
1628 .into(),
1629 is_static: true,
1630 statements: Some(vec!["return std::numeric_limits<uintptr_t>::max();".into()]),
1631 ..Default::default()
1632 }),
1633 ));
1634
1635 target_struct.members.push((
1636 Access::Private,
1637 Declaration::Function(Function {
1638 name: "item_tree".into(),
1639 signature: "() -> slint::cbindgen_private::Slice<slint::private_api::ItemTreeNode>"
1640 .into(),
1641 is_static: true,
1642 statements: Some(vec![
1643 "static const slint::private_api::ItemTreeNode children[] {".to_owned(),
1644 format!(" {} }};", item_tree_array.join(", \n")),
1645 "return slint::private_api::make_slice(std::span(children));".to_owned(),
1646 ]),
1647 ..Default::default()
1648 }),
1649 ));
1650
1651 target_struct.members.push((
1652 Access::Private,
1653 Declaration::Function(Function {
1654 name: "item_array".into(),
1655 signature: "() -> const slint::private_api::ItemArray".into(),
1656 is_static: true,
1657 statements: Some(vec![
1658 "static const slint::private_api::ItemArrayEntry items[] {".to_owned(),
1659 format!(" {} }};", item_array.join(", \n")),
1660 "return slint::private_api::make_slice(std::span(items));".to_owned(),
1661 ]),
1662 ..Default::default()
1663 }),
1664 ));
1665
1666 target_struct.members.push((
1667 Access::Private,
1668 Declaration::Function(Function {
1669 name: "layout_info".into(),
1670 signature:
1671 "([[maybe_unused]] slint::private_api::ItemTreeRef component, slint::cbindgen_private::Orientation o) -> slint::cbindgen_private::LayoutInfo"
1672 .into(),
1673 is_static: true,
1674 statements: Some(vec![format!(
1675 "return reinterpret_cast<const {}*>(component.instance)->layout_info(o);",
1676 item_tree_class_name
1677 )]),
1678 ..Default::default()
1679 }),
1680 ));
1681
1682 target_struct.members.push((
1683 Access::Private,
1684 Declaration::Function(Function {
1685 name: "item_geometry".into(),
1686 signature:
1687 "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index) -> slint::cbindgen_private::LogicalRect"
1688 .into(),
1689 is_static: true,
1690 statements: Some(vec![format!(
1691 "return reinterpret_cast<const {}*>(component.instance)->item_geometry(index);",
1692 item_tree_class_name
1693 ), ]),
1694 ..Default::default()
1695 }),
1696 ));
1697
1698 target_struct.members.push((
1699 Access::Private,
1700 Declaration::Function(Function {
1701 name: "accessible_role".into(),
1702 signature:
1703 "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index) -> slint::cbindgen_private::AccessibleRole"
1704 .into(),
1705 is_static: true,
1706 statements: Some(vec![format!(
1707 "return reinterpret_cast<const {}*>(component.instance)->accessible_role(index);",
1708 item_tree_class_name
1709 )]),
1710 ..Default::default()
1711 }),
1712 ));
1713
1714 target_struct.members.push((
1715 Access::Private,
1716 Declaration::Function(Function {
1717 name: "accessible_string_property".into(),
1718 signature:
1719 "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index, slint::cbindgen_private::AccessibleStringProperty what, slint::SharedString *result) -> bool"
1720 .into(),
1721 is_static: true,
1722 statements: Some(vec![format!(
1723 "if (auto r = reinterpret_cast<const {}*>(component.instance)->accessible_string_property(index, what)) {{ *result = *r; return true; }} else {{ return false; }}",
1724 item_tree_class_name
1725 )]),
1726 ..Default::default()
1727 }),
1728 ));
1729
1730 target_struct.members.push((
1731 Access::Private,
1732 Declaration::Function(Function {
1733 name: "accessibility_action".into(),
1734 signature:
1735 "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index, const slint::cbindgen_private::AccessibilityAction *action) -> void"
1736 .into(),
1737 is_static: true,
1738 statements: Some(vec![format!(
1739 "reinterpret_cast<const {}*>(component.instance)->accessibility_action(index, *action);",
1740 item_tree_class_name
1741 )]),
1742 ..Default::default()
1743 }),
1744 ));
1745
1746 target_struct.members.push((
1747 Access::Private,
1748 Declaration::Function(Function {
1749 name: "supported_accessibility_actions".into(),
1750 signature:
1751 "([[maybe_unused]] slint::private_api::ItemTreeRef component, uint32_t index) -> uint32_t"
1752 .into(),
1753 is_static: true,
1754 statements: Some(vec![format!(
1755 "return reinterpret_cast<const {}*>(component.instance)->supported_accessibility_actions(index);",
1756 item_tree_class_name
1757 )]),
1758 ..Default::default()
1759 }),
1760 ));
1761
1762 target_struct.members.push((
1763 Access::Private,
1764 Declaration::Function(Function {
1765 name: "element_infos".into(),
1766 signature:
1767 "([[maybe_unused]] slint::private_api::ItemTreeRef component, [[maybe_unused]] uint32_t index, [[maybe_unused]] slint::SharedString *result) -> bool"
1768 .into(),
1769 is_static: true,
1770 statements: Some(if root.has_debug_info {
1771 vec![
1772 format!("if (auto infos = reinterpret_cast<const {}*>(component.instance)->element_infos(index)) {{ *result = *infos; }};",
1773 item_tree_class_name),
1774 "return true;".into()
1775 ]
1776 } else {
1777 vec!["return false;".into()]
1778 }),
1779 ..Default::default()
1780 }),
1781 ));
1782
1783 target_struct.members.push((
1784 Access::Private,
1785 Declaration::Function(Function {
1786 name: "window_adapter".into(),
1787 signature:
1788 "(slint::private_api::ItemTreeRef component, [[maybe_unused]] bool do_create, slint::cbindgen_private::Option<slint::private_api::WindowAdapterRc>* result) -> void"
1789 .into(),
1790 is_static: true,
1791 statements: Some(vec![format!(
1792 "*reinterpret_cast<slint::private_api::WindowAdapterRc*>(result) = reinterpret_cast<const {item_tree_class_name}*>(component.instance)->globals->window().window_handle();"
1793 )]),
1794 ..Default::default()
1795 }),
1796 ));
1797
1798 target_struct.members.push((
1799 Access::Public,
1800 Declaration::Var(Var {
1801 ty: "static const slint::private_api::ItemTreeVTable".into(),
1802 name: "static_vtable".into(),
1803 ..Default::default()
1804 }),
1805 ));
1806
1807 file.definitions.push(Declaration::Var(Var {
1808 ty: "const slint::private_api::ItemTreeVTable".into(),
1809 name: format_smolstr!("{}::static_vtable", item_tree_class_name),
1810 init: Some(format!(
1811 "{{ visit_children, get_item_ref, get_subtree_range, get_subtree, \
1812 get_item_tree, parent_node, embed_component, subtree_index, layout_info, \
1813 item_geometry, accessible_role, accessible_string_property, accessibility_action, \
1814 supported_accessibility_actions, element_infos, window_adapter, \
1815 slint::private_api::drop_in_place<{item_tree_class_name}>, slint::private_api::dealloc }}"
1816 )),
1817 ..Default::default()
1818 }));
1819
1820 let mut create_parameters = Vec::new();
1821 let mut init_parent_parameters = "";
1822
1823 if let Some(parent) = &parent_ctx {
1824 let parent_type =
1825 format!("class {} const *", ident(&root.sub_components[parent.sub_component].name));
1826 create_parameters.push(format!("{parent_type} parent"));
1827
1828 init_parent_parameters = ", parent";
1829 }
1830
1831 let mut create_code = vec![
1832 format!(
1833 "auto self_rc = vtable::VRc<slint::private_api::ItemTreeVTable, {0}>::make();",
1834 target_struct.name
1835 ),
1836 format!("auto self = const_cast<{0} *>(&*self_rc);", target_struct.name),
1837 "self->self_weak = vtable::VWeak(self_rc).into_dyn();".into(),
1838 ];
1839
1840 if is_popup_menu {
1841 create_code.push("self->globals = globals;".into());
1842 create_parameters.push("const SharedGlobals *globals".into());
1843 } else if parent_ctx.is_none() {
1844 create_code.push("slint::cbindgen_private::slint_ensure_backend();".into());
1845
1846 #[cfg(feature = "bundle-translations")]
1847 if let Some(translations) = &root.translations {
1848 let lang_len = translations.languages.len();
1849 create_code.push(format!(
1850 "std::array<slint::cbindgen_private::Slice<uint8_t>, {lang_len}> languages {{ {} }};",
1851 translations
1852 .languages
1853 .iter()
1854 .map(|l| format!("slint::private_api::string_to_slice({l:?})"))
1855 .join(", ")
1856 ));
1857 create_code.push("slint::cbindgen_private::slint_translate_set_bundled_languages(slint::private_api::make_slice(std::span(languages)));".to_string());
1858 }
1859
1860 create_code.push("self->globals = &self->m_globals;".into());
1861 create_code.push("self->m_globals.root_weak = self->self_weak;".into());
1862 }
1863
1864 let global_access = if parent_ctx.is_some() { "parent->globals" } else { "self->globals" };
1865 create_code.extend([
1866 format!(
1867 "slint::private_api::register_item_tree(&self_rc.into_dyn(), {global_access}->m_window);",
1868 ),
1869 format!("self->init({global_access}, self->self_weak, 0, 1 {init_parent_parameters});"),
1870 ]);
1871
1872 if parent_ctx.is_none() && !is_popup_menu {
1875 create_code.push("self->user_init();".to_string());
1876 create_code.push("self->window();".to_string())
1878 }
1879
1880 create_code
1881 .push(format!("return slint::ComponentHandle<{0}>{{ self_rc }};", target_struct.name));
1882
1883 target_struct.members.push((
1884 Access::Public,
1885 Declaration::Function(Function {
1886 name: "create".into(),
1887 signature: format!(
1888 "({}) -> slint::ComponentHandle<{}>",
1889 create_parameters.join(","),
1890 target_struct.name
1891 ),
1892 statements: Some(create_code),
1893 is_static: true,
1894 ..Default::default()
1895 }),
1896 ));
1897
1898 let destructor = vec![format!(
1899 "if (auto &window = globals->m_window) window->window_handle().unregister_item_tree(this, item_array());"
1900 )];
1901
1902 target_struct.members.push((
1903 Access::Public,
1904 Declaration::Function(Function {
1905 name: format_smolstr!("~{}", target_struct.name),
1906 signature: "()".to_owned(),
1907 is_constructor_or_destructor: true,
1908 statements: Some(destructor),
1909 ..Default::default()
1910 }),
1911 ));
1912}
1913
1914fn generate_sub_component(
1915 target_struct: &mut Struct,
1916 component: llr::SubComponentIdx,
1917 root: &llr::CompilationUnit,
1918 parent_ctx: Option<&ParentScope>,
1919 field_access: Access,
1920 file: &mut File,
1921 conditional_includes: &ConditionalIncludes,
1922) {
1923 let globals_type_ptr = "const class SharedGlobals*";
1924
1925 let mut init_parameters = vec![
1926 format!("{} globals", globals_type_ptr),
1927 "slint::cbindgen_private::ItemTreeWeak enclosing_component".into(),
1928 "uint32_t tree_index".into(),
1929 "uint32_t tree_index_of_first_child".into(),
1930 ];
1931
1932 let mut init: Vec<String> =
1933 vec!["auto self = this;".into(), "self->self_weak = enclosing_component;".into()];
1934
1935 target_struct.members.push((
1936 Access::Public,
1937 Declaration::Var(Var {
1938 ty: "slint::cbindgen_private::ItemTreeWeak".into(),
1939 name: "self_weak".into(),
1940 ..Default::default()
1941 }),
1942 ));
1943
1944 target_struct.members.push((
1945 field_access,
1946 Declaration::Var(Var {
1947 ty: globals_type_ptr.into(),
1948 name: "globals".into(),
1949 ..Default::default()
1950 }),
1951 ));
1952 init.push("self->globals = globals;".into());
1953
1954 target_struct.members.push((
1955 field_access,
1956 Declaration::Var(Var {
1957 ty: "uint32_t".into(),
1958 name: "tree_index_of_first_child".into(),
1959 ..Default::default()
1960 }),
1961 ));
1962 init.push("this->tree_index_of_first_child = tree_index_of_first_child;".into());
1963
1964 target_struct.members.push((
1965 field_access,
1966 Declaration::Var(Var {
1967 ty: "uint32_t".into(),
1968 name: "tree_index".into(),
1969 ..Default::default()
1970 }),
1971 ));
1972 init.push("self->tree_index = tree_index;".into());
1973
1974 if let Some(parent_ctx) = &parent_ctx {
1975 let parent_type = ident(&root.sub_components[parent_ctx.sub_component].name);
1976 init_parameters.push(format!("class {parent_type} const *parent"));
1977
1978 target_struct.members.push((
1979 field_access,
1980 Declaration::Var(Var {
1981 ty: format_smolstr!(
1982 "vtable::VWeakMapped<slint::private_api::ItemTreeVTable, class {parent_type} const>"
1983 )
1984 .clone(),
1985 name: "parent".into(),
1986 ..Default::default()
1987 }),
1988 ));
1989 init.push(format!("self->parent = vtable::VRcMapped<slint::private_api::ItemTreeVTable, const {parent_type}>(parent->self_weak.lock().value(), parent);"));
1990 }
1991
1992 let ctx = EvaluationContext::new_sub_component(
1993 root,
1994 component,
1995 CppGeneratorContext { global_access: "self->globals".into(), conditional_includes },
1996 parent_ctx,
1997 );
1998
1999 let component = &root.sub_components[component];
2000
2001 let parent_ctx = ParentScope::new(&ctx, None);
2002
2003 component.popup_windows.iter().for_each(|popup| {
2004 let component_id = ident(&root.sub_components[popup.item_tree.root].name);
2005 let mut popup_struct = Struct { name: component_id.clone(), ..Default::default() };
2006 generate_item_tree(
2007 &mut popup_struct,
2008 &popup.item_tree,
2009 root,
2010 Some(&parent_ctx),
2011 false,
2012 component_id,
2013 Access::Public,
2014 file,
2015 conditional_includes,
2016 );
2017 file.definitions.extend(popup_struct.extract_definitions());
2018 file.declarations.push(Declaration::Struct(popup_struct));
2019 });
2020 for menu in &component.menu_item_trees {
2021 let component_id = ident(&root.sub_components[menu.root].name);
2022 let mut menu_struct = Struct { name: component_id.clone(), ..Default::default() };
2023 generate_item_tree(
2024 &mut menu_struct,
2025 menu,
2026 root,
2027 Some(&parent_ctx),
2028 false,
2029 component_id,
2030 Access::Public,
2031 file,
2032 conditional_includes,
2033 );
2034 file.definitions.extend(menu_struct.extract_definitions());
2035 file.declarations.push(Declaration::Struct(menu_struct));
2036 }
2037
2038 for property in component.properties.iter() {
2039 let cpp_name = ident(&property.name);
2040 let ty =
2041 format_smolstr!("slint::private_api::Property<{}>", property.ty.cpp_type().unwrap());
2042 target_struct.members.push((
2043 field_access,
2044 Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2045 ));
2046 }
2047 for callback in component.callbacks.iter() {
2048 let cpp_name = ident(&callback.name);
2049 let param_types = callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
2050 let ty = format_smolstr!(
2051 "slint::private_api::Callback<{}({})>",
2052 callback.ret_ty.cpp_type().unwrap(),
2053 param_types.join(", ")
2054 );
2055 target_struct.members.push((
2056 field_access,
2057 Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2058 ));
2059 }
2060
2061 for (i, _) in component.change_callbacks.iter().enumerate() {
2062 target_struct.members.push((
2063 field_access,
2064 Declaration::Var(Var {
2065 ty: "slint::private_api::ChangeTracker".into(),
2066 name: format_smolstr!("change_tracker{}", i),
2067 ..Default::default()
2068 }),
2069 ));
2070 }
2071
2072 let mut user_init = vec!["[[maybe_unused]] auto self = this;".into()];
2073
2074 let mut children_visitor_cases = Vec::new();
2075 let mut subtrees_ranges_cases = Vec::new();
2076 let mut subtrees_components_cases = Vec::new();
2077
2078 for sub in &component.sub_components {
2079 let field_name = ident(&sub.name);
2080 let sub_sc = &root.sub_components[sub.ty];
2081 let local_tree_index: u32 = sub.index_in_tree as _;
2082 let local_index_of_first_child: u32 = sub.index_of_first_child_in_tree as _;
2083
2084 let global_index = if local_tree_index == 0 {
2087 "tree_index".into()
2088 } else {
2089 format!("tree_index_of_first_child + {local_tree_index} - 1")
2090 };
2091 let global_children = if local_index_of_first_child == 0 {
2092 "0".into()
2093 } else {
2094 format!("tree_index_of_first_child + {local_index_of_first_child} - 1")
2095 };
2096
2097 init.push(format!(
2098 "this->{field_name}.init(globals, self_weak.into_dyn(), {global_index}, {global_children});"
2099 ));
2100 user_init.push(format!("this->{field_name}.user_init();"));
2101
2102 let sub_component_repeater_count = sub_sc.repeater_count(root);
2103 if sub_component_repeater_count > 0 {
2104 let mut case_code = String::new();
2105 let repeater_offset = sub.repeater_offset;
2106
2107 for local_repeater_index in 0..sub_component_repeater_count {
2108 write!(case_code, "case {}: ", repeater_offset + local_repeater_index).unwrap();
2109 }
2110
2111 children_visitor_cases.push(format!(
2112 "\n {case_code} {{
2113 return self->{field_name}.visit_dynamic_children(dyn_index - {repeater_offset}, order, visitor);
2114 }}",
2115 ));
2116 subtrees_ranges_cases.push(format!(
2117 "\n {case_code} {{
2118 return self->{field_name}.subtree_range(dyn_index - {repeater_offset});
2119 }}",
2120 ));
2121 subtrees_components_cases.push(format!(
2122 "\n {case_code} {{
2123 self->{field_name}.subtree_component(dyn_index - {repeater_offset}, subtree_index, result);
2124 return;
2125 }}",
2126 ));
2127 }
2128
2129 target_struct.members.push((
2130 field_access,
2131 Declaration::Var(Var {
2132 ty: ident(&sub_sc.name),
2133 name: field_name,
2134 ..Default::default()
2135 }),
2136 ));
2137 }
2138
2139 for (i, _) in component.popup_windows.iter().enumerate() {
2140 target_struct.members.push((
2141 field_access,
2142 Declaration::Var(Var {
2143 ty: ident("mutable uint32_t"),
2144 name: format_smolstr!("popup_id_{}", i),
2145 ..Default::default()
2146 }),
2147 ));
2148 }
2149
2150 for (prop1, prop2, fields) in &component.two_way_bindings {
2151 if fields.is_empty() {
2152 let ty = ctx.property_ty(prop1).cpp_type().unwrap();
2153 let p1 = access_member(prop1, &ctx).unwrap();
2154 init.push(
2155 access_member(prop2, &ctx).then(|p2| {
2156 format!("slint::private_api::Property<{ty}>::link_two_way(&{p1}, &{p2})",)
2157 }) + ";",
2158 );
2159 } else {
2160 let mut access = "x".to_string();
2161 let mut ty = ctx.property_ty(prop2);
2162 let cpp_ty = ty.cpp_type().unwrap();
2163 for f in fields {
2164 let Type::Struct(s) = &ty else {
2165 panic!("Field of two way binding on a non-struct type")
2166 };
2167 access = struct_field_access(access, s, f);
2168 ty = s.fields.get(f).unwrap();
2169 }
2170
2171 let p1 = access_member(prop1, &ctx).unwrap();
2172 init.push(
2173 access_member(prop2, &ctx).then(|p2|
2174 format!("slint::private_api::Property<{cpp_ty}>::link_two_way_with_map(&{p2}, &{p1}, [](const auto &x){{ return {access}; }}, [](auto &x, const auto &v){{ {access} = v; }})")
2175 ) + ";",
2176 );
2177 }
2178 }
2179
2180 let mut properties_init_code = Vec::new();
2181 for (prop, expression) in &component.property_init {
2182 handle_property_init(prop, expression, &mut properties_init_code, &ctx)
2183 }
2184 for prop in &component.const_properties {
2185 let p = access_local_member(prop, &ctx);
2186 properties_init_code.push(format!("{p}.set_constant();"));
2187 }
2188
2189 for item in &component.items {
2190 target_struct.members.push((
2191 field_access,
2192 Declaration::Var(Var {
2193 ty: format_smolstr!("slint::cbindgen_private::{}", ident(&item.ty.class_name)),
2194 name: ident(&item.name),
2195 init: Some("{}".to_owned()),
2196 ..Default::default()
2197 }),
2198 ));
2199 }
2200
2201 for (idx, repeated) in component.repeated.iter_enumerated() {
2202 let sc = &root.sub_components[repeated.sub_tree.root];
2203 let data_type = repeated.data_prop.map(|data_prop| sc.properties[data_prop].ty.clone());
2204
2205 generate_repeated_component(
2206 repeated,
2207 root,
2208 ParentScope::new(&ctx, Some(idx)),
2209 data_type.as_ref(),
2210 file,
2211 conditional_includes,
2212 );
2213
2214 let idx = usize::from(idx);
2215 let repeater_id = format_smolstr!("repeater_{}", idx);
2216
2217 let model = compile_expression(&repeated.model.borrow(), &ctx);
2218
2219 properties_init_code.push(format!(
2221 "self->{repeater_id}.set_model_binding([self] {{ (void)self; return {model}; }});",
2222 ));
2223
2224 let ensure_updated = if let Some(listview) = &repeated.listview {
2225 let vp_y = access_member(&listview.viewport_y, &ctx).unwrap();
2226 let vp_h = access_member(&listview.viewport_height, &ctx).unwrap();
2227 let lv_h = access_member(&listview.listview_height, &ctx).unwrap();
2228 let vp_w = access_member(&listview.viewport_width, &ctx).unwrap();
2229 let lv_w = access_member(&listview.listview_width, &ctx).unwrap();
2230
2231 format!(
2232 "self->{repeater_id}.ensure_updated_listview(self, &{vp_w}, &{vp_h}, &{vp_y}, {lv_w}.get(), {lv_h}.get());"
2233 )
2234 } else {
2235 format!("self->{repeater_id}.ensure_updated(self);")
2236 };
2237
2238 children_visitor_cases.push(format!(
2239 "\n case {idx}: {{
2240 {ensure_updated}
2241 return self->{repeater_id}.visit(order, visitor);
2242 }}",
2243 ));
2244 subtrees_ranges_cases.push(format!(
2245 "\n case {idx}: {{
2246 {ensure_updated}
2247 return self->{repeater_id}.index_range();
2248 }}",
2249 ));
2250 subtrees_components_cases.push(format!(
2251 "\n case {idx}: {{
2252 {ensure_updated}
2253 *result = self->{repeater_id}.instance_at(subtree_index);
2254 return;
2255 }}",
2256 ));
2257
2258 let rep_type = match data_type {
2259 Some(data_type) => {
2260 format_smolstr!(
2261 "slint::private_api::Repeater<class {}, {}>",
2262 ident(&sc.name),
2263 data_type.cpp_type().unwrap()
2264 )
2265 }
2266 None => format_smolstr!("slint::private_api::Conditional<class {}>", ident(&sc.name)),
2267 };
2268 target_struct.members.push((
2269 field_access,
2270 Declaration::Var(Var { ty: rep_type, name: repeater_id, ..Default::default() }),
2271 ));
2272 }
2273
2274 init.extend(properties_init_code);
2275
2276 user_init.extend(component.init_code.iter().map(|e| {
2277 let mut expr_str = compile_expression(&e.borrow(), &ctx);
2278 expr_str.push(';');
2279 expr_str
2280 }));
2281
2282 user_init.extend(component.change_callbacks.iter().enumerate().map(|(idx, (p, e))| {
2283 let code = compile_expression(&e.borrow(), &ctx);
2284 let prop = compile_expression(&llr::Expression::PropertyReference(p.clone()), &ctx);
2285 format!("self->change_tracker{idx}.init(self, [](auto self) {{ return {prop}; }}, []([[maybe_unused]] auto self, auto) {{ {code}; }});")
2286 }));
2287
2288 if !component.timers.is_empty() {
2289 let mut update_timers = vec!["auto self = this;".into()];
2290 for (i, tmr) in component.timers.iter().enumerate() {
2291 user_init.push("self->update_timers();".to_string());
2292 let name = format_smolstr!("timer{}", i);
2293 let running = compile_expression(&tmr.running.borrow(), &ctx);
2294 let interval = compile_expression(&tmr.interval.borrow(), &ctx);
2295 let callback = compile_expression(&tmr.triggered.borrow(), &ctx);
2296 update_timers.push(format!("if ({running}) {{"));
2297 update_timers
2298 .push(format!(" auto interval = std::chrono::milliseconds({interval});"));
2299 update_timers.push(format!(
2300 " if (!self->{name}.running() || self->{name}.interval() != interval)"
2301 ));
2302 update_timers.push(format!(" self->{name}.start(slint::TimerMode::Repeated, interval, [self] {{ {callback}; }});"));
2303 update_timers.push(format!("}} else {{ self->{name}.stop(); }}"));
2304 target_struct.members.push((
2305 field_access,
2306 Declaration::Var(Var { ty: "slint::Timer".into(), name, ..Default::default() }),
2307 ));
2308 }
2309 target_struct.members.push((
2310 field_access,
2311 Declaration::Function(Function {
2312 name: "update_timers".into(),
2313 signature: "() -> void".into(),
2314 statements: Some(update_timers),
2315 ..Default::default()
2316 }),
2317 ));
2318 }
2319
2320 target_struct.members.extend(
2321 generate_functions(component.functions.as_ref(), &ctx).map(|x| (Access::Public, x)),
2322 );
2323
2324 target_struct.members.push((
2325 field_access,
2326 Declaration::Function(Function {
2327 name: "init".into(),
2328 signature: format!("({}) -> void", init_parameters.join(",")),
2329 statements: Some(init),
2330 ..Default::default()
2331 }),
2332 ));
2333
2334 target_struct.members.push((
2335 field_access,
2336 Declaration::Function(Function {
2337 name: "user_init".into(),
2338 signature: "() -> void".into(),
2339 statements: Some(user_init),
2340 ..Default::default()
2341 }),
2342 ));
2343
2344 target_struct.members.push((
2345 field_access,
2346 Declaration::Function(Function {
2347 name: "layout_info".into(),
2348 signature: "(slint::cbindgen_private::Orientation o) const -> slint::cbindgen_private::LayoutInfo"
2349 .into(),
2350 statements: Some(vec![
2351 "[[maybe_unused]] auto self = this;".into(),
2352 format!(
2353 "return o == slint::cbindgen_private::Orientation::Horizontal ? {} : {};",
2354 compile_expression(&component.layout_info_h.borrow(), &ctx),
2355 compile_expression(&component.layout_info_v.borrow(), &ctx)
2356 ),
2357 ]),
2358 ..Default::default()
2359 }),
2360 ));
2361
2362 let mut dispatch_item_function = |name: &str,
2363 signature: &str,
2364 forward_args: &str,
2365 code: Vec<String>| {
2366 let mut code = ["[[maybe_unused]] auto self = this;".into()]
2367 .into_iter()
2368 .chain(code)
2369 .collect::<Vec<_>>();
2370
2371 let mut else_ = "";
2372 for sub in &component.sub_components {
2373 let sub_sc = &ctx.compilation_unit.sub_components[sub.ty];
2374 let sub_items_count = sub_sc.child_item_count(ctx.compilation_unit);
2375 code.push(format!("{else_}if (index == {}) {{", sub.index_in_tree,));
2376 code.push(format!(" return self->{}.{name}(0{forward_args});", ident(&sub.name)));
2377 if sub_items_count > 1 {
2378 code.push(format!(
2379 "}} else if (index >= {} && index < {}) {{",
2380 sub.index_of_first_child_in_tree,
2381 sub.index_of_first_child_in_tree + sub_items_count - 1
2382 + sub_sc.repeater_count(ctx.compilation_unit)
2383 ));
2384 code.push(format!(
2385 " return self->{}.{name}(index - {}{forward_args});",
2386 ident(&sub.name),
2387 sub.index_of_first_child_in_tree - 1
2388 ));
2389 }
2390 else_ = "} else ";
2391 }
2392 let ret =
2393 if signature.contains("->") && !signature.contains("-> void") { "{}" } else { "" };
2394 code.push(format!("{else_}return {ret};"));
2395 target_struct.members.push((
2396 field_access,
2397 Declaration::Function(Function {
2398 name: name.into(),
2399 signature: signature.into(),
2400 statements: Some(code),
2401 ..Default::default()
2402 }),
2403 ));
2404 };
2405
2406 let mut item_geometry_cases = vec!["switch (index) {".to_string()];
2407 item_geometry_cases.extend(
2408 component
2409 .geometries
2410 .iter()
2411 .enumerate()
2412 .filter_map(|(i, x)| x.as_ref().map(|x| (i, x)))
2413 .map(|(index, expr)| {
2414 format!(
2415 " case {index}: return slint::private_api::convert_anonymous_rect({});",
2416 compile_expression(&expr.borrow(), &ctx)
2417 )
2418 }),
2419 );
2420 item_geometry_cases.push("}".into());
2421
2422 dispatch_item_function(
2423 "item_geometry",
2424 "(uint32_t index) const -> slint::cbindgen_private::Rect",
2425 "",
2426 item_geometry_cases,
2427 );
2428
2429 let mut accessible_role_cases = vec!["switch (index) {".into()];
2430 let mut accessible_string_cases = vec!["switch ((index << 8) | uintptr_t(what)) {".into()];
2431 let mut accessibility_action_cases =
2432 vec!["switch ((index << 8) | uintptr_t(action.tag)) {".into()];
2433 let mut supported_accessibility_actions = BTreeMap::<u32, BTreeSet<_>>::new();
2434 for ((index, what), expr) in &component.accessible_prop {
2435 let e = compile_expression(&expr.borrow(), &ctx);
2436 if what == "Role" {
2437 accessible_role_cases.push(format!(" case {index}: return {e};"));
2438 } else if let Some(what) = what.strip_prefix("Action") {
2439 let has_args = matches!(&*expr.borrow(), llr::Expression::CallBackCall { arguments, .. } if !arguments.is_empty());
2440
2441 accessibility_action_cases.push(if has_args {
2442 let member = ident(&crate::generator::to_kebab_case(what));
2443 format!(" case ({index} << 8) | uintptr_t(slint::cbindgen_private::AccessibilityAction::Tag::{what}): {{ auto arg_0 = action.{member}._0; return {e}; }}")
2444 } else {
2445 format!(" case ({index} << 8) | uintptr_t(slint::cbindgen_private::AccessibilityAction::Tag::{what}): return {e};")
2446 });
2447 supported_accessibility_actions
2448 .entry(*index)
2449 .or_default()
2450 .insert(format!("slint::cbindgen_private::SupportedAccessibilityAction_{what}"));
2451 } else {
2452 accessible_string_cases.push(format!(" case ({index} << 8) | uintptr_t(slint::cbindgen_private::AccessibleStringProperty::{what}): return {e};"));
2453 }
2454 }
2455 accessible_role_cases.push("}".into());
2456 accessible_string_cases.push("}".into());
2457 accessibility_action_cases.push("}".into());
2458
2459 let mut supported_accessibility_actions_cases = vec!["switch (index) {".into()];
2460 supported_accessibility_actions_cases.extend(supported_accessibility_actions.into_iter().map(
2461 |(index, values)| format!(" case {index}: return {};", values.into_iter().join("|")),
2462 ));
2463 supported_accessibility_actions_cases.push("}".into());
2464
2465 dispatch_item_function(
2466 "accessible_role",
2467 "(uint32_t index) const -> slint::cbindgen_private::AccessibleRole",
2468 "",
2469 accessible_role_cases,
2470 );
2471 dispatch_item_function(
2472 "accessible_string_property",
2473 "(uint32_t index, slint::cbindgen_private::AccessibleStringProperty what) const -> std::optional<slint::SharedString>",
2474 ", what",
2475 accessible_string_cases,
2476 );
2477
2478 dispatch_item_function(
2479 "accessibility_action",
2480 "(uint32_t index, const slint::cbindgen_private::AccessibilityAction &action) const -> void",
2481 ", action",
2482 accessibility_action_cases,
2483 );
2484
2485 dispatch_item_function(
2486 "supported_accessibility_actions",
2487 "(uint32_t index) const -> uint32_t",
2488 "",
2489 supported_accessibility_actions_cases,
2490 );
2491
2492 let mut element_infos_cases = vec!["switch (index) {".to_string()];
2493 element_infos_cases.extend(
2494 component
2495 .element_infos
2496 .iter()
2497 .map(|(index, ids)| format!(" case {index}: return \"{ids}\";")),
2498 );
2499 element_infos_cases.push("}".into());
2500
2501 dispatch_item_function(
2502 "element_infos",
2503 "(uint32_t index) const -> std::optional<slint::SharedString>",
2504 "",
2505 element_infos_cases,
2506 );
2507
2508 if !children_visitor_cases.is_empty() {
2509 target_struct.members.push((
2510 field_access,
2511 Declaration::Function(Function {
2512 name: "visit_dynamic_children".into(),
2513 signature: "(uint32_t dyn_index, [[maybe_unused]] slint::private_api::TraversalOrder order, [[maybe_unused]] slint::private_api::ItemVisitorRefMut visitor) const -> uint64_t".into(),
2514 statements: Some(vec![
2515 " auto self = this;".to_owned(),
2516 format!(" switch(dyn_index) {{ {} }};", children_visitor_cases.join("")),
2517 " std::abort();".to_owned(),
2518 ]),
2519 ..Default::default()
2520 }),
2521 ));
2522 target_struct.members.push((
2523 field_access,
2524 Declaration::Function(Function {
2525 name: "subtree_range".into(),
2526 signature: "(uintptr_t dyn_index) const -> slint::private_api::IndexRange".into(),
2527 statements: Some(vec![
2528 "[[maybe_unused]] auto self = this;".to_owned(),
2529 format!(" switch(dyn_index) {{ {} }};", subtrees_ranges_cases.join("")),
2530 " std::abort();".to_owned(),
2531 ]),
2532 ..Default::default()
2533 }),
2534 ));
2535 target_struct.members.push((
2536 field_access,
2537 Declaration::Function(Function {
2538 name: "subtree_component".into(),
2539 signature: "(uintptr_t dyn_index, [[maybe_unused]] uintptr_t subtree_index, [[maybe_unused]] slint::private_api::ItemTreeWeak *result) const -> void".into(),
2540 statements: Some(vec![
2541 "[[maybe_unused]] auto self = this;".to_owned(),
2542 format!(" switch(dyn_index) {{ {} }};", subtrees_components_cases.join("")),
2543 " std::abort();".to_owned(),
2544 ]),
2545 ..Default::default()
2546 }),
2547 ));
2548 }
2549}
2550
2551fn generate_layout_item_info_decl(
2555 root_sc: &llr::SubComponent,
2556 ctx: &EvaluationContext,
2557) -> Declaration {
2558 const SIGNATURE: &str = "(slint::cbindgen_private::Orientation o, [[maybe_unused]] std::optional<size_t> child_index) const -> slint::cbindgen_private::LayoutItemInfo";
2559
2560 if root_sc.row_child_templates.is_none()
2561 || (root_sc.grid_layout_children.is_empty()
2562 && !llr::has_inner_repeaters(&root_sc.row_child_templates))
2563 {
2564 return Declaration::Function(Function {
2565 name: "layout_item_info".into(),
2566 signature: SIGNATURE.to_owned(),
2567 statements: Some(vec!["return { layout_info({&static_vtable, const_cast<void *>(static_cast<const void *>(this))}, o) };".into()]),
2568 ..Function::default()
2569 });
2570 }
2571
2572 let templates = root_sc.row_child_templates.as_ref().unwrap();
2573 let n = templates.len();
2574
2575 let mut body = String::from(
2579 "[[maybe_unused]] auto self = this;\n\
2580 if (child_index.has_value()) {\n\
2581 size_t index = *child_index;\n\
2582 size_t count = 0;\n",
2583 );
2584 for (i, entry) in templates.iter().enumerate() {
2585 let is_last = i + 1 == n;
2586 match entry {
2587 llr::RowChildTemplateInfo::Static { child_index } => {
2588 let child = &root_sc.grid_layout_children[*child_index];
2589 let layout_info_h_code = compile_expression(&child.layout_info_h.borrow(), ctx);
2590 let layout_info_v_code = compile_expression(&child.layout_info_v.borrow(), ctx);
2591 let advance = if is_last { String::new() } else { "count += 1;\n".to_owned() };
2592 write!(
2593 body,
2594 "if (count == index) {{\n\
2595 return {{ (o == slint::cbindgen_private::Orientation::Horizontal) ? ({layout_info_h_code}) : ({layout_info_v_code}) }};\n\
2596 }}\n\
2597 {advance}",
2598 )
2599 .unwrap();
2600 }
2601 llr::RowChildTemplateInfo::Repeated { repeater_index } => {
2602 let inner_rep_id = format!("repeater_{}", usize::from(*repeater_index));
2603 let advance =
2604 if is_last { String::new() } else { "count += inner_len;\n".to_owned() };
2605 write!(
2606 body,
2607 "{{\n\
2608 size_t inner_len = {inner_rep_id}.len();\n\
2609 if (index >= count && index - count < inner_len) {{\n\
2610 if (auto vrc = {inner_rep_id}.instance_at(index - count).lock()) {{\n\
2611 auto vref = vrc->borrow();\n\
2612 return {{ vref.vtable->layout_info(vref, o) }};\n\
2613 }}\n\
2614 }}\n\
2615 {advance}}}\n",
2616 )
2617 .unwrap();
2618 }
2619 }
2620 }
2621 body.push_str(
2622 "return { slint::cbindgen_private::LayoutInfo{ std::numeric_limits<float>::max(), 100.f, 0, 0, 0, 0 } };\n\
2625 }\n\
2626 return { layout_info({&static_vtable, const_cast<void *>(static_cast<const void *>(this))}, o) };",
2627 );
2628 Declaration::Function(Function {
2629 name: "layout_item_info".into(),
2630 signature: SIGNATURE.to_owned(),
2631 statements: Some(vec![body]),
2632 ..Function::default()
2633 })
2634}
2635
2636fn generate_flexbox_layout_item_info_decl(
2637 root_sc: &llr::SubComponent,
2638 ctx: &EvaluationContext,
2639) -> Declaration {
2640 const SIGNATURE: &str = "(slint::cbindgen_private::Orientation o, [[maybe_unused]] std::optional<size_t> child_index) const -> slint::cbindgen_private::FlexboxLayoutItemInfo";
2641
2642 let body = if let Some(expr) = &root_sc.flexbox_layout_item_info_for_repeated {
2643 let compiled = compile_expression(&expr.borrow(), ctx);
2644 format!(
2645 "[[maybe_unused]] auto self = this; \
2646 auto info = {compiled}; \
2647 info.constraint = layout_item_info(o, child_index).constraint; \
2648 return info;"
2649 )
2650 } else {
2651 "auto base = layout_item_info(o, child_index); \
2652 return { base.constraint, 0.0f, 0.0f, -1.0f, slint::cbindgen_private::FlexboxLayoutAlignSelf::Auto, 0 };"
2653 .to_owned()
2654 };
2655
2656 Declaration::Function(Function {
2657 name: "flexbox_layout_item_info".into(),
2658 signature: SIGNATURE.to_owned(),
2659 statements: Some(vec![body]),
2660 ..Function::default()
2661 })
2662}
2663
2664fn generate_grid_layout_input_decl(
2667 root_sc: &llr::SubComponent,
2668 ctx: &EvaluationContext,
2669) -> Option<Declaration> {
2670 let expr = root_sc.grid_layout_input_for_repeated.as_ref()?;
2671 let compiled_expr = compile_expression(&expr.borrow(), ctx);
2672 let statement =
2674 if compiled_expr.is_empty() || compiled_expr.ends_with(';') || compiled_expr.ends_with('}')
2675 {
2676 compiled_expr
2677 } else {
2678 format!("{compiled_expr};")
2679 };
2680
2681 let fn_body: Vec<String> = if llr::has_inner_repeaters(&root_sc.row_child_templates) {
2683 let templates = root_sc.row_child_templates.as_ref().unwrap();
2684 let static_count = llr::static_child_count(templates);
2685 let auto_val = i_slint_common::ROW_COL_AUTO;
2686 let mut fill_code = if static_count > 0 {
2691 format!(
2692 "std::array<slint::cbindgen_private::GridLayoutInputData, {static_count}> statics{{}};\n\
2693 {{\n\
2694 // Intentionally shadows the outer `result` so the compiled statement fills `statics`.\n\
2695 auto result = std::span<slint::cbindgen_private::GridLayoutInputData>{{statics.data(), statics.size()}};\n\
2696 {statement}\n\
2697 }}\n\
2698 size_t static_idx = 0;\n\
2699 size_t write_idx = 0;\n"
2700 )
2701 } else {
2702 String::from("size_t write_idx = 0;\n")
2703 };
2704 for entry in templates {
2705 match entry {
2706 llr::RowChildTemplateInfo::Static { .. } => {
2707 write!(
2708 fill_code,
2709 "if (write_idx < result.size()) {{\n\
2710 auto data = statics[static_idx];\n\
2711 data.new_row = (write_idx == 0) && new_row;\n\
2712 result[write_idx] = data;\n\
2713 }}\n\
2714 ++write_idx; ++static_idx;\n"
2715 )
2716 .unwrap();
2717 }
2718 llr::RowChildTemplateInfo::Repeated { repeater_index } => {
2719 let inner_rep_id = format!("repeater_{}", usize::from(*repeater_index));
2720 write!(
2721 fill_code,
2722 "this->{inner_rep_id}.ensure_updated(this);\n\
2723 {inner_rep_id}.for_each([&]([[maybe_unused]] const auto &) {{\n\
2724 if (write_idx < result.size()) {{\n\
2725 result[write_idx] = slint::cbindgen_private::GridLayoutInputData {{ (write_idx == 0) && new_row, {auto_val:.1}f, {auto_val:.1}f, 1.0f, 1.0f }};\n\
2726 }}\n\
2727 ++write_idx;\n\
2728 }});\n"
2729 )
2730 .unwrap();
2731 }
2732 }
2733 }
2734 write!(
2737 fill_code,
2738 "while (write_idx < result.size()) {{\n\
2739 result[write_idx] = slint::cbindgen_private::GridLayoutInputData {{ false, {auto_val:.1}f, {auto_val:.1}f, 1.0f, 1.0f }};\n\
2740 ++write_idx;\n\
2741 }}\n"
2742 )
2743 .unwrap();
2744 vec!["[[maybe_unused]] auto self = this;".into(), fill_code]
2745 } else {
2746 vec!["[[maybe_unused]] auto self = this;".into(), statement]
2747 };
2748
2749 Some(Declaration::Function(Function {
2750 name: "grid_layout_input_for_repeated".into(),
2751 signature: "([[maybe_unused]] bool new_row, [[maybe_unused]] std::span<slint::cbindgen_private::GridLayoutInputData> result) const -> void"
2752 .to_owned(),
2753 statements: Some(fn_body),
2754 ..Function::default()
2755 }))
2756}
2757
2758fn generate_repeated_component(
2759 repeated: &llr::RepeatedElement,
2760 unit: &llr::CompilationUnit,
2761 parent_ctx: ParentScope,
2762 model_data_type: Option<&Type>,
2763 file: &mut File,
2764 conditional_includes: &ConditionalIncludes,
2765) {
2766 let root_sc = &unit.sub_components[repeated.sub_tree.root];
2767 let repeater_id = ident(&root_sc.name);
2768 let mut repeater_struct = Struct { name: repeater_id.clone(), ..Default::default() };
2769 generate_item_tree(
2770 &mut repeater_struct,
2771 &repeated.sub_tree,
2772 unit,
2773 Some(&parent_ctx),
2774 false,
2775 repeater_id.clone(),
2776 Access::Public,
2777 file,
2778 conditional_includes,
2779 );
2780
2781 let ctx = EvaluationContext {
2782 compilation_unit: unit,
2783 current_scope: EvaluationScope::SubComponent(repeated.sub_tree.root, Some(&parent_ctx)),
2784 generator_state: CppGeneratorContext {
2785 global_access: "self->globals".into(),
2786 conditional_includes,
2787 },
2788 argument_types: &[],
2789 };
2790
2791 let access_prop = |idx: &llr::PropertyIdx| {
2792 access_member(
2793 &llr::LocalMemberReference { sub_component_path: Vec::new(), reference: (*idx).into() }
2794 .into(),
2795 &ctx,
2796 )
2797 .unwrap()
2798 };
2799 let index_prop = repeated.index_prop.iter().map(access_prop);
2800 let data_prop = repeated.data_prop.iter().map(access_prop);
2801
2802 if let Some(model_data_type) = model_data_type {
2803 let mut update_statements = vec!["[[maybe_unused]] auto self = this;".into()];
2804 update_statements.extend(index_prop.map(|prop| format!("{prop}.set(i);")));
2805 update_statements.extend(data_prop.map(|prop| format!("{prop}.set(data);")));
2806
2807 repeater_struct.members.push((
2808 Access::Public, Declaration::Function(Function {
2810 name: "update_data".into(),
2811 signature: format!(
2812 "([[maybe_unused]] int i, [[maybe_unused]] const {} &data) const -> void",
2813 model_data_type.cpp_type().unwrap()
2814 ),
2815 statements: Some(update_statements),
2816 ..Function::default()
2817 }),
2818 ));
2819 }
2820
2821 repeater_struct.members.push((
2822 Access::Public, Declaration::Function(Function {
2824 name: "init".into(),
2825 signature: "() -> void".into(),
2826 statements: Some(vec!["user_init();".into()]),
2827 ..Function::default()
2828 }),
2829 ));
2830
2831 if let Some(listview) = &repeated.listview {
2832 let p_y = access_member(&listview.prop_y, &ctx).unwrap();
2833 let p_height = access_member(&listview.prop_height, &ctx).unwrap();
2834
2835 repeater_struct.members.push((
2836 Access::Public, Declaration::Function(Function {
2838 name: "listview_layout".into(),
2839 signature: "(float *offset_y) const -> float".to_owned(),
2840 statements: Some(vec![
2841 "[[maybe_unused]] auto self = this;".into(),
2842 format!("{}.set(*offset_y);", p_y),
2843 format!("*offset_y += {}.get();", p_height),
2844 "return layout_info({&static_vtable, const_cast<void *>(static_cast<const void *>(this))}, slint::cbindgen_private::Orientation::Horizontal).min;".into(),
2845 ]),
2846 ..Function::default()
2847 }),
2848 ));
2849 } else {
2850 repeater_struct.members.push((
2851 Access::Public, generate_layout_item_info_decl(root_sc, &ctx),
2853 ));
2854 repeater_struct
2855 .members
2856 .push((Access::Public, generate_flexbox_layout_item_info_decl(root_sc, &ctx)));
2857 if let Some(decl) = generate_grid_layout_input_decl(root_sc, &ctx) {
2858 repeater_struct.members.push((Access::Public, decl));
2859 }
2860 }
2861
2862 if let Some(index_prop) = repeated.index_prop {
2863 let subtree_index_func = repeater_struct
2865 .members
2866 .iter_mut()
2867 .find(|(_, d)| matches!(d, Declaration::Function(f) if f.name == "subtree_index"));
2868
2869 if let Declaration::Function(f) = &mut subtree_index_func.unwrap().1 {
2870 let index = access_prop(&index_prop);
2871 f.statements = Some(vec![
2872 format!(
2873 "auto self = reinterpret_cast<const {}*>(component.instance);",
2874 repeater_id
2875 ),
2876 format!("return {index}.get();"),
2877 ]);
2878 }
2879 }
2880
2881 file.definitions.extend(repeater_struct.extract_definitions().collect::<Vec<_>>());
2882 file.declarations.push(Declaration::Struct(repeater_struct));
2883}
2884
2885fn generate_global(
2886 file: &mut File,
2887 conditional_includes: &ConditionalIncludes,
2888 global_idx: llr::GlobalIdx,
2889 global: &llr::GlobalComponent,
2890 root: &llr::CompilationUnit,
2891) {
2892 let mut global_struct = Struct { name: ident(&global.name), ..Default::default() };
2893
2894 for property in global.properties.iter() {
2895 let cpp_name = ident(&property.name);
2896 let ty =
2897 format_smolstr!("slint::private_api::Property<{}>", property.ty.cpp_type().unwrap());
2898 global_struct.members.push((
2899 Access::Public,
2903 Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2904 ));
2905 }
2906 for callback in global.callbacks.iter().filter(|p| p.use_count.get() > 0) {
2907 let cpp_name = ident(&callback.name);
2908 let param_types = callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
2909 let ty = format_smolstr!(
2910 "slint::private_api::Callback<{}({})>",
2911 callback.ret_ty.cpp_type().unwrap(),
2912 param_types.join(", ")
2913 );
2914 global_struct.members.push((
2915 Access::Public,
2919 Declaration::Var(Var { ty, name: cpp_name, ..Default::default() }),
2920 ));
2921 }
2922
2923 let mut init = vec!["(void)this->globals;".into()];
2924 let ctx = EvaluationContext::new_global(
2925 root,
2926 global_idx,
2927 CppGeneratorContext { global_access: "this->globals".into(), conditional_includes },
2928 );
2929
2930 for (property_index, expression) in &global.init_values {
2931 handle_property_init(
2932 &llr::LocalMemberReference::from(property_index.clone()).into(),
2933 expression,
2934 &mut init,
2935 &ctx,
2936 )
2937 }
2938
2939 for (i, _) in global.change_callbacks.iter() {
2940 global_struct.members.push((
2941 Access::Private,
2942 Declaration::Var(Var {
2943 ty: "slint::private_api::ChangeTracker".into(),
2944 name: format_smolstr!("change_tracker{}", usize::from(*i)),
2945 ..Default::default()
2946 }),
2947 ));
2948 }
2949
2950 init.extend(global.change_callbacks.iter().map(|(p, e)| {
2951 let code = compile_expression(&e.borrow(), &ctx);
2952 let prop = access_member(&llr::LocalMemberReference::from(*p).into(), &ctx);
2953 prop.then(|prop| {
2954 format!("this->change_tracker{}.init(this, [this]([[maybe_unused]] auto self) {{ return {prop}.get(); }}, [this]([[maybe_unused]] auto self, auto) {{ {code}; }});", usize::from(*p))
2955 })
2956 }));
2957
2958 global_struct.members.push((
2959 Access::Public,
2960 Declaration::Function(Function {
2961 name: ident(&global.name),
2962 signature: "(const class SharedGlobals *globals)".into(),
2963 is_constructor_or_destructor: true,
2964 statements: Some(Vec::new()),
2965 constructor_member_initializers: vec!["globals(globals)".into()],
2966 ..Default::default()
2967 }),
2968 ));
2969 global_struct.members.push((
2970 Access::Private,
2971 Declaration::Function(Function {
2972 name: ident("init"),
2973 signature: "() -> void".into(),
2974 statements: Some(init),
2975 ..Default::default()
2976 }),
2977 ));
2978 global_struct.members.push((
2979 Access::Private,
2980 Declaration::Var(Var {
2981 ty: "const class SharedGlobals*".into(),
2982 name: "globals".into(),
2983 ..Default::default()
2984 }),
2985 ));
2986 global_struct.friends.push(SmolStr::new_static(SHARED_GLOBAL_CLASS));
2987
2988 generate_public_api_for_properties(
2989 &mut global_struct.members,
2990 &global.public_properties,
2991 &global.private_properties,
2992 &ctx,
2993 );
2994 global_struct
2995 .members
2996 .extend(generate_functions(global.functions.as_ref(), &ctx).map(|x| (Access::Public, x)));
2997
2998 file.definitions.extend(global_struct.extract_definitions().collect::<Vec<_>>());
2999 file.declarations.push(Declaration::Struct(global_struct));
3000}
3001
3002fn generate_global_builtin(
3003 file: &mut File,
3004 conditional_includes: &ConditionalIncludes,
3005 global_idx: llr::GlobalIdx,
3006 global: &llr::GlobalComponent,
3007 root: &llr::CompilationUnit,
3008) {
3009 let mut global_struct = Struct { name: ident(&global.name), ..Default::default() };
3010 let ctx = EvaluationContext::new_global(
3011 root,
3012 global_idx,
3013 CppGeneratorContext {
3014 global_access: "\n#error binding in builtin global\n".into(),
3015 conditional_includes,
3016 },
3017 );
3018
3019 global_struct.members.push((
3020 Access::Public,
3021 Declaration::Function(Function {
3022 name: ident(&global.name),
3023 signature: format!(
3024 "(std::shared_ptr<slint::cbindgen_private::{}> builtin)",
3025 ident(&global.name)
3026 ),
3027 is_constructor_or_destructor: true,
3028 statements: Some(Vec::new()),
3029 constructor_member_initializers: vec!["builtin(std::move(builtin))".into()],
3030 ..Default::default()
3031 }),
3032 ));
3033 global_struct.members.push((
3034 Access::Private,
3035 Declaration::Var(Var {
3036 ty: format_smolstr!(
3037 "std::shared_ptr<slint::cbindgen_private::{}>",
3038 ident(&global.name)
3039 ),
3040 name: "builtin".into(),
3041 ..Default::default()
3042 }),
3043 ));
3044 global_struct.friends.push(SmolStr::new_static(SHARED_GLOBAL_CLASS));
3045
3046 generate_public_api_for_properties(
3047 &mut global_struct.members,
3048 &global.public_properties,
3049 &global.private_properties,
3050 &ctx,
3051 );
3052 file.definitions.extend(global_struct.extract_definitions().collect::<Vec<_>>());
3053 file.declarations.push(Declaration::Struct(global_struct));
3054}
3055
3056fn generate_functions<'a>(
3057 functions: &'a [llr::Function],
3058 ctx: &'a EvaluationContext<'_>,
3059) -> impl Iterator<Item = Declaration> + 'a {
3060 functions.iter().map(|f| {
3061 let mut ctx2 = ctx.clone();
3062 ctx2.argument_types = &f.args;
3063 let ret = if f.ret_ty != Type::Void { "return " } else { "" };
3064 let body = vec![
3065 "[[maybe_unused]] auto self = this;".into(),
3066 format!("{ret}{};", compile_expression(&f.code, &ctx2)),
3067 ];
3068 Declaration::Function(Function {
3069 name: concatenate_ident(&format_smolstr!("fn_{}", f.name)),
3070 signature: format!(
3071 "({}) const -> {}",
3072 f.args
3073 .iter()
3074 .enumerate()
3075 .map(|(i, ty)| format!("[[maybe_unused]] {} arg_{}", ty.cpp_type().unwrap(), i))
3076 .join(", "),
3077 f.ret_ty.cpp_type().unwrap()
3078 ),
3079 statements: Some(body),
3080 ..Default::default()
3081 })
3082 })
3083}
3084
3085fn generate_public_api_for_properties(
3086 declarations: &mut Vec<(Access, Declaration)>,
3087 public_properties: &llr::PublicProperties,
3088 private_properties: &llr::PrivateProperties,
3089 ctx: &EvaluationContext,
3090) {
3091 for p in public_properties {
3092 let prop_ident = concatenate_ident(&p.name);
3093
3094 let access = access_member(&p.prop, ctx).unwrap();
3095
3096 if let Type::Callback(callback) = &p.ty {
3097 let param_types =
3098 callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
3099 let callback_emitter = vec![
3100 "slint::private_api::assert_main_thread();".into(),
3101 "[[maybe_unused]] auto self = this;".into(),
3102 format!(
3103 "return {}.call({});",
3104 access,
3105 (0..callback.args.len()).map(|i| format!("arg_{i}")).join(", ")
3106 ),
3107 ];
3108 declarations.push((
3109 Access::Public,
3110 Declaration::Function(Function {
3111 name: format_smolstr!("invoke_{prop_ident}"),
3112 signature: format!(
3113 "({}) const -> {}",
3114 param_types
3115 .iter()
3116 .enumerate()
3117 .map(|(i, ty)| format!("{ty} arg_{i}"))
3118 .join(", "),
3119 callback.return_type.cpp_type().unwrap()
3120 ),
3121 statements: Some(callback_emitter),
3122 ..Default::default()
3123 }),
3124 ));
3125 declarations.push((
3126 Access::Public,
3127 Declaration::Function(Function {
3128 name: format_smolstr!("on_{}", concatenate_ident(&p.name)),
3129 template_parameters: Some(format!(
3130 "std::invocable<{}> Functor",
3131 param_types.join(", "),
3132 )),
3133 signature: "(Functor && callback_handler) const".into(),
3134 statements: Some(vec![
3135 "slint::private_api::assert_main_thread();".into(),
3136 "[[maybe_unused]] auto self = this;".into(),
3137 format!("{}.set_handler(std::forward<Functor>(callback_handler));", access),
3138 ]),
3139 ..Default::default()
3140 }),
3141 ));
3142 } else if let Type::Function(function) = &p.ty {
3143 let param_types =
3144 function.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
3145 let ret = function.return_type.cpp_type().unwrap();
3146 let call_code = vec![
3147 "[[maybe_unused]] auto self = this;".into(),
3148 format!(
3149 "{}{access}({});",
3150 if function.return_type == Type::Void { "" } else { "return " },
3151 (0..function.args.len()).map(|i| format!("arg_{i}")).join(", ")
3152 ),
3153 ];
3154 declarations.push((
3155 Access::Public,
3156 Declaration::Function(Function {
3157 name: format_smolstr!("invoke_{}", concatenate_ident(&p.name)),
3158 signature: format!(
3159 "({}) const -> {ret}",
3160 param_types
3161 .iter()
3162 .enumerate()
3163 .map(|(i, ty)| format!("{ty} arg_{i}"))
3164 .join(", "),
3165 ),
3166 statements: Some(call_code),
3167 ..Default::default()
3168 }),
3169 ));
3170 } else {
3171 let cpp_property_type = p.ty.cpp_type().expect("Invalid type in public properties");
3172 let prop_getter: Vec<String> = vec![
3173 "slint::private_api::assert_main_thread();".into(),
3174 "[[maybe_unused]] auto self = this;".into(),
3175 format!("return {}.get();", access),
3176 ];
3177 declarations.push((
3178 Access::Public,
3179 Declaration::Function(Function {
3180 name: format_smolstr!("get_{}", &prop_ident),
3181 signature: format!("() const -> {}", &cpp_property_type),
3182 statements: Some(prop_getter),
3183 ..Default::default()
3184 }),
3185 ));
3186
3187 if !p.read_only {
3188 let prop_setter: Vec<String> = vec![
3189 "slint::private_api::assert_main_thread();".into(),
3190 "[[maybe_unused]] auto self = this;".into(),
3191 property_set_value_code(&p.prop, "value", ctx) + ";",
3192 ];
3193 declarations.push((
3194 Access::Public,
3195 Declaration::Function(Function {
3196 name: format_smolstr!("set_{}", &prop_ident),
3197 signature: format!("(const {} &value) const -> void", &cpp_property_type),
3198 statements: Some(prop_setter),
3199 ..Default::default()
3200 }),
3201 ));
3202 } else {
3203 declarations.push((
3204 Access::Private,
3205 Declaration::Function(Function {
3206 name: format_smolstr!("set_{}", &prop_ident),
3207 signature: format!(
3208 "(const {cpp_property_type} &) const = delete /* property '{}' is declared as 'out' (read-only). Declare it as 'in' or 'in-out' to enable the setter */", p.name
3209 ),
3210 ..Default::default()
3211 }),
3212 ));
3213 }
3214 }
3215 }
3216
3217 for (name, ty) in private_properties {
3218 let prop_ident = concatenate_ident(name);
3219
3220 if let Type::Function(function) = &ty {
3221 let param_types = function.args.iter().map(|t| t.cpp_type().unwrap()).join(", ");
3222 declarations.push((
3223 Access::Private,
3224 Declaration::Function(Function {
3225 name: format_smolstr!("invoke_{prop_ident}"),
3226 signature: format!(
3227 "({param_types}) const = delete /* the function '{name}' is declared as private. Declare it as 'public' */",
3228 ),
3229 ..Default::default()
3230 }),
3231 ));
3232 } else {
3233 declarations.push((
3234 Access::Private,
3235 Declaration::Function(Function {
3236 name: format_smolstr!("get_{prop_ident}"),
3237 signature: format!(
3238 "() const = delete /* the property '{name}' is declared as private. Declare it as 'in', 'out', or 'in-out' to make it public */",
3239 ),
3240 ..Default::default()
3241 }),
3242 ));
3243 declarations.push((
3244 Access::Private,
3245 Declaration::Function(Function {
3246 name: format_smolstr!("set_{}", &prop_ident),
3247 signature: format!(
3248 "(const auto &) const = delete /* property '{name}' is declared as private. Declare it as 'in' or 'in-out' to make it public */",
3249 ),
3250 ..Default::default()
3251 }),
3252 ));
3253 }
3254 }
3255}
3256
3257fn follow_sub_component_path<'a>(
3258 compilation_unit: &'a llr::CompilationUnit,
3259 root: llr::SubComponentIdx,
3260 sub_component_path: &[llr::SubComponentInstanceIdx],
3261) -> (String, &'a llr::SubComponent) {
3262 let mut compo_path = String::new();
3263 let mut sub_component = &compilation_unit.sub_components[root];
3264 for i in sub_component_path {
3265 let sub_component_name = ident(&sub_component.sub_components[*i].name);
3266 write!(compo_path, "{sub_component_name}.").unwrap();
3267 sub_component = &compilation_unit.sub_components[sub_component.sub_components[*i].ty];
3268 }
3269 (compo_path, sub_component)
3270}
3271
3272fn access_window_field(ctx: &EvaluationContext) -> String {
3273 format!("{}->window().window_handle()", ctx.generator_state.global_access)
3274}
3275
3276fn access_member(reference: &llr::MemberReference, ctx: &EvaluationContext) -> MemberAccess {
3278 match reference {
3279 llr::MemberReference::Relative { parent_level, local_reference } => {
3280 let mut path = MemberAccess::Direct("self".to_string());
3281 for _ in 0..*parent_level {
3282 path = path.and_then(|x| format!("{x}->parent.lock()"));
3283 }
3284 if let Some(sub_component) = ctx.parent_sub_component_idx(*parent_level) {
3285 let (compo_path, sub_component) = follow_sub_component_path(
3286 ctx.compilation_unit,
3287 sub_component,
3288 &local_reference.sub_component_path,
3289 );
3290 match &local_reference.reference {
3291 llr::LocalMemberIndex::Property(property_index) => {
3292 let property_name = ident(&sub_component.properties[*property_index].name);
3293 path.with_member(format!("->{compo_path}{property_name}"))
3294 }
3295 llr::LocalMemberIndex::Callback(callback_index) => {
3296 let callback_name = ident(&sub_component.callbacks[*callback_index].name);
3297 path.with_member(format!("->{compo_path}{callback_name}"))
3298 }
3299 llr::LocalMemberIndex::Function(function_index) => {
3300 let function_name = ident(&sub_component.functions[*function_index].name);
3301 path.with_member(format!("->{compo_path}fn_{function_name}"))
3302 }
3303 llr::LocalMemberIndex::Native { item_index, prop_name } => {
3304 let item_name = ident(&sub_component.items[*item_index].name);
3305 if prop_name.is_empty()
3306 || matches!(
3307 sub_component.items[*item_index].ty.lookup_property(prop_name),
3308 Some(Type::Function { .. })
3309 )
3310 {
3311 path.with_member(format!("->{compo_path}{item_name}"))
3314 } else {
3315 let property_name = ident(prop_name);
3316 path.with_member(format!("->{compo_path}{item_name}.{property_name}"))
3317 }
3318 }
3319 }
3320 } else if let Some(current_global) = ctx.current_global() {
3321 match &local_reference.reference {
3322 llr::LocalMemberIndex::Property(property_index) => {
3323 let property_name = ident(¤t_global.properties[*property_index].name);
3324 MemberAccess::Direct(format!("this->{property_name}"))
3325 }
3326 llr::LocalMemberIndex::Function(function_index) => {
3327 let function_name = ident(¤t_global.functions[*function_index].name);
3328 MemberAccess::Direct(format!("this->fn_{function_name}"))
3329 }
3330 llr::LocalMemberIndex::Callback(callback_index) => {
3331 let callback_name = ident(¤t_global.callbacks[*callback_index].name);
3332 MemberAccess::Direct(format!("this->{callback_name}"))
3333 }
3334 _ => unreachable!(),
3335 }
3336 } else {
3337 unreachable!()
3338 }
3339 }
3340 llr::MemberReference::Global { global_index, member } => {
3341 let global = &ctx.compilation_unit.globals[*global_index];
3342 let name = match member {
3343 llr::LocalMemberIndex::Property(property_index) => ident(
3344 &ctx.compilation_unit.globals[*global_index].properties[*property_index].name,
3345 ),
3346 llr::LocalMemberIndex::Callback(callback_index) => ident(
3347 &ctx.compilation_unit.globals[*global_index].callbacks[*callback_index].name,
3348 ),
3349 llr::LocalMemberIndex::Function(function_index) => ident(&format!(
3350 "fn_{}",
3351 &ctx.compilation_unit.globals[*global_index].functions[*function_index].name,
3352 )),
3353 _ => unreachable!(),
3354 };
3355 if matches!(ctx.current_scope, EvaluationScope::Global(i) if i == *global_index) {
3356 if global.is_builtin {
3357 MemberAccess::Direct(format!("builtin->{name}"))
3358 } else {
3359 MemberAccess::Direct(format!("this->{name}"))
3360 }
3361 } else {
3362 let global_access = &ctx.generator_state.global_access;
3363 let global_id = format!("global_{}", concatenate_ident(&global.name));
3364 MemberAccess::Direct(format!("{global_access}->{global_id}->{name}"))
3365 }
3366 }
3367 }
3368}
3369
3370fn access_local_member(reference: &llr::LocalMemberReference, ctx: &EvaluationContext) -> String {
3371 access_member(&reference.clone().into(), ctx).unwrap()
3372}
3373
3374#[derive(Clone)]
3378enum MemberAccess {
3379 Direct(String),
3381 Option(String),
3383 OptionWithMember(String, String),
3387}
3388
3389impl MemberAccess {
3390 fn then(&self, f: impl FnOnce(&str) -> String) -> String {
3392 match self {
3393 MemberAccess::Direct(t) => f(t),
3394 MemberAccess::Option(t) => {
3395 format!("slint::private_api::optional_then({t}, [&](auto&&x) {{ {}; }})", f("x"))
3396 }
3397 MemberAccess::OptionWithMember(t, m) => {
3398 format!(
3399 "slint::private_api::optional_then({t}, [&](auto&&x) {{ {}; }})",
3400 f(&format!("x{}", m))
3401 )
3402 }
3403 }
3404 }
3405
3406 fn map_or_default(&self, f: impl FnOnce(&str) -> String) -> String {
3407 match self {
3408 MemberAccess::Direct(t) => f(t),
3409 MemberAccess::Option(t) => {
3410 format!(
3411 "slint::private_api::optional_or_default(slint::private_api::optional_transform({t}, [&](auto&&x) {{ return {}; }}))",
3412 f("x")
3413 )
3414 }
3415 MemberAccess::OptionWithMember(t, m) => {
3416 format!(
3417 "slint::private_api::optional_or_default(slint::private_api::optional_transform({t}, [&](auto&&x) {{ return {}; }}))",
3418 f(&format!("x{}", m))
3419 )
3420 }
3421 }
3422 }
3423
3424 fn and_then(&self, f: impl Fn(&str) -> String) -> MemberAccess {
3425 match self {
3426 MemberAccess::Direct(t) => MemberAccess::Option(f(t)),
3427 MemberAccess::Option(t) => MemberAccess::Option(format!(
3428 "slint::private_api::optional_and_then({t}, [&](auto&&x) {{ return {}; }})",
3429 f("x")
3430 )),
3431 MemberAccess::OptionWithMember(t, m) => MemberAccess::Option(format!(
3432 "slint::private_api::optional_and_then({t}, [&](auto&&x) {{ return {}; }})",
3433 f(&format!("x{}", m))
3434 )),
3435 }
3436 }
3437
3438 fn get_property(self) -> String {
3439 self.map_or_default(|x| format!("{x}.get()"))
3440 }
3441
3442 #[track_caller]
3444 fn unwrap(self) -> String {
3445 match self {
3446 MemberAccess::Direct(t) => t,
3447 _ => panic!("not a local property?"),
3448 }
3449 }
3450
3451 fn with_member(self, member: String) -> MemberAccess {
3452 match self {
3453 MemberAccess::Direct(t) => MemberAccess::Direct(format!("{t}{member}")),
3454 MemberAccess::Option(t) => MemberAccess::OptionWithMember(t, member),
3455 MemberAccess::OptionWithMember(t, m) => {
3456 MemberAccess::OptionWithMember(t, format!("{m}{member}"))
3457 }
3458 }
3459 }
3460}
3461
3462fn native_prop_info<'a, 'b>(
3466 item_ref: &'b llr::MemberReference,
3467 ctx: &'a EvaluationContext,
3468) -> (&'a NativeClass, &'b str) {
3469 let llr::MemberReference::Relative { parent_level, local_reference } = item_ref else {
3470 unreachable!()
3471 };
3472 let llr::LocalMemberIndex::Native { item_index, prop_name } = &local_reference.reference else {
3473 unreachable!()
3474 };
3475
3476 let (_, sub_component) = follow_sub_component_path(
3477 ctx.compilation_unit,
3478 ctx.parent_sub_component_idx(*parent_level).unwrap(),
3479 &local_reference.sub_component_path,
3480 );
3481 (&sub_component.items[*item_index].ty, prop_name)
3482}
3483
3484fn shared_string_literal(string: &str) -> String {
3485 format!(r#"slint::SharedString(u8"{}")"#, escape_string(string))
3486}
3487
3488fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String {
3489 use llr::Expression;
3490 match expr {
3491 Expression::StringLiteral(s) => shared_string_literal(s),
3492 Expression::NumberLiteral(num) => {
3493 if !num.is_finite() {
3494 "0.0".to_string()
3496 } else if num.abs() > 1_000_000_000. {
3497 format!("{num:+e}")
3499 } else {
3500 num.to_string()
3501 }
3502 }
3503 Expression::BoolLiteral(b) => b.to_string(),
3504 Expression::KeysLiteral(ks) => {
3505 format!(
3506 "[&](const slint::SharedString &key, bool alt, bool control, bool shift, bool meta, bool ignoreShift, bool ignoreAlt) {{
3507 slint::Keys out;
3508 slint::private_api::make_keys(out, key, alt, control, shift, meta, ignoreShift, ignoreAlt);
3509 return out;
3510 }}({}, {}, {}, {}, {}, {}, {})",
3511 shared_string_literal(&ks.key),
3512 ks.modifiers.alt,
3513 ks.modifiers.control,
3514 ks.modifiers.shift,
3515 ks.modifiers.meta,
3516 ks.ignore_shift,
3517 ks.ignore_alt,
3518 )
3519 }
3520 Expression::PropertyReference(nr) => access_member(nr, ctx).get_property(),
3521 Expression::BuiltinFunctionCall { function, arguments } => {
3522 compile_builtin_function_call(function.clone(), arguments, ctx)
3523 }
3524 Expression::CallBackCall { callback, arguments } => {
3525 let f = access_member(callback, ctx);
3526 let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
3527 if expr.ty(ctx) == Type::Void {
3528 f.then(|f| format!("{f}.call({})", a.join(",")))
3529 } else {
3530 f.map_or_default(|f| format!("{f}.call({})", a.join(",")))
3531 }
3532 }
3533 Expression::FunctionCall { function, arguments } => {
3534 let f = access_member(function, ctx);
3535 let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
3536 if expr.ty(ctx) == Type::Void {
3537 f.then(|f| format!("{}({})", f, a.join(",")))
3538 } else {
3539 f.map_or_default(|f| format!("{}({})", f, a.join(",")))
3540 }
3541 }
3542 Expression::ItemMemberFunctionCall { function } => {
3543 let item = access_member(function, ctx);
3544 let item_rc = access_item_rc(function, ctx);
3545 let window = access_window_field(ctx);
3546 let (native, name) = native_prop_info(function, ctx);
3547 let function_name = format!(
3548 "slint_{}_{}",
3549 native.class_name.to_lowercase(),
3550 ident(name).to_lowercase()
3551 );
3552 if expr.ty(ctx) == Type::Void {
3553 item.then(|item| {
3554 format!("{function_name}(&{item}, &{window}.handle(), &{item_rc})")
3555 })
3556 } else {
3557 item.map_or_default(|item| {
3558 format!("{function_name}(&{item}, &{window}.handle(), &{item_rc})")
3559 })
3560 }
3561 }
3562 Expression::ExtraBuiltinFunctionCall { function, arguments, return_ty: _ } => {
3563 let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
3564 format!("slint::private_api::{}({})", ident(function), a.join(","))
3565 }
3566 Expression::FunctionParameterReference { index, .. } => format!("arg_{index}"),
3567 Expression::StoreLocalVariable { name, value } => {
3568 format!("[[maybe_unused]] auto {} = {};", ident(name), compile_expression(value, ctx))
3569 }
3570 Expression::ReadLocalVariable { name, .. } => ident(name).to_string(),
3571 Expression::StructFieldAccess { base, name } => match base.ty(ctx) {
3572 Type::Struct(s) => struct_field_access(compile_expression(base, ctx), &s, name),
3573 _ => panic!("Expression::ObjectAccess's base expression is not an Object type"),
3574 },
3575 Expression::ArrayIndex { array, index } => {
3576 format!(
3577 "slint::private_api::access_array_index({}, {})",
3578 compile_expression(array, ctx),
3579 compile_expression(index, ctx)
3580 )
3581 }
3582 Expression::Cast { from, to } => {
3583 let f = compile_expression(from, ctx);
3584 match (from.ty(ctx), to) {
3585 (Type::Float32, Type::Int32) => {
3586 format!("static_cast<int>({f})")
3587 }
3588 (from, Type::String) if from.as_unit_product().is_some() => {
3589 format!("slint::SharedString::from_number({f})")
3590 }
3591 (Type::Float32, Type::Model) | (Type::Int32, Type::Model) => {
3592 format!(
3593 "std::make_shared<slint::private_api::UIntModel>(std::max<int>(0, {f}))"
3594 )
3595 }
3596 (Type::Array(_), Type::Model) => f,
3597 (Type::Float32, Type::Color) => {
3598 format!("slint::Color::from_argb_encoded({f})")
3599 }
3600 (Type::Color, Type::Brush) => {
3601 format!("slint::Brush({f})")
3602 }
3603 (Type::Brush, Type::Color) => {
3604 format!("{f}.color()")
3605 }
3606 (Type::Struct(lhs), Type::Struct(rhs)) => {
3607 debug_assert_eq!(
3608 lhs.fields, rhs.fields,
3609 "cast of struct with deferent fields should be handled before llr"
3610 );
3611 match (&lhs.name, &rhs.name) {
3612 (StructName::None, targetstruct) if targetstruct.is_some() => {
3613 format!(
3615 "[&](const auto &o){{ {struct_name} s; {fields} return s; }}({obj})",
3616 struct_name = to.cpp_type().unwrap(),
3617 fields = lhs
3618 .fields
3619 .keys()
3620 .enumerate()
3621 .map(|(i, n)| format!("s.{} = std::get<{}>(o); ", ident(n), i))
3622 .join(""),
3623 obj = f,
3624 )
3625 }
3626 (sourcestruct, StructName::None) if sourcestruct.is_some() => {
3627 format!(
3629 "[&](const auto &o){{ return std::make_tuple({}); }}({f})",
3630 rhs.fields.keys().map(|n| format!("o.{}", ident(n))).join(", ")
3631 )
3632 }
3633 _ => f,
3634 }
3635 }
3636 (Type::Array(..), Type::PathData)
3637 if matches!(
3638 from.as_ref(),
3639 Expression::Array { element_ty: Type::Struct { .. }, .. }
3640 ) =>
3641 {
3642 let path_elements = match from.as_ref() {
3643 Expression::Array { element_ty: _, values, output: _ } => {
3644 values.iter().map(|path_elem_expr| {
3645 let (field_count, qualified_elem_type_name) =
3646 match path_elem_expr.ty(ctx) {
3647 Type::Struct(s) if s.name.is_some() => {
3648 (s.fields.len(), s.name.cpp_type().unwrap().clone())
3649 }
3650 _ => unreachable!(),
3651 };
3652 let elem_type_name = qualified_elem_type_name
3654 .split("::")
3655 .last()
3656 .unwrap()
3657 .strip_prefix("Path")
3658 .unwrap();
3659 let elem_init = if field_count > 0 {
3660 compile_expression(path_elem_expr, ctx)
3661 } else {
3662 String::new()
3663 };
3664 format!(
3665 "slint::private_api::PathElement::{elem_type_name}({elem_init})"
3666 )
3667 })
3668 }
3669 _ => {
3670 unreachable!()
3671 }
3672 }
3673 .collect::<Vec<_>>();
3674 if !path_elements.is_empty() {
3675 format!(
3676 r#"[&](){{
3677 slint::private_api::PathElement elements[{}] = {{
3678 {}
3679 }};
3680 return slint::private_api::PathData(&elements[0], std::size(elements));
3681 }}()"#,
3682 path_elements.len(),
3683 path_elements.join(",")
3684 )
3685 } else {
3686 "slint::private_api::PathData()".into()
3687 }
3688 }
3689 (Type::Struct { .. }, Type::PathData)
3690 if matches!(from.as_ref(), Expression::Struct { .. }) =>
3691 {
3692 let (events, points) = match from.as_ref() {
3693 Expression::Struct { ty: _, values } => (
3694 compile_expression(&values["events"], ctx),
3695 compile_expression(&values["points"], ctx),
3696 ),
3697 _ => {
3698 unreachable!()
3699 }
3700 };
3701 format!(
3702 r#"[&](auto events, auto points){{
3703 return slint::private_api::PathData(events.ptr, events.len, points.ptr, points.len);
3704 }}({events}, {points})"#
3705 )
3706 }
3707 (Type::Enumeration(e), Type::String) => {
3708 let mut cases = e.values.iter().enumerate().map(|(idx, v)| {
3709 let c = compile_expression(
3710 &Expression::EnumerationValue(EnumerationValue {
3711 value: idx,
3712 enumeration: e.clone(),
3713 }),
3714 ctx,
3715 );
3716 format!("case {c}: return {v:?};")
3717 });
3718 format!(
3719 "[&]() -> slint::SharedString {{ switch ({f}) {{ {} default: return {{}}; }} }}()",
3720 cases.join(" ")
3721 )
3722 }
3723 _ => f,
3724 }
3725 }
3726 Expression::CodeBlock(sub) => match sub.len() {
3727 0 => String::new(),
3728 1 => compile_expression(&sub[0], ctx),
3729 len => {
3730 let mut x = sub.iter().enumerate().map(|(i, e)| {
3731 if i == len - 1 {
3732 return_compile_expression(e, ctx, None) + ";"
3733 } else {
3734 compile_expression(e, ctx)
3735 }
3736 });
3737 format!("[&]{{ {} }}()", x.join(";"))
3738 }
3739 },
3740 Expression::PropertyAssignment { property, value } => {
3741 let value = compile_expression(value, ctx);
3742 property_set_value_code(property, &value, ctx)
3743 }
3744 Expression::ModelDataAssignment { level, value } => {
3745 let value = compile_expression(value, ctx);
3746 let mut path = "self".to_string();
3747 let EvaluationScope::SubComponent(mut sc, mut par) = ctx.current_scope else {
3748 unreachable!()
3749 };
3750 let mut repeater_index = None;
3751 for _ in 0..=*level {
3752 let x = par.unwrap();
3753 par = x.parent;
3754 repeater_index = x.repeater_index;
3755 sc = x.sub_component;
3756 write!(path, "->parent.lock().value()").unwrap();
3757 }
3758 let repeater_index = repeater_index.unwrap();
3759 let local_reference = ctx.compilation_unit.sub_components[sc].repeated[repeater_index]
3760 .index_prop
3761 .unwrap()
3762 .into();
3763 let index_prop =
3764 llr::MemberReference::Relative { parent_level: *level, local_reference };
3765 let index_access = access_member(&index_prop, ctx).get_property();
3766 write!(path, "->repeater_{}", usize::from(repeater_index)).unwrap();
3767 format!("{path}.model_set_row_data({index_access}, {value})")
3768 }
3769 Expression::ArrayIndexAssignment { array, index, value } => {
3770 debug_assert!(matches!(array.ty(ctx), Type::Array(_)));
3771 let base_e = compile_expression(array, ctx);
3772 let index_e = compile_expression(index, ctx);
3773 let value_e = compile_expression(value, ctx);
3774 format!(
3775 "[&](auto index, const auto &base) {{ if (index >= 0. && std::size_t(index) < base->row_count()) base->set_row_data(index, {value_e}); }}({index_e}, {base_e})"
3776 )
3777 }
3778 Expression::SliceIndexAssignment { slice_name, index, value } => {
3779 let value_e = compile_expression(value, ctx);
3780 format!("{slice_name}[{index}] = {value_e}")
3781 }
3782 Expression::BinaryExpression { lhs, rhs, op } => {
3783 let lhs_str = compile_expression(lhs, ctx);
3784 let rhs_str = compile_expression(rhs, ctx);
3785
3786 let lhs_ty = lhs.ty(ctx);
3787
3788 if lhs_ty.as_unit_product().is_some() && (*op == '=' || *op == '!') {
3789 let op = if *op == '=' { "<" } else { ">=" };
3790 format!(
3791 "(std::abs(float({lhs_str} - {rhs_str})) {op} std::numeric_limits<float>::epsilon())"
3792 )
3793 } else {
3794 let mut buffer = [0; 3];
3795 format!(
3796 "({lhs_str} {op} {rhs_str})",
3797 op = match op {
3798 '=' => "==",
3799 '!' => "!=",
3800 '≤' => "<=",
3801 '≥' => ">=",
3802 '&' => "&&",
3803 '|' => "||",
3804 '/' => "/(float)",
3805 '-' => "-(float)", _ => op.encode_utf8(&mut buffer),
3807 },
3808 )
3809 }
3810 }
3811 Expression::UnaryOp { sub, op } => {
3812 format!("({op} {sub})", sub = compile_expression(sub, ctx), op = op,)
3813 }
3814 Expression::ImageReference { resource_ref, nine_slice } => {
3815 let image = match resource_ref {
3816 crate::expression_tree::ImageReference::None => r#"slint::Image()"#.to_string(),
3817 crate::expression_tree::ImageReference::AbsolutePath(path) => format!(
3818 r#"slint::Image::load_from_path(slint::SharedString(u8"{}"))"#,
3819 escape_string(path.as_str())
3820 ),
3821 crate::expression_tree::ImageReference::EmbeddedData { resource_id, extension } => {
3822 let symbol = format!("slint_embedded_resource_{resource_id}");
3823 format!(
3824 r#"slint::private_api::load_image_from_embedded_data({symbol}, "{}")"#,
3825 escape_string(extension)
3826 )
3827 }
3828 crate::expression_tree::ImageReference::EmbeddedTexture { resource_id } => {
3829 format!(
3830 "slint::private_api::image_from_embedded_textures(&slint_embedded_resource_{resource_id})"
3831 )
3832 }
3833 };
3834 match &nine_slice {
3835 Some([a, b, c, d]) => {
3836 format!(
3837 "([&] {{ auto image = {image}; image.set_nine_slice_edges({a}, {b}, {c}, {d}); return image; }})()"
3838 )
3839 }
3840 None => image,
3841 }
3842 }
3843 Expression::Condition { condition, true_expr, false_expr } => {
3844 let ty = expr.ty(ctx);
3845 let cond_code = compile_expression(condition, ctx);
3846 let cond_code = remove_parentheses(&cond_code);
3847 let true_code = compile_expression(true_expr, ctx);
3848 let false_code = compile_expression(false_expr, ctx);
3849 if ty == Type::Void {
3850 format!("if ({cond_code}) {{ {true_code}; }} else {{ {false_code}; }}")
3851 } else {
3852 format!("({cond_code} ? {true_code} : {false_code})")
3853 }
3854 }
3855 Expression::Array { element_ty, values, output } => {
3856 let ty = element_ty.cpp_type().unwrap();
3857 let mut val = values
3858 .iter()
3859 .map(|e| format!("{ty} ( {expr} )", expr = compile_expression(e, ctx), ty = ty));
3860 match output {
3861 llr::ArrayOutput::Model => format!(
3862 "std::make_shared<slint::private_api::ArrayModel<{count},{ty}>>({val})",
3863 count = values.len(),
3864 ty = ty,
3865 val = val.join(", ")
3866 ),
3867 llr::ArrayOutput::Slice => format!(
3868 "slint::private_api::make_slice<{ty}>(std::array<{ty}, {count}>{{ {val} }}.data(), {count})",
3869 count = values.len(),
3870 ty = ty,
3871 val = val.join(", ")
3872 ),
3873 llr::ArrayOutput::Vector => {
3874 format!("std::vector<{ty}>{{ {val} }}", ty = ty, val = val.join(", "))
3875 }
3876 }
3877 }
3878 Expression::Struct { ty, values } => {
3879 if ty.name.is_none() {
3880 let mut elem = ty.fields.iter().map(|(k, t)| {
3881 values
3882 .get(k)
3883 .map(|e| compile_expression(e, ctx))
3884 .map(|e| {
3885 if t.as_unit_product().is_some() {
3887 format!("{}({e})", t.cpp_type().unwrap())
3888 } else {
3889 e
3890 }
3891 })
3892 .unwrap_or_else(|| "(Error: missing member in object)".to_owned())
3893 });
3894 format!("std::make_tuple({})", elem.join(", "))
3895 } else {
3896 format!(
3897 "[&]({args}){{ {ty} o{{}}; {fields}return o; }}({vals})",
3898 args = (0..values.len()).map(|i| format!("const auto &a_{i}")).join(", "),
3899 ty = Type::Struct(ty.clone()).cpp_type().unwrap(),
3900 fields = values
3901 .keys()
3902 .enumerate()
3903 .map(|(i, f)| format!("o.{} = a_{}; ", ident(f), i))
3904 .join(""),
3905 vals = values.values().map(|e| compile_expression(e, ctx)).join(", "),
3906 )
3907 }
3908 }
3909 Expression::EasingCurve(EasingCurve::Linear) => {
3910 "slint::cbindgen_private::EasingCurve()".into()
3911 }
3912 Expression::EasingCurve(EasingCurve::CubicBezier(a, b, c, d)) => format!(
3913 "slint::cbindgen_private::EasingCurve(slint::cbindgen_private::EasingCurve::Tag::CubicBezier, {a}, {b}, {c}, {d})"
3914 ),
3915 Expression::EasingCurve(EasingCurve::EaseInElastic) => {
3916 "slint::cbindgen_private::EasingCurve::Tag::EaseInElastic".into()
3917 }
3918 Expression::EasingCurve(EasingCurve::EaseOutElastic) => {
3919 "slint::cbindgen_private::EasingCurve::Tag::EaseOutElastic".into()
3920 }
3921 Expression::EasingCurve(EasingCurve::EaseInOutElastic) => {
3922 "slint::cbindgen_private::EasingCurve::Tag::EaseInOutElastic".into()
3923 }
3924 Expression::EasingCurve(EasingCurve::EaseInBounce) => {
3925 "slint::cbindgen_private::EasingCurve::Tag::EaseInBounce".into()
3926 }
3927 Expression::EasingCurve(EasingCurve::EaseOutBounce) => {
3928 "slint::cbindgen_private::EasingCurve::Tag::EaseOutElastic".into()
3929 }
3930 Expression::EasingCurve(EasingCurve::EaseInOutBounce) => {
3931 "slint::cbindgen_private::EasingCurve::Tag::EaseInOutElastic".into()
3932 }
3933 Expression::LinearGradient { angle, stops } => {
3934 let angle = compile_expression(angle, ctx);
3935 let mut stops_it = stops.iter().map(|(color, stop)| {
3936 let color = compile_expression(color, ctx);
3937 let position = compile_expression(stop, ctx);
3938 format!("slint::private_api::GradientStop{{ {color}, float({position}), }}")
3939 });
3940 format!(
3941 "[&] {{ const slint::private_api::GradientStop stops[] = {{ {} }}; return slint::Brush(slint::private_api::LinearGradientBrush({}, stops, {})); }}()",
3942 stops_it.join(", "),
3943 angle,
3944 stops.len()
3945 )
3946 }
3947 Expression::RadialGradient { stops } => {
3948 let mut stops_it = stops.iter().map(|(color, stop)| {
3949 let color = compile_expression(color, ctx);
3950 let position = compile_expression(stop, ctx);
3951 format!("slint::private_api::GradientStop{{ {color}, float({position}), }}")
3952 });
3953 format!(
3954 "[&] {{ const slint::private_api::GradientStop stops[] = {{ {} }}; return slint::Brush(slint::private_api::RadialGradientBrush(stops, {})); }}()",
3955 stops_it.join(", "),
3956 stops.len()
3957 )
3958 }
3959 Expression::ConicGradient { from_angle, stops } => {
3960 let from_angle = compile_expression(from_angle, ctx);
3961 let mut stops_it = stops.iter().map(|(color, stop)| {
3962 let color = compile_expression(color, ctx);
3963 let position = compile_expression(stop, ctx);
3964 format!("slint::private_api::GradientStop{{ {color}, float({position}), }}")
3965 });
3966 format!(
3967 "[&] {{ const slint::private_api::GradientStop stops[] = {{ {} }}; return slint::Brush(slint::private_api::ConicGradientBrush(float({}), stops, {})); }}()",
3968 stops_it.join(", "),
3969 from_angle,
3970 stops.len()
3971 )
3972 }
3973 Expression::EnumerationValue(value) => {
3974 let prefix =
3975 if value.enumeration.node.is_some() { "" } else { "slint::cbindgen_private::" };
3976 format!(
3977 "{prefix}{}::{}",
3978 ident(&value.enumeration.name),
3979 ident(&value.to_pascal_case()),
3980 )
3981 }
3982 Expression::LayoutCacheAccess {
3983 layout_cache_prop,
3984 index,
3985 repeater_index,
3986 entries_per_item,
3987 } => {
3988 let cache = access_member(layout_cache_prop, ctx);
3989 cache.map_or_default(|cache| {
3990 if let Some(ri) = repeater_index {
3991 format!(
3992 "slint::private_api::layout_cache_access({}.get(), {}, {}, {})",
3993 cache,
3994 index,
3995 compile_expression(ri, ctx),
3996 entries_per_item
3997 )
3998 } else {
3999 format!("{cache}.get()[{index}]")
4000 }
4001 })
4002 }
4003 Expression::GridRepeaterCacheAccess {
4004 layout_cache_prop,
4005 index,
4006 repeater_index,
4007 stride,
4008 child_offset,
4009 inner_repeater_index,
4010 entries_per_item,
4011 } => {
4012 let cache = access_member(layout_cache_prop, ctx);
4013 cache.map_or_default(|cache| {
4014 let stride_val = compile_expression(stride, ctx);
4015 let col_offset = if let Some(inner_ri) = inner_repeater_index {
4016 format!(
4017 "{} + {} * {}",
4018 child_offset,
4019 compile_expression(inner_ri, ctx),
4020 entries_per_item
4021 )
4022 } else {
4023 child_offset.to_string()
4024 };
4025 format!(
4026 "slint::private_api::layout_cache_grid_repeater_access({}.get(), {}, {}, {}, {})",
4027 cache,
4028 index,
4029 compile_expression(repeater_index, ctx),
4030 stride_val,
4031 col_offset
4032 )
4033 })
4034 }
4035 Expression::WithLayoutItemInfo {
4036 cells_variable,
4037 repeater_indices_var_name,
4038 repeater_steps_var_name,
4039 elements,
4040 orientation,
4041 sub_expression,
4042 } => generate_with_layout_item_info(
4043 cells_variable,
4044 repeater_indices_var_name.as_ref().map(SmolStr::as_str),
4045 repeater_steps_var_name.as_ref().map(SmolStr::as_str),
4046 elements.as_ref(),
4047 *orientation,
4048 sub_expression,
4049 ctx,
4050 ),
4051 Expression::WithFlexboxLayoutItemInfo {
4052 cells_h_variable,
4053 cells_v_variable,
4054 repeater_indices_var_name,
4055 elements,
4056 sub_expression,
4057 } => generate_with_flexbox_layout_item_info(
4058 cells_h_variable,
4059 cells_v_variable,
4060 repeater_indices_var_name.as_ref().map(SmolStr::as_str),
4061 elements.as_ref(),
4062 sub_expression,
4063 ctx,
4064 ),
4065 Expression::WithGridInputData {
4066 cells_variable,
4067 repeater_indices_var_name,
4068 repeater_steps_var_name,
4069 elements,
4070 sub_expression,
4071 } => generate_with_grid_input_data(
4072 cells_variable,
4073 repeater_indices_var_name,
4074 repeater_steps_var_name,
4075 elements.as_ref(),
4076 sub_expression,
4077 ctx,
4078 ),
4079 Expression::MinMax { ty, op, lhs, rhs } => {
4080 let ident = match op {
4081 MinMaxOp::Min => "min",
4082 MinMaxOp::Max => "max",
4083 };
4084 let lhs_code = compile_expression(lhs, ctx);
4085 let rhs_code = compile_expression(rhs, ctx);
4086 format!(
4087 r#"std::{ident}<{ty}>({lhs_code}, {rhs_code})"#,
4088 ty = ty.cpp_type().unwrap_or_default(),
4089 ident = ident,
4090 lhs_code = lhs_code,
4091 rhs_code = rhs_code
4092 )
4093 }
4094 Expression::EmptyComponentFactory => panic!("component-factory not yet supported in C++"),
4095 Expression::TranslationReference { format_args, string_index, plural } => {
4096 let args = compile_expression(format_args, ctx);
4097 match plural {
4098 Some(plural) => {
4099 let plural = compile_expression(plural, ctx);
4100 format!(
4101 "slint::private_api::translate_from_bundle_with_plural(slint_translation_bundle_plural_{string_index}_str, slint_translation_bundle_plural_{string_index}_idx, slint_translated_plural_rules, {args}, {plural})"
4102 )
4103 }
4104 None => format!(
4105 "slint::private_api::translate_from_bundle(slint_translation_bundle_{string_index}, {args})"
4106 ),
4107 }
4108 }
4109 }
4110}
4111
4112fn struct_field_access(base: String, s: &crate::langtype::Struct, name: &str) -> String {
4113 if s.name.is_none() {
4114 let index = s
4115 .fields
4116 .keys()
4117 .position(|k| k == name)
4118 .expect("Expression::ObjectAccess: Cannot find a key in an object");
4119 format!("std::get<{}>({})", index, base)
4120 } else {
4121 format!("{}.{}", base, ident(name))
4122 }
4123}
4124
4125fn compile_builtin_function_call(
4126 function: BuiltinFunction,
4127 arguments: &[llr::Expression],
4128 ctx: &EvaluationContext,
4129) -> String {
4130 let mut a = arguments.iter().map(|a| compile_expression(a, ctx));
4131 let pi_180 = std::f64::consts::PI / 180.0;
4132
4133 match function {
4134 BuiltinFunction::GetWindowScaleFactor => {
4135 format!("{}.scale_factor()", access_window_field(ctx))
4136 }
4137 BuiltinFunction::GetWindowDefaultFontSize => {
4138 "slint::private_api::get_resolved_default_font_size(*this)".to_string()
4139 }
4140 BuiltinFunction::AnimationTick => "slint::cbindgen_private::slint_animation_tick()".into(),
4141 BuiltinFunction::Debug => {
4142 ctx.generator_state.conditional_includes.iostream.set(true);
4143 format!("slint::private_api::debug({});", a.join(","))
4144 }
4145 BuiltinFunction::Mod => {
4146 ctx.generator_state.conditional_includes.cmath.set(true);
4147 format!("([](float a, float b) {{ auto r = std::fmod(a, b); return r >= 0 ? r : r + std::abs(b); }})({},{})", a.next().unwrap(), a.next().unwrap())
4148 }
4149 BuiltinFunction::Round => {
4150 ctx.generator_state.conditional_includes.cmath.set(true);
4151 format!("std::round({})", a.next().unwrap())
4152 }
4153 BuiltinFunction::Ceil => {
4154 ctx.generator_state.conditional_includes.cmath.set(true);
4155 format!("std::ceil({})", a.next().unwrap())
4156 }
4157 BuiltinFunction::Floor => {
4158 ctx.generator_state.conditional_includes.cmath.set(true);
4159 format!("std::floor({})", a.next().unwrap())
4160 }
4161 BuiltinFunction::Sqrt => {
4162 ctx.generator_state.conditional_includes.cmath.set(true);
4163 format!("std::sqrt({})", a.next().unwrap())
4164 }
4165 BuiltinFunction::Abs => {
4166 ctx.generator_state.conditional_includes.cmath.set(true);
4167 format!("std::abs({})", a.next().unwrap())
4168 }
4169 BuiltinFunction::Log => {
4170 ctx.generator_state.conditional_includes.cmath.set(true);
4171 format!("std::log({}) / std::log({})", a.next().unwrap(), a.next().unwrap())
4172 }
4173 BuiltinFunction::Ln => {
4174 ctx.generator_state.conditional_includes.cmath.set(true);
4175 format!("std::log({})", a.next().unwrap())
4176 }
4177 BuiltinFunction::Pow => {
4178 ctx.generator_state.conditional_includes.cmath.set(true);
4179 format!("std::pow(({}), ({}))", a.next().unwrap(), a.next().unwrap())
4180 }
4181 BuiltinFunction::Exp => {
4182 ctx.generator_state.conditional_includes.cmath.set(true);
4183 format!("std::exp({})", a.next().unwrap())
4184 }
4185 BuiltinFunction::Sin => {
4186 ctx.generator_state.conditional_includes.cmath.set(true);
4187 format!("std::sin(({}) * {})", a.next().unwrap(), pi_180)
4188 }
4189 BuiltinFunction::Cos => {
4190 ctx.generator_state.conditional_includes.cmath.set(true);
4191 format!("std::cos(({}) * {})", a.next().unwrap(), pi_180)
4192 }
4193 BuiltinFunction::Tan => {
4194 ctx.generator_state.conditional_includes.cmath.set(true);
4195 format!("std::tan(({}) * {})", a.next().unwrap(), pi_180)
4196 }
4197 BuiltinFunction::ASin => {
4198 ctx.generator_state.conditional_includes.cmath.set(true);
4199 format!("std::asin({}) / {}", a.next().unwrap(), pi_180)
4200 }
4201 BuiltinFunction::ACos => {
4202 ctx.generator_state.conditional_includes.cmath.set(true);
4203 format!("std::acos({}) / {}", a.next().unwrap(), pi_180)
4204 }
4205 BuiltinFunction::ATan => {
4206 ctx.generator_state.conditional_includes.cmath.set(true);
4207 format!("std::atan({}) / {}", a.next().unwrap(), pi_180)
4208 }
4209 BuiltinFunction::ATan2 => {
4210 ctx.generator_state.conditional_includes.cmath.set(true);
4211 format!("std::atan2({}, {}) / {}", a.next().unwrap(), a.next().unwrap(), pi_180)
4212 }
4213 BuiltinFunction::ToFixed => {
4214 format!("[](double n, int d) {{ slint::SharedString out; slint::cbindgen_private::slint_shared_string_from_number_fixed(&out, n, std::max(d, 0)); return out; }}({}, {})",
4215 a.next().unwrap(), a.next().unwrap(),
4216 )
4217 }
4218 BuiltinFunction::ToPrecision => {
4219 format!("[](double n, int p) {{ slint::SharedString out; slint::cbindgen_private::slint_shared_string_from_number_precision(&out, n, std::max(p, 0)); return out; }}({}, {})",
4220 a.next().unwrap(), a.next().unwrap(),
4221 )
4222 }
4223 BuiltinFunction::SetFocusItem => {
4224 if let [llr::Expression::PropertyReference(pr)] = arguments {
4225 let window = access_window_field(ctx);
4226 let focus_item = access_item_rc(pr, ctx);
4227 format!("{window}.set_focus_item({focus_item}, true, slint::cbindgen_private::FocusReason::Programmatic);")
4228 } else {
4229 panic!("internal error: invalid args to SetFocusItem {arguments:?}")
4230 }
4231 }
4232 BuiltinFunction::ClearFocusItem => {
4233 if let [llr::Expression::PropertyReference(pr)] = arguments {
4234 let window = access_window_field(ctx);
4235 let focus_item = access_item_rc(pr, ctx);
4236 format!("{window}.set_focus_item({focus_item}, false, slint::cbindgen_private::FocusReason::Programmatic);")
4237 } else {
4238 panic!("internal error: invalid args to ClearFocusItem {arguments:?}")
4239 }
4240 }
4241 BuiltinFunction::StringIsFloat => {
4252 ctx.generator_state.conditional_includes.cstdlib.set(true);
4253 format!("[](const auto &a){{ float res = 0; return slint::cbindgen_private::slint_string_to_float(&a, &res); }}({})", a.next().unwrap())
4254 }
4255 BuiltinFunction::StringToFloat => {
4256 ctx.generator_state.conditional_includes.cstdlib.set(true);
4257 format!("[](const auto &a){{ float res = 0; slint::cbindgen_private::slint_string_to_float(&a, &res); return res; }}({})", a.next().unwrap())
4258 }
4259 BuiltinFunction::StringIsEmpty => {
4260 format!("{}.empty()", a.next().unwrap())
4261 }
4262 BuiltinFunction::StringCharacterCount => {
4263 format!("[](const auto &a){{ return slint::cbindgen_private::slint_string_character_count(&a); }}({})", a.next().unwrap())
4264 }
4265 BuiltinFunction::StringToLowercase => {
4266 format!("{}.to_lowercase()", a.next().unwrap())
4267 }
4268 BuiltinFunction::StringToUppercase => {
4269 format!("{}.to_uppercase()", a.next().unwrap())
4270 }
4271 BuiltinFunction::KeysToString => {
4272 format!("{}.to_string()", a.next().unwrap())
4273 }
4274 BuiltinFunction::ColorRgbaStruct => {
4275 format!("{}.to_argb_uint()", a.next().unwrap())
4276 }
4277 BuiltinFunction::ColorHsvaStruct => {
4278 format!("{}.to_hsva()", a.next().unwrap())
4279 }
4280 BuiltinFunction::ColorOklchStruct => {
4281 format!("{}.to_oklch()", a.next().unwrap())
4282 }
4283 BuiltinFunction::ColorBrighter => {
4284 format!("{}.brighter({})", a.next().unwrap(), a.next().unwrap())
4285 }
4286 BuiltinFunction::ColorDarker => {
4287 format!("{}.darker({})", a.next().unwrap(), a.next().unwrap())
4288 }
4289 BuiltinFunction::ColorTransparentize => {
4290 format!("{}.transparentize({})", a.next().unwrap(), a.next().unwrap())
4291 }
4292 BuiltinFunction::ColorMix => {
4293 format!("{}.mix({}, {})", a.next().unwrap(), a.next().unwrap(), a.next().unwrap())
4294 }
4295 BuiltinFunction::ColorWithAlpha => {
4296 format!("{}.with_alpha({})", a.next().unwrap(), a.next().unwrap())
4297 }
4298 BuiltinFunction::ImageSize => {
4299 format!("{}.size()", a.next().unwrap())
4300 }
4301 BuiltinFunction::ArrayLength => {
4302 format!("slint::private_api::model_length({})", a.next().unwrap())
4303 }
4304 BuiltinFunction::Rgb => {
4305 format!("slint::Color::from_argb_uint8(std::clamp(static_cast<float>({a}) * 255., 0., 255.), std::clamp(static_cast<int>({r}), 0, 255), std::clamp(static_cast<int>({g}), 0, 255), std::clamp(static_cast<int>({b}), 0, 255))",
4306 r = a.next().unwrap(),
4307 g = a.next().unwrap(),
4308 b = a.next().unwrap(),
4309 a = a.next().unwrap(),
4310 )
4311 }
4312 BuiltinFunction::Hsv => {
4313 format!("slint::Color::from_hsva(static_cast<float>({h}), std::clamp(static_cast<float>({s}), 0.f, 1.f), std::clamp(static_cast<float>({v}), 0.f, 1.f), std::clamp(static_cast<float>({a}), 0.f, 1.f))",
4314 h = a.next().unwrap(),
4315 s = a.next().unwrap(),
4316 v = a.next().unwrap(),
4317 a = a.next().unwrap(),
4318 )
4319 }
4320 BuiltinFunction::Oklch => {
4321 format!("slint::Color::from_oklch(std::clamp(static_cast<float>({l}), 0.f, 1.f), std::max(static_cast<float>({c}), 0.f), static_cast<float>({h}), std::clamp(static_cast<float>({alpha}), 0.f, 1.f))",
4322 l = a.next().unwrap(),
4323 c = a.next().unwrap(),
4324 h = a.next().unwrap(),
4325 alpha = a.next().unwrap(),
4326 )
4327 }
4328 BuiltinFunction::ColorScheme => {
4329 format!("{}.color_scheme()", access_window_field(ctx))
4330 }
4331 BuiltinFunction::AccentColor => {
4332 format!("{}.accent_color()", access_window_field(ctx))
4333 }
4334 BuiltinFunction::SupportsNativeMenuBar => {
4335 format!("{}.supports_native_menu_bar()", access_window_field(ctx))
4336 }
4337 BuiltinFunction::SetupMenuBar => {
4338 let window = access_window_field(ctx);
4339 let [llr::Expression::PropertyReference(entries_r), llr::Expression::PropertyReference(sub_menu_r), llr::Expression::PropertyReference(activated_r), llr::Expression::NumberLiteral(tree_index), llr::Expression::BoolLiteral(no_native), rest @ ..] = arguments
4340 else {
4341 panic!("internal error: incorrect argument count to SetupMenuBar")
4342 };
4343
4344 let current_sub_component = ctx.current_sub_component().unwrap();
4345 let item_tree_id = ident(&ctx.compilation_unit.sub_components[current_sub_component.menu_item_trees[*tree_index as usize].root].name);
4346 let access_entries = access_member(entries_r, ctx).unwrap();
4347 let access_sub_menu = access_member(sub_menu_r, ctx).unwrap();
4348 let access_activated = access_member(activated_r, ctx).unwrap();
4349 if *no_native {
4350 format!(r"{{
4351 auto item_tree = {item_tree_id}::create(self);
4352 auto item_tree_dyn = item_tree.into_dyn();
4353 auto menu_wrapper = slint::private_api::create_menu_wrapper(item_tree_dyn);
4354 slint::private_api::slint_windowrc_setup_menu_bar_shortcuts(&{window}.handle(), &menu_wrapper);
4355 slint::private_api::setup_popup_menu_from_menu_item_tree(menu_wrapper, {access_entries}, {access_sub_menu}, {access_activated});
4356 }}")
4357 } else {
4358 let condition = if let [condition] = &rest {
4359 let condition = compile_expression(condition, ctx);
4360 format!(r"[](auto menu_tree) {{
4361 auto self_mapped = reinterpret_cast<const {item_tree_id} *>(menu_tree->operator->())->parent.lock();
4362 [[maybe_unused]] auto self = &**self_mapped;
4363 return {condition};
4364 }}")
4365 } else {
4366 "nullptr".to_string()
4367 };
4368
4369 format!(r"{{
4370 auto item_tree = {item_tree_id}::create(self);
4371 auto item_tree_dyn = item_tree.into_dyn();
4372 auto menu_wrapper = slint::private_api::create_menu_wrapper(item_tree_dyn, {condition});
4373 slint::private_api::slint_windowrc_setup_menu_bar_shortcuts(&{window}.handle(), &menu_wrapper);
4374 if ({window}.supports_native_menu_bar()) {{
4375 slint::cbindgen_private::slint_windowrc_setup_native_menu_bar(&{window}.handle(), &menu_wrapper);
4376 }} else {{
4377 slint::private_api::setup_popup_menu_from_menu_item_tree(menu_wrapper, {access_entries}, {access_sub_menu}, {access_activated});
4378 }}
4379 }}")
4380 }
4381 }
4382 BuiltinFunction::Use24HourFormat => {
4383 "slint::cbindgen_private::slint_date_time_use_24_hour_format()".to_string()
4384 }
4385 BuiltinFunction::MonthDayCount => {
4386 format!("slint::cbindgen_private::slint_date_time_month_day_count({}, {})", a.next().unwrap(), a.next().unwrap())
4387 }
4388 BuiltinFunction::MonthOffset => {
4389 format!("slint::cbindgen_private::slint_date_time_month_offset({}, {})", a.next().unwrap(), a.next().unwrap())
4390 }
4391 BuiltinFunction::FormatDate => {
4392 format!("[](const auto &format, int d, int m, int y) {{ slint::SharedString out; slint::cbindgen_private::slint_date_time_format_date(&format, d, m, y, &out); return out; }}({}, {}, {}, {})",
4393 a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap()
4394 )
4395 }
4396 BuiltinFunction::DateNow => {
4397 "[] { int32_t d=0, m=0, y=0; slint::cbindgen_private::slint_date_time_date_now(&d, &m, &y); return std::make_shared<slint::private_api::ArrayModel<3,int32_t>>(d, m, y); }()".into()
4398 }
4399 BuiltinFunction::ValidDate => {
4400 format!(
4401 "[](const auto &a, const auto &b) {{ int32_t d=0, m=0, y=0; return slint::cbindgen_private::slint_date_time_parse_date(&a, &b, &d, &m, &y); }}({}, {})",
4402 a.next().unwrap(), a.next().unwrap()
4403 )
4404 }
4405 BuiltinFunction::ParseDate => {
4406 format!(
4407 "[](const auto &a, const auto &b) {{ int32_t d=0, m=0, y=0; slint::cbindgen_private::slint_date_time_parse_date(&a, &b, &d, &m, &y); return std::make_shared<slint::private_api::ArrayModel<3,int32_t>>(d, m, y); }}({}, {})",
4408 a.next().unwrap(), a.next().unwrap()
4409 )
4410 }
4411 BuiltinFunction::SetTextInputFocused => {
4412 format!("{}.set_text_input_focused({})", access_window_field(ctx), a.next().unwrap())
4413 }
4414 BuiltinFunction::TextInputFocused => {
4415 format!("{}.text_input_focused()", access_window_field(ctx))
4416 }
4417 BuiltinFunction::ShowPopupWindow => {
4418 if let [llr::Expression::NumberLiteral(popup_index), close_policy, llr::Expression::PropertyReference(parent_ref)] =
4419 arguments
4420 {
4421 let mut component_access = MemberAccess::Direct("self".into());
4422 let llr::MemberReference::Relative { parent_level, .. } = parent_ref else {unreachable!()};
4423 for _ in 0..*parent_level {
4424 component_access = component_access.and_then(|x| format!("{x}->parent.lock()"));
4425 }
4426
4427 let window = access_window_field(ctx);
4428 let current_sub_component = &ctx.compilation_unit.sub_components[ctx.parent_sub_component_idx(*parent_level).unwrap()];
4429 let popup = ¤t_sub_component.popup_windows[*popup_index as usize];
4430 let popup_window_id =
4431 ident(&ctx.compilation_unit.sub_components[popup.item_tree.root].name);
4432 let parent_component = access_item_rc(parent_ref, ctx);
4433 let parent_ctx = ParentScope::new(ctx, None);
4434 let popup_ctx = EvaluationContext::new_sub_component(
4435 ctx.compilation_unit,
4436 popup.item_tree.root,
4437 CppGeneratorContext { global_access: "self->globals".into(), conditional_includes: ctx.generator_state.conditional_includes },
4438 Some(&parent_ctx),
4439 );
4440 let position = compile_expression(&popup.position.borrow(), &popup_ctx);
4441 let close_policy = compile_expression(close_policy, ctx);
4442 component_access.then(|component_access| format!(
4443 "{window}.close_popup({component_access}->popup_id_{popup_index}); {component_access}->popup_id_{popup_index} = {window}.template show_popup<{popup_window_id}>(&*({component_access}), [=](auto self) {{ return {position}; }}, {close_policy}, {{ {parent_component} }})"
4444 ))
4445 } else {
4446 panic!("internal error: invalid args to ShowPopupWindow {arguments:?}")
4447 }
4448 }
4449 BuiltinFunction::ClosePopupWindow => {
4450 if let [llr::Expression::NumberLiteral(popup_index), llr::Expression::PropertyReference(parent_ref)] = arguments {
4451 let mut component_access = MemberAccess::Direct("self".into());
4452 let llr::MemberReference::Relative { parent_level, .. } = parent_ref else {unreachable!()};
4453 for _ in 0..*parent_level {
4454 component_access = component_access.and_then(|x| format!("{x}->parent.lock()"));
4455 }
4456
4457 let window = access_window_field(ctx);
4458 component_access.then(|component_access| format!("{window}.close_popup({component_access}->popup_id_{popup_index})"))
4459 } else {
4460 panic!("internal error: invalid args to ClosePopupWindow {arguments:?}")
4461 }
4462 }
4463
4464 BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
4465 let [llr::Expression::PropertyReference(context_menu_ref), entries, position] = arguments
4466 else {
4467 panic!("internal error: invalid args to ShowPopupMenu {arguments:?}")
4468 };
4469
4470 let context_menu = access_member(context_menu_ref, ctx);
4471 let context_menu_rc = access_item_rc(context_menu_ref, ctx);
4472 let position = compile_expression(position, ctx);
4473 let popup = ctx
4474 .compilation_unit
4475 .popup_menu
4476 .as_ref()
4477 .expect("there should be a popup menu if we want to show it");
4478 let popup_id = ident(&ctx.compilation_unit.sub_components[popup.item_tree.root].name);
4479 let window = access_window_field(ctx);
4480
4481 let popup_ctx = EvaluationContext::new_sub_component(
4482 ctx.compilation_unit,
4483 popup.item_tree.root,
4484 CppGeneratorContext { global_access: "self->globals".into(), conditional_includes: ctx.generator_state.conditional_includes },
4485 None,
4486 );
4487 let access_entries = access_member(&popup.entries, &popup_ctx).unwrap();
4488 let access_sub_menu = access_member(&popup.sub_menu, &popup_ctx).unwrap();
4489 let access_activated = access_member(&popup.activated, &popup_ctx).unwrap();
4490 let access_close = access_member(&popup.close, &popup_ctx).unwrap();
4491
4492 let close_popup = context_menu.then(|context_menu| {
4493 format!("{window}.close_popup({context_menu}.popup_id)")
4494 });
4495 let set_id = context_menu
4496 .then(|context_menu| format!("{context_menu}.popup_id = id"));
4497
4498 if let llr::Expression::NumberLiteral(tree_index) = entries {
4499 let current_sub_component = ctx.current_sub_component().unwrap();
4501 let item_tree_id = ident(&ctx.compilation_unit.sub_components[current_sub_component.menu_item_trees[*tree_index as usize].root].name);
4502 format!(r"{{
4503 auto item_tree = {item_tree_id}::create(self);
4504 auto item_tree_dyn = item_tree.into_dyn();
4505 auto menu_wrapper = slint::private_api::create_menu_wrapper(item_tree_dyn);
4506 {close_popup};
4507 auto id = {window}.template show_popup_menu<{popup_id}>({globals}, {position}, {{ {context_menu_rc} }}, [self, &menu_wrapper](auto popup_menu) {{
4508 auto parent_weak = self->self_weak;
4509 auto self_ = self;
4510 auto self = popup_menu;
4511 slint::private_api::setup_popup_menu_from_menu_item_tree(menu_wrapper, {access_entries}, {access_sub_menu}, {access_activated});
4512 {access_close}.set_handler([parent_weak,self = self_] {{ if(auto lock = parent_weak.lock()) {{ {close_popup}; }} }});
4513 }}, menu_wrapper);
4514 {set_id};
4515 }}", globals = ctx.generator_state.global_access)
4516 } else {
4517 let forward_callback = |access, cb, default| {
4519 let call = context_menu.map_or_default(|context_menu| format!("{context_menu}.{cb}.call(entry)"));
4520 format!("{access}.set_handler(
4521 [parent_weak,self = self_](const auto &entry) {{
4522 if(auto lock = parent_weak.lock()) {{
4523 return {call};
4524 }} else {{
4525 return {default};
4526 }}
4527 }});")
4528 };
4529 let fw_sub_menu = forward_callback(access_sub_menu, "sub_menu", "std::shared_ptr<slint::Model<slint::cbindgen_private::MenuEntry>>()");
4530 let fw_activated = forward_callback(access_activated, "activated", "");
4531 let entries = compile_expression(entries, ctx);
4532 format!(r"
4533 {close_popup};
4534 auto id = {window}.template show_popup_menu<{popup_id}>({globals}, {position}, {{ {context_menu_rc} }}, [self](auto popup_menu) {{
4535 auto parent_weak = self->self_weak;
4536 auto self_ = self;
4537 auto entries = {entries};
4538 auto self = popup_menu;
4539 {access_entries}.set(std::move(entries));
4540 {fw_sub_menu}
4541 {fw_activated}
4542 {access_close}.set_handler([parent_weak,self = self_] {{ if(auto lock = parent_weak.lock()) {{ {close_popup}; }} }});
4543 }});
4544 {set_id};
4545 ", globals = ctx.generator_state.global_access)
4546 }
4547 }
4548 BuiltinFunction::SetSelectionOffsets => {
4549 if let [llr::Expression::PropertyReference(pr), from, to] = arguments {
4550 let item = access_member(pr, ctx);
4551 let item_rc = access_item_rc(pr, ctx);
4552 let window = access_window_field(ctx);
4553 let start = compile_expression(from, ctx);
4554 let end = compile_expression(to, ctx);
4555 item.then(|item| {
4556 format!("slint_textinput_set_selection_offsets(&{item}, &{window}.handle(), &{item_rc}, static_cast<int>({start}), static_cast<int>({end}))")
4557 })
4558 } else {
4559 panic!("internal error: invalid args to set-selection-offsets {arguments:?}")
4560 }
4561 }
4562 BuiltinFunction::ItemFontMetrics => {
4563 if let [llr::Expression::PropertyReference(pr)] = arguments {
4564 let item_rc = access_item_rc(pr, ctx);
4565 let window = access_window_field(ctx);
4566 format!("slint_cpp_text_item_fontmetrics(&{window}.handle(), &{item_rc})")
4567 } else {
4568 panic!("internal error: invalid args to ItemFontMetrics {arguments:?}")
4569 }
4570 }
4571 BuiltinFunction::ItemAbsolutePosition => {
4572 if let [llr::Expression::PropertyReference(pr)] = arguments {
4573 let item_rc = access_item_rc(pr, ctx);
4574 format!("slint::LogicalPosition(slint::cbindgen_private::slint_item_absolute_position(&{item_rc}))")
4575 } else {
4576 panic!("internal error: invalid args to ItemAbsolutePosition {arguments:?}")
4577 }
4578 }
4579 BuiltinFunction::RegisterCustomFontByPath => {
4580 if let [llr::Expression::StringLiteral(path)] = arguments {
4581 let window = access_window_field(ctx);
4582 format!("{window}.register_font_from_path(\"{}\");", escape_string(path))
4583 } else {
4584 panic!(
4585 "internal error: argument to RegisterCustomFontByPath must be a string literal"
4586 )
4587 }
4588 }
4589 BuiltinFunction::RegisterCustomFontByMemory => {
4590 if let [llr::Expression::NumberLiteral(resource_id)] = &arguments {
4591 let window = access_window_field(ctx);
4592 let resource_id: usize = *resource_id as _;
4593 let symbol = format!("slint_embedded_resource_{resource_id}");
4594 format!("{window}.register_font_from_data({symbol}, std::size({symbol}));")
4595 } else {
4596 panic!("internal error: invalid args to RegisterCustomFontByMemory {arguments:?}")
4597 }
4598 }
4599 BuiltinFunction::RegisterBitmapFont => {
4600 if let [llr::Expression::NumberLiteral(resource_id)] = &arguments {
4601 let window = access_window_field(ctx);
4602 let resource_id: usize = *resource_id as _;
4603 let symbol = format!("slint_embedded_resource_{resource_id}");
4604 format!("{window}.register_bitmap_font({symbol});")
4605 } else {
4606 panic!("internal error: invalid args to RegisterBitmapFont {arguments:?}")
4607 }
4608 }
4609 BuiltinFunction::ImplicitLayoutInfo(orient) => {
4610 if let [llr::Expression::PropertyReference(pr), constraint_expr] = arguments {
4611 let native = native_prop_info(pr, ctx).0;
4612 let item_rc = access_item_rc(pr, ctx);
4613 let constraint = compile_expression(constraint_expr, ctx);
4614 access_member(pr, ctx).then(|item|
4615 format!(
4616 "slint::private_api::item_layout_info({vt}, const_cast<slint::cbindgen_private::{ty}*>(&{item}), {o}, {constraint}, &{window}, {item_rc})",
4617 vt = native.cpp_vtable_getter,
4618 ty = native.class_name,
4619 o = to_cpp_orientation(orient),
4620 window = access_window_field(ctx),
4621 ))
4622 } else {
4623 panic!("internal error: invalid args to ImplicitLayoutInfo {arguments:?}")
4624 }
4625 }
4626 BuiltinFunction::Translate => {
4627 format!("slint::private_api::translate({})", a.join(","))
4628 }
4629 BuiltinFunction::UpdateTimers => {
4630 "self->update_timers()".into()
4631 }
4632 BuiltinFunction::DetectOperatingSystem => {
4633 "slint::cbindgen_private::slint_detect_operating_system()".to_string()
4634 }
4635 BuiltinFunction::StartTimer => unreachable!(),
4637 BuiltinFunction::StopTimer => unreachable!(),
4638 BuiltinFunction::RestartTimer => {
4639 if let [llr::Expression::NumberLiteral(timer_index)] = arguments {
4640 format!("const_cast<slint::Timer&>(self->timer{}).restart()", timer_index)
4641 } else {
4642 panic!("internal error: invalid args to RetartTimer {arguments:?}")
4643 }
4644 }
4645 BuiltinFunction::OpenUrl => {
4646 let url = a.next().unwrap();
4647 let window = access_window_field(ctx);
4648 format!("slint::private_api::open_url({url}, {window})")
4649 }
4650 BuiltinFunction::ParseMarkdown => {
4651 let format_string = a.next().unwrap();
4652 let args = a.next().unwrap();
4653 format!("slint::private_api::parse_markdown({}, {})", format_string, args)
4654 }
4655 BuiltinFunction::StringToStyledText => {
4656 let string = a.next().unwrap();
4657 format!("slint::private_api::string_to_styled_text({})", string)
4658 }
4659 }
4660}
4661
4662fn build_inner_ensure_code(templates: &[llr::RowChildTemplateInfo], static_count: usize) -> String {
4665 templates
4666 .iter()
4667 .filter_map(|e| match e {
4668 llr::RowChildTemplateInfo::Repeated { repeater_index } => {
4669 let inner_rep_id = format!("repeater_{}", usize::from(*repeater_index));
4670 Some(format!(
4671 "sub_comp->{inner_rep_id}.ensure_updated(&*sub_comp);\n\
4672 max_total = std::max(max_total, {static_count} + sub_comp->{inner_rep_id}.len());\n"
4673 ))
4674 }
4675 _ => None,
4676 })
4677 .collect()
4678}
4679
4680fn generate_repeater_loop_code(
4681 repeater_index: llr::RepeatedElementIdx,
4682 row_child_templates: &Option<Vec<llr::RowChildTemplateInfo>>,
4683 repeater_steps_var_name: &Option<SmolStr>,
4684 repeater_idx: usize,
4685 dynamic_stride_var_name: &str,
4686 dynamic_loop_code: impl FnOnce(String, usize, String, String) -> String,
4687 static_loop_code: impl FnOnce(String, usize, bool, String) -> String,
4688) -> String {
4689 let repeater_id = format!("repeater_{}", usize::from(repeater_index));
4690 if llr::has_inner_repeaters(row_child_templates) {
4691 let templates = row_child_templates.as_ref().unwrap();
4692 let static_count = llr::static_child_count(templates);
4693 let inner_ensure = build_inner_ensure_code(templates, static_count);
4694 let rs_init = repeater_steps_var_name.as_ref().map_or(String::new(), |rs| {
4695 format!("{rs}_array[{repeater_idx}] = {dynamic_stride_var_name};")
4696 });
4697 dynamic_loop_code(repeater_id, static_count, inner_ensure, rs_init)
4698 } else {
4699 let step = row_child_templates.as_deref().map_or(1, |t| t.len());
4700 let rs_init = repeater_steps_var_name
4701 .as_ref()
4702 .map_or(String::new(), |rs| format!("{rs}_array[{repeater_idx}] = {step};"));
4703 static_loop_code(repeater_id, step, row_child_templates.is_none(), rs_init)
4704 }
4705}
4706
4707fn generate_with_layout_item_info(
4708 cells_variable: &str,
4709 repeated_indices_var_name: Option<&str>,
4710 repeater_steps_var_name: Option<&str>,
4711 elements: &[Either<llr::Expression, llr::LayoutRepeatedElement>],
4712 orientation: Orientation,
4713 sub_expression: &llr::Expression,
4714 ctx: &llr_EvaluationContext<CppGeneratorContext>,
4715) -> String {
4716 let repeated_indices_var_name = repeated_indices_var_name.map(ident);
4717 let repeater_steps_var_name = repeater_steps_var_name.map(ident);
4718 let mut push_code =
4719 "std::vector<slint::cbindgen_private::LayoutItemInfo> cells_vector;".to_owned();
4720 let mut repeater_idx = 0usize;
4721
4722 for item in elements {
4723 match item {
4724 Either::Left(value) => {
4725 write!(
4726 push_code,
4727 "cells_vector.push_back({{ {} }});",
4728 compile_expression(value, ctx)
4729 )
4730 .unwrap();
4731 }
4732 Either::Right(repeater) => {
4733 let repeater_index = usize::from(repeater.repeater_index);
4734 write!(push_code, "self->repeater_{repeater_index}.ensure_updated(self);").unwrap();
4735
4736 if let Some(ri) = &repeated_indices_var_name {
4737 write!(
4738 push_code,
4739 "{ri}_array[{c}] = cells_vector.size();",
4740 c = repeater_idx * 2
4741 )
4742 .unwrap();
4743 write!(
4744 push_code,
4745 "{ri}_array[{c}] = self->repeater_{repeater_index}.len();",
4746 c = repeater_idx * 2 + 1,
4747 )
4748 .unwrap();
4749 }
4750 let repeater_loop_code = generate_repeater_loop_code(
4751 repeater.repeater_index,
4752 &repeater.row_child_templates,
4753 &repeater_steps_var_name,
4754 repeater_idx,
4755 "max_total",
4756 |repeater_id, static_count, inner_ensure, rs_init| {
4757 format!(
4758 "{{
4759 size_t max_total = {static_count};
4760 self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4761 {inner_ensure}
4762 }});
4763 {rs_init}
4764 self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4765 for (size_t child_idx = 0; child_idx < max_total; ++child_idx) {{
4766 cells_vector.push_back(sub_comp->layout_item_info({o}, child_idx));
4767 }}
4768 }});
4769 }}",
4770 o = to_cpp_orientation(orientation),
4771 )
4772 },
4773 |repeater_id, step, is_column_repeater, rs_init| {
4774 if step == 0 {
4775 rs_init
4776 } else if step == 1 && is_column_repeater {
4777 format!(
4779 "{rs_init}self->{repeater_id}.for_each([&](const auto &sub_comp){{ cells_vector.push_back(sub_comp->layout_item_info({o}, std::nullopt)); }});",
4780 o = to_cpp_orientation(orientation),
4781 )
4782 } else {
4783 format!(
4784 "{rs_init}self->{repeater_id}.for_each([&](const auto &sub_comp){{
4785 for (size_t child_idx = 0; child_idx < {step}; ++child_idx) {{
4786 cells_vector.push_back(sub_comp->layout_item_info({o}, child_idx));
4787 }}
4788 }});",
4789 o = to_cpp_orientation(orientation),
4790 )
4791 }
4792 },
4793 );
4794 push_code.push_str(&repeater_loop_code);
4795 repeater_idx += 1;
4796 }
4797 }
4798 }
4799
4800 let ri = repeated_indices_var_name.as_ref().map_or(String::new(), |ri| {
4801 write!(
4802 push_code,
4803 "slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4804 )
4805 .unwrap();
4806 format!("std::array<int, {}> {ri}_array;", 2 * repeater_idx)
4807 });
4808 let rs = repeater_steps_var_name.as_ref().map_or(String::new(), |rs| {
4809 write!(
4810 push_code,
4811 "slint::cbindgen_private::Slice<int> {rs} = slint::private_api::make_slice(std::span({rs}_array));"
4812 )
4813 .unwrap();
4814 format!("std::array<int, {}> {rs}_array;", repeater_idx)
4815 });
4816 format!(
4817 "[&]{{ {ri} {rs} {push_code} slint::cbindgen_private::Slice<slint::cbindgen_private::LayoutItemInfo>{} = slint::private_api::make_slice(std::span(cells_vector)); return {}; }}()",
4818 ident(cells_variable),
4819 compile_expression(sub_expression, ctx)
4820 )
4821}
4822
4823fn generate_with_flexbox_layout_item_info(
4824 cells_h_variable: &str,
4825 cells_v_variable: &str,
4826 repeated_indices_var_name: Option<&str>,
4827 elements: &[Either<(llr::Expression, llr::Expression), llr::LayoutRepeatedElement>],
4828 sub_expression: &llr::Expression,
4829 ctx: &llr_EvaluationContext<CppGeneratorContext>,
4830) -> String {
4831 let repeated_indices_var_name = repeated_indices_var_name.map(ident);
4832 let mut push_code =
4833 "std::vector<slint::cbindgen_private::FlexboxLayoutItemInfo> cells_vector_h; std::vector<slint::cbindgen_private::FlexboxLayoutItemInfo> cells_vector_v;".to_owned();
4834 let mut repeater_idx = 0usize;
4835
4836 for item in elements {
4837 match item {
4838 Either::Left((value_h, value_v)) => {
4839 write!(
4840 push_code,
4841 "cells_vector_h.push_back({{ {} }}); cells_vector_v.push_back({{ {} }});",
4842 compile_expression(value_h, ctx),
4843 compile_expression(value_v, ctx)
4844 )
4845 .unwrap();
4846 }
4847 Either::Right(repeater) => {
4848 let repeater_index = usize::from(repeater.repeater_index);
4849 write!(push_code, "self->repeater_{repeater_index}.ensure_updated(self);").unwrap();
4850
4851 if let Some(ri) = &repeated_indices_var_name {
4852 write!(
4853 push_code,
4854 "{ri}_array[{c}] = cells_vector_h.size();",
4855 c = repeater_idx * 2
4856 )
4857 .unwrap();
4858 write!(
4859 push_code,
4860 "{ri}_array[{c}] = self->repeater_{repeater_index}.len();",
4861 c = repeater_idx * 2 + 1,
4862 )
4863 .unwrap();
4864 }
4865 repeater_idx += 1;
4866 write!(
4867 push_code,
4868 "self->repeater_{repeater_index}.for_each([&](const auto &sub_comp){{ \
4869 cells_vector_h.push_back(sub_comp->flexbox_layout_item_info(slint::cbindgen_private::Orientation::Horizontal, std::nullopt)); \
4870 cells_vector_v.push_back(sub_comp->flexbox_layout_item_info(slint::cbindgen_private::Orientation::Vertical, std::nullopt)); }});"
4871 )
4872 .unwrap();
4873 }
4874 }
4875 }
4876
4877 let ri = repeated_indices_var_name.as_ref().map_or(String::new(), |ri| {
4878 write!(
4879 push_code,
4880 "slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4881 )
4882 .unwrap();
4883 format!("std::array<int, {}> {ri}_array;", 2 * repeater_idx)
4884 });
4885 format!(
4886 "[&]{{ {ri} {push_code} [[maybe_unused]] slint::cbindgen_private::Slice<slint::cbindgen_private::FlexboxLayoutItemInfo>{cells_h} = slint::private_api::make_slice(std::span(cells_vector_h)); [[maybe_unused]] slint::cbindgen_private::Slice<slint::cbindgen_private::FlexboxLayoutItemInfo>{cells_v} = slint::private_api::make_slice(std::span(cells_vector_v)); return {}; }}()",
4887 compile_expression(sub_expression, ctx),
4888 cells_h = ident(cells_h_variable),
4889 cells_v = ident(cells_v_variable),
4890 )
4891}
4892
4893fn generate_with_grid_input_data(
4894 cells_variable: &str,
4895 repeated_indices_var_name: &SmolStr,
4896 repeater_steps_var_name: &SmolStr,
4897 elements: &[Either<llr::Expression, llr::GridLayoutRepeatedElement>],
4898 sub_expression: &llr::Expression,
4899 ctx: &llr_EvaluationContext<CppGeneratorContext>,
4900) -> String {
4901 let repeated_indices_var_name = Some(ident(repeated_indices_var_name));
4902 let repeater_steps_var_name = Some(ident(repeater_steps_var_name));
4903 let mut push_code =
4904 "std::vector<slint::cbindgen_private::GridLayoutInputData> cells_vector;".to_owned();
4905 let mut repeater_idx = 0usize;
4906 let mut has_new_row_bool = false;
4907
4908 for item in elements {
4909 match item {
4910 Either::Left(value) => {
4911 write!(
4912 push_code,
4913 "cells_vector.push_back({{ {} }});",
4914 compile_expression(value, ctx)
4915 )
4916 .unwrap();
4917 }
4918 Either::Right(repeater) => {
4919 let repeater_id = format!("repeater_{}", usize::from(repeater.repeater_index));
4920 write!(push_code, "self->{repeater_id}.ensure_updated(self);").unwrap();
4921
4922 if let Some(ri) = &repeated_indices_var_name {
4923 write!(push_code, "{ri}_array[{}] = cells_vector.size();", repeater_idx * 2)
4924 .unwrap();
4925 write!(
4926 push_code,
4927 "{ri}_array[{c}] = self->{repeater_id}.len();",
4928 c = repeater_idx * 2 + 1,
4929 )
4930 .unwrap();
4931 }
4932 let maybe_bool = if has_new_row_bool { "" } else { "bool " };
4933 let repeater_loop_code = generate_repeater_loop_code(
4934 repeater.repeater_index,
4935 &repeater.row_child_templates,
4936 &repeater_steps_var_name,
4937 repeater_idx,
4938 "total_item_count",
4939 |repeater_id, static_count, inner_ensure, rs_init| {
4940 format!(
4941 "{maybe_bool} new_row = {new_row};
4942 {{
4943 size_t max_total = {static_count};
4944 self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4945 {inner_ensure}
4946 }});
4947 size_t total_item_count = max_total;
4948 {rs_init}
4949 auto start_offset = cells_vector.size();
4950 cells_vector.resize(start_offset + self->{repeater_id}.len() * total_item_count);
4951 std::size_t i = 0;
4952 self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4953 auto offset = start_offset + i * total_item_count;
4954 sub_comp->grid_layout_input_for_repeated(new_row, std::span(cells_vector).subspan(offset, total_item_count));
4955 ++i;
4956 }});
4957 }}",
4958 new_row = repeater.new_row,
4959 )
4960 },
4961 |repeater_id, step, is_column_repeater, rs_init| {
4962 let reset_new_row =
4963 if is_column_repeater { "new_row = false;" } else { "" };
4964 format!(
4965 "{rs_init}{maybe_bool} new_row = {new_row};
4966 {{
4967 auto start_offset = cells_vector.size();
4968 cells_vector.resize(start_offset + self->{repeater_id}.len() * {step});
4969 std::size_t i = 0;
4970 self->{repeater_id}.for_each([&](const auto &sub_comp) {{
4971 auto offset = start_offset + i * {step};
4972 sub_comp->grid_layout_input_for_repeated(new_row, std::span(cells_vector).subspan(offset, {step}));
4973 {reset_new_row}
4974 ++i;
4975 }});
4976 }}",
4977 new_row = repeater.new_row,
4978 )
4979 },
4980 );
4981 push_code.push_str(&repeater_loop_code);
4982 repeater_idx += 1;
4983 has_new_row_bool = true;
4984 }
4985 }
4986 }
4987
4988 let ri = repeated_indices_var_name.as_ref().map_or(String::new(), |ri| {
4989 write!(
4990 push_code,
4991 "slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4992 )
4993 .unwrap();
4994 format!("std::array<int, {}> {ri}_array;", 2 * repeater_idx)
4995 });
4996 let rs = repeater_steps_var_name.as_ref().map_or(String::new(), |rs| {
4997 write!(
4998 push_code,
4999 "slint::cbindgen_private::Slice<int> {rs} = slint::private_api::make_slice(std::span({rs}_array));"
5000 )
5001 .unwrap();
5002 format!("std::array<int, {}> {rs}_array;", repeater_idx)
5003 });
5004 format!(
5005 "[&]{{ {ri} {rs} {push_code} slint::cbindgen_private::Slice<slint::cbindgen_private::GridLayoutInputData>{} = slint::private_api::make_slice(std::span(cells_vector)); return {}; }}()",
5006 ident(cells_variable),
5007 compile_expression(sub_expression, ctx)
5008 )
5009}
5010
5011fn return_compile_expression(
5014 expr: &llr::Expression,
5015 ctx: &EvaluationContext,
5016 ret_type: Option<&Type>,
5017) -> String {
5018 let e = compile_expression(expr, ctx);
5019 if ret_type == Some(&Type::Void) || ret_type == Some(&Type::Invalid) {
5020 e
5021 } else {
5022 let ty = expr.ty(ctx);
5023 if ty == Type::Invalid && ret_type.is_some() {
5024 format!("{e}; return {{}}")
5026 } else if ty == Type::Invalid || ty == Type::Void {
5027 e
5028 } else {
5029 format!("return {e}")
5030 }
5031 }
5032}
5033
5034pub fn generate_type_aliases(file: &mut File, doc: &Document) {
5035 let type_aliases = doc
5036 .exports
5037 .iter()
5038 .filter_map(|export| match &export.1 {
5039 Either::Left(component) if !component.is_global() => {
5040 Some((&export.0.name, component.id.clone()))
5041 }
5042 Either::Right(ty) => match &ty {
5043 Type::Struct(s) if s.node().is_some() => {
5044 Some((&export.0.name, s.name.cpp_type().unwrap()))
5045 }
5046 Type::Enumeration(en) => Some((&export.0.name, en.name.clone())),
5047 _ => None,
5048 },
5049 _ => None,
5050 })
5051 .filter(|(export_name, type_name)| *export_name != type_name)
5052 .map(|(export_name, type_name)| {
5053 Declaration::TypeAlias(TypeAlias {
5054 old_name: ident(&type_name),
5055 new_name: ident(export_name),
5056 })
5057 });
5058
5059 file.declarations.extend(type_aliases);
5060}
5061
5062#[cfg(feature = "bundle-translations")]
5063fn generate_translation(
5064 translations: &crate::translations::Translations,
5065 compilation_unit: &llr::CompilationUnit,
5066 declarations: &mut Vec<Declaration>,
5067) {
5068 for (idx, m) in translations.strings.iter().enumerate() {
5069 declarations.push(Declaration::Var(Var {
5070 ty: "const char8_t* const".into(),
5071 name: format_smolstr!("slint_translation_bundle_{idx}"),
5072 array_size: Some(m.len()),
5073 init: Some(format!(
5074 "{{ {} }}",
5075 m.iter()
5076 .map(|s| match s {
5077 Some(s) => format_smolstr!("u8\"{}\"", escape_string(s.as_str())),
5078 None => "nullptr".into(),
5079 })
5080 .join(", ")
5081 )),
5082 ..Default::default()
5083 }));
5084 }
5085 for (idx, ms) in translations.plurals.iter().enumerate() {
5086 let all_strs = ms.iter().flatten().flatten();
5087 let all_strs_len = all_strs.clone().count();
5088 declarations.push(Declaration::Var(Var {
5089 ty: "const char8_t* const".into(),
5090 name: format_smolstr!("slint_translation_bundle_plural_{}_str", idx),
5091 array_size: Some(all_strs_len),
5092 init: Some(format!(
5093 "{{ {} }}",
5094 all_strs.map(|s| format_smolstr!("u8\"{}\"", escape_string(s.as_str()))).join(", ")
5095 )),
5096 ..Default::default()
5097 }));
5098
5099 let mut count = 0;
5100 declarations.push(Declaration::Var(Var {
5101 ty: "const uint32_t".into(),
5102 name: format_smolstr!("slint_translation_bundle_plural_{}_idx", idx),
5103 array_size: Some(ms.len()),
5104 init: Some(format!(
5105 "{{ {} }}",
5106 ms.iter()
5107 .map(|x| {
5108 count += x.as_ref().map_or(0, |x| x.len());
5109 count
5110 })
5111 .join(", ")
5112 )),
5113 ..Default::default()
5114 }));
5115 }
5116
5117 if !translations.plurals.is_empty() {
5118 let ctx = EvaluationContext {
5119 compilation_unit,
5120 current_scope: EvaluationScope::Global(0.into()),
5121 generator_state: CppGeneratorContext {
5122 global_access: "\n#error \"language rule can't access state\";".into(),
5123 conditional_includes: &Default::default(),
5124 },
5125 argument_types: &[Type::Int32],
5126 };
5127
5128 declarations.push(Declaration::Var(Var {
5129 ty: format_smolstr!(
5130 "const std::array<uintptr_t (*const)(int32_t), {}>",
5131 translations.plural_rules.len()
5132 ),
5133 name: "slint_translated_plural_rules".into(),
5134 init: Some(format!(
5135 "{{ {} }}",
5136 translations
5137 .plural_rules
5138 .iter()
5139 .map(|s| match s {
5140 Some(s) => {
5141 format!(
5142 "[]([[maybe_unused]] int32_t arg_0) -> uintptr_t {{ return {}; }}",
5143 compile_expression(s, &ctx)
5144 )
5145 }
5146 None => "nullptr".into(),
5147 })
5148 .join(", ")
5149 )),
5150 ..Default::default()
5151 }));
5152 }
5153}