1
/****************************************************************************
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtQuick module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL21$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 or version 3 as published by the Free
20
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22
** following information to ensure the GNU Lesser General Public License
23
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26
** In addition, as a special exception, Digia gives you certain additional
27
** rights. These rights are described in the Digia Qt LGPL Exception
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32
****************************************************************************/
34
#include "qaccessiblequickitem_p.h"
36
#include <QtGui/qtextdocument.h>
38
#include "QtQuick/private/qquickitem_p.h"
39
#include "QtQuick/private/qquicktext_p.h"
40
#include "QtQuick/private/qquickaccessibleattached_p.h"
41
#include "QtQuick/qquicktextdocument.h"
44
#ifndef QT_NO_ACCESSIBILITY
46
QAccessibleQuickItem::QAccessibleQuickItem(QQuickItem *item)
47
: QQmlAccessible(item), m_doc(textDocument())
51
int QAccessibleQuickItem::childCount() const
53
return childItems().count();
56
QRect QAccessibleQuickItem::rect() const
58
const QRect r = itemScreenRect(item());
61
qWarning() << item()->metaObject()->className() << item()->property("accessibleText") << r;
66
QRect QAccessibleQuickItem::viewRect() const
68
// ### no window in some cases.
69
if (!item()->window()) {
73
QQuickWindow *window = item()->window();
74
QPoint screenPos = window->mapToGlobal(QPoint(0,0));
75
return QRect(screenPos, window->size());
79
bool QAccessibleQuickItem::clipsChildren() const
81
return static_cast<QQuickItem *>(item())->clip();
84
QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const
87
if (!rect().contains(x, y))
91
const QList<QQuickItem*> kids = accessibleUnignoredChildren(item(), true);
92
for (int i = kids.count() - 1; i >= 0; --i) {
93
QAccessibleInterface *childIface = QAccessible::queryAccessibleInterface(kids.at(i));
94
if (QAccessibleInterface *childChild = childIface->childAt(x, y))
96
if (childIface && !childIface->state().invisible) {
97
if (childIface->rect().contains(x, y))
105
QAccessibleInterface *QAccessibleQuickItem::parent() const
107
QQuickItem *parent = item()->parentItem();
108
QQuickWindow *window = item()->window();
109
QQuickItem *ci = window ? window->contentItem() : 0;
110
while (parent && !QQuickItemPrivate::get(parent)->isAccessible && parent != ci)
111
parent = parent->parentItem();
115
// Jump out to the scene widget if the parent is the root item.
116
// There are two root items, QQuickWindow::rootItem and
117
// QQuickView::declarativeRoot. The former is the true root item,
118
// but is not a part of the accessibility tree. Check if we hit
119
// it here and return an interface for the scene instead.
120
return QAccessible::queryAccessibleInterface(window);
122
while (parent && !parent->d_func()->isAccessible)
123
parent = parent->parentItem();
124
return QAccessible::queryAccessibleInterface(parent);
130
QAccessibleInterface *QAccessibleQuickItem::child(int index) const
132
QList<QQuickItem *> children = childItems();
134
if (index < 0 || index >= children.count())
137
QQuickItem *child = children.at(index);
138
if (!child) // FIXME can this happen?
141
return QAccessible::queryAccessibleInterface(child);
144
int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const
146
QList<QQuickItem*> kids = childItems();
147
return kids.indexOf(static_cast<QQuickItem*>(iface->object()));
150
static void unignoredChildren(QQuickItem *item, QList<QQuickItem *> *items, bool paintOrder)
152
QList<QQuickItem*> childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems()
153
: item->childItems();
154
Q_FOREACH (QQuickItem *child, childItems) {
155
if (QQuickItemPrivate::get(child)->isAccessible) {
156
items->append(child);
158
unignoredChildren(child, items, paintOrder);
163
QList<QQuickItem *> accessibleUnignoredChildren(QQuickItem *item, bool paintOrder)
165
QList<QQuickItem *> items;
166
unignoredChildren(item, &items, paintOrder);
170
QList<QQuickItem *> QAccessibleQuickItem::childItems() const
172
return accessibleUnignoredChildren(item());
175
QAccessible::State QAccessibleQuickItem::state() const
177
QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item());
179
return QAccessible::State();
181
QAccessible::State st = attached->state();
183
if (!item()->window() || !item()->window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity()))
186
if (item()->activeFocusOnTab())
188
if (item()->hasActiveFocus())
193
QAccessible::Role QAccessibleQuickItem::role() const
195
// Workaround for setAccessibleRole() not working for
196
// Text items. Text items are special since they are defined
197
// entirely from C++ (setting the role from QML works.)
198
if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
199
return QAccessible::StaticText;
201
QVariant v = QQuickAccessibleAttached::property(item(), "role");
203
QAccessible::Role role = (QAccessible::Role)v.toInt(&ok);
204
if (!ok) // Not sure if this check is needed.
205
role = QAccessible::Client;
209
bool QAccessibleQuickItem::isAccessible() const
211
return item()->d_func()->isAccessible;
214
QStringList QAccessibleQuickItem::actionNames() const
216
QStringList actions = QQmlAccessible::actionNames();
217
if (state().focusable)
218
actions.append(QAccessibleActionInterface::setFocusAction());
220
// ### The following can lead to duplicate action names. We'll fix that when we kill QQmlAccessible
221
if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()))
222
attached->availableActions(&actions);
226
void QAccessibleQuickItem::doAction(const QString &actionName)
228
bool accepted = false;
229
if (actionName == QAccessibleActionInterface::setFocusAction()) {
230
item()->forceActiveFocus();
233
if (QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()))
234
accepted = attached->doAction(actionName);
236
QQmlAccessible::doAction(actionName);
239
QStringList QAccessibleQuickItem::keyBindingsForAction(const QString &actionName) const
241
return QQmlAccessible::keyBindingsForAction(actionName);
244
QString QAccessibleQuickItem::text(QAccessible::Text textType) const
246
// handles generic behavior not specific to an item
248
case QAccessible::Name: {
249
QVariant accessibleName = QQuickAccessibleAttached::property(object(), "name");
250
if (!accessibleName.isNull())
251
return accessibleName.toString();
253
case QAccessible::Description: {
254
QVariant accessibleDecription = QQuickAccessibleAttached::property(object(), "description");
255
if (!accessibleDecription.isNull())
256
return accessibleDecription.toString();
258
#ifdef Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION
259
case QAccessible::DebugDescription: {
261
debugString = QString::fromLatin1(object()->metaObject()->className()) + QLatin1Char(' ');
262
debugString += isAccessible() ? QLatin1String("enabled") : QLatin1String("disabled");
266
case QAccessible::Value:
267
case QAccessible::Help:
268
case QAccessible::Accelerator:
273
// the following block handles item-specific behavior
274
if (role() == QAccessible::EditableText) {
275
if (textType == QAccessible::Value) {
276
if (QTextDocument *doc = textDocument()) {
277
return doc->toPlainText();
279
QVariant text = object()->property("text");
280
return text.toString();
287
void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t)
289
QAccessible::Role r = role();
290
if (t == QAccessible::ValueInterface &&
291
(r == QAccessible::Slider ||
292
r == QAccessible::SpinBox ||
293
r == QAccessible::Dial ||
294
r == QAccessible::ScrollBar))
295
return static_cast<QAccessibleValueInterface*>(this);
297
if (t == QAccessible::TextInterface &&
298
(r == QAccessible::EditableText))
299
return static_cast<QAccessibleTextInterface*>(this);
301
return QQmlAccessible::interface_cast(t);
304
QVariant QAccessibleQuickItem::currentValue() const
306
return item()->property("value");
309
void QAccessibleQuickItem::setCurrentValue(const QVariant &value)
311
item()->setProperty("value", value);
314
QVariant QAccessibleQuickItem::maximumValue() const
316
return item()->property("maximumValue");
319
QVariant QAccessibleQuickItem::minimumValue() const
321
return item()->property("minimumValue");
324
QVariant QAccessibleQuickItem::minimumStepSize() const
326
return item()->property("stepSize");
331
Shared between QAccessibleQuickItem and QAccessibleQuickView
333
QRect itemScreenRect(QQuickItem *item)
335
// ### no window in some cases.
336
// ### Should we really check for 0 opacity?
337
if (!item->window() ||!item->isVisible() || qFuzzyIsNull(item->opacity())) {
341
QSize itemSize((int)item->width(), (int)item->height());
342
// ### If the bounding rect fails, we first try the implicit size, then we go for the
343
// parent size. WE MIGHT HAVE TO REVISIT THESE FALLBACKS.
344
if (itemSize.isEmpty()) {
345
itemSize = QSize((int)item->implicitWidth(), (int)item->implicitHeight());
346
if (itemSize.isEmpty() && item->parentItem())
347
// ### Seems that the above fallback is not enough, fallback to use the parent size...
348
itemSize = QSize((int)item->parentItem()->width(), (int)item->parentItem()->height());
351
QPointF scenePoint = item->mapToScene(QPointF(0, 0));
352
QPoint screenPos = item->window()->mapToGlobal(scenePoint.toPoint());
353
return QRect(screenPos, itemSize);
356
QTextDocument *QAccessibleQuickItem::textDocument() const
358
QVariant docVariant = item()->property("textDocument");
359
if (docVariant.canConvert<QQuickTextDocument*>()) {
360
QQuickTextDocument *qqdoc = docVariant.value<QQuickTextDocument*>();
361
return qqdoc->textDocument();
366
int QAccessibleQuickItem::characterCount() const
369
QTextCursor cursor = QTextCursor(m_doc);
370
cursor.movePosition(QTextCursor::End);
371
return cursor.position();
373
return text(QAccessible::Value).size();
376
int QAccessibleQuickItem::cursorPosition() const
378
QVariant pos = item()->property("cursorPosition");
382
void QAccessibleQuickItem::setCursorPosition(int position)
384
item()->setProperty("cursorPosition", position);
387
QString QAccessibleQuickItem::text(int startOffset, int endOffset) const
390
QTextCursor cursor = QTextCursor(m_doc);
391
cursor.setPosition(startOffset);
392
cursor.setPosition(endOffset, QTextCursor::KeepAnchor);
393
return cursor.selectedText();
395
return text(QAccessible::Value).mid(startOffset, endOffset - startOffset);
398
QString QAccessibleQuickItem::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType,
399
int *startOffset, int *endOffset) const
401
Q_ASSERT(startOffset);
405
QTextCursor cursor = QTextCursor(m_doc);
406
cursor.setPosition(offset);
407
QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
408
cursor.setPosition(boundaries.first - 1);
409
boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
411
*startOffset = boundaries.first;
412
*endOffset = boundaries.second;
414
return text(boundaries.first, boundaries.second);
416
return QAccessibleTextInterface::textBeforeOffset(offset, boundaryType, startOffset, endOffset);
420
QString QAccessibleQuickItem::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType,
421
int *startOffset, int *endOffset) const
423
Q_ASSERT(startOffset);
427
QTextCursor cursor = QTextCursor(m_doc);
428
cursor.setPosition(offset);
429
QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
430
cursor.setPosition(boundaries.second);
431
boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
433
*startOffset = boundaries.first;
434
*endOffset = boundaries.second;
436
return text(boundaries.first, boundaries.second);
438
return QAccessibleTextInterface::textAfterOffset(offset, boundaryType, startOffset, endOffset);
442
QString QAccessibleQuickItem::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType,
443
int *startOffset, int *endOffset) const
445
Q_ASSERT(startOffset);
449
QTextCursor cursor = QTextCursor(m_doc);
450
cursor.setPosition(offset);
451
QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
453
*startOffset = boundaries.first;
454
*endOffset = boundaries.second;
455
return text(boundaries.first, boundaries.second);
457
return QAccessibleTextInterface::textAtOffset(offset, boundaryType, startOffset, endOffset);
461
void QAccessibleQuickItem::selection(int selectionIndex, int *startOffset, int *endOffset) const
463
if (selectionIndex == 0) {
464
*startOffset = item()->property("selectionStart").toInt();
465
*endOffset = item()->property("selectionEnd").toInt();
472
int QAccessibleQuickItem::selectionCount() const
474
if (item()->property("selectionStart").toInt() != item()->property("selectionEnd").toInt())
479
void QAccessibleQuickItem::addSelection(int /* startOffset */, int /* endOffset */)
483
void QAccessibleQuickItem::removeSelection(int /* selectionIndex */)
487
void QAccessibleQuickItem::setSelection(int /* selectionIndex */, int /* startOffset */, int /* endOffset */)
493
#endif // QT_NO_ACCESSIBILITY