aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan-Arve Saether <jan-arve.saether@nokia.com>2012-01-03 09:40:16 +0100
committerQt by Nokia <qt-info@nokia.com>2012-01-12 12:21:59 +0100
commit9ab84bd46861557afb190ba4cb2481b35a7401e5 (patch)
tree32c2c128aa0783cbe0bfe9f43d437c9ac916a929
parent0cafd920cced1f726a8b6422b6fb496ecf846f34 (diff)
Make QAccessibleQuickView::childAt() work properly with overlapping items
The previous code did not consider items that were overlapped due to having different z coordinates. The approach used is now the same as found in QQuickCanvas::mousePressEvent(). Strictly speaking, this is a violation of childAt (since it will disregard the implementation of childAt of all the descendants along the path down to the item actually returned.) However, I don't see any good reason for that the implementation for childAt() would be different than how mousePressEvent behaves. It should also perform better than any other solution I managed to think of. Change-Id: I2d3fa2282437c7b5533c6149c62fc456ccf2ccfa Reviewed-by: Frederik Gladhorn <frederik.gladhorn@nokia.com>
-rw-r--r--src/plugins/accessible/quick/qaccessiblequickitem.cpp49
-rw-r--r--src/plugins/accessible/quick/qaccessiblequickitem.h3
-rw-r--r--src/plugins/accessible/quick/qaccessiblequickview.cpp48
-rw-r--r--src/plugins/accessible/shared/qdeclarativeaccessible.cpp5
-rw-r--r--tests/auto/declarative/qdeclarativeaccessibility/data/hittest.qml176
-rw-r--r--tests/auto/declarative/qdeclarativeaccessibility/data/widgets/TextRect.qml26
-rw-r--r--tests/auto/declarative/qdeclarativeaccessibility/tst_qdeclarativeaccessibility.cpp47
7 files changed, 309 insertions, 45 deletions
diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.cpp b/src/plugins/accessible/quick/qaccessiblequickitem.cpp
index 5cedfe62d6..10442b275e 100644
--- a/src/plugins/accessible/quick/qaccessiblequickitem.cpp
+++ b/src/plugins/accessible/quick/qaccessiblequickitem.cpp
@@ -58,26 +58,7 @@ int QAccessibleQuickItem::childCount() const
QRect QAccessibleQuickItem::rect() const
{
- // ### no canvas in some cases.
- // ### Should we really check for 0 opacity?
- if (!item()->canvas() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) {
- return QRect();
- }
-
- QSizeF size = QSizeF(item()->width(), item()->height());
- // ### If the bounding rect fails, we first try the implicit size, then we go for the
- // parent size. WE MIGHT HAVE TO REVISIT THESE FALLBACKS.
- if (size.isEmpty()) {
- size = QSizeF(item()->implicitWidth(), item()->implicitHeight());
- if (size.isEmpty())
- // ### Seems that the above fallback is not enough, fallback to use the parent size...
- size = QSizeF(item()->parentItem()->width(), item()->parentItem()->height());
- }
-
- QRectF sceneRect = item()->mapRectToScene(QRectF(QPointF(0, 0), size));
- QPoint screenPos = item()->canvas()->mapToGlobal(sceneRect.topLeft().toPoint());
-
- QRect r = QRect(screenPos, sceneRect.size().toSize());
+ const QRect r = itemScreenRect(item());
if (!r.isValid()) {
qWarning() << item()->metaObject()->className() << item()->property("accessibleText") << r;
@@ -297,5 +278,33 @@ QVariant QAccessibleQuickItemValueInterface::minimumValue() const
return item()->property("minimumValue");
}
+/*!
+ \internal
+ Shared between QAccessibleQuickItem and QAccessibleQuickView
+*/
+QRect itemScreenRect(QQuickItem *item)
+{
+ // ### no canvas in some cases.
+ // ### Should we really check for 0 opacity?
+ if (!item->canvas() ||!item->isVisible() || qFuzzyIsNull(item->opacity())) {
+ return QRect();
+ }
+
+ QSize itemSize((int)item->width(), (int)item->height());
+ // ### If the bounding rect fails, we first try the implicit size, then we go for the
+ // parent size. WE MIGHT HAVE TO REVISIT THESE FALLBACKS.
+ if (itemSize.isEmpty()) {
+ itemSize = QSize((int)item->implicitWidth(), (int)item->implicitHeight());
+ if (itemSize.isEmpty())
+ // ### Seems that the above fallback is not enough, fallback to use the parent size...
+ itemSize = QSize((int)item->parentItem()->width(), (int)item->parentItem()->height());
+ }
+
+ QPointF scenePoint = item->mapToScene(QPointF(0, 0));
+ QPoint screenPos = item->canvas()->mapToGlobal(scenePoint.toPoint());
+ return QRect(screenPos, itemSize);
+}
+
+
QT_END_NAMESPACE
diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.h b/src/plugins/accessible/quick/qaccessiblequickitem.h
index 5ef3a19247..75e7081dc0 100644
--- a/src/plugins/accessible/quick/qaccessiblequickitem.h
+++ b/src/plugins/accessible/quick/qaccessiblequickitem.h
@@ -77,6 +77,9 @@ protected:
QQuickItem *item() const { return static_cast<QQuickItem*>(object()); }
};
+QRect itemScreenRect(QQuickItem *item);
+
+
class QAccessibleQuickItemValueInterface: public QAccessibleQuickItem, public QAccessibleValueInterface
{
public:
diff --git a/src/plugins/accessible/quick/qaccessiblequickview.cpp b/src/plugins/accessible/quick/qaccessiblequickview.cpp
index 6a896efd29..d15e01d96c 100644
--- a/src/plugins/accessible/quick/qaccessiblequickview.cpp
+++ b/src/plugins/accessible/quick/qaccessiblequickview.cpp
@@ -42,6 +42,7 @@
#include "qaccessiblequickview.h"
#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickitem_p.h>
#include "qaccessiblequickitem.h"
#include "qdeclarativeaccessible.h"
@@ -69,8 +70,8 @@ QAccessibleInterface *QAccessibleQuickView::parent() const
QAccessibleInterface *QAccessibleQuickView::child(int index) const
{
if (index == 0) {
- QQuickItem *declarativeRoot = view()->rootObject();
- return new QAccessibleQuickItem(declarativeRoot);
+ if (QQuickItem *declarativeRoot = view()->rootObject())
+ return new QAccessibleQuickItem(declarativeRoot);
}
return 0;
}
@@ -108,11 +109,48 @@ QString QAccessibleQuickView::text(QAccessible::Text text) const
return view()->windowTitle();
}
+
+/*!
+ \internal
+
+ Can also return \a item itself
+ */
+static QQuickItem *childAt_helper(QQuickItem *item, int x, int y)
+{
+ if (item->opacity() == 0.0 || !item->isVisible() || !item->isEnabled())
+ return 0;
+
+ if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
+ if (!itemScreenRect(item).contains(x, y))
+ return 0;
+ }
+
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ for (int i = children.count() - 1; i >= 0; --i) {
+ QQuickItem *child = children.at(i);
+ if (QQuickItem *childChild = childAt_helper(child, x, y))
+ return childChild;
+ }
+
+ QRect screenRect = itemScreenRect(item);
+
+ if (screenRect.contains(x, y))
+ return item;
+
+ return 0;
+}
+
QAccessibleInterface *QAccessibleQuickView::childAt(int x, int y) const
{
- Q_UNUSED(x);
- Q_UNUSED(y);
- return child(0); // return the top-level QML item
+ Q_ASSERT(view());
+ QQuickItem *root = view()->rootItem();
+ if (root) {
+ if (QQuickItem *item = childAt_helper(root, x, y))
+ return QAccessible::queryAccessibleInterface(item);
+ }
+ return 0;
}
int QAccessibleQuickView::indexOfChild(const QAccessibleInterface *iface) const
diff --git a/src/plugins/accessible/shared/qdeclarativeaccessible.cpp b/src/plugins/accessible/shared/qdeclarativeaccessible.cpp
index 7db5f07cbe..1b35594e74 100644
--- a/src/plugins/accessible/shared/qdeclarativeaccessible.cpp
+++ b/src/plugins/accessible/shared/qdeclarativeaccessible.cpp
@@ -70,9 +70,8 @@ QFlags<QAccessible::RelationFlag> QDeclarativeAccessible::relationTo(const QAcce
QAccessibleInterface *QDeclarativeAccessible::childAt(int x, int y) const
{
- // Look for children first.
- // Start with the last child first, because children are ordered in paint order
- // (which is opposite of hit test order)
+ // Note that this function will disregard stacking order.
+ // (QAccessibleQuickView::childAt() does this correctly and more efficient)
// If the item clips its children, we can return early if the coordinate is outside its rect
if (clipsChildren()) {
diff --git a/tests/auto/declarative/qdeclarativeaccessibility/data/hittest.qml b/tests/auto/declarative/qdeclarativeaccessibility/data/hittest.qml
new file mode 100644
index 0000000000..52b652e233
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeaccessibility/data/hittest.qml
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+import QtQuick 2.0
+import "widgets"
+
+Rectangle {
+ id: page
+ width: 640
+ height: 480
+ color: "white"
+ Rectangle {
+ id: header
+ color: "#c0c0c0"
+ height: usage.height + chkClip.height
+ anchors.left: parent.left
+ anchors.right: parent.right
+ Text {
+ id: usage
+ text: "Use an a11y inspect tool to see if all visible rectangles can be found with hit testing."
+ }
+ Rectangle {
+ id: chkClip
+ property bool checked: true
+
+ color: (checked ? "#f0f0f0" : "#c0c0c0")
+ height: label.height
+ width: label.width
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: chkClip.checked = !chkClip.checked
+ }
+ Text {
+ id: label
+ text: "Click here to toggle clipping"
+ }
+ }
+ }
+ TextRect {
+ clip: chkClip.checked
+ z: 2
+ id: rect1
+ text: "rect1"
+ width: 100
+ height: 100
+ color: "#ffc0c0"
+ anchors.top: header.bottom
+ TextRect {
+ id: rect10
+ text: "rect10"
+ width: 100
+ height: 100
+ x: 50
+ y: 50
+ color: "#ffa0a0"
+ TextRect {
+ id: rect100
+ text: "rect100"
+ width: 100
+ height: 100
+ x: 80
+ y: 80
+ color: "#ff8080"
+ }
+ TextRect {
+ id: rect101
+ text: "rect101"
+ x: 100
+ y: 70
+ z: 3
+ width: 100
+ height: 100
+ color: "#e06060"
+ }
+ TextRect {
+ id: rect102
+ text: "rect102"
+ width: 100
+ height: 100
+ x: 150
+ y: 60
+ color: "#c04040"
+ }
+ }
+ }
+
+ TextRect {
+ x: 0
+ y: 50
+ id: rect2
+ text: "rect2"
+ width: 100
+ height: 100
+ color: "#c0c0ff"
+ TextRect {
+ id: rect20
+ text: "rect20"
+ width: 100
+ height: 100
+ x: 50
+ y: 50
+ color: "#a0a0ff"
+ TextRect {
+ id: rect200
+ text: "rect200"
+ width: 100
+ height: 100
+ x: 80
+ y: 80
+ color: "#8080ff"
+ }
+ TextRect {
+ id: rect201
+ text: "rect201"
+ x: 100
+ y: 70
+ z: 100
+ width: 100
+ height: 100
+ color: "#6060e0"
+ }
+ TextRect {
+ id: rect202
+ text: "rect202"
+ width: 100
+ height: 100
+ x: 150
+ y: 60
+ color: "#4040c0"
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/declarative/qdeclarativeaccessibility/data/widgets/TextRect.qml b/tests/auto/declarative/qdeclarativeaccessibility/data/widgets/TextRect.qml
new file mode 100644
index 0000000000..937686974b
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeaccessibility/data/widgets/TextRect.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: button
+
+ property alias text : buttonText.text
+ Accessible.name: text
+ Accessible.description: "This button does " + text
+ Accessible.role: Accessible.Client
+
+ signal clicked
+
+ width: 40
+ height: 40
+ border.width: 2
+ border.color: "black";
+
+ Text {
+ id: buttonText
+ text: "TextRect"
+ anchors.centerIn: parent
+ font.pixelSize: parent.height * .1
+ style: Text.Sunken; color: "white"; styleColor: "black"; smooth: true
+ }
+
+}
diff --git a/tests/auto/declarative/qdeclarativeaccessibility/tst_qdeclarativeaccessibility.cpp b/tests/auto/declarative/qdeclarativeaccessibility/tst_qdeclarativeaccessibility.cpp
index 725d28288e..a20094de61 100644
--- a/tests/auto/declarative/qdeclarativeaccessibility/tst_qdeclarativeaccessibility.cpp
+++ b/tests/auto/declarative/qdeclarativeaccessibility/tst_qdeclarativeaccessibility.cpp
@@ -421,33 +421,46 @@ QAI topLevelChildAt(QAccessibleInterface *iface, int x, int y)
void tst_QDeclarativeAccessibility::hitTest()
{
- QQuickView *canvas = new QQuickView();
- canvas->setSource(testFileUrl("statictext.qml"));
+ QQuickView *canvas = new QQuickView;
+ canvas->setSource(testFileUrl("hittest.qml"));
canvas->show();
QAI iface = QAI(QAccessible::queryAccessibleInterface(canvas));
QVERIFY(iface.data());
- QAI item = QAI(iface->child(0));
- QRect itemRect = item->rect();
+ QAI rootItem = QAI(iface->child(0));
+ QRect rootRect = rootItem->rect();
// hit the root item
- QAI itemHit = QAI(iface->childAt(itemRect.x() + 5, itemRect.y() + 5));
+ QAI itemHit(iface->childAt(rootRect.x() + 200, rootRect.y() + 50));
QVERIFY(itemHit);
- QCOMPARE(itemRect, itemHit->rect());
+ QCOMPARE(rootRect, itemHit->rect());
- // hit a text element
- QAI textChild = QAI(item->child(0));
- QAI textChildHit = topLevelChildAt(iface.data(), itemRect.x() + 105, itemRect.y() + 25);
- QVERIFY(textChildHit);
- QCOMPARE(textChild->rect(), textChildHit->rect());
- QCOMPARE(textChildHit->text(QAccessible::Name), QLatin1String("Hello Accessibility"));
+ // hit rect1
+ QAI rect1(rootItem->child(1));
+ QRect rect1Rect = rect1->rect();
+ itemHit = QAI(rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10));
+ QVERIFY(itemHit);
+ QCOMPARE(rect1Rect, itemHit->rect());
+ QCOMPARE(itemHit->text(QAccessible::Name), QLatin1String("rect1"));
// should also work from top level (app)
- QAI app = QAI(QAccessible::queryAccessibleInterface(qApp));
- QAI textChildHit2 = topLevelChildAt(app.data(), itemRect.x() + 105, itemRect.y() + 25);
- QVERIFY(textChildHit2);
- QCOMPARE(textChild->rect(), textChildHit2->rect());
- QCOMPARE(textChildHit2->text(QAccessible::Name), QLatin1String("Hello Accessibility"));
+ QAI app(QAccessible::queryAccessibleInterface(qApp));
+ QAI itemHit2(topLevelChildAt(app.data(), rect1Rect.x() + 10, rect1Rect.y() + 10));
+ QVERIFY(itemHit2);
+ QCOMPARE(itemHit2->rect(), rect1Rect);
+ QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1"));
+
+ // hit rect201
+ QAI rect2(rootItem->child(2));
+ QAI rect20(rect2->child(1));
+ QAI rect201(rect20->child(2));
+ QVERIFY(rect201);
+
+ QRect rect201Rect = rect201->rect();
+ itemHit = QAI(iface->childAt(rect201Rect.x() + 20, rect201Rect.y() + 20));
+ QVERIFY(itemHit);
+ QCOMPARE(itemHit->rect(), rect201Rect);
+ QCOMPARE(itemHit->text(QAccessible::Name), QLatin1String("rect201"));
delete canvas;
}