1use crate::instance::Instance;
11use i_slint_core::SharedString;
12use i_slint_core::accessibility::{
13 AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
14};
15use i_slint_core::item_tree::{
16 IndexRange, ItemTree, ItemTreeNode, ItemTreeVTable, ItemVisitorVTable, ItemWeak,
17 TraversalOrder, VisitChildrenResult,
18};
19use i_slint_core::items::{AccessibleRole, ItemVTable};
20use i_slint_core::layout::{LayoutInfo, Orientation};
21use i_slint_core::lengths::LogicalRect;
22use i_slint_core::slice::Slice;
23use i_slint_core::window::WindowAdapterRc;
24use std::pin::Pin;
25use vtable::{VRef, VRefMut, VWeak};
26
27i_slint_core::ItemTreeVTable_static!(static INTERPRETER_INSTANCE_VT for Instance);
28
29pub(crate) fn sub_component_path_of(
33 target: &crate::instance::SubComponentInstance,
34 parent_root: &Instance,
35) -> Vec<i_slint_compiler::llr::SubComponentInstanceIdx> {
36 fn walk(
37 current: &crate::instance::SubComponentInstance,
38 target_ptr: *const crate::instance::SubComponentInstance,
39 path: &mut Vec<i_slint_compiler::llr::SubComponentInstanceIdx>,
40 ) -> bool {
41 if std::ptr::eq(current as *const _, target_ptr) {
42 return true;
43 }
44 for (idx, nested) in current.sub_components.iter().enumerate() {
45 path.push(idx.into());
46 if walk(nested, target_ptr, path) {
47 return true;
48 }
49 path.pop();
50 }
51 false
52 }
53 let mut path = Vec::new();
54 walk(&parent_root.root_sub_component, target as *const _, &mut path);
55 path
56}
57
58impl i_slint_core::item_tree::ItemTree for Instance {
59 fn visit_children_item(
60 self: Pin<&Self>,
61 index: isize,
62 order: TraversalOrder,
63 visitor: VRefMut<'_, ItemVisitorVTable>,
64 ) -> VisitChildrenResult {
65 let this = self.get_ref();
66 let weak = this.self_weak.get().unwrap().clone();
67 if index >= 0 && this.z_sort_table.get(index as usize).is_some_and(|e| e.is_some()) {
68 i_slint_core::item_tree::visit_item_tree_z_sorted(
69 &vtable::VRc::into_dyn(weak.upgrade().unwrap()),
70 &this.tree_nodes[..],
71 index,
72 order,
73 visitor,
74 &mut |order, visitor, dyn_index| {
75 self.visit_dynamic_children(dyn_index, order, visitor)
76 },
77 &mut |push| self.collect_z_sorted_children(index, push),
78 )
79 } else {
80 i_slint_core::item_tree::visit_item_tree(
81 &vtable::VRc::into_dyn(weak.upgrade().unwrap()),
82 &this.tree_nodes[..],
83 index,
84 order,
85 visitor,
86 &mut |order, visitor, dyn_index| {
87 self.visit_dynamic_children(dyn_index, order, visitor)
88 },
89 )
90 }
91 }
92
93 fn get_item_ref(self: Pin<&Self>, index: u32) -> Pin<VRef<'_, ItemVTable>> {
94 let this = self.get_ref();
98 let entry = this
99 .item_table
100 .get(index as usize)
101 .and_then(Option::as_ref)
102 .expect("get_item_ref: tree index is not a static item");
103 let mut current: &crate::instance::SubComponentInstance = &this.root_sub_component;
107 for &sub_idx in entry.0.iter() {
108 current = ¤t.sub_components[sub_idx];
109 }
110 Pin::as_ref(¤t.items[entry.1]).as_item_ref()
111 }
112
113 fn ensure_instantiated(self: Pin<&Self>) -> bool {
114 self.get_ref().ensure_instantiated()
115 }
116
117 fn get_subtree_range(self: Pin<&Self>, index: u32) -> IndexRange {
118 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(index) else {
119 return IndexRange { start: 0, end: 0 };
120 };
121 self.get_ref().ensure_updated(index);
125 if let Some(cc) = crate::instance::component_container_item(&sub, rep_idx) {
126 return cc.subtree_range();
127 }
128 let repeater = &sub.repeaters[rep_idx];
129 let range = repeater.range();
130 IndexRange { start: range.start, end: range.end }
131 }
132
133 fn get_subtree(
134 self: Pin<&Self>,
135 index: u32,
136 subindex: usize,
137 result: &mut VWeak<ItemTreeVTable, vtable::Dyn>,
138 ) {
139 self.get_ref().ensure_updated(index);
140 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(index) else {
141 return;
142 };
143 if let Some(cc) = crate::instance::component_container_item(&sub, rep_idx) {
144 if subindex == 0 {
145 *result = cc.subtree_component();
146 }
147 return;
148 }
149 let repeater = &sub.repeaters[rep_idx];
150 if let Some(instance) = repeater.instance_at(subindex) {
151 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance));
152 }
153 }
154
155 fn get_item_tree(self: Pin<&Self>) -> Slice<'_, ItemTreeNode> {
156 Slice::from(&*self.get_ref().tree_nodes)
157 }
158
159 fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) {
160 let this = self.get_ref();
164 if let Some((outer_weak, outer_index)) = this.embedded_in.get()
168 && let Some(outer) = outer_weak.upgrade()
169 {
170 *result = i_slint_core::items::ItemRc::new(outer, *outer_index).downgrade();
171 return;
172 }
173 let Some(parent_sub) = this.parent_instance.upgrade() else { return };
174 let Some(parent_root_vrc) = parent_sub.root.get().and_then(|w| w.upgrade()) else {
175 return;
176 };
177 let parent_dyn = vtable::VRc::into_dyn(parent_root_vrc.clone());
178 if let Some((_, repeater_idx)) = this.root_sub_component.repeated_in.get() {
179 let rep_idx = *repeater_idx;
185 let parent_path = sub_component_path_of(&parent_sub, &parent_root_vrc);
186 for (flat, entry) in parent_root_vrc.dynamic_table.iter().enumerate() {
187 if let Some((path, idx)) = entry.as_ref()
188 && path.as_ref() == parent_path.as_slice()
189 && *idx == rep_idx
190 {
191 *result = i_slint_core::items::ItemRc::new(parent_dyn, flat as u32).downgrade();
192 return;
193 }
194 }
195 } else {
196 *result = i_slint_core::items::ItemRc::new(parent_dyn, 0).downgrade();
199 }
200 }
201
202 fn embed_component(
203 self: Pin<&Self>,
204 parent: &VWeak<ItemTreeVTable>,
205 parent_item_tree_index: u32,
206 ) -> bool {
207 let this = self.get_ref();
210 this.embedded_in.set((parent.clone(), parent_item_tree_index)).is_ok()
211 }
212
213 fn subtree_index(self: Pin<&Self>) -> usize {
214 let this = self.get_ref();
217 let sc = &this.root_sub_component.compilation_unit.sub_components
218 [this.root_sub_component.sub_component_idx];
219 for (idx, prop) in sc.properties.iter_enumerated() {
220 if prop.name == "model_index"
221 && let crate::Value::Number(n) =
222 Pin::as_ref(&this.root_sub_component.properties[idx]).get()
223 {
224 return n as usize;
225 }
226 }
227 0
229 }
230
231 fn layout_info(self: Pin<&Self>, orientation: Orientation) -> LayoutInfo {
232 let this = self.get_ref();
233 let sc_idx = this.root_sub_component.sub_component_idx;
234 let cu = &this.root_sub_component.compilation_unit;
235 let sc = &cu.sub_components[sc_idx];
236 let expr = match orientation {
237 Orientation::Horizontal => sc.layout_info_h.borrow(),
238 Orientation::Vertical => sc.layout_info_v.borrow(),
239 };
240 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
241 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default()
242 }
243
244 fn item_geometry(self: Pin<&Self>, item_index: u32) -> LogicalRect {
245 let this = self.get_ref();
251 let Some(entry) = this.item_table.get(item_index as usize).and_then(Option::as_ref) else {
252 return LogicalRect::default();
253 };
254 let mut owner_rc = this.root_sub_component.clone();
255 for &sub_idx in entry.0.iter() {
256 owner_rc = owner_rc.sub_components[sub_idx].clone();
257 }
258 let cu = owner_rc.compilation_unit.clone();
259 let sc = &cu.sub_components[owner_rc.sub_component_idx];
260 let item = &sc.items[entry.1];
261 let parent_placement = if !entry.0.is_empty() && item.index_in_tree == 0 {
274 let mut parent_rc = this.root_sub_component.clone();
275 for &sub_idx in &entry.0[..entry.0.len() - 1] {
276 parent_rc = parent_rc.sub_components[sub_idx].clone();
277 }
278 let placement = entry.0[entry.0.len() - 1];
279 let parent_sc = &cu.sub_components[parent_rc.sub_component_idx];
280 let placement_idx = parent_sc.sub_components[placement].index_in_tree as usize;
281 parent_sc
282 .geometries
283 .get(placement_idx)
284 .and_then(|g| g.clone())
285 .map(|expr| (expr, parent_rc))
286 } else {
287 None
288 };
289 let (expr_cell, ctx_owner) = if let Some(pair) = parent_placement {
290 pair
291 } else {
292 let tree_local_idx = item.index_in_tree as usize;
293 match sc.geometries.get(tree_local_idx) {
294 Some(Some(expr)) => (expr.clone(), owner_rc),
295 _ => return LogicalRect::default(),
296 }
297 };
298 let expr = expr_cell.borrow();
299 let mut ctx = crate::eval::EvalContext::new(ctx_owner);
300 let crate::Value::Struct(s) = crate::eval::eval_expression(&mut ctx, &expr) else {
301 return LogicalRect::default();
302 };
303 let as_f32 = |name: &str| -> f32 {
304 match s.get_field(name) {
305 Some(crate::Value::Number(n)) => *n as f32,
306 _ => 0.0,
307 }
308 };
309 LogicalRect::new(
310 i_slint_core::lengths::LogicalPoint::new(as_f32("x"), as_f32("y")),
311 i_slint_core::lengths::LogicalSize::new(as_f32("width"), as_f32("height")),
312 )
313 }
314
315 fn accessible_role(self: Pin<&Self>, item_index: u32) -> AccessibleRole {
316 let Some((owner, local_idx)) = resolve_accessible_item(self.get_ref(), item_index) else {
317 return AccessibleRole::default();
318 };
319 let cu = owner.compilation_unit.clone();
320 let sc = &cu.sub_components[owner.sub_component_idx];
321 let Some(expr) = sc.accessible_prop.get(&(local_idx, "Role".to_string())) else {
322 return AccessibleRole::default();
323 };
324 let mut ctx = crate::eval::EvalContext::new(owner);
325 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default()
326 }
327
328 fn accessible_string_property(
329 self: Pin<&Self>,
330 item_index: u32,
331 what: AccessibleStringProperty,
332 result: &mut SharedString,
333 ) -> bool {
334 let what_str = accessible_string_property_name(what);
335 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
336 let cu = owner.compilation_unit.clone();
337 let sc = &cu.sub_components[owner.sub_component_idx];
338 if let Some(expr) = sc.accessible_prop.get(&(local_idx, what_str.clone())) {
339 let mut ctx = crate::eval::EvalContext::new(owner);
340 if let crate::Value::String(s) =
341 crate::eval::eval_expression(&mut ctx, &expr.borrow())
342 {
343 *result = s;
344 return true;
345 }
346 }
347 }
348 false
349 }
350
351 fn accessibility_action(self: Pin<&Self>, item_index: u32, action: &AccessibilityAction) {
352 let what = format!("Action{}", accessibility_action_name(action));
353 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
354 let cu = owner.compilation_unit.clone();
355 let sc = &cu.sub_components[owner.sub_component_idx];
356 if let Some(expr) = sc.accessible_prop.get(&(local_idx, what.clone())) {
357 let args = accessibility_action_args(action);
358 let mut ctx = crate::eval::EvalContext::with_arguments(owner, args);
359 crate::eval::eval_expression(&mut ctx, &expr.borrow());
360 return;
361 }
362 }
363 }
364
365 fn supported_accessibility_actions(
366 self: Pin<&Self>,
367 item_index: u32,
368 ) -> SupportedAccessibilityAction {
369 let mut actions = SupportedAccessibilityAction::default();
370 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
371 let cu = owner.compilation_unit.clone();
372 let sc = &cu.sub_components[owner.sub_component_idx];
373 for (idx, key) in sc.accessible_prop.keys() {
374 if *idx == local_idx
375 && let Some(action_name) = key.strip_prefix("Action")
376 {
377 actions |= SupportedAccessibilityAction::from_name(action_name)
378 .unwrap_or_else(|| panic!("Not an accessible action: {action_name:?}"));
379 }
380 }
381 }
382 actions
383 }
384
385 fn item_element_infos(self: Pin<&Self>, item_index: u32, result: &mut SharedString) -> bool {
386 let this = self.get_ref();
387 let Some(entry) = this.item_table.get(item_index as usize).and_then(Option::as_ref) else {
388 return false;
389 };
390 let cu = &this.root_sub_component.compilation_unit;
391 let mut owner_sc_idx = this.root_sub_component.sub_component_idx;
401 let mut local_idx = item_index;
402 for &sub_step in entry.0.iter() {
403 let owner_sc = &cu.sub_components[owner_sc_idx];
404 if let Some(info) = owner_sc.element_infos.get(&local_idx) {
405 *result = info.as_str().into();
406 return true;
407 }
408 let nested = &owner_sc.sub_components[sub_step];
409 if local_idx == nested.index_in_tree {
411 local_idx = 0;
412 } else if nested.index_of_first_child_in_tree > 0 {
413 local_idx = local_idx + 1 - nested.index_of_first_child_in_tree;
414 }
415 owner_sc_idx = nested.ty;
416 }
417 let owner_sc = &cu.sub_components[owner_sc_idx];
418 let item_local_idx = owner_sc.items[entry.1].index_in_tree;
419 if let Some(infos) = owner_sc.element_infos.get(&item_local_idx) {
420 *result = infos.as_str().into();
421 true
422 } else {
423 false
424 }
425 }
426
427 fn element_declared_properties(
428 self: Pin<&Self>,
429 item_index: u32,
430 result: &mut SharedString,
431 ) -> bool {
432 let this = self.get_ref();
433 let cu = this.root_sub_component.compilation_unit.clone();
434 if !cu.has_debug_info {
435 return false;
436 }
437 if let Some((owner, local_idx)) = resolve_element_properties_owner(this, item_index) {
438 let sc = &cu.sub_components[owner.sub_component_idx];
439 if let Some(props) = sc.element_properties.get(&local_idx) {
440 let mut encoded = String::new();
441 for p in props {
442 use std::fmt::Write;
443 writeln!(encoded, "{}:{}", p.name, p.ty).unwrap();
444 }
445 *result = encoded.as_str().into();
446 }
447 }
448 true
449 }
450
451 fn element_property_value(
452 self: Pin<&Self>,
453 item_index: u32,
454 property_name: Slice<u8>,
455 result: &mut SharedString,
456 ) -> bool {
457 let this = self.get_ref();
458 let cu = this.root_sub_component.compilation_unit.clone();
459 if !cu.has_debug_info {
460 return false;
461 }
462 let Ok(name) = core::str::from_utf8(property_name.as_slice()) else {
463 return false;
464 };
465 let Some((owner, local_idx)) = resolve_element_properties_owner(this, item_index) else {
466 return false;
467 };
468 let sc = &cu.sub_components[owner.sub_component_idx];
469 let Some(prop) = sc
470 .element_properties
471 .get(&local_idx)
472 .and_then(|props| props.iter().find(|p| p.name == name))
473 else {
474 return false;
475 };
476 let ctx = crate::eval::EvalContext::new(owner);
477 let value = crate::eval::load_property(&ctx, &prop.prop);
478 format_element_property_value(&value, &prop.ty, result)
479 }
480
481 fn window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
482 let this = self.get_ref();
485 if let Some(adapter) = this.window_adapter.get() {
486 *result = Some(adapter.clone());
487 return;
488 }
489 let mut parent_sub = this.parent_instance.upgrade();
490 while let Some(sub) = parent_sub {
491 let Some(root_vrc) = sub.root.get().and_then(|w| w.upgrade()) else { break };
492 if let Some(adapter) = root_vrc.window_adapter.get() {
493 *result = Some(adapter.clone());
494 return;
495 }
496 parent_sub = root_vrc.parent_instance.upgrade();
497 }
498 if do_create {
499 *result = this.window_adapter_or_default();
500 }
501 }
502}
503
504fn resolve_accessible_item(
507 instance: &Instance,
508 item_index: u32,
509) -> Option<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
510 let entry = instance.item_table.get(item_index as usize).and_then(Option::as_ref)?;
511 let mut owner = instance.root_sub_component.clone();
512 for &sub_idx in entry.0.iter() {
513 let next = owner.sub_components[sub_idx].clone();
514 owner = next;
515 }
516 let cu = &owner.compilation_unit;
517 let sc = &cu.sub_components[owner.sub_component_idx];
518 let local_idx = sc.items[entry.1].index_in_tree;
519 Some((owner, local_idx))
520}
521
522fn resolve_element_properties_owner(
526 instance: &Instance,
527 item_index: u32,
528) -> Option<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
529 let entry = instance.item_table.get(item_index as usize).and_then(Option::as_ref)?;
530 let cu = instance.root_sub_component.compilation_unit.clone();
531 let mut owner = instance.root_sub_component.clone();
532 let mut local_idx = item_index;
533 for &sub_step in entry.0.iter() {
534 let sc = &cu.sub_components[owner.sub_component_idx];
535 if sc.element_properties.contains_key(&local_idx) {
536 return Some((owner, local_idx));
537 }
538 let nested = &sc.sub_components[sub_step];
539 if local_idx == nested.index_in_tree {
541 local_idx = 0;
542 } else if nested.index_of_first_child_in_tree > 0 {
543 local_idx = local_idx + 1 - nested.index_of_first_child_in_tree;
544 }
545 let next = owner.sub_components[sub_step].clone();
546 owner = next;
547 }
548 let sc = &cu.sub_components[owner.sub_component_idx];
549 let item_local_idx = sc.items[entry.1].index_in_tree;
550 sc.element_properties.contains_key(&item_local_idx).then_some((owner, item_local_idx))
551}
552
553fn format_element_property_value(
557 value: &crate::Value,
558 ty: &i_slint_compiler::langtype::Type,
559 result: &mut SharedString,
560) -> bool {
561 use i_slint_compiler::langtype::Type;
562 use i_slint_core::debug_info;
563 match ty {
564 Type::Bool => {
565 let crate::Value::Bool(b) = value else { return false };
566 *result = debug_info::format_bool(*b);
567 }
568 Type::Int32 => {
569 let crate::Value::Number(n) = value else { return false };
570 *result = debug_info::format_integer(*n as i32 as i64);
571 }
572 Type::Duration => {
573 let crate::Value::Number(n) = value else { return false };
574 *result = debug_info::format_integer(*n as i64);
575 }
576 Type::Float32
577 | Type::Angle
578 | Type::Percent
579 | Type::PhysicalLength
580 | Type::LogicalLength
581 | Type::Rem => {
582 let crate::Value::Number(n) = value else { return false };
583 *result = debug_info::format_float(*n as f32);
584 }
585 Type::String => {
586 let crate::Value::String(s) = value else { return false };
587 *result = s.clone();
588 }
589 Type::Color | Type::Brush => {
590 let crate::Value::Brush(b) = value else { return false };
591 let Some(formatted) = debug_info::format_brush(b) else { return false };
592 *result = formatted;
593 }
594 Type::Enumeration(_) => {
595 let crate::Value::EnumerationValue(_, v) = value else { return false };
596 *result = v.replace('_', "-").as_str().into();
597 }
598 _ => return false,
599 }
600 true
601}
602
603fn resolve_accessible_candidates(
609 instance: &Instance,
610 item_index: u32,
611) -> Vec<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
612 let mut out = Vec::new();
613 let Some(entry) = instance.item_table.get(item_index as usize).and_then(Option::as_ref) else {
614 return out;
615 };
616 if !entry.0.is_empty() {
619 out.push((instance.root_sub_component.clone(), item_index));
620 }
621 let mut owner = instance.root_sub_component.clone();
623 for &sub_idx in entry.0.iter() {
624 let next = owner.sub_components[sub_idx].clone();
625 owner = next;
626 }
627 let cu = &owner.compilation_unit;
628 let sc = &cu.sub_components[owner.sub_component_idx];
629 let local_idx = sc.items[entry.1].index_in_tree;
630 out.push((owner, local_idx));
631 out
632}
633
634fn accessible_string_property_name(what: AccessibleStringProperty) -> String {
638 i_slint_compiler::generator::to_pascal_case(&what.to_string())
639}
640
641fn accessibility_action_name(action: &AccessibilityAction) -> &'static str {
642 match action {
643 AccessibilityAction::Default => "Default",
644 AccessibilityAction::Decrement => "Decrement",
645 AccessibilityAction::Increment => "Increment",
646 AccessibilityAction::Expand => "Expand",
647 AccessibilityAction::ReplaceSelectedText(_) => "ReplaceSelectedText",
648 AccessibilityAction::SetValue(_) => "SetValue",
649 AccessibilityAction::SetSelectionOffsets(..) => "SetSelectionOffsets",
650 }
651}
652
653fn accessibility_action_args(action: &AccessibilityAction) -> Vec<crate::Value> {
654 match action {
655 AccessibilityAction::ReplaceSelectedText(s) | AccessibilityAction::SetValue(s) => {
656 vec![crate::Value::String(s.clone())]
657 }
658 AccessibilityAction::SetSelectionOffsets(anchor, focus) => {
659 vec![crate::Value::Number(*anchor as f64), crate::Value::Number(*focus as f64)]
660 }
661 _ => Vec::new(),
662 }
663}