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