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 let sorted = self.compute_z_sorted_children(index);
68 i_slint_core::item_tree::visit_item_tree(
69 &vtable::VRc::into_dyn(weak.upgrade().unwrap()),
70 &this.tree_nodes[..],
71 index,
72 order,
73 visitor,
74 &mut |order, visitor, dyn_index, instance| {
75 self.visit_dynamic_children(dyn_index, order, visitor, instance)
76 },
77 sorted.as_deref(),
78 )
79 }
80
81 fn get_item_ref(self: Pin<&Self>, index: u32) -> Pin<VRef<'_, ItemVTable>> {
82 let this = self.get_ref();
86 let entry = this
87 .item_table
88 .get(index as usize)
89 .and_then(Option::as_ref)
90 .expect("get_item_ref: tree index is not a static item");
91 let mut current: &crate::instance::SubComponentInstance = &this.root_sub_component;
95 for &sub_idx in entry.0.iter() {
96 current = ¤t.sub_components[sub_idx];
97 }
98 Pin::as_ref(¤t.items[entry.1]).as_item_ref()
99 }
100
101 fn ensure_instantiated(self: Pin<&Self>) -> bool {
102 self.get_ref().ensure_instantiated()
103 }
104
105 fn get_subtree_range(self: Pin<&Self>, index: u32) -> IndexRange {
106 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(index) else {
107 return IndexRange { start: 0, end: 0 };
108 };
109 self.get_ref().ensure_updated(index);
113 if let Some(cc) = crate::instance::component_container_item(&sub, rep_idx) {
114 return cc.subtree_range();
115 }
116 let repeater = &sub.repeaters[rep_idx];
117 let range = repeater.range();
118 IndexRange { start: range.start, end: range.end }
119 }
120
121 fn get_subtree(
122 self: Pin<&Self>,
123 index: u32,
124 subindex: usize,
125 result: &mut VWeak<ItemTreeVTable, vtable::Dyn>,
126 ) {
127 self.get_ref().ensure_updated(index);
128 let Some((sub, rep_idx)) = self.get_ref().dynamic_at(index) else {
129 return;
130 };
131 if let Some(cc) = crate::instance::component_container_item(&sub, rep_idx) {
132 if subindex == 0 {
133 *result = cc.subtree_component();
134 }
135 return;
136 }
137 let repeater = &sub.repeaters[rep_idx];
138 if let Some(instance) = repeater.instance_at(subindex) {
139 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance));
140 }
141 }
142
143 fn get_item_tree(self: Pin<&Self>) -> Slice<'_, ItemTreeNode> {
144 Slice::from(&*self.get_ref().tree_nodes)
145 }
146
147 fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) {
148 let this = self.get_ref();
152 if let Some((outer_weak, outer_index)) = this.embedded_in.get()
156 && let Some(outer) = outer_weak.upgrade()
157 {
158 *result = i_slint_core::items::ItemRc::new(outer, *outer_index).downgrade();
159 return;
160 }
161 let Some(parent_sub) = this.parent_instance.upgrade() else { return };
162 let Some(parent_root_vrc) = parent_sub.root.get().and_then(|w| w.upgrade()) else {
163 return;
164 };
165 let parent_dyn = vtable::VRc::into_dyn(parent_root_vrc.clone());
166 if let Some((_, repeater_idx)) = this.root_sub_component.repeated_in.get() {
167 let rep_idx = *repeater_idx;
173 let parent_path = sub_component_path_of(&parent_sub, &parent_root_vrc);
174 for (flat, entry) in parent_root_vrc.dynamic_table.iter().enumerate() {
175 if let Some((path, idx)) = entry.as_ref()
176 && path.as_ref() == parent_path.as_slice()
177 && *idx == rep_idx
178 {
179 *result = i_slint_core::items::ItemRc::new(parent_dyn, flat as u32).downgrade();
180 return;
181 }
182 }
183 } else {
184 *result = i_slint_core::items::ItemRc::new(parent_dyn, 0).downgrade();
187 }
188 }
189
190 fn embed_component(
191 self: Pin<&Self>,
192 parent: &VWeak<ItemTreeVTable>,
193 parent_item_tree_index: u32,
194 ) -> bool {
195 let this = self.get_ref();
198 this.embedded_in.set((parent.clone(), parent_item_tree_index)).is_ok()
199 }
200
201 fn subtree_index(self: Pin<&Self>) -> usize {
202 let this = self.get_ref();
205 let sc = &this.root_sub_component.compilation_unit.sub_components
206 [this.root_sub_component.sub_component_idx];
207 for (idx, prop) in sc.properties.iter_enumerated() {
208 if prop.name == "model_index"
209 && let crate::Value::Number(n) =
210 Pin::as_ref(&this.root_sub_component.properties[idx]).get()
211 {
212 return n as usize;
213 }
214 }
215 0
217 }
218
219 fn layout_info(self: Pin<&Self>, orientation: Orientation) -> LayoutInfo {
220 let this = self.get_ref();
221 let sc_idx = this.root_sub_component.sub_component_idx;
222 let cu = &this.root_sub_component.compilation_unit;
223 let sc = &cu.sub_components[sc_idx];
224 let expr = match orientation {
225 Orientation::Horizontal => sc.layout_info_h.borrow(),
226 Orientation::Vertical => sc.layout_info_v.borrow(),
227 };
228 let mut ctx = crate::eval::EvalContext::new(this.root_sub_component.clone());
229 crate::eval::eval_expression(&mut ctx, &expr).try_into().unwrap_or_default()
230 }
231
232 fn item_geometry(self: Pin<&Self>, item_index: u32) -> LogicalRect {
233 let this = self.get_ref();
239 let Some(entry) = this.item_table.get(item_index as usize).and_then(Option::as_ref) else {
240 return LogicalRect::default();
241 };
242 let mut owner_rc = this.root_sub_component.clone();
243 for &sub_idx in entry.0.iter() {
244 owner_rc = owner_rc.sub_components[sub_idx].clone();
245 }
246 let cu = owner_rc.compilation_unit.clone();
247 let sc = &cu.sub_components[owner_rc.sub_component_idx];
248 let item = &sc.items[entry.1];
249 let parent_placement = if !entry.0.is_empty() && item.index_in_tree == 0 {
262 let mut parent_rc = this.root_sub_component.clone();
263 for &sub_idx in &entry.0[..entry.0.len() - 1] {
264 parent_rc = parent_rc.sub_components[sub_idx].clone();
265 }
266 let placement = entry.0[entry.0.len() - 1];
267 let parent_sc = &cu.sub_components[parent_rc.sub_component_idx];
268 let placement_idx = parent_sc.sub_components[placement].index_in_tree as usize;
269 parent_sc
270 .geometries
271 .get(placement_idx)
272 .and_then(|g| g.clone())
273 .map(|expr| (expr, parent_rc))
274 } else {
275 None
276 };
277 let (expr_cell, ctx_owner) = if let Some(pair) = parent_placement {
278 pair
279 } else {
280 let tree_local_idx = item.index_in_tree as usize;
281 match sc.geometries.get(tree_local_idx) {
282 Some(Some(expr)) => (expr.clone(), owner_rc),
283 _ => return LogicalRect::default(),
284 }
285 };
286 let expr = expr_cell.borrow();
287 let mut ctx = crate::eval::EvalContext::new(ctx_owner);
288 let crate::Value::Struct(s) = crate::eval::eval_expression(&mut ctx, &expr) else {
289 return LogicalRect::default();
290 };
291 let as_f32 = |name: &str| -> f32 {
292 match s.get_field(name) {
293 Some(crate::Value::Number(n)) => *n as f32,
294 _ => 0.0,
295 }
296 };
297 LogicalRect::new(
298 i_slint_core::lengths::LogicalPoint::new(as_f32("x"), as_f32("y")),
299 i_slint_core::lengths::LogicalSize::new(as_f32("width"), as_f32("height")),
300 )
301 }
302
303 fn accessible_role(self: Pin<&Self>, item_index: u32) -> AccessibleRole {
304 let Some((owner, local_idx)) = resolve_accessible_item(self.get_ref(), item_index) else {
305 return AccessibleRole::default();
306 };
307 let cu = owner.compilation_unit.clone();
308 let sc = &cu.sub_components[owner.sub_component_idx];
309 let Some(expr) = sc.accessible_prop.get(&(local_idx, "Role".to_string())) else {
310 return AccessibleRole::default();
311 };
312 let mut ctx = crate::eval::EvalContext::new(owner);
313 crate::eval::eval_expression(&mut ctx, &expr.borrow()).try_into().unwrap_or_default()
314 }
315
316 fn accessible_string_property(
317 self: Pin<&Self>,
318 item_index: u32,
319 what: AccessibleStringProperty,
320 result: &mut SharedString,
321 ) -> bool {
322 let what_str = accessible_string_property_name(what);
323 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
324 let cu = owner.compilation_unit.clone();
325 let sc = &cu.sub_components[owner.sub_component_idx];
326 if let Some(expr) = sc.accessible_prop.get(&(local_idx, what_str.clone())) {
327 let mut ctx = crate::eval::EvalContext::new(owner);
328 if let crate::Value::String(s) =
329 crate::eval::eval_expression(&mut ctx, &expr.borrow())
330 {
331 *result = s;
332 return true;
333 }
334 }
335 }
336 false
337 }
338
339 fn accessibility_action(self: Pin<&Self>, item_index: u32, action: &AccessibilityAction) {
340 let what = format!("Action{}", accessibility_action_name(action));
341 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
342 let cu = owner.compilation_unit.clone();
343 let sc = &cu.sub_components[owner.sub_component_idx];
344 if let Some(expr) = sc.accessible_prop.get(&(local_idx, what.clone())) {
345 let args = accessibility_action_args(action);
346 let mut ctx = crate::eval::EvalContext::with_arguments(owner, args);
347 crate::eval::eval_expression(&mut ctx, &expr.borrow());
348 return;
349 }
350 }
351 }
352
353 fn supported_accessibility_actions(
354 self: Pin<&Self>,
355 item_index: u32,
356 ) -> SupportedAccessibilityAction {
357 let mut actions = SupportedAccessibilityAction::default();
358 for (owner, local_idx) in resolve_accessible_candidates(self.get_ref(), item_index) {
359 let cu = owner.compilation_unit.clone();
360 let sc = &cu.sub_components[owner.sub_component_idx];
361 for (idx, key) in sc.accessible_prop.keys() {
362 if *idx == local_idx
363 && let Some(action_name) = key.strip_prefix("Action")
364 {
365 actions |= SupportedAccessibilityAction::from_name(action_name)
366 .unwrap_or_else(|| panic!("Not an accessible action: {action_name:?}"));
367 }
368 }
369 }
370 actions
371 }
372
373 fn item_element_infos(self: Pin<&Self>, item_index: u32, result: &mut SharedString) -> bool {
374 let this = self.get_ref();
375 let Some(entry) = this.item_table.get(item_index as usize).and_then(Option::as_ref) else {
376 return false;
377 };
378 let cu = &this.root_sub_component.compilation_unit;
379 let mut owner_sc_idx = this.root_sub_component.sub_component_idx;
389 let mut local_idx = item_index;
390 for &sub_step in entry.0.iter() {
391 let owner_sc = &cu.sub_components[owner_sc_idx];
392 if let Some(info) = owner_sc.element_infos.get(&local_idx) {
393 *result = info.as_str().into();
394 return true;
395 }
396 let nested = &owner_sc.sub_components[sub_step];
397 if local_idx == nested.index_in_tree {
399 local_idx = 0;
400 } else if nested.index_of_first_child_in_tree > 0 {
401 local_idx = local_idx + 1 - nested.index_of_first_child_in_tree;
402 }
403 owner_sc_idx = nested.ty;
404 }
405 let owner_sc = &cu.sub_components[owner_sc_idx];
406 let item_local_idx = owner_sc.items[entry.1].index_in_tree;
407 if let Some(infos) = owner_sc.element_infos.get(&item_local_idx) {
408 *result = infos.as_str().into();
409 true
410 } else {
411 false
412 }
413 }
414
415 fn window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
416 let this = self.get_ref();
419 if let Some(adapter) = this.window_adapter.get() {
420 *result = Some(adapter.clone());
421 return;
422 }
423 let mut parent_sub = this.parent_instance.upgrade();
424 while let Some(sub) = parent_sub {
425 let Some(root_vrc) = sub.root.get().and_then(|w| w.upgrade()) else { break };
426 if let Some(adapter) = root_vrc.window_adapter.get() {
427 *result = Some(adapter.clone());
428 return;
429 }
430 parent_sub = root_vrc.parent_instance.upgrade();
431 }
432 if do_create {
433 *result = this.window_adapter_or_default();
434 }
435 }
436}
437
438fn resolve_accessible_item(
441 instance: &Instance,
442 item_index: u32,
443) -> Option<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
444 let entry = instance.item_table.get(item_index as usize).and_then(Option::as_ref)?;
445 let mut owner = instance.root_sub_component.clone();
446 for &sub_idx in entry.0.iter() {
447 let next = owner.sub_components[sub_idx].clone();
448 owner = next;
449 }
450 let cu = &owner.compilation_unit;
451 let sc = &cu.sub_components[owner.sub_component_idx];
452 let local_idx = sc.items[entry.1].index_in_tree;
453 Some((owner, local_idx))
454}
455
456fn resolve_accessible_candidates(
462 instance: &Instance,
463 item_index: u32,
464) -> Vec<(Pin<std::rc::Rc<crate::instance::SubComponentInstance>>, u32)> {
465 let mut out = Vec::new();
466 let Some(entry) = instance.item_table.get(item_index as usize).and_then(Option::as_ref) else {
467 return out;
468 };
469 if !entry.0.is_empty() {
472 out.push((instance.root_sub_component.clone(), item_index));
473 }
474 let mut owner = instance.root_sub_component.clone();
476 for &sub_idx in entry.0.iter() {
477 let next = owner.sub_components[sub_idx].clone();
478 owner = next;
479 }
480 let cu = &owner.compilation_unit;
481 let sc = &cu.sub_components[owner.sub_component_idx];
482 let local_idx = sc.items[entry.1].index_in_tree;
483 out.push((owner, local_idx));
484 out
485}
486
487fn accessible_string_property_name(what: AccessibleStringProperty) -> String {
491 i_slint_compiler::generator::to_pascal_case(&what.to_string())
492}
493
494fn accessibility_action_name(action: &AccessibilityAction) -> &'static str {
495 match action {
496 AccessibilityAction::Default => "Default",
497 AccessibilityAction::Decrement => "Decrement",
498 AccessibilityAction::Increment => "Increment",
499 AccessibilityAction::Expand => "Expand",
500 AccessibilityAction::ReplaceSelectedText(_) => "ReplaceSelectedText",
501 AccessibilityAction::SetValue(_) => "SetValue",
502 AccessibilityAction::SetSelection(..) => "SetSelection",
503 }
504}
505
506fn accessibility_action_args(action: &AccessibilityAction) -> Vec<crate::Value> {
507 match action {
508 AccessibilityAction::ReplaceSelectedText(s) | AccessibilityAction::SetValue(s) => {
509 vec![crate::Value::String(s.clone())]
510 }
511 AccessibilityAction::SetSelection(anchor, focus) => {
512 vec![crate::Value::Number(*anchor as f64), crate::Value::Number(*focus as f64)]
513 }
514 _ => Vec::new(),
515 }
516}