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 QList<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 QList<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 QList<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 QList<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 QList<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 if (item()->property("readOnly").toBool() || role() == QAccessible::ProgressBar)
474 state.readOnly = true;
475 return state;
476}
477
478QList<std::pair<QAccessibleInterface *, QAccessible::Relation>>
479QAccessibleQuickItem::relations(QAccessible::Relation match) const
480{
481 QList<std::pair<QAccessibleInterface *, QAccessible::Relation>> rels =
482 QAccessibleObject::relations(match);
483 if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item())) {
484 if (match & QAccessible::Labelled) {
485 if (auto *labelFor = attached->labelFor()) {
486 rels.append({QAccessible::queryAccessibleInterface(labelFor),
487 QAccessible::Labelled});
488 }
489 }
490
491 if (match & QAccessible::Label) {
492 if (auto *labelledBy = attached->labelledBy()) {
493 rels.append({QAccessible::queryAccessibleInterface(labelledBy),
494 QAccessible::Label});
495 }
496 }
497 }
498 return rels;
499}
500
501QAccessible::Role QAccessibleQuickItem::role() const
502{
503 // Workaround for setAccessibleRole() not working for
504 // Text items. Text items are special since they are defined
505 // entirely from C++ (setting the role from QML works.)
506
507 QAccessible::Role role = QAccessible::NoRole;
508 if (item())
509 role = QQuickItemPrivate::get(item())->effectiveAccessibleRole();
510 if (role == QAccessible::NoRole) {
511 if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
512 role = QAccessible::StaticText;
513 else if (qobject_cast<QQuickTextInput*>(const_cast<QQuickItem *>(item())))
514 role = QAccessible::EditableText;
515 else
516 role = QAccessible::Client;
517 }
518
519 return role;
520}
521
522bool QAccessibleQuickItem::isAccessible() const
523{
524 return item()->d_func()->isAccessible;
525}
526
527QStringList QAccessibleQuickItem::actionNames() const
528{
529 QStringList actions;
530 switch (role()) {
531 case QAccessible::Link:
532 case QAccessible::PushButton:
533 case QAccessible::MenuItem:
534 actions << QAccessibleActionInterface::pressAction();
535 break;
536 case QAccessible::RadioButton:
537 case QAccessible::CheckBox:
538 case QAccessible::Switch:
539 actions << QAccessibleActionInterface::toggleAction()
540 << QAccessibleActionInterface::pressAction();
541 break;
542 case QAccessible::Slider:
543 case QAccessible::SpinBox:
544 case QAccessible::ScrollBar:
545 actions << QAccessibleActionInterface::increaseAction()
546 << QAccessibleActionInterface::decreaseAction();
547 break;
548 default:
549 break;
550 }
551 if (state().focusable)
552 actions.append(QAccessibleActionInterface::setFocusAction());
553
554 // ### The following can lead to duplicate action names.
555 if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()))
556 attached->availableActions(&actions);
557 return actions;
558}
559
560void QAccessibleQuickItem::doAction(const QString &actionName)
561{
562 bool accepted = false;
563 if (actionName == QAccessibleActionInterface::setFocusAction()) {
564 item()->forceActiveFocus();
565 accepted = true;
566 }
567 if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()))
568 accepted = attached->doAction(actionName);
569
570 if (accepted)
571 return;
572 // Look for and call the accessible[actionName]Action() function on the item.
573 // This allows for overriding the default action handling.
574 const QByteArray functionName = "accessible" + actionName.toLatin1() + "Action";
575 if (object()->metaObject()->indexOfMethod(QByteArray(functionName + "()")) != -1) {
576 QMetaObject::invokeMethod(object(), functionName);
577 return;
578 }
579
580 // Role-specific default action handling follows. Items are expected to provide
581 // properties according to role conventions. These will then be read and/or updated
582 // by the accessibility system.
583 // Checkable roles : checked
584 // Value-based roles : (via the value interface: value, minimumValue, maximumValue), stepSize
585 switch (role()) {
586 case QAccessible::RadioButton:
587 case QAccessible::CheckBox:
588 case QAccessible::Switch: {
589 QVariant checked = object()->property("checked");
590 if (checked.isValid()) {
591 if (actionName == QAccessibleActionInterface::toggleAction() ||
592 actionName == QAccessibleActionInterface::pressAction()) {
593
594 object()->setProperty("checked", QVariant(!checked.toBool()));
595 }
596 }
597 break;
598 }
599 case QAccessible::Slider:
600 case QAccessible::SpinBox:
601 case QAccessible::Dial:
602 case QAccessible::ScrollBar: {
603 if (actionName != QAccessibleActionInterface::increaseAction() &&
604 actionName != QAccessibleActionInterface::decreaseAction())
605 break;
606
607 // Update the value using QAccessibleValueInterface, respecting
608 // the minimum and maximum value (if set). Also check for and
609 // use the "stepSize" property on the item
610 if (QAccessibleValueInterface *valueIface = valueInterface()) {
611 QVariant valueV = valueIface->currentValue();
612 qreal newValue = valueV.toReal();
613
614 QVariant stepSizeV = object()->property("stepSize");
615 qreal stepSize = stepSizeV.isValid() ? stepSizeV.toReal() : qreal(1.0);
616 if (actionName == QAccessibleActionInterface::increaseAction()) {
617 newValue += stepSize;
618 } else {
619 newValue -= stepSize;
620 }
621
622 QVariant minimumValueV = valueIface->minimumValue();
623 if (minimumValueV.isValid()) {
624 newValue = qMax(newValue, minimumValueV.toReal());
625 }
626 QVariant maximumValueV = valueIface->maximumValue();
627 if (maximumValueV.isValid()) {
628 newValue = qMin(newValue, maximumValueV.toReal());
629 }
630
631 valueIface->setCurrentValue(QVariant(newValue));
632 }
633 break;
634 }
635 default:
636 break;
637 }
638}
639
640QStringList QAccessibleQuickItem::keyBindingsForAction(const QString &actionName) const
641{
642 Q_UNUSED(actionName);
643 return QStringList();
644}
645
646QString QAccessibleQuickItem::text(QAccessible::Text textType) const
647{
648 // handles generic behavior not specific to an item
649 switch (textType) {
650 case QAccessible::Name: {
651 QVariant accessibleName = QQuickAccessibleAttached::property(object(), "name");
652 if (!accessibleName.isNull())
653 return accessibleName.toString();
654 break;}
655 case QAccessible::Description: {
656 QVariant accessibleDecription = QQuickAccessibleAttached::property(object(), "description");
657 if (!accessibleDecription.isNull())
658 return accessibleDecription.toString();
659 break;}
660 case QAccessible::Identifier: {
661 QVariant accessibleIdentifier = QQuickAccessibleAttached::property(object(), "id");
662 if (!accessibleIdentifier.isNull())
663 return accessibleIdentifier.toString();
664 auto quickItem = item();
665 if (quickItem->isComponentComplete()) {
666 QQmlContext *context = qmlContext(quickItem);
667 if (context) {
668 const auto objectId = context->nameForObject(quickItem);
669 if (!objectId.isEmpty())
670 return objectId;
671 }
672 }
673 break;}
674#ifdef Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION
675 case QAccessible::DebugDescription: {
676 QString debugString;
677 debugString = QString::fromLatin1(object()->metaObject()->className()) + QLatin1Char(' ');
678 debugString += isAccessible() ? QLatin1String("enabled") : QLatin1String("disabled");
679 return debugString;
680 break; }
681#endif
682 case QAccessible::Value:
683 case QAccessible::Help:
684 case QAccessible::Accelerator:
685 default:
686 break;
687 }
688
689 // the following block handles item-specific behavior
690 if (role() == QAccessible::EditableText) {
691 if (textType == QAccessible::Value) {
692 if (auto textInput = qobject_cast<QQuickTextInput *>(item()))
693 return textInput->displayText();
694 if (QTextDocument *doc = textDocument()) {
695 return doc->toPlainText();
696 }
697 QVariant text = object()->property("text");
698 return text.toString();
699 }
700 }
701
702 return QString();
703}
704
705void QAccessibleQuickItem::setText(QAccessible::Text textType, const QString &text)
706{
707 if (role() != QAccessible::EditableText)
708 return;
709 if (textType != QAccessible::Value)
710 return;
711
712 if (QTextDocument *doc = textDocument()) {
713 doc->setPlainText(text);
714 return;
715 }
716 auto textPropertyName = "text";
717 if (object()->metaObject()->indexOfProperty(textPropertyName) >= 0)
718 object()->setProperty(textPropertyName, text);
719}
720
721void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t)
722{
723 const QAccessible::Role r = role();
724 switch (t) {
725 case QAccessible::ActionInterface:
726 return static_cast<QAccessibleActionInterface*>(this);
727 case QAccessible::ValueInterface:
728 if (r == QAccessible::Slider
729 || r == QAccessible::SpinBox
730 || r == QAccessible::Dial
731 || r == QAccessible::ScrollBar
732 || r == QAccessible::ProgressBar) {
733 return static_cast<QAccessibleValueInterface*>(this);
734 }
735 break;
736 case QAccessible::TextInterface:
737 if (r == QAccessible::EditableText
738 || r == QAccessible::StaticText
739 || r == QAccessible::Heading) {
740 return static_cast<QAccessibleTextInterface*>(this);
741 }
742 break;
743 default:
744 break;
745 }
746
747 return QAccessibleObject::interface_cast(t);
748}
749
750QVariant QAccessibleQuickItem::currentValue() const
751{
752 return item()->property("value");
753}
754
755void QAccessibleQuickItem::setCurrentValue(const QVariant &value)
756{
757 item()->setProperty("value", value);
758}
759
760QVariant QAccessibleQuickItem::maximumValue() const
761{
762 const auto minimumValue = item()->property("minimumValue");
763 const auto maximumValue = item()->property("maximumValue");
764 const auto from = item()->property("from");
765 const auto to = item()->property("to");
766
767 if (minimumValue.isValid() && maximumValue.isValid())
768 return maximumValue;
769
770 if (from.isValid() && to.isValid())
771 return to;
772
773 return QVariant();
774}
775
776QVariant QAccessibleQuickItem::minimumValue() const
777{
778 const auto minimumValue = item()->property("minimumValue");
779 const auto maximumValue = item()->property("maximumValue");
780 const auto from = item()->property("from");
781 const auto to = item()->property("to");
782
783 if (minimumValue.isValid() && maximumValue.isValid())
784 return minimumValue;
785
786 if (from.isValid() && to.isValid())
787 return from;
788
789 return QVariant();
790}
791
792QVariant QAccessibleQuickItem::minimumStepSize() const
793{
794 return item()->property("stepSize");
795}
796
797/*!
798 \internal
799 Shared between QAccessibleQuickItem and QAccessibleQuickView
800*/
801QRect itemScreenRect(QQuickItem *item)
802{
803 // ### no window in some cases.
804 // ### Should we really check for 0 opacity?
805 if (!item->window() ||!item->isVisible() || qFuzzyIsNull(item->opacity())) {
806 return QRect();
807 }
808
809 QSizeF itemSize(item->width(), item->height());
810 // ### If the bounding rect fails, we first try the implicit size, then we go for the
811 // parent size. WE MIGHT HAVE TO REVISIT THESE FALLBACKS.
812 if (itemSize.isEmpty()) {
813 itemSize = QSize(item->implicitWidth(), item->implicitHeight());
814 if (itemSize.isEmpty() && item->parentItem())
815 // ### Seems that the above fallback is not enough, fallback to use the parent size...
816 itemSize = QSize(item->parentItem()->width(), item->parentItem()->height());
817 }
818
819 QRectF sceneRect = item->mapRectToScene(QRectF(QPointF(0, 0), itemSize));
820 QPoint screenPos = item->window()->mapToGlobal(sceneRect.topLeft().toPoint());
821 QSize screenSize = sceneRect.size().toSize();
822 return QRect(screenPos, screenSize);
823}
824
825QTextDocument *QAccessibleQuickItem::textDocument() const
826{
827 QVariant docVariant = item()->property("textDocument");
828 if (docVariant.canConvert<QQuickTextDocument*>()) {
829 QQuickTextDocument *qqdoc = docVariant.value<QQuickTextDocument*>();
830 return qqdoc->textDocument();
831 }
832 return nullptr;
833}
834
835int QAccessibleQuickItem::characterCount() const
836{
837 if (m_doc) {
838 QTextCursor cursor = QTextCursor(m_doc);
839 cursor.movePosition(QTextCursor::End);
840 return cursor.position();
841 }
842
843 if (role() == QAccessible::EditableText)
844 return text(QAccessible::Value).size();
845
846 return text(QAccessible::Name).size();
847}
848
849int QAccessibleQuickItem::cursorPosition() const
850{
851 QVariant pos = item()->property("cursorPosition");
852 return pos.toInt();
853}
854
855void QAccessibleQuickItem::setCursorPosition(int position)
856{
857 item()->setProperty("cursorPosition", position);
858}
859
860QString QAccessibleQuickItem::text(int startOffset, int endOffset) const
861{
862 if (m_doc) {
863 QTextCursor cursor = QTextCursor(m_doc);
864 cursor.setPosition(startOffset);
865 cursor.setPosition(endOffset, QTextCursor::KeepAnchor);
866 return cursor.selectedText();
867 }
868
869 if (role() == QAccessible::EditableText)
870 return text(QAccessible::Value).mid(startOffset, endOffset - startOffset);
871
872 return text(QAccessible::Name).mid(startOffset, endOffset - startOffset);
873}
874
875QString QAccessibleQuickItem::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType,
876 int *startOffset, int *endOffset) const
877{
878 Q_ASSERT(startOffset);
879 Q_ASSERT(endOffset);
880
881 if (m_doc) {
882 return qt_accTextBeforeOffsetHelper(*this, QTextCursor(m_doc), offset, boundaryType, startOffset, endOffset);
883 } else {
884 return QAccessibleTextInterface::textBeforeOffset(offset, boundaryType, startOffset, endOffset);
885 }
886}
887
888QString QAccessibleQuickItem::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType,
889 int *startOffset, int *endOffset) const
890{
891 Q_ASSERT(startOffset);
892 Q_ASSERT(endOffset);
893
894 if (m_doc) {
895 return qt_accTextAfterOffsetHelper(*this, QTextCursor(m_doc), offset, boundaryType, startOffset, endOffset);
896 } else {
897 return QAccessibleTextInterface::textAfterOffset(offset, boundaryType, startOffset, endOffset);
898 }
899}
900
901QString QAccessibleQuickItem::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType,
902 int *startOffset, int *endOffset) const
903{
904 Q_ASSERT(startOffset);
905 Q_ASSERT(endOffset);
906
907 if (m_doc) {
908 return qt_accTextAtOffsetHelper(*this, QTextCursor(m_doc), offset, boundaryType, startOffset, endOffset);
909 } else {
910 return QAccessibleTextInterface::textAtOffset(offset, boundaryType, startOffset, endOffset);
911 }
912}
913
914void QAccessibleQuickItem::selection(int selectionIndex, int *startOffset, int *endOffset) const
915{
916 if (selectionIndex == 0) {
917 *startOffset = item()->property("selectionStart").toInt();
918 *endOffset = item()->property("selectionEnd").toInt();
919 } else {
920 *startOffset = 0;
921 *endOffset = 0;
922 }
923}
924
925int QAccessibleQuickItem::selectionCount() const
926{
927 if (item()->property("selectionStart").toInt() != item()->property("selectionEnd").toInt())
928 return 1;
929 return 0;
930}
931
932void QAccessibleQuickItem::addSelection(int startOffset, int endOffset)
933{
934 setSelection(0, startOffset, endOffset);
935}
936void QAccessibleQuickItem::removeSelection(int /* selectionIndex */)
937{
938
939}
940void QAccessibleQuickItem::setSelection(int /* selectionIndex */, int /* startOffset */, int /* endOffset */)
941{
942
943}
944
945
946#endif // accessibility
947
948QT_END_NAMESPACE