Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qaccessiblequickitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtGui/private/qaccessiblehelper_p.h>
8#include <QtGui/qtextdocument.h>
9
10#include "QtQuick/private/qquickitem_p.h"
11#include "QtQuick/private/qquicktext_p.h"
12#include <private/qquicktext_p_p.h>
13
14#include "QtQuick/private/qquicktextinput_p.h"
15#include "QtQuick/private/qquickaccessibleattached_p.h"
16#include "QtQuick/qquicktextdocument.h"
17#include "QtQuick/qquickrendercontrol.h"
18QT_BEGIN_NAMESPACE
19
20#if QT_CONFIG(accessibility)
21
22class QAccessibleHyperlink : public QAccessibleInterface, public QAccessibleHyperlinkInterface {
23public:
24 QAccessibleHyperlink(QQuickItem *parentTextItem, int linkIndex);
25
26 // check for valid pointers
27 bool isValid() const override;
28 QObject *object() const override;
29 QWindow *window() const override;
30
31 // navigation, hierarchy
32 QAccessibleInterface *parent() const override;
33 QAccessibleInterface *child(int index) const override;
34 int childCount() const override;
35 int indexOfChild(const QAccessibleInterface *iface) const override;
36 QAccessibleInterface *childAt(int x, int y) const override;
37
38 // properties and state
39 QString text(QAccessible::Text) const override;
40 void setText(QAccessible::Text, const QString &text) override;
41 QRect rect() const override;
42 QAccessible::Role role() const override;
43 QAccessible::State state() const override;
44
45 void *interface_cast(QAccessible::InterfaceType t) override;
46
47 // QAccessibleHyperlinkInterface
48 QString anchor() const override
49 {
50 const QVector<QQuickTextPrivate::LinkDesc> links = QQuickTextPrivate::get(textItem())->getLinks();
51 if (linkIndex < links.size())
52 return links.at(linkIndex).m_anchor;
53 return QString();
54 }
55
56 QString anchorTarget() const override
57 {
58 const QVector<QQuickTextPrivate::LinkDesc> links = QQuickTextPrivate::get(textItem())->getLinks();
59 if (linkIndex < links.size())
60 return links.at(linkIndex).m_anchorTarget;
61 return QString();
62 }
63
64 int startIndex() const override
65 {
66 const QVector<QQuickTextPrivate::LinkDesc> links = QQuickTextPrivate::get(textItem())->getLinks();
67 if (linkIndex < links.size())
68 return links.at(linkIndex).m_startIndex;
69 return -1;
70 }
71
72 int endIndex() const override
73 {
74 const QVector<QQuickTextPrivate::LinkDesc> links = QQuickTextPrivate::get(textItem())->getLinks();
75 if (linkIndex < links.size())
76 return links.at(linkIndex).m_endIndex;
77 return -1;
78 }
79
80private:
81 QQuickText *textItem() const { return qobject_cast<QQuickText*>(parentTextItem); }
82 QQuickItem *parentTextItem;
83 const int linkIndex;
84
85 friend class QAccessibleQuickItem;
86};
87
88
89QAccessibleHyperlink::QAccessibleHyperlink(QQuickItem *parentTextItem, int linkIndex)
90 : parentTextItem(parentTextItem),
91 linkIndex(linkIndex)
92{
93}
94
95
96bool QAccessibleHyperlink::isValid() const
97{
98 return textItem();
99}
100
101
102QObject *QAccessibleHyperlink::object() const
103{
104 return nullptr;
105}
106
107
108QWindow *QAccessibleHyperlink::window() const
109{
110 return textItem()->window();
111}
112
113
114/* \reimp */
115QRect QAccessibleHyperlink::rect() const
116{
117 const QVector<QQuickTextPrivate::LinkDesc> links = QQuickTextPrivate::get(textItem())->getLinks();
118 if (linkIndex < links.size()) {
119 const QPoint tl = itemScreenRect(textItem()).topLeft();
120 return links.at(linkIndex).rect.translated(tl);
121 }
122 return QRect();
123}
124
125/* \reimp */
126QAccessibleInterface *QAccessibleHyperlink::childAt(int, int) const
127{
128 return nullptr;
129}
130
131/* \reimp */
132QAccessibleInterface *QAccessibleHyperlink::parent() const
133{
134 return QAccessible::queryAccessibleInterface(textItem());
135}
136
137/* \reimp */
138QAccessibleInterface *QAccessibleHyperlink::child(int) const
139{
140 return nullptr;
141}
142
143/* \reimp */
144int QAccessibleHyperlink::childCount() const
145{
146 return 0;
147}
148
149/* \reimp */
150int QAccessibleHyperlink::indexOfChild(const QAccessibleInterface *) const
151{
152 return -1;
153}
154
155/* \reimp */
156QAccessible::State QAccessibleHyperlink::state() const
157{
158 QAccessible::State s;
159 s.selectable = true;
160 s.focusable = true;
161 s.selectableText = true;
162 s.selected = false;
163 return s;
164}
165
166/* \reimp */
167QAccessible::Role QAccessibleHyperlink::role() const
168{
169 return QAccessible::Link;
170}
171
172/* \reimp */
173QString QAccessibleHyperlink::text(QAccessible::Text t) const
174{
175 // AT servers have different behaviors:
176 // Wordpad on windows have this behavior:
177 // * Name returns the anchor target (URL)
178 // * Value returns the anchor target (URL)
179
180 // Other AT servers (e.g. MS Edge on Windows) does what seems to be more sensible:
181 // * Name returns the anchor name
182 // * Value returns the anchor target (URL)
183 if (t == QAccessible::Name)
184 return anchor();
185 if (t == QAccessible::Value)
186 return anchorTarget();
187 return QString();
188}
189
190/* \reimp */
191void QAccessibleHyperlink::setText(QAccessible::Text, const QString &)
192{
193
194}
195
196/* \reimp */
197void *QAccessibleHyperlink::interface_cast(QAccessible::InterfaceType t)
198{
199 if (t == QAccessible::HyperlinkInterface)
200 return static_cast<QAccessibleHyperlinkInterface*>(this);
201 return nullptr;
202}
203
204
205/*!
206 * \internal
207 * \brief QAccessibleQuickItem::QAccessibleQuickItem
208 * \param item
209 */
210QAccessibleQuickItem::QAccessibleQuickItem(QQuickItem *item)
211 : QAccessibleObject(item), m_doc(textDocument())
212{
213}
214
215bool QAccessibleQuickItem::isValid() const
216{
217 return item() && !QQuickItemPrivate::get(item())->inDestructor;
218}
219
220
221QWindow *QAccessibleQuickItem::window() const
222{
223 QQuickWindow *window = item()->window();
224
225 // For QQuickWidget the above window will be the offscreen QQuickWindow,
226 // which is not a part of the accessibility tree. Detect this case and
227 // return the window for the QQuickWidget instead.
228 if (window && !window->handle()) {
229 if (QQuickRenderControl *renderControl = QQuickWindowPrivate::get(window)->renderControl) {
230 if (QWindow *renderWindow = renderControl->renderWindow(nullptr))
231 return renderWindow;
232 }
233 }
234
235 return window;
236}
237
238int QAccessibleQuickItem::childCount() const
239{
240 // see comment in QAccessibleQuickItem::child() as to why we do this
241 int cc = 0;
242 if (QQuickText *textItem = qobject_cast<QQuickText*>(item())) {
243 cc = QQuickTextPrivate::get(textItem)->getLinks().size();
244 }
245 cc += childItems().size();
246 return cc;
247}
248
249QRect QAccessibleQuickItem::rect() const
250{
251 const QRect r = itemScreenRect(item());
252 return r;
253}
254
255QRect QAccessibleQuickItem::viewRect() const
256{
257 // ### no window in some cases.
258 if (!item()->window()) {
259 return QRect();
260 }
261
262 QQuickWindow *window = item()->window();
263 QPoint screenPos = window->mapToGlobal(QPoint(0,0));
264 return QRect(screenPos, window->size());
265}
266
267
268bool QAccessibleQuickItem::clipsChildren() const
269{
270 return static_cast<QQuickItem *>(item())->clip();
271}
272
273QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const
274{
275 if (item()->clip()) {
276 if (!rect().contains(x, y))
277 return nullptr;
278 }
279
280 // special case for text interfaces
281 if (QQuickText *textItem = qobject_cast<QQuickText*>(item())) {
282 const auto hyperLinkChildCount = QQuickTextPrivate::get(textItem)->getLinks().size();
283 for (auto i = 0; i < hyperLinkChildCount; i++) {
284 QAccessibleInterface *iface = child(i);
285 if (iface->rect().contains(x,y)) {
286 return iface;
287 }
288 }
289 }
290
291 // general item hit test
292 const QList<QQuickItem*> kids = accessibleUnignoredChildren(item(), true);
293 for (int i = kids.size() - 1; i >= 0; --i) {
294 QAccessibleInterface *childIface = QAccessible::queryAccessibleInterface(kids.at(i));
295 if (QAccessibleInterface *childChild = childIface->childAt(x, y))
296 return childChild;
297 if (childIface && !childIface->state().invisible) {
298 if (childIface->rect().contains(x, y))
299 return childIface;
300 }
301 }
302
303 return nullptr;
304}
305
306QAccessibleInterface *QAccessibleQuickItem::parent() const
307{
308 QQuickItem *parent = item()->parentItem();
309 QQuickWindow *itemWindow = item()->window();
310 QQuickItem *ci = itemWindow ? itemWindow->contentItem() : nullptr;
311 while (parent && !QQuickItemPrivate::get(parent)->isAccessible && parent != ci)
312 parent = parent->parentItem();
313
314 if (parent) {
315 if (parent == ci) {
316 if (itemWindow && !itemWindow->handle()) {
317 // If the item's window is a QQuickWidgetOffscreenWindow, then use
318 // the QQuickWidget as the accessible parent of the item. Since the
319 // QQuickWidget reports the quick items as its accessible children,
320 // why not report QQuickWidget as their accessible parent?
321 // The QQuickWidget instance has been set as the "_q_parentWidget"
322 // property of the QQuickWidgetOffscreenWindow when creating the
323 // instance of its offscreenWindow
324 const auto parentWidgetProp = itemWindow->property("_q_parentWidget");
325 if (parentWidgetProp.isValid()) {
326 if (QObject *parentWidget = parentWidgetProp.value<QObject *>())
327 return QAccessible::queryAccessibleInterface(parentWidget);
328 }
329 }
330 // Jump out to the window if the parent is the root item
331 return QAccessible::queryAccessibleInterface(window());
332 } else {
333 while (parent && !parent->d_func()->isAccessible)
334 parent = parent->parentItem();
335 return QAccessible::queryAccessibleInterface(parent);
336 }
337 }
338 return nullptr;
339}
340
341QAccessibleInterface *QAccessibleQuickItem::child(int index) const
342{
343 /* Text with hyperlinks will have dedicated children interfaces representing each hyperlink.
344
345 For the pathological case when a Text node has hyperlinks in its text *and* accessible
346 quick items as children, we put the hyperlink a11y interfaces as the first children, then
347 the other interfaces follows the hyperlink children (as siblings).
348
349 For example, suppose you have two links in the text and an image as a child of the text,
350 it will have the following a11y hierarchy:
351
352 [a11y:TextInterface]
353 |
354 +- [a11y:HyperlinkInterface]
355 +- [a11y:HyperlinkInterface]
356 +- [a11y:ImageInterface]
357
358 Having this order (as opposed to having hyperlink interfaces last) will at least
359 ensure that the child id of hyperlink children is not altered when child is added/removed
360 to the text item and marked accessible.
361 In addition, hyperlink interfaces as children should be the common case, so it is preferred
362 to explore those first when iterating.
363 */
364 if (index < 0)
365 return nullptr;
366
367
368 if (QQuickText *textItem = qobject_cast<QQuickText*>(item())) {
369 const int hyperLinkChildCount = QQuickTextPrivate::get(textItem)->getLinks().size();
370 if (index < hyperLinkChildCount) {
371 auto it = m_childToId.constFind(index);
372 if (it != m_childToId.constEnd())
373 return QAccessible::accessibleInterface(it.value());
374
375 QAccessibleHyperlink *iface = new QAccessibleHyperlink(item(), index);
376 QAccessible::Id id = QAccessible::registerAccessibleInterface(iface);
377 m_childToId.insert(index, id);
378 return iface;
379 }
380 index -= hyperLinkChildCount;
381 }
382
383 QList<QQuickItem *> children = childItems();
384 if (index < children.size()) {
385 QQuickItem *child = children.at(index);
386 return QAccessible::queryAccessibleInterface(child);
387 }
388 return nullptr;
389}
390
391int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const
392{
393 int hyperLinkChildCount = 0;
394 if (QQuickText *textItem = qobject_cast<QQuickText*>(item())) {
395 hyperLinkChildCount = QQuickTextPrivate::get(textItem)->getLinks().size();
396 if (QAccessibleHyperlinkInterface *hyperLinkIface = const_cast<QAccessibleInterface *>(iface)->hyperlinkInterface()) {
397 // ### assumes that there is only one subclass implementing QAccessibleHyperlinkInterface
398 // Alternatively, we could simply iterate with child() and do a linear search for it
399 QAccessibleHyperlink *hyperLink = static_cast<QAccessibleHyperlink*>(hyperLinkIface);
400 if (hyperLink->textItem() == static_cast<QQuickText*>(item())) {
401 return hyperLink->linkIndex;
402 }
403 }
404 }
405 QList<QQuickItem*> kids = childItems();
406 int idx = kids.indexOf(static_cast<QQuickItem*>(iface->object()));
407 if (idx >= 0)
408 idx += hyperLinkChildCount;
409 return idx;
410}
411
412static void unignoredChildren(QQuickItem *item, QList<QQuickItem *> *items, bool paintOrder)
413{
414 const QList<QQuickItem*> childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems()
415 : item->childItems();
416 for (QQuickItem *child : childItems) {
417 if (QQuickItemPrivate::get(child)->isAccessible) {
418 items->append(child);
419 } else {
420 unignoredChildren(child, items, paintOrder);
421 }
422 }
423}
424
425QList<QQuickItem *> accessibleUnignoredChildren(QQuickItem *item, bool paintOrder)
426{
427 QList<QQuickItem *> items;
428 unignoredChildren(item, &items, paintOrder);
429 return items;
430}
431
432QList<QQuickItem *> QAccessibleQuickItem::childItems() const
433{
434 return accessibleUnignoredChildren(item());
435}
436
437static bool isTextRole(QAccessible::Role role)
438{
439 return role == QAccessible::EditableText || role == QAccessible::StaticText;
440}
441
442QAccessible::State QAccessibleQuickItem::state() const
443{
444 QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item());
445 if (!attached)
446 return QAccessible::State();
447
448 QAccessible::State state = attached->state();
449
450 QRect viewRect_ = viewRect();
451 QRect itemRect = rect();
452
453 if (viewRect_.isNull() || itemRect.isNull() || !window() || !window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity()))
454 state.invisible = true;
455 if (!viewRect_.intersects(itemRect))
456 state.offscreen = true;
457 if ((role() == QAccessible::CheckBox
458 || role() == QAccessible::RadioButton
459 || role() == QAccessible::Switch)
460 && object()->property("checked").toBool())
461 state.checked = true;
462 if (item()->activeFocusOnTab() || isTextRole(role()))
463 state.focusable = true;
464 if (item()->hasActiveFocus())
465 state.focused = true;
466 if (role() == QAccessible::EditableText)
467 if (auto ti = qobject_cast<QQuickTextInput *>(item()))
468 state.passwordEdit = ti->echoMode() != QQuickTextInput::Normal;
469 if (!item()->isEnabled()) {
470 state.focusable = false;
471 state.disabled = true;
472 }
473 return state;
474}
475
476QList<std::pair<QAccessibleInterface *, QAccessible::Relation>>
477QAccessibleQuickItem::relations(QAccessible::Relation match) const
478{
479 QList<std::pair<QAccessibleInterface *, QAccessible::Relation>> rels =
480 QAccessibleObject::relations(match);
481 if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item())) {
482 if (match & QAccessible::Labelled) {
483 if (auto *labelFor = attached->labelFor()) {
484 rels.append({QAccessible::queryAccessibleInterface(labelFor),
485 QAccessible::Labelled});
486 }
487 }
488
489 if (match & QAccessible::Label) {
490 if (auto *labelledBy = attached->labelledBy()) {
491 rels.append({QAccessible::queryAccessibleInterface(labelledBy),
492 QAccessible::Label});
493 }
494 }
495 }
496 return rels;
497}
498
499QAccessible::Role QAccessibleQuickItem::role() const
500{
501 // Workaround for setAccessibleRole() not working for
502 // Text items. Text items are special since they are defined
503 // entirely from C++ (setting the role from QML works.)
504
505 QAccessible::Role role = QAccessible::NoRole;
506 if (item())
507 role = QQuickItemPrivate::get(item())->effectiveAccessibleRole();
508 if (role == QAccessible::NoRole) {
509 if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
510 role = QAccessible::StaticText;
511 else if (qobject_cast<QQuickTextInput*>(const_cast<QQuickItem *>(item())))
512 role = QAccessible::EditableText;
513 else
514 role = QAccessible::Client;
515 }
516
517 return role;
518}
519
520bool QAccessibleQuickItem::isAccessible() const
521{
522 return item()->d_func()->isAccessible;
523}
524
525QStringList QAccessibleQuickItem::actionNames() const
526{
527 QStringList actions;
528 switch (role()) {
529 case QAccessible::Link:
530 case QAccessible::PushButton:
531 case QAccessible::MenuItem:
532 actions << QAccessibleActionInterface::pressAction();
533 break;
534 case QAccessible::RadioButton:
535 case QAccessible::CheckBox:
536 case QAccessible::Switch:
537 actions << QAccessibleActionInterface::toggleAction()
538 << QAccessibleActionInterface::pressAction();
539 break;
540 case QAccessible::Slider:
541 case QAccessible::SpinBox:
542 case QAccessible::ScrollBar:
543 actions << QAccessibleActionInterface::increaseAction()
544 << QAccessibleActionInterface::decreaseAction();
545 break;
546 default:
547 break;
548 }
549 if (state().focusable)
550 actions.append(QAccessibleActionInterface::setFocusAction());
551
552 // ### The following can lead to duplicate action names.
553 if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()))
554 attached->availableActions(&actions);
555 return actions;
556}
557
558void QAccessibleQuickItem::doAction(const QString &actionName)
559{
560 bool accepted = false;
561 if (actionName == QAccessibleActionInterface::setFocusAction()) {
562 item()->forceActiveFocus();
563 accepted = true;
564 }
565 if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()))
566 accepted = attached->doAction(actionName);
567
568 if (accepted)
569 return;
570 // Look for and call the accessible[actionName]Action() function on the item.
571 // This allows for overriding the default action handling.
572 const QByteArray functionName = "accessible" + actionName.toLatin1() + "Action";
573 if (object()->metaObject()->indexOfMethod(QByteArray(functionName + "()")) != -1) {
574 QMetaObject::invokeMethod(object(), functionName);
575 return;
576 }
577
578 // Role-specific default action handling follows. Items are expected to provide
579 // properties according to role conventions. These will then be read and/or updated
580 // by the accessibility system.
581 // Checkable roles : checked
582 // Value-based roles : (via the value interface: value, minimumValue, maximumValue), stepSize
583 switch (role()) {
584 case QAccessible::RadioButton:
585 case QAccessible::CheckBox:
586 case QAccessible::Switch: {
587 QVariant checked = object()->property("checked");
588 if (checked.isValid()) {
589 if (actionName == QAccessibleActionInterface::toggleAction() ||
590 actionName == QAccessibleActionInterface::pressAction()) {
591
592 object()->setProperty("checked", QVariant(!checked.toBool()));
593 }
594 }
595 break;
596 }
597 case QAccessible::Slider:
598 case QAccessible::SpinBox:
599 case QAccessible::Dial:
600 case QAccessible::ScrollBar: {
601 if (actionName != QAccessibleActionInterface::increaseAction() &&
602 actionName != QAccessibleActionInterface::decreaseAction())
603 break;
604
605 // Update the value using QAccessibleValueInterface, respecting
606 // the minimum and maximum value (if set). Also check for and
607 // use the "stepSize" property on the item
608 if (QAccessibleValueInterface *valueIface = valueInterface()) {
609 QVariant valueV = valueIface->currentValue();
610 qreal newValue = valueV.toReal();
611
612 QVariant stepSizeV = object()->property("stepSize");
613 qreal stepSize = stepSizeV.isValid() ? stepSizeV.toReal() : qreal(1.0);
614 if (actionName == QAccessibleActionInterface::increaseAction()) {
615 newValue += stepSize;
616 } else {
617 newValue -= stepSize;
618 }
619
620 QVariant minimumValueV = valueIface->minimumValue();
621 if (minimumValueV.isValid()) {
622 newValue = qMax(newValue, minimumValueV.toReal());
623 }
624 QVariant maximumValueV = valueIface->maximumValue();
625 if (maximumValueV.isValid()) {
626 newValue = qMin(newValue, maximumValueV.toReal());
627 }
628
629 valueIface->setCurrentValue(QVariant(newValue));
630 }
631 break;
632 }
633 default:
634 break;
635 }
636}
637
638QStringList QAccessibleQuickItem::keyBindingsForAction(const QString &actionName) const
639{
640 Q_UNUSED(actionName);
641 return QStringList();
642}
643
644QString QAccessibleQuickItem::text(QAccessible::Text textType) const
645{
646 // handles generic behavior not specific to an item
647 switch (textType) {
648 case QAccessible::Name: {
649 QVariant accessibleName = QQuickAccessibleAttached::property(object(), "name");
650 if (!accessibleName.isNull())
651 return accessibleName.toString();
652 break;}
653 case QAccessible::Description: {
654 QVariant accessibleDecription = QQuickAccessibleAttached::property(object(), "description");
655 if (!accessibleDecription.isNull())
656 return accessibleDecription.toString();
657 break;}
658 case QAccessible::Identifier: {
659 QVariant accessibleIdentifier = QQuickAccessibleAttached::property(object(), "id");
660 if (!accessibleIdentifier.isNull())
661 return accessibleIdentifier.toString();
662 auto quickItem = item();
663 if (quickItem->isComponentComplete()) {
664 QQmlContext *context = qmlContext(quickItem);
665 if (context) {
666 const auto objectId = context->nameForObject(quickItem);
667 if (!objectId.isEmpty())
668 return objectId;
669 }
670 }
671 break;}
672#ifdef Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION
673 case QAccessible::DebugDescription: {
674 QString debugString;
675 debugString = QString::fromLatin1(object()->metaObject()->className()) + QLatin1Char(' ');
676 debugString += isAccessible() ? QLatin1String("enabled") : QLatin1String("disabled");
677 return debugString;
678 break; }
679#endif
680 case QAccessible::Value:
681 case QAccessible::Help:
682 case QAccessible::Accelerator:
683 default:
684 break;
685 }
686
687 // the following block handles item-specific behavior
688 if (role() == QAccessible::EditableText) {
689 if (textType == QAccessible::Value) {
690 if (auto textInput = qobject_cast<QQuickTextInput *>(item()))
691 return textInput->displayText();
692 if (QTextDocument *doc = textDocument()) {
693 return doc->toPlainText();
694 }
695 QVariant text = object()->property("text");
696 return text.toString();
697 }
698 }
699
700 return QString();
701}
702
703void QAccessibleQuickItem::setText(QAccessible::Text textType, const QString &text)
704{
705 if (role() != QAccessible::EditableText)
706 return;
707 if (textType != QAccessible::Value)
708 return;
709
710 if (QTextDocument *doc = textDocument()) {
711 doc->setPlainText(text);
712 return;
713 }
714 auto textPropertyName = "text";
715 if (object()->metaObject()->indexOfProperty(textPropertyName) >= 0)
716 object()->setProperty(textPropertyName, text);
717}
718
719void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t)
720{
721 const QAccessible::Role r = role();
722 switch (t) {
723 case QAccessible::ActionInterface:
724 return static_cast<QAccessibleActionInterface*>(this);
725 case QAccessible::ValueInterface:
726 if (r == QAccessible::Slider
727 || r == QAccessible::SpinBox
728 || r == QAccessible::Dial
729 || r == QAccessible::ScrollBar
730 || r == QAccessible::ProgressBar) {
731 return static_cast<QAccessibleValueInterface*>(this);
732 }
733 break;
734 case QAccessible::TextInterface:
735 if (r == QAccessible::EditableText
736 || r == QAccessible::StaticText
737 || r == QAccessible::Heading) {
738 return static_cast<QAccessibleTextInterface*>(this);
739 }
740 break;
741 default:
742 break;
743 }
744
745 return QAccessibleObject::interface_cast(t);
746}
747
748QVariant QAccessibleQuickItem::currentValue() const
749{
750 return item()->property("value");
751}
752
753void QAccessibleQuickItem::setCurrentValue(const QVariant &value)
754{
755 item()->setProperty("value", value);
756}
757
758QVariant QAccessibleQuickItem::maximumValue() const
759{
760 const auto minimumValue = item()->property("minimumValue");
761 const auto maximumValue = item()->property("maximumValue");
762 const auto from = item()->property("from");
763 const auto to = item()->property("to");
764
765 if (minimumValue.isValid() && maximumValue.isValid())
766 return maximumValue;
767
768 if (from.isValid() && to.isValid())
769 return to;
770
771 return QVariant();
772}
773
774QVariant QAccessibleQuickItem::minimumValue() const
775{
776 const auto minimumValue = item()->property("minimumValue");
777 const auto maximumValue = item()->property("maximumValue");
778 const auto from = item()->property("from");
779 const auto to = item()->property("to");
780
781 if (minimumValue.isValid() && maximumValue.isValid())
782 return minimumValue;
783
784 if (from.isValid() && to.isValid())
785 return from;
786
787 return QVariant();
788}
789
790QVariant QAccessibleQuickItem::minimumStepSize() const
791{
792 return item()->property("stepSize");
793}
794
795/*!
796 \internal
797 Shared between QAccessibleQuickItem and QAccessibleQuickView
798*/
799QRect itemScreenRect(QQuickItem *item)
800{
801 // ### no window in some cases.
802 // ### Should we really check for 0 opacity?
803 if (!item->window() ||!item->isVisible() || qFuzzyIsNull(item->opacity())) {
804 return QRect();
805 }
806
807 QSizeF itemSize(item->width(), item->height());
808 // ### If the bounding rect fails, we first try the implicit size, then we go for the
809 // parent size. WE MIGHT HAVE TO REVISIT THESE FALLBACKS.
810 if (itemSize.isEmpty()) {
811 itemSize = QSize(item->implicitWidth(), item->implicitHeight());
812 if (itemSize.isEmpty() && item->parentItem())
813 // ### Seems that the above fallback is not enough, fallback to use the parent size...
814 itemSize = QSize(item->parentItem()->width(), item->parentItem()->height());
815 }
816
817 QRectF sceneRect = item->mapRectToScene(QRectF(QPointF(0, 0), itemSize));
818 QPoint screenPos = item->window()->mapToGlobal(sceneRect.topLeft().toPoint());
819 QSize screenSize = sceneRect.size().toSize();
820 return QRect(screenPos, screenSize);
821}
822
823QTextDocument *QAccessibleQuickItem::textDocument() const
824{
825 QVariant docVariant = item()->property("textDocument");
826 if (docVariant.canConvert<QQuickTextDocument*>()) {
827 QQuickTextDocument *qqdoc = docVariant.value<QQuickTextDocument*>();
828 return qqdoc->textDocument();
829 }
830 return nullptr;
831}
832
833int QAccessibleQuickItem::characterCount() const
834{
835 if (m_doc) {
836 QTextCursor cursor = QTextCursor(m_doc);
837 cursor.movePosition(QTextCursor::End);
838 return cursor.position();
839 }
840
841 if (role() == QAccessible::EditableText)
842 return text(QAccessible::Value).size();
843
844 return text(QAccessible::Name).size();
845}
846
847int QAccessibleQuickItem::cursorPosition() const
848{
849 QVariant pos = item()->property("cursorPosition");
850 return pos.toInt();
851}
852
853void QAccessibleQuickItem::setCursorPosition(int position)
854{
855 item()->setProperty("cursorPosition", position);
856}
857
858QString QAccessibleQuickItem::text(int startOffset, int endOffset) const
859{
860 if (m_doc) {
861 QTextCursor cursor = QTextCursor(m_doc);
862 cursor.setPosition(startOffset);
863 cursor.setPosition(endOffset, QTextCursor::KeepAnchor);
864 return cursor.selectedText();
865 }
866
867 if (role() == QAccessible::EditableText)
868 return text(QAccessible::Value).mid(startOffset, endOffset - startOffset);
869
870 return text(QAccessible::Name).mid(startOffset, endOffset - startOffset);
871}
872
873QString QAccessibleQuickItem::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType,
874 int *startOffset, int *endOffset) const
875{
876 Q_ASSERT(startOffset);
877 Q_ASSERT(endOffset);
878
879 if (m_doc) {
880 return qt_accTextBeforeOffsetHelper(*this, QTextCursor(m_doc), offset, boundaryType, startOffset, endOffset);
881 } else {
882 return QAccessibleTextInterface::textBeforeOffset(offset, boundaryType, startOffset, endOffset);
883 }
884}
885
886QString QAccessibleQuickItem::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType,
887 int *startOffset, int *endOffset) const
888{
889 Q_ASSERT(startOffset);
890 Q_ASSERT(endOffset);
891
892 if (m_doc) {
893 return qt_accTextAfterOffsetHelper(*this, QTextCursor(m_doc), offset, boundaryType, startOffset, endOffset);
894 } else {
895 return QAccessibleTextInterface::textAfterOffset(offset, boundaryType, startOffset, endOffset);
896 }
897}
898
899QString QAccessibleQuickItem::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType,
900 int *startOffset, int *endOffset) const
901{
902 Q_ASSERT(startOffset);
903 Q_ASSERT(endOffset);
904
905 if (m_doc) {
906 return qt_accTextAtOffsetHelper(*this, QTextCursor(m_doc), offset, boundaryType, startOffset, endOffset);
907 } else {
908 return QAccessibleTextInterface::textAtOffset(offset, boundaryType, startOffset, endOffset);
909 }
910}
911
912void QAccessibleQuickItem::selection(int selectionIndex, int *startOffset, int *endOffset) const
913{
914 if (selectionIndex == 0) {
915 *startOffset = item()->property("selectionStart").toInt();
916 *endOffset = item()->property("selectionEnd").toInt();
917 } else {
918 *startOffset = 0;
919 *endOffset = 0;
920 }
921}
922
923int QAccessibleQuickItem::selectionCount() const
924{
925 if (item()->property("selectionStart").toInt() != item()->property("selectionEnd").toInt())
926 return 1;
927 return 0;
928}
929
930void QAccessibleQuickItem::addSelection(int startOffset, int endOffset)
931{
932 setSelection(0, startOffset, endOffset);
933}
934void QAccessibleQuickItem::removeSelection(int /* selectionIndex */)
935{
936
937}
938void QAccessibleQuickItem::setSelection(int /* selectionIndex */, int /* startOffset */, int /* endOffset */)
939{
940
941}
942
943
944#endif // accessibility
945
946QT_END_NAMESPACE