Skip to content

Commit

Permalink
Make QAccessibleQuickView::childAt() work properly with overlapping i…
Browse files Browse the repository at this point in the history
…tems

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>
  • Loading branch information
Jan-Arve Saether authored and Qt by Nokia committed Jan 12, 2012
1 parent 0cafd92 commit 9ab84bd
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 45 deletions.
49 changes: 29 additions & 20 deletions src/plugins/accessible/quick/qaccessiblequickitem.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions src/plugins/accessible/quick/qaccessiblequickitem.h
Expand Up @@ -77,6 +77,9 @@ class QAccessibleQuickItem : public QDeclarativeAccessible
QQuickItem *item() const { return static_cast<QQuickItem*>(object()); }
};

QRect itemScreenRect(QQuickItem *item);


class QAccessibleQuickItemValueInterface: public QAccessibleQuickItem, public QAccessibleValueInterface
{
public:
Expand Down
48 changes: 43 additions & 5 deletions src/plugins/accessible/quick/qaccessiblequickview.cpp
Expand Up @@ -42,6 +42,7 @@
#include "qaccessiblequickview.h"

#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickitem_p.h>

#include "qaccessiblequickitem.h"
#include "qdeclarativeaccessible.h"
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions src/plugins/accessible/shared/qdeclarativeaccessible.cpp
Expand Up @@ -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()) {
Expand Down
176 changes: 176 additions & 0 deletions 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"
}
}
}

}
@@ -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
}

}

0 comments on commit 9ab84bd

Please sign in to comment.