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 window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
428 let this = self.get_ref();
431 if let Some(adapter) = this.window_adapter.get() {
432 *result = Some(adapter.clone());
433 return;
434 }
435 let mut parent_sub = this.parent_instance.upgrade();
436 while let Some(sub) = parent_sub {
437 let Some(root_vrc) = sub.root.get().and_then(|w| w.upgrade()) else { break };
438 if let Some(adapter) = root_vrc.window_adapter.get() {
439 *result = Some(adapter.clone());
440 return;
441 }
442 parent_sub = root_vrc.parent_instance.upgrade();
443 }
444 if do_create {
445 *result = this.window_adapter_or_default();
446 }
447 }
448}
449
450fn resolve_accessible_item(
453 instance: &Instance,
454 item_index: u32,
455) -> Option<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
456 let entry = instance.item_table.get(item_index as usize).and_then(Option::as_ref)?;
457 let mut owner = instance.root_sub_component.clone();
458 for &sub_idx in entry.0.iter() {
459 let next = owner.sub_components[sub_idx].clone();
460 owner = next;
461 }
462 let cu = &owner.compilation_unit;
463 let sc = &cu.sub_components[owner.sub_component_idx];
464 let local_idx = sc.items[entry.1].index_in_tree;
465 Some((owner, local_idx))
466}
467
468fn resolve_accessible_candidates(
474 instance: &Instance,
475 item_index: u32,
476) -> Vec<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
477 let mut out = Vec::new();
478 let Some(entry) = instance.item_table.get(item_index as usize).and_then(Option::as_ref) else {
479 return out;
480 };
481 if !entry.0.is_empty() {
484 out.push((instance.root_sub_component.clone(), item_index));
485 }
486 let mut owner = instance.root_sub_component.clone();
488 for &sub_idx in entry.0.iter() {
489 let next = owner.sub_components[sub_idx].clone();
490 owner = next;
491 }
492 let cu = &owner.compilation_unit;
493 let sc = &cu.sub_components[owner.sub_component_idx];
494 let local_idx = sc.items[entry.1].index_in_tree;
495 out.push((owner, local_idx));
496 out
497}
498
499fn accessible_string_property_name(what: AccessibleStringProperty) -> String {
503 i_slint_compiler::generator::to_pascal_case(&what.to_string())
504}
505
506fn accessibility_action_name(action: &AccessibilityAction) -> &'static str {
507 match action {
508 AccessibilityAction::Default => "Default",
509 AccessibilityAction::Decrement => "Decrement",
510 AccessibilityAction::Increment => "Increment",
511 AccessibilityAction::Expand => "Expand",
512 AccessibilityAction::ReplaceSelectedText(_) => "ReplaceSelectedText",
513 AccessibilityAction::SetValue(_) => "SetValue",
514 AccessibilityAction::SetSelectionOffsets(..) => "SetSelectionOffsets",
515 }
516}
517
518fn accessibility_action_args(action: &AccessibilityAction) -> Vec<crate::Value> {
519 match action {
520 AccessibilityAction::ReplaceSelectedText(s) | AccessibilityAction::SetValue(s) => {
521 vec![crate::Value::String(s.clone())]
522 }
523 AccessibilityAction::SetSelectionOffsets(anchor, focus) => {
524 vec![crate::Value::Number(*anchor as f64), crate::Value::Number(*focus as f64)]
525 }
526 _ => Vec::new(),
527 }
528}