1use crate::accessible_generated::*;
7use crate::qt_window::QtWindow;
8
9use i_slint_core::accessibility::{
10 AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
11};
12use i_slint_core::item_tree::{ItemRc, ItemWeak};
13use i_slint_core::properties::{PropertyDirtyHandler, PropertyTracker};
14use i_slint_core::window::WindowInner;
15use i_slint_core::SharedVector;
16
17use cpp::*;
18use pin_project::pin_project;
19use qttypes::QString;
20
21use core::ffi::c_void;
22use std::pin::Pin;
23
24const NAME: u32 = QAccessible_Text_Name;
26const DESCRIPTION: u32 = QAccessible_Text_Description;
27const VALUE: u32 = QAccessible_Text_Value;
28const CHECKED: u32 = QAccessible_Text_UserText;
29const VALUE_MINIMUM: u32 = CHECKED + 1;
30const VALUE_MAXIMUM: u32 = VALUE_MINIMUM + 1;
31const VALUE_STEP: u32 = VALUE_MAXIMUM + 1;
32const CHECKABLE: u32 = VALUE_STEP + 1;
33const EXPANDABLE: u32 = CHECKABLE + 1;
34const EXPANDED: u32 = EXPANDABLE + 1;
35const READ_ONLY: u32 = EXPANDED + 1;
36
37pub struct AccessibleItemPropertiesTracker {
38 obj: *mut c_void,
39}
40
41impl PropertyDirtyHandler for AccessibleItemPropertiesTracker {
42 fn notify(self: Pin<&Self>) {
43 let obj = self.obj;
44 cpp!(unsafe [obj as "QObject*"] {
45 QTimer::singleShot(0, [obj = QPointer(obj)]() {
46 if (!obj)
47 return;
48
49 auto accessible_item = static_cast<Slint_accessible_item*>(QAccessible::queryAccessibleInterface(obj));
50 auto data = accessible_item->data();
51 rust!(AccessibleItemPropertiesTracker_rearm [data: Pin<&SlintAccessibleItemData> as "void*"] {
52 data.arm_state_tracker();
53 });
54
55 QAccessible::State s = {};
56 s.checked = true; auto event = QAccessibleStateChangeEvent(obj, s);
58 QAccessible::updateAccessibility(&event);
59 });
60 });
61 }
62}
63
64pub struct ValuePropertyTracker {
65 obj: *mut c_void,
66}
67
68impl PropertyDirtyHandler for ValuePropertyTracker {
69 fn notify(self: Pin<&Self>) {
70 let obj = self.obj;
71 cpp!(unsafe [obj as "QObject*"] {
72 QTimer::singleShot(0, [ obj = QPointer(obj)]() {
73 if (!obj)
74 return;
75
76 auto accessible_item = static_cast<Slint_accessible_item*>(QAccessible::queryAccessibleInterface(obj));
77 auto data = accessible_item->data();
78 rust!(ValuePropertyTracker_rearm [data: Pin<&SlintAccessibleItemData> as "void*"] {
79 data.arm_value_tracker();
80 });
81
82 auto event = QAccessibleValueChangeEvent(obj, accessible_item->currentValue());
83 QAccessible::updateAccessibility(&event);
84 });
85 });
86 }
87}
88
89pub struct LabelPropertyTracker {
90 obj: *mut c_void,
91}
92
93impl PropertyDirtyHandler for LabelPropertyTracker {
94 fn notify(self: Pin<&Self>) {
95 let obj = self.obj;
96 cpp!(unsafe [obj as "QObject*"] {
97 QTimer::singleShot(0, [obj = QPointer(obj)]() {
98 if (!obj)
99 return;
100
101 auto accessible_item = static_cast<Slint_accessible_item*>(QAccessible::queryAccessibleInterface(obj));
102 auto data = accessible_item->data();
103 rust!(LabelPropertyTracker_rearm [data: Pin<&SlintAccessibleItemData> as "void*"] {
104 data.arm_label_tracker();
105 });
106
107 auto event = QAccessibleEvent(obj, QAccessible::NameChanged);
108 QAccessible::updateAccessibility(&event);
109 });
110 });
111 }
112}
113
114pub struct DescriptionPropertyTracker {
115 obj: *mut c_void,
116}
117
118impl PropertyDirtyHandler for DescriptionPropertyTracker {
119 fn notify(self: Pin<&Self>) {
120 let obj = self.obj;
121 cpp!(unsafe [obj as "QObject*"] {
122 QTimer::singleShot(0, [obj = QPointer(obj)]() {
123 if (!obj)
124 return;
125
126 auto accessible_item = static_cast<Slint_accessible_item*>(QAccessible::queryAccessibleInterface(obj));
127 auto data = accessible_item->data();
128 rust!(DescriptionPropertyTracker_rearm [data: Pin<&SlintAccessibleItemData> as "void*"] {
129 data.arm_description_tracker();
130 });
131
132 auto event = QAccessibleEvent(obj, QAccessible::DescriptionChanged);
133 QAccessible::updateAccessibility(&event);
134 });
135 });
136 }
137}
138
139pub struct FocusDelegationPropertyTracker {
140 obj: *mut c_void,
141}
142
143impl PropertyDirtyHandler for FocusDelegationPropertyTracker {
144 fn notify(self: Pin<&Self>) {
145 let obj = self.obj;
146 cpp!(unsafe [obj as "QObject*"] {
147 QTimer::singleShot(0, [obj = QPointer(obj)]() {
148 if (!obj)
149 return;
150
151 auto accessible_item = static_cast<Slint_accessible_item*>(QAccessible::queryAccessibleInterface(obj));
152 auto data = accessible_item->data();
153 rust!(FocusDelegationPropertyTracker_rearm [data: Pin<&SlintAccessibleItemData> as "void*"] {
154 data.arm_focus_delegation_tracker();
155 });
156
157 accessible_item->delegateFocus();
158 });
159 });
160 }
161}
162
163#[pin_project]
164pub struct SlintAccessibleItemData {
165 #[pin]
166 state_tracker: PropertyTracker<AccessibleItemPropertiesTracker>,
167 #[pin]
168 value_tracker: PropertyTracker<ValuePropertyTracker>,
169 #[pin]
170 label_tracker: PropertyTracker<LabelPropertyTracker>,
171 #[pin]
172 description_tracker: PropertyTracker<DescriptionPropertyTracker>,
173 #[pin]
174 focus_delegation_tracker: PropertyTracker<FocusDelegationPropertyTracker>,
175 item: ItemWeak,
176}
177
178impl SlintAccessibleItemData {
179 fn new_pin_box(obj: *mut c_void, item: &ItemWeak) -> Pin<Box<Self>> {
180 let state_tracker =
181 PropertyTracker::new_with_dirty_handler(AccessibleItemPropertiesTracker { obj });
182 let value_tracker = PropertyTracker::new_with_dirty_handler(ValuePropertyTracker { obj });
183 let label_tracker = PropertyTracker::new_with_dirty_handler(LabelPropertyTracker { obj });
184 let description_tracker =
185 PropertyTracker::new_with_dirty_handler(DescriptionPropertyTracker { obj });
186 let focus_delegation_tracker =
187 PropertyTracker::new_with_dirty_handler(FocusDelegationPropertyTracker { obj });
188
189 let result = Box::pin(Self {
190 state_tracker,
191 value_tracker,
192 label_tracker,
193 description_tracker,
194 focus_delegation_tracker,
195 item: item.clone(),
196 });
197
198 result.as_ref().arm_state_tracker();
199 result.as_ref().arm_value_tracker();
200 result.as_ref().arm_label_tracker();
201 result.as_ref().arm_description_tracker();
202 result.as_ref().arm_focus_delegation_tracker();
203
204 result
205 }
206
207 fn arm_state_tracker(self: Pin<&Self>) {
208 let item = self.item.clone();
209 let p = self.project_ref();
210 p.state_tracker.evaluate_as_dependency_root(move || {
211 if let Some(item_rc) = item.upgrade() {
212 item_rc.accessible_string_property(AccessibleStringProperty::Checkable);
213 item_rc.accessible_string_property(AccessibleStringProperty::Checked);
214 item_rc.accessible_string_property(AccessibleStringProperty::Expandable);
215 item_rc.accessible_string_property(AccessibleStringProperty::Expanded);
216 item_rc.accessible_string_property(AccessibleStringProperty::ReadOnly);
217 }
218 });
219 }
220
221 fn arm_value_tracker(self: Pin<&Self>) {
222 let item = self.item.clone();
223 let p = self.project_ref();
224 p.value_tracker.evaluate_as_dependency_root(move || {
225 if let Some(item_rc) = item.upgrade() {
226 item_rc.accessible_string_property(AccessibleStringProperty::Value);
227 item_rc.accessible_string_property(AccessibleStringProperty::ValueMinimum);
228 item_rc.accessible_string_property(AccessibleStringProperty::ValueMaximum);
229 item_rc.accessible_string_property(AccessibleStringProperty::ValueStep);
230 }
231 });
232 }
233
234 fn arm_label_tracker(self: Pin<&Self>) {
235 let item = self.item.clone();
236 let p = self.project_ref();
237 p.label_tracker.evaluate_as_dependency_root(move || {
238 if let Some(item_rc) = item.upgrade() {
239 item_rc.accessible_string_property(AccessibleStringProperty::Label);
240 }
241 });
242 }
243
244 fn arm_description_tracker(self: Pin<&Self>) {
245 let item = self.item.clone();
246 let p = self.project_ref();
247 p.description_tracker.evaluate_as_dependency_root(move || {
248 if let Some(item_rc) = item.upgrade() {
249 item_rc.accessible_string_property(AccessibleStringProperty::Description);
250 }
251 });
252 }
253
254 fn arm_focus_delegation_tracker(self: Pin<&Self>) {
255 let item = self.item.clone();
256 let p = self.project_ref();
257 p.focus_delegation_tracker.evaluate_as_dependency_root(move || {
258 if let Some(item_rc) = item.upgrade() {
259 item_rc.accessible_string_property(AccessibleStringProperty::DelegateFocus);
260 }
261 });
262 }
263}
264
265cpp! {{
266 #include <QtGui/QAccessible>
267 #include <QtWidgets/QWidget>
268
269 #include <memory>
270
271 const uint32_t CHECKED { QAccessible::UserText };
273 const uint32_t VALUE_MINIMUM { CHECKED + 1 };
274 const uint32_t VALUE_MAXIMUM { VALUE_MINIMUM + 1 };
275 const uint32_t VALUE_STEP { VALUE_MAXIMUM + 1 };
276 const uint32_t CHECKABLE { VALUE_STEP + 1 };
277 const uint32_t EXPANDABLE { CHECKABLE + 1 };
278 const uint32_t EXPANDED { EXPANDABLE + 1 };
279 const uint32_t READ_ONLY { EXPANDED + 1 };
280
281 class Descendents {
286 public:
287 Descendents(void *root_item) {
288 rustDescendents = rust!(Descendents_ctor [root_item: *mut c_void as "void*"] ->
289 SharedVector<ItemRc> as "void*" {
290 i_slint_core::accessibility::accessible_descendents(
291 &*(root_item as *mut ItemRc))
292 .collect()
293 });
294 }
295
296 size_t count() const {
297 return rust!(Descendents_count [rustDescendents: SharedVector<ItemRc> as "void*"] -> usize as "size_t" {
298 rustDescendents.len()
299 });
300 }
301
302 void* itemAt(size_t index) {
303 return rust!(Descendents_itemAt [rustDescendents: SharedVector<ItemRc> as "void*",
304 index: usize as "size_t"]
305 -> *mut ItemWeak as "void*" {
306 let item_rc = rustDescendents[index].clone();
307 let mut item_weak = Box::new(item_rc.downgrade());
308
309 Box::into_raw(item_weak)
310 });
311 }
312
313 QAccessible::Role roleAt(size_t index) const {
314 return rust!(Descendents_roleAt [rustDescendents: SharedVector<ItemRc> as "void*",
315 index: usize as "size_t"]
316 -> u32 as "QAccessible::Role" {
317 match rustDescendents[index].accessible_role() {
318 i_slint_core::items::AccessibleRole::None => QAccessible_Role_NoRole,
319 i_slint_core::items::AccessibleRole::Button => QAccessible_Role_Button,
320 i_slint_core::items::AccessibleRole::Checkbox => QAccessible_Role_CheckBox,
321 i_slint_core::items::AccessibleRole::Combobox => QAccessible_Role_ComboBox,
322 i_slint_core::items::AccessibleRole::List => QAccessible_Role_List,
323 i_slint_core::items::AccessibleRole::Slider => QAccessible_Role_Slider,
324 i_slint_core::items::AccessibleRole::Spinbox => QAccessible_Role_SpinBox,
325 i_slint_core::items::AccessibleRole::Tab => QAccessible_Role_PageTab,
326 i_slint_core::items::AccessibleRole::TabList => QAccessible_Role_PageTabList,
327 i_slint_core::items::AccessibleRole::Text => QAccessible_Role_StaticText,
328 i_slint_core::items::AccessibleRole::ProgressIndicator => QAccessible_Role_ProgressBar,
329 i_slint_core::items::AccessibleRole::Table => QAccessible_Role_Table,
330 i_slint_core::items::AccessibleRole::Tree => QAccessible_Role_Tree,
331 i_slint_core::items::AccessibleRole::TextInput => QAccessible_Role_EditableText,
332 i_slint_core::items::AccessibleRole::Switch => QAccessible_Role_CheckBox,
333 i_slint_core::items::AccessibleRole::ListItem => QAccessible_Role_ListItem,
334 i_slint_core::items::AccessibleRole::TabPanel => QAccessible_Role_Pane,
335 i_slint_core::items::AccessibleRole::Groupbox => QAccessible_Role_Grouping,
336 i_slint_core::items::AccessibleRole::Image => QAccessible_Role_Graphic,
337 _ => QAccessible_Role_NoRole,
338 }
339 });
340 }
341
342 ~Descendents() {
343 auto descendentsPtr = &rustDescendents;
344 rust!(Descendents_dtor [descendentsPtr: *mut SharedVector<ItemRc> as "void**"] {
345 core::ptr::read(descendentsPtr);
346 });
347 }
348
349 private:
350 void *rustDescendents;
351 };
352
353 void *root_item_for_window(void *rustWindow) {
354 return rust!(root_item_for_window_ [rustWindow: &crate::qt_window::QtWindow as "void*"]
355 -> *mut c_void as "void*" {
356 let root_item = Box::new(ItemRc::new(WindowInner::from_pub(&rustWindow.window).component(), 0).downgrade());
357 Box::into_raw(root_item) as _
358 });
359 }
360
361 QString item_string_property(void *data, uint32_t what) {
362 return rust!(item_string_property_
363 [data: &SlintAccessibleItemData as "void*", what: u32 as "uint32_t"]
364 -> QString as "QString" {
365
366 if let Some(item) = data.item.upgrade() {
367 let string = match what {
368 NAME => item.accessible_string_property(AccessibleStringProperty::Label),
369 DESCRIPTION => item.accessible_string_property(AccessibleStringProperty::Description),
370 VALUE => item.accessible_string_property(AccessibleStringProperty::Value),
371 CHECKED => item.accessible_string_property(AccessibleStringProperty::Checked),
372 VALUE_MINIMUM => item.accessible_string_property(AccessibleStringProperty::ValueMinimum),
373 VALUE_MAXIMUM => item.accessible_string_property(AccessibleStringProperty::ValueMaximum),
374 VALUE_STEP => item.accessible_string_property(AccessibleStringProperty::ValueStep),
375 CHECKABLE => item.accessible_string_property(AccessibleStringProperty::Checkable),
376 EXPANDABLE => item.accessible_string_property(AccessibleStringProperty::Expandable),
377 EXPANDED => item.accessible_string_property(AccessibleStringProperty::Expanded),
378 READ_ONLY => item.accessible_string_property(AccessibleStringProperty::ReadOnly),
379 _ => None,
380 };
381 if let Some(string) = string {
382 return QString::from(string.as_ref())
383 }
384 };
385 QString::default()
386 });
387 }
388
389 class Slint_accessible : public QAccessibleInterface {
395 public:
396 Slint_accessible(QAccessible::Role role, QAccessibleInterface *parent) :
397 has_focus(false), has_focus_delegation(false), m_role(role), m_parent(parent)
398 { }
399
400 ~Slint_accessible() {
401 qDeleteAll(m_children);
402 }
403
404 virtual void *rustItem() const = 0;
405
406 virtual QWidget *qwidget() const = 0;
408
409 QPoint mapToGlobal(const QPoint p) const {
410 return qwidget()->mapToGlobal(p);
411 }
412
413 QPoint mapFromGlobal(const QPoint p) const {
414 return qwidget()->mapFromGlobal(p);
415 }
416
417 void clearFocus() {
418 has_focus = false;
419 has_focus_delegation = false;
420
421 for (int i = 0; i < rawChildCount(); ++i) {
422 static_cast<Slint_accessible *>(child(i))->clearFocus();
423 }
424 }
425
426 virtual void delegateFocus() const {
427 sendFocusChangeEvent();
428 }
429
430 bool focusItem(void *item) const {
432 auto my_item = rustItem();
433 if (rust!(Slint_accessible_findItem [item: &ItemWeak as "void *", my_item: &ItemWeak as "void*"] -> bool as "bool" {
434 item == my_item
435 })) {
436 has_focus = true;
437
438 delegateFocus();
439 return true;
440 }
441
442 for (int i = 0; i < rawChildCount(); ++i) {
443 if (static_cast<Slint_accessible *>(child(i))->focusItem(item)) {
444 return true;
445 }
446 }
447 return false;
448 }
449
450 void sendFocusChangeEvent() const {
451 auto event = QAccessibleEvent(object(), QAccessible::Focus);
452 QAccessible::updateAccessibility(&event);
453 has_focus_delegation = true;
454 }
455
456 bool isValid() const override {
457 return true;
458 }
459
460 QAccessibleInterface *parent() const override {
462 return m_parent;
463 }
464
465 QAccessibleInterface *focusChild() const override {
466 if (has_focus_delegation) {
467 return const_cast<QAccessibleInterface *>(static_cast<const QAccessibleInterface *>(this));
468 }
469 for (int i = 0; i < childCount(); ++i) {
470 if (auto focus = child(i)->focusChild()) return focus;
471 }
472 return nullptr;
473 }
474
475 int indexOfChild(const QAccessibleInterface *child) const override {
476 return m_children.indexOf(child->object()); }
478
479 int rawChildCount() const {
483 return m_children.count();
484 }
485
486 QAccessibleInterface *rawChild(int index) const {
488 if (0 <= index && index < rawChildCount())
489 return QAccessible::queryAccessibleInterface(m_children[index]);
490 return nullptr;
491 }
492
493 int childCount() const override {
497 return rawChildCount();
498 }
499
500 QAccessibleInterface *child(int index) const override {
501 if (0 <= index && index < childCount())
502 return QAccessible::queryAccessibleInterface(m_children[index]);
503 return nullptr;
504 }
505
506 void setText(QAccessible::Text t, const QString &text) override {
507 Q_UNUSED(t); Q_UNUSED(text);
508 }
509
510 QAccessible::Role role() const override {
511 return m_role;
512 }
513
514 QRect rect() const override {
515 auto item = rustItem();
516 QRectF r = rust!(Slint_accessible_item_rect
517 [item: *const ItemWeak as "void*"] -> qttypes::QRectF as "QRectF" {
518 if let Some(item_rc) = item.as_ref().unwrap().upgrade() {
519 let geometry = item_rc.geometry();
520
521 let mapped = item_rc.map_to_window(geometry.origin);
522
523 qttypes::QRectF {
524 x: mapped.x as _,
525 y: mapped.y as _,
526 width: geometry.width() as _,
527 height: geometry.height() as _,
528 }
529 } else {
530 Default::default()
531 }
532 });
533 auto topLeft = mapToGlobal(QPoint(static_cast<int>(r.left()), static_cast<int>(r.top())));
534 auto bottomRight = mapToGlobal(QPoint(static_cast<int>(r.right()), static_cast<int>(r.bottom())));
535 return QRect(topLeft, bottomRight);
536 }
537
538 QAccessibleInterface *childAt(int x, int y) const override {
539 for (int i = 0; i < childCount(); ++i) {
540 auto c = child(i);
541 auto r = c->rect();
542 if (r.contains(x, y)) return c;
543 }
544 return nullptr;
545 }
546
547 void updateAccessibilityTree() const;
548
549 protected:
550 mutable bool has_focus;
551 mutable bool has_focus_delegation;
552
553 private:
554 QAccessible::Role m_role = QAccessible::NoRole;
555 QAccessibleInterface *m_parent = nullptr;
556 mutable QList<QObject*> m_children;
557 };
558
559 class Slint_accessible_item : public Slint_accessible, public QAccessibleValueInterface, public QAccessibleActionInterface {
564 public:
565 Slint_accessible_item(void *item, QObject *obj, QAccessible::Role role, QAccessibleInterface *parent) :
566 Slint_accessible(role, parent), m_object(obj)
567 {
568 m_data = rust!(Slint_accessible_item_ctor [obj: *mut c_void as "QObject*",
569 item: &ItemWeak as "void*"] ->
570 *mut SlintAccessibleItemData as "void*" {
571 let data = SlintAccessibleItemData::new_pin_box(obj, item);
572 unsafe { Box::into_raw(Pin::into_inner_unchecked(data)) }
573 });
574 }
575
576 QAccessibleActionInterface *actionInterface() { return this; }
577 QAccessibleValueInterface *valueInterface() { return this; }
578
579
580 ~Slint_accessible_item() {
581 rust!(Slint_accessible_item_dtor [m_data: *mut SlintAccessibleItemData as "void*"] {
582 unsafe { Pin::new_unchecked(Box::from_raw(m_data)) };
583 });
584 }
585
586 void *rustItem() const override {
587 return rust!(Slint_accessible_item_rustItem [m_data: Pin<&SlintAccessibleItemData> as "void*"] -> *const ItemWeak as "void*" {
588 &m_data.item
589 });
590 }
591
592 QObject *object() const override {
593 return m_object;
594 }
595
596 QWidget *qwidget() const override {
597 return dynamic_cast<Slint_accessible *>(parent())->qwidget();
598 }
599
600 void *data() const {
601 return m_data;
602 }
603
604 QWindow *window() const override {
605 return parent()->window();
606 }
607
608 void delegateFocus() const override {
609 if (!has_focus) { return; }
610
611 auto index = rust!(Slint_accessible_item_delegate_focus [m_data: Pin<&SlintAccessibleItemData> as "void*"] -> i32 as "int" {
612 m_data.item.upgrade()
613 .and_then(|i| { i.accessible_string_property(AccessibleStringProperty::DelegateFocus) })
614 .and_then(|s| s.as_str().parse::<i32>().ok()).unwrap_or(-1)
615 });
616
617 if (index >= 0 && index < rawChildCount()) {
618 static_cast<Slint_accessible_item*>(rawChild(index))->sendFocusChangeEvent();
619 } else {
620 sendFocusChangeEvent();
621 }
622 }
623
624 QString text(QAccessible::Text t) const override {
626 return item_string_property(m_data, t);
627 }
628
629 QAccessible::State state() const override {
630 auto checked = item_string_property(m_data, CHECKED);
631
632 QAccessible::State state;
633 state.active = 1;
634 state.focusable = 1;
635 state.focused = has_focus_delegation;
636 state.checked = (checked == "true") ? 1 : 0;
637 state.checkable = (item_string_property(m_data, CHECKABLE) == "true") ? 1 : 0;
638 if (item_string_property(m_data, EXPANDABLE) == "true") {
639 state.expandable = 1;
640 if (item_string_property(m_data, EXPANDED) == "true") {
641 state.expanded = 1;
642 } else {
643 state.collapsed = 1;
644 }
645 }
646 state.readOnly = (item_string_property(m_data, READ_ONLY) == "true") ? 1 : 0;
647 return state; }
649
650 void *interface_cast(QAccessible::InterfaceType t) override {
651 if (t == QAccessible::ValueInterface && !item_string_property(m_data, QAccessible::Value).isEmpty()) {
652 return static_cast<QAccessibleValueInterface*>(this);
653 } else if (t == QAccessible::ActionInterface) {
654 return static_cast<QAccessibleActionInterface*>(this);
655 }
656 return QAccessibleInterface::interface_cast(t);
657 }
658
659 QVariant currentValue() const override {
661 return item_string_property(m_data, QAccessible::Value);
662 }
663
664 void setCurrentValue(const QVariant &value) override {
665 QString value_string = value.toString();
666 rust!(Slint_accessible_setCurrentValue [m_data: Pin<&SlintAccessibleItemData> as "void*", value_string: qttypes::QString as "QString"] {
667 let Some(item) = m_data.item.upgrade() else {return};
668 item.accessible_action(&AccessibilityAction::SetValue(i_slint_core::format!("{value_string}")));
669 });
670 }
671
672 QVariant maximumValue() const override {
673 return item_string_property(m_data, VALUE_MAXIMUM);
674 }
675
676 QVariant minimumValue() const override {
677 return item_string_property(m_data, VALUE_MINIMUM);
678 }
679
680 QVariant minimumStepSize() const override {
681 return item_string_property(m_data, VALUE_STEP);
682 }
683
684 QStringList actionNames() const override {
685 int supported = rust!(Slint_accessible_item_supported [m_data: Pin<&SlintAccessibleItemData> as "void*"] -> SupportedAccessibilityAction as "uint" {
686 m_data.item.upgrade().map(|i| i.supported_accessibility_actions()).unwrap_or_default()
687 });
688 QStringList actions;
689 if (supported & rust!(Slint_accessible_item_an1 [] -> SupportedAccessibilityAction as "uint" { SupportedAccessibilityAction::Default }))
690 actions << QAccessibleActionInterface::pressAction();
691 if (supported & rust!(Slint_accessible_item_an2 [] -> SupportedAccessibilityAction as "uint" { SupportedAccessibilityAction::Increment }))
692 actions << QAccessibleActionInterface::increaseAction();
693 if (supported & rust!(Slint_accessible_item_an3 [] -> SupportedAccessibilityAction as "uint" { SupportedAccessibilityAction::Decrement }))
694 actions << QAccessibleActionInterface::decreaseAction();
695 if (supported & rust!(Slint_accessible_item_an4 [] -> SupportedAccessibilityAction as "uint" { SupportedAccessibilityAction::Expand }))
696 actions << QAccessibleActionInterface::pressAction();
697 return actions;
698 }
699
700 void doAction(const QString &actionName) override {
701 if (actionName == QAccessibleActionInterface::pressAction()) {
702 rust!(Slint_accessible_item_do_action1 [m_data: Pin<&SlintAccessibleItemData> as "void*"] {
703 let Some(item) = m_data.item.upgrade() else {return};
704 let supported_actions = item.supported_accessibility_actions();
705 if supported_actions.contains(SupportedAccessibilityAction::Expand) {
706 item.accessible_action(&AccessibilityAction::Expand);
707 } else {
708 item.accessible_action(&AccessibilityAction::Default);
709 }
710 });
711 } else if (actionName == QAccessibleActionInterface::increaseAction()) {
712 rust!(Slint_accessible_item_do_action2 [m_data: Pin<&SlintAccessibleItemData> as "void*"] {
713 let Some(item) = m_data.item.upgrade() else {return};
714 item.accessible_action(&AccessibilityAction::Increment);
715 });
716 } else if (actionName == QAccessibleActionInterface::decreaseAction()) {
717 rust!(Slint_accessible_item_do_action3 [m_data: Pin<&SlintAccessibleItemData> as "void*"] {
718 let Some(item) = m_data.item.upgrade() else {return};
719 item.accessible_action(&AccessibilityAction::Decrement);
720 });
721 }
722 }
723
724 QStringList keyBindingsForAction(const QString &) const override {
725 return QStringList();
726 }
727
728 private:
729 QObject *m_object = nullptr;
730 mutable void *m_data = nullptr;
731 };
732
733 class Slint_accessible_window : public Slint_accessible {
738 public:
739 Slint_accessible_window(QWidget *widget, void *rust_window) :
740 Slint_accessible(QAccessible::Window, QAccessible::queryAccessibleInterface(qApp)),
741 m_widget(widget),
742 m_rustWindow(rust_window)
743 { }
744
745 ~Slint_accessible_window()
746 {
747 rust!(Slint_accessible_window_dtor [m_rustWindow: *mut c_void as "void*"] {
748 alloc::rc::Weak::from_raw(m_rustWindow as *const QtWindow); });
750 }
751
752 bool isUsed() const {
753 return is_used;
754 }
755
756 void *rustItem() const override {
757 return root_item_for_window(m_rustWindow);
758 }
759
760 QObject *object() const override {
761 return m_widget;
762 }
763
764 QWidget *qwidget() const override {
765 return m_widget;
766 }
767
768 QWindow *window() const override {
769 return qobject_cast<QWidget *>(object())->windowHandle();
770 }
771
772 int childCount() const override {
773 if (!is_used) { updateAccessibilityTree(); }
774 is_used = true;
775 return Slint_accessible::childCount();
776 }
777
778 QString text(QAccessible::Text t) const override {
780 switch (t) {
781 case QAccessible::Name: return qobject_cast<QWidget*>(object())->windowTitle();
782 default: return QString();
783 }
784 }
785
786 QAccessible::State state() const override {
787 QAccessible::State state;
788 state.active = 1;
789 state.focusable = 1;
790 return state;
791 }
792
793 private:
794 QWidget *m_widget;
795 void *m_rustWindow; mutable bool is_used = false;
797 };
798
799 QList<QObject *> deleteStaleItems(QList<QObject *> &¤t_children) {
800 current_children.erase(std::remove_if(current_children.begin(), current_children.end(), [](QObject *o) {
802 auto ai = dynamic_cast<Slint_accessible_item *>(QAccessible::queryAccessibleInterface(o));
803 Q_ASSERT(ai);
804 auto data = ai->data();
805
806 if (rust!(Slint_delete_stale_items
807 [data: Pin<&SlintAccessibleItemData> as "void*"] -> bool as "bool" {
808 data.item.upgrade().is_none()
809 })) {
810 o->deleteLater();
811 return true;
812 } else {
813 return false;
814 }
815 }), current_children.end());
816
817 return std::move(current_children);
818 }
819
820 int indexOfItem(const QList<QObject *> &existing, void *item) {
821 for (int i = 0; i < existing.count(); ++i) {
822 auto data = dynamic_cast<Slint_accessible_item *>(QAccessible::queryAccessibleInterface(existing[i]));
823 if (rust!(Slint_indexOfItems [data: Pin<&SlintAccessibleItemData> as "void*", item: &ItemWeak as "void*"] -> bool as "bool" {
824 data.item == *item
825 })) {
826 return i;
827 }
828 }
829 return -1;
830 }
831
832 QList<QObject *> updateItems(QList<QObject *> &¤t_children,
833 Descendents &descendents,
834 Slint_accessible *parent) {
835 QList<QObject *> children = {};
836 children.reserve(descendents.count());
837
838 for (size_t i = 0; i < descendents.count(); ++i) {
839 auto item = descendents.itemAt(i);
840 auto index = indexOfItem(current_children, item);
841 QObject *object = nullptr;
842 Slint_accessible_item *ai = nullptr;
843
844 if (index == -1) {
845 object = new QObject();
847 auto role = descendents.roleAt(i);
848 ai = new Slint_accessible_item(item, object, role, parent);
849
850 QAccessible::registerAccessibleInterface(ai);
851 } else {
852 object = current_children[index];
854 ai = dynamic_cast<Slint_accessible_item *>(QAccessible::queryAccessibleInterface(object));
855
856 current_children.removeAt(index);
857 }
858
859 Q_ASSERT(ai);
860 Q_ASSERT(object);
861
862 ai->updateAccessibilityTree();
863
864 children.append(object);
865 }
866
867 return children;
868 }
869
870 void Slint_accessible::updateAccessibilityTree() const {
871 QList<QObject *> valid_objects = deleteStaleItems(std::move(m_children));
872 auto descendents = Descendents(rustItem());
873
874 m_children = updateItems(std::move(valid_objects), descendents,
875 const_cast<Slint_accessible *>(this));
876 }
877}}