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