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