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