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