1use crate::instance::Instance;
11use i_slint_core::SharedString;
12use i_slint_core::SlintContext;
13use i_slint_core::accessibility::{
14 AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
15};
16use i_slint_core::item_tree::{
17 IndexRange, ItemTree, ItemTreeNode, ItemTreeVTable, ItemVisitorVTable, ItemWeak,
18 TraversalOrder, VisitChildrenResult,
19};
20use i_slint_core::items::{AccessibleRole, ItemVTable};
21use i_slint_core::layout::{LayoutInfo, Orientation};
22use i_slint_core::lengths::LogicalRect;
23use i_slint_core::slice::Slice;
24use i_slint_core::window::WindowAdapterRc;
25use std::pin::Pin;
26use vtable::{VRef, VRefMut, VWeak};
27
28i_slint_core::ItemTreeVTable_static!(static INTERPRETER_INSTANCE_VT for Instance);
29
30pub(crate) fn sub_component_path_of(
34 target: &crate::instance::SubComponentInstance,
35 parent_root: &Instance,
36) -> Vec<i_slint_compiler::llr::SubComponentInstanceIdx> {
37 fn walk(
38 current: &crate::instance::SubComponentInstance,
39 target_ptr: *const crate::instance::SubComponentInstance,
40 path: &mut Vec<i_slint_compiler::llr::SubComponentInstanceIdx>,
41 ) -> bool {
42 if std::ptr::eq(current as *const _, target_ptr) {
43 return true;
44 }
45 for (idx, nested) in current.sub_components.iter().enumerate() {
46 path.push(idx.into());
47 if walk(nested, target_ptr, path) {
48 return true;
49 }
50 path.pop();
51 }
52 false
53 }
54 let mut path = Vec::new();
55 walk(&parent_root.root_sub_component, target as *const _, &mut path);
56 path
57}
58
59impl i_slint_core::item_tree::ItemTree for Instance {
60 fn visit_children_item(
61 self: Pin<&Self>,
62 index: isize,
63 order: TraversalOrder,
64 visitor: VRefMut<'_, ItemVisitorVTable>,
65 ) -> VisitChildrenResult {
66 let this = self.get_ref();
67 let weak = this.self_weak.get().unwrap().clone();
68 if index >= 0 && this.z_sort_table.get(index as usize).is_some_and(|e| e.is_some()) {
69 i_slint_core::item_tree::visit_item_tree_z_sorted(
70 &vtable::VRc::into_dyn(weak.upgrade().unwrap()),
71 &this.tree_nodes[..],
72 index,
73 order,
74 visitor,
75 &mut |order, visitor, dyn_index| {
76 self.visit_dynamic_children(dyn_index, order, visitor)
77 },
78 &mut |push| self.collect_z_sorted_children(index, push),
79 )
80 } else {
81 i_slint_core::item_tree::visit_item_tree(
82 &vtable::VRc::into_dyn(weak.upgrade().unwrap()),
83 &this.tree_nodes[..],
84 index,
85 order,
86 visitor,
87 &mut |order, visitor, dyn_index| {
88 self.visit_dynamic_children(dyn_index, order, visitor)
89 },
90 )
91 }
92 }
93
94 fn get_item_ref(self: Pin<&Self>, index: u32) -> Pin<VRef<'_, ItemVTable>> {
95 let this = self.get_ref();
99 let entry = this
100 .item_table
101 .get(index as usize)
102 .and_then(Option::as_ref)
103 .expect("get_item_ref: tree index is not a static item");
104 let mut current: &crate::instance::SubComponentInstance = &this.root_sub_component;
108 for &sub_idx in entry.0.iter() {
109 current = ¤t.sub_components[sub_idx];
110 }
111 Pin::as_ref(¤t.items[entry.1]).as_item_ref()
112 }
113
114 fn ensure_instantiated(self: Pin<&Self>) -> bool {
115 self.get_ref().ensure_instantiated()
116 }
117
118 fn get_subtree_range(self: Pin<&Self>, index: u32) -> IndexRange {
119 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(index) else {
120 return IndexRange { start: 0, end: 0 };
121 };
122 if let Some(cc) = crate::instance::component_container_item(&sub, rep_idx) {
123 return cc.subtree_range();
124 }
125 let repeater = &sub.repeaters[rep_idx];
126 repeater.track_instance_changes();
127 let range = repeater.range();
128 IndexRange { start: range.start, end: range.end }
129 }
130
131 fn get_subtree(
132 self: Pin<&Self>,
133 index: u32,
134 subindex: usize,
135 result: &mut VWeak<ItemTreeVTable, vtable::Dyn>,
136 ) {
137 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(index) else {
138 return;
139 };
140 if let Some(cc) = crate::instance::component_container_item(&sub, rep_idx) {
141 if subindex == 0 {
142 *result = cc.subtree_component();
143 }
144 return;
145 }
146 let repeater = &sub.repeaters[rep_idx];
147 if let Some(instance) = repeater.instance_at(subindex) {
148 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance));
149 }
150 }
151
152 fn get_item_tree(self: Pin<&Self>) -> Slice<'_, ItemTreeNode> {
153 Slice::from(&*self.get_ref().tree_nodes)
154 }
155
156 fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) {
157 let this = self.get_ref();
161 if let Some((outer_weak, outer_index)) = this.embedded_in.get()
165 && let Some(outer) = outer_weak.upgrade()
166 {
167 *result = i_slint_core::items::ItemRc::new(outer, *outer_index).downgrade();
168 return;
169 }
170 let Some(parent_sub) = this.parent_instance.upgrade() else { return };
171 let Some(parent_root_vrc) = parent_sub.root.get().and_then(|w| w.upgrade()) else {
172 return;
173 };
174 let parent_dyn = vtable::VRc::into_dyn(parent_root_vrc.clone());
175 if let Some((_, repeater_idx)) = this.root_sub_component.repeated_in.get() {
176 let rep_idx = *repeater_idx;
182 let parent_path = sub_component_path_of(&parent_sub, &parent_root_vrc);
183 for (flat, entry) in parent_root_vrc.dynamic_table.iter().enumerate() {
184 if let Some((path, idx)) = entry.as_ref()
185 && path.as_ref() == parent_path.as_slice()
186 && *idx == rep_idx
187 {
188 *result = i_slint_core::items::ItemRc::new(parent_dyn, flat as u32).downgrade();
189 return;
190 }
191 }
192 } else {
193 *result = i_slint_core::items::ItemRc::new(parent_dyn, 0).downgrade();
196 }
197 }
198
199 fn embed_component(
200 self: Pin<&Self>,
201 parent: &VWeak<ItemTreeVTable>,
202 parent_item_tree_index: u32,
203 ) -> bool {
204 let this = self.get_ref();
207 this.embedded_in.set((parent.clone(), parent_item_tree_index)).is_ok()
208 }
209
210 fn subtree_index(self: Pin<&Self>) -> usize {
211 let this = self.get_ref();
214 let sc = &this.root_sub_component.compilation_unit.sub_components
215 [this.root_sub_component.sub_component_idx];
216 for (idx, prop) in sc.properties.iter_enumerated() {
217 if prop.name == "model_index"
218 && let crate::Value::Number(n) =
219 Pin::as_ref(&this.root_sub_component.properties[idx]).get()
220 {
221 return n as usize;
222 }
223 }
224 0
226 }
227
228 fn layout_info(self: Pin<&Self>, orientation: Orientation) -> LayoutInfo {
229 let this = self.get_ref();
230 let sc_idx = this.root_sub_component.sub_component_idx;
231 let cu = &this.root_sub_component.compilation_unit;
232 let sc = &cu.sub_components[sc_idx];
233 let expr = match orientation {
234 Orientation::Horizontal => sc.layout_info_h.borrow(),
235 Orientation::Vertical => sc.layout_info_v.borrow(),
236 };
237 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
238 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default()
239 }
240
241 fn item_geometry(self: Pin<&Self>, item_index: u32) -> LogicalRect {
242 let this = self.get_ref();
248 let Some(entry) = this.item_table.get(item_index as usize).and_then(Option::as_ref) else {
249 return LogicalRect::default();
250 };
251 let mut owner_rc = this.root_sub_component.clone();
252 for &sub_idx in entry.0.iter() {
253 owner_rc = owner_rc.sub_components[sub_idx].clone();
254 }
255 let cu = owner_rc.compilation_unit.clone();
256 let sc = &cu.sub_components[owner_rc.sub_component_idx];
257 let item = &sc.items[entry.1];
258 let parent_placement = if !entry.0.is_empty() && item.index_in_tree == 0 {
271 let mut parent_rc = this.root_sub_component.clone();
272 for &sub_idx in &entry.0[..entry.0.len() - 1] {
273 parent_rc = parent_rc.sub_components[sub_idx].clone();
274 }
275 let placement = entry.0[entry.0.len() - 1];
276 let parent_sc = &cu.sub_components[parent_rc.sub_component_idx];
277 let placement_idx = parent_sc.sub_components[placement].index_in_tree as usize;
278 parent_sc
279 .geometries
280 .get(placement_idx)
281 .and_then(|g| g.clone())
282 .map(|expr| (expr, parent_rc))
283 } else {
284 None
285 };
286 let (expr_cell, ctx_owner) = if let Some(pair) = parent_placement {
287 pair
288 } else {
289 let tree_local_idx = item.index_in_tree as usize;
290 match sc.geometries.get(tree_local_idx) {
291 Some(Some(expr)) => (expr.clone(), owner_rc),
292 _ => return LogicalRect::default(),
293 }
294 };
295 let expr = expr_cell.borrow();
296 let mut ctx = crate::eval::EvalContext::new(ctx_owner);
297 let crate::Value::Struct(s) = crate::eval::eval_expression(&mut ctx, &expr) else {
298 return LogicalRect::default();
299 };
300 let as_f32 = |name: &str| -> f32 {
301 match s.get_field(name) {
302 Some(crate::Value::Number(n)) => *n as f32,
303 _ => 0.0,
304 }
305 };
306 LogicalRect::new(
307 i_slint_core::lengths::LogicalPoint::new(as_f32("x"), as_f32("y")),
308 i_slint_core::lengths::LogicalSize::new(as_f32("width"), as_f32("height")),
309 )
310 }
311
312 fn accessible_role(self: Pin<&Self>, item_index: u32) -> AccessibleRole {
313 let Some((owner, local_idx)) = resolve_accessible_item(self.get_ref(), item_index) else {
314 return AccessibleRole::default();
315 };
316 let cu = owner.compilation_unit.clone();
317 let sc = &cu.sub_components[owner.sub_component_idx];
318 let Some(expr) = sc.accessible_prop.get(&(local_idx, "Role".to_string())) else {
319 return AccessibleRole::default();
320 };
321 let mut ctx = crate::eval::EvalContext::new(owner);
322 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default()
323 }
324
325 fn accessible_string_property(
326 self: Pin<&Self>,
327 item_index: u32,
328 what: AccessibleStringProperty,
329 result: &mut SharedString,
330 ) -> bool {
331 let what_str = accessible_string_property_name(what);
332 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
333 let cu = owner.compilation_unit.clone();
334 let sc = &cu.sub_components[owner.sub_component_idx];
335 if let Some(expr) = sc.accessible_prop.get(&(local_idx, what_str.clone())) {
336 let mut ctx = crate::eval::EvalContext::new(owner);
337 if let crate::Value::String(s) =
338 crate::eval::eval_expression(&mut ctx, &expr.borrow())
339 {
340 *result = s;
341 return true;
342 }
343 }
344 }
345 false
346 }
347
348 fn accessibility_action(self: Pin<&Self>, item_index: u32, action: &AccessibilityAction) {
349 let what = format!("Action{}", accessibility_action_name(action));
350 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
351 let cu = owner.compilation_unit.clone();
352 let sc = &cu.sub_components[owner.sub_component_idx];
353 if let Some(expr) = sc.accessible_prop.get(&(local_idx, what.clone())) {
354 let args = accessibility_action_args(action);
355 let mut ctx = crate::eval::EvalContext::with_arguments(owner, args);
356 crate::eval::eval_expression(&mut ctx, &expr.borrow());
357 return;
358 }
359 }
360 }
361
362 fn supported_accessibility_actions(
363 self: Pin<&Self>,
364 item_index: u32,
365 ) -> SupportedAccessibilityAction {
366 let mut actions = SupportedAccessibilityAction::default();
367 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
368 let cu = owner.compilation_unit.clone();
369 let sc = &cu.sub_components[owner.sub_component_idx];
370 for (idx, key) in sc.accessible_prop.keys() {
371 if *idx == local_idx
372 && let Some(action_name) = key.strip_prefix("Action")
373 {
374 actions |= SupportedAccessibilityAction::from_name(action_name)
375 .unwrap_or_else(|| panic!("Not an accessible action: {action_name:?}"));
376 }
377 }
378 }
379 actions
380 }
381
382 fn item_element_infos(self: Pin<&Self>, item_index: u32, result: &mut SharedString) -> bool {
383 let this = self.get_ref();
384 let cu = &this.root_sub_component.compilation_unit;
385 if !cu.has_debug_info {
390 return false;
391 }
392 let Some(entry) = this.item_table.get(item_index as usize).and_then(Option::as_ref) else {
393 return true;
394 };
395 let mut owner_sc_idx = this.root_sub_component.sub_component_idx;
405 let mut local_idx = item_index;
406 for &sub_step in entry.0.iter() {
407 let owner_sc = &cu.sub_components[owner_sc_idx];
408 if let Some(info) = owner_sc.element_infos.get(&local_idx) {
409 *result = info.as_str().into();
410 return true;
411 }
412 let nested = &owner_sc.sub_components[sub_step];
413 if local_idx == nested.index_in_tree {
415 local_idx = 0;
416 } else if nested.index_of_first_child_in_tree > 0 {
417 local_idx = local_idx + 1 - nested.index_of_first_child_in_tree;
418 }
419 owner_sc_idx = nested.ty;
420 }
421 let owner_sc = &cu.sub_components[owner_sc_idx];
422 let item_local_idx = owner_sc.items[entry.1].index_in_tree;
423 if let Some(infos) = owner_sc.element_infos.get(&item_local_idx) {
424 *result = infos.as_str().into();
425 }
426 true
427 }
428
429 fn slint_context(self: Pin<&Self>, result: &mut Option<SlintContext>) {
430 *result = Some(self.get_ref().globals.context.clone());
431 }
432
433 fn window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
434 let this = self.get_ref();
437 if let Some(adapter) = this.window_adapter.get() {
438 *result = Some(adapter.clone());
439 return;
440 }
441 let mut parent_sub = this.parent_instance.upgrade();
442 while let Some(sub) = parent_sub {
443 let Some(root_vrc) = sub.root.get().and_then(|w| w.upgrade()) else { break };
444 if let Some(adapter) = root_vrc.window_adapter.get() {
445 *result = Some(adapter.clone());
446 return;
447 }
448 parent_sub = root_vrc.parent_instance.upgrade();
449 }
450 if do_create {
451 *result = this.window_adapter_or_default();
452 }
453 }
454}
455
456fn resolve_accessible_item(
459 instance: &Instance,
460 item_index: u32,
461) -> Option<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
462 let entry = instance.item_table.get(item_index as usize).and_then(Option::as_ref)?;
463 let mut owner = instance.root_sub_component.clone();
464 for &sub_idx in entry.0.iter() {
465 let next = owner.sub_components[sub_idx].clone();
466 owner = next;
467 }
468 let cu = &owner.compilation_unit;
469 let sc = &cu.sub_components[owner.sub_component_idx];
470 let local_idx = sc.items[entry.1].index_in_tree;
471 Some((owner, local_idx))
472}
473
474fn resolve_accessible_candidates(
480 instance: &Instance,
481 item_index: u32,
482) -> Vec<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
483 let mut out = Vec::new();
484 let Some(entry) = instance.item_table.get(item_index as usize).and_then(Option::as_ref) else {
485 return out;
486 };
487 if !entry.0.is_empty() {
490 out.push((instance.root_sub_component.clone(), item_index));
491 }
492 let mut owner = instance.root_sub_component.clone();
494 for &sub_idx in entry.0.iter() {
495 let next = owner.sub_components[sub_idx].clone();
496 owner = next;
497 }
498 let cu = &owner.compilation_unit;
499 let sc = &cu.sub_components[owner.sub_component_idx];
500 let local_idx = sc.items[entry.1].index_in_tree;
501 out.push((owner, local_idx));
502 out
503}
504
505fn accessible_string_property_name(what: AccessibleStringProperty) -> String {
509 i_slint_compiler::generator::to_pascal_case(&what.to_string())
510}
511
512fn accessibility_action_name(action: &AccessibilityAction) -> &'static str {
513 match action {
514 AccessibilityAction::Default => "Default",
515 AccessibilityAction::Decrement => "Decrement",
516 AccessibilityAction::Increment => "Increment",
517 AccessibilityAction::Expand => "Expand",
518 AccessibilityAction::ReplaceSelectedText(_) => "ReplaceSelectedText",
519 AccessibilityAction::SetValue(_) => "SetValue",
520 AccessibilityAction::SetSelectionOffsets(..) => "SetSelectionOffsets",
521 }
522}
523
524fn accessibility_action_args(action: &AccessibilityAction) -> Vec<crate::Value> {
525 match action {
526 AccessibilityAction::ReplaceSelectedText(s) | AccessibilityAction::SetValue(s) => {
527 vec![crate::Value::String(s.clone())]
528 }
529 AccessibilityAction::SetSelectionOffsets(anchor, focus) => {
530 vec![crate::Value::Number(*anchor as f64), crate::Value::Number(*focus as f64)]
531 }
532 _ => Vec::new(),
533 }
534}