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