Commit db16f3e5 authored by Jan Arve Saether's avatar Jan Arve Saether Committed by Jan Arve Sæther
Browse files

Improve hit testing so that it works better with ignored items

The hit testing won't work very well with the upcoming patch that
changes which items that can be ignored.
(basically it doesn't consider the isAccessible flag,
so childAt_helper might return a node that was supposed to be ignored)

Earlier this was a sensible optimization in order to avoid too many heap
allocations and deallocations of interfaces, but these are cheap now, so
we can do it the do it the 'proper way' (i.e. before this patch we
didn't respect the a11y hierarchy as given by QAccessibleInterface
child() and parent(). This also uses the QAccessibleInterface::rect()
directly now instead of using the itemScreenRect() function, which
was shared between QAccessibleQuickWindow and QAccessibleQuickItem.

Since this changes the order of child interfaces to paint order (i.e.
second child interface is on top of first child interface), we need to
ensure that we hit test child interfaces in the correct order.
They should now always be processe...
parent d6661ca4
Branches
Tags
No related merge requests found
Showing with 57 additions and 85 deletions
...@@ -82,17 +82,42 @@ bool QAccessibleQuickItem::clipsChildren() const ...@@ -82,17 +82,42 @@ bool QAccessibleQuickItem::clipsChildren() const
return static_cast<QQuickItem *>(item())->clip(); return static_cast<QQuickItem *>(item())->clip();
} }
QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const
{
if (item()->clip()) {
if (!rect().contains(x, y))
return 0;
}
const QList<QQuickItem*> kids = accessibleUnignoredChildren(item(), true);
for (int i = kids.count() - 1; i >= 0; --i) {
QAccessibleInterface *childIface = QAccessible::queryAccessibleInterface(kids.at(i));
if (QAccessibleInterface *childChild = childIface->childAt(x, y))
return childChild;
if (childIface && !childIface->state().invisible) {
if (childIface->rect().contains(x, y))
return childIface;
}
}
return 0;
}
QAccessibleInterface *QAccessibleQuickItem::parent() const QAccessibleInterface *QAccessibleQuickItem::parent() const
{ {
QQuickItem *parent = item()->parentItem(); QQuickItem *parent = item()->parentItem();
QQuickWindow *window = item()->window();
QQuickItem *ci = window ? window->contentItem() : 0;
while (parent && parent != ci)
parent = parent->parentItem();
if (parent) { if (parent) {
QQuickWindow *window = item()->window(); if (parent == ci) {
// Jump out to the scene widget if the parent is the root item. // Jump out to the scene widget if the parent is the root item.
// There are two root items, QQuickWindow::rootItem and // There are two root items, QQuickWindow::rootItem and
// QQuickView::declarativeRoot. The former is the true root item, // QQuickView::declarativeRoot. The former is the true root item,
// but is not a part of the accessibility tree. Check if we hit // but is not a part of the accessibility tree. Check if we hit
// it here and return an interface for the scene instead. // it here and return an interface for the scene instead.
if (window && (parent == window->contentItem())) {
return QAccessible::queryAccessibleInterface(window); return QAccessible::queryAccessibleInterface(window);
} else { } else {
return QAccessible::queryAccessibleInterface(parent); return QAccessible::queryAccessibleInterface(parent);
...@@ -121,6 +146,19 @@ int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const ...@@ -121,6 +146,19 @@ int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const
return kids.indexOf(static_cast<QQuickItem*>(iface->object())); return kids.indexOf(static_cast<QQuickItem*>(iface->object()));
} }
QList<QQuickItem *> accessibleUnignoredChildren(QQuickItem *item, bool paintOrder)
{
QList<QQuickItem *> items;
QList<QQuickItem*> childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems()
: item->childItems();
Q_FOREACH (QQuickItem *child, childItems) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(child);
if (itemPrivate->isAccessible)
items.append(child);
}
return items;
}
QList<QQuickItem *> QAccessibleQuickItem::childItems() const QList<QQuickItem *> QAccessibleQuickItem::childItems() const
{ {
if ( role() == QAccessible::Button || if ( role() == QAccessible::Button ||
...@@ -133,13 +171,7 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const ...@@ -133,13 +171,7 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const
role() == QAccessible::ProgressBar) role() == QAccessible::ProgressBar)
return QList<QQuickItem *>(); return QList<QQuickItem *>();
QList<QQuickItem *> items; return accessibleUnignoredChildren(item());
Q_FOREACH (QQuickItem *child, item()->childItems()) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(child);
if (itemPrivate->isAccessible)
items.append(child);
}
return items;
} }
QAccessible::State QAccessibleQuickItem::state() const QAccessible::State QAccessibleQuickItem::state() const
......
...@@ -53,6 +53,7 @@ public: ...@@ -53,6 +53,7 @@ public:
QRect viewRect() const; QRect viewRect() const;
bool clipsChildren() const; bool clipsChildren() const;
QAccessibleInterface *childAt(int x, int y) const;
QAccessibleInterface *parent() const; QAccessibleInterface *parent() const;
QAccessibleInterface *child(int index) const; QAccessibleInterface *child(int index) const;
...@@ -118,6 +119,7 @@ private: ...@@ -118,6 +119,7 @@ private:
}; };
QRect itemScreenRect(QQuickItem *item); QRect itemScreenRect(QQuickItem *item);
QList<QQuickItem *> accessibleUnignoredChildren(QQuickItem *item, bool paintOrder = false);
#endif // QT_NO_ACCESSIBILITY #endif // QT_NO_ACCESSIBILITY
......
...@@ -53,7 +53,7 @@ QAccessibleQuickWindow::QAccessibleQuickWindow(QQuickWindow *object) ...@@ -53,7 +53,7 @@ QAccessibleQuickWindow::QAccessibleQuickWindow(QQuickWindow *object)
QQuickItem *QAccessibleQuickWindow::rootItem() const QQuickItem *QAccessibleQuickWindow::rootItem() const
{ {
if (QQuickItem *ci = window()->contentItem()) { if (QQuickItem *ci = window()->contentItem()) {
const QList<QQuickItem *> &childItems = ci->childItems(); const QList<QQuickItem *> &childItems = accessibleUnignoredChildren(ci);
if (!childItems.isEmpty()) if (!childItems.isEmpty())
return childItems.first(); return childItems.first();
} }
...@@ -110,56 +110,17 @@ QString QAccessibleQuickWindow::text(QAccessible::Text text) const ...@@ -110,56 +110,17 @@ QString QAccessibleQuickWindow::text(QAccessible::Text text) const
return window()->title(); return window()->title();
} }
/*!
\internal
Can also return \a item itself
*/
static QQuickItem *childAt_helper(QQuickItem *item, int x, int y)
{
if (!item->isVisible() || !item->isEnabled())
return 0;
if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
if (!itemScreenRect(item).contains(x, y))
return 0;
}
QAccessibleInterface *accessibleInterface = QAccessible::queryAccessibleInterface(item);
// this item has no Accessible attached property
if (!accessibleInterface)
return 0;
if (accessibleInterface->childCount() == 0) {
return (itemScreenRect(item).contains(x, y)) ? item : 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 *QAccessibleQuickWindow::childAt(int x, int y) const QAccessibleInterface *QAccessibleQuickWindow::childAt(int x, int y) const
{ {
Q_ASSERT(window()); Q_ASSERT(window());
QQuickItem *root = rootItem(); for (int i = childCount() - 1; i >= 0; --i) {
if (root) { QAccessibleInterface *childIface = child(i);
if (QQuickItem *item = childAt_helper(root, x, y)) if (childIface && !childIface->state().invisible) {
return QAccessible::queryAccessibleInterface(item); if (QAccessibleInterface *iface = childIface->childAt(x, y))
return QAccessible::queryAccessibleInterface(root); return iface;
if (childIface->rect().contains(x, y))
return childIface;
}
} }
return 0; return 0;
} }
......
...@@ -54,27 +54,6 @@ QQmlAccessible::~QQmlAccessible() ...@@ -54,27 +54,6 @@ QQmlAccessible::~QQmlAccessible()
{ {
} }
QAccessibleInterface *QQmlAccessible::childAt(int x, int y) const
{
// 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()) {
if (!rect().contains(x, y))
return 0;
}
for (int i = childCount() - 1; i >= 0; --i) {
QAccessibleInterface *childIface = child(i);
if (childIface && !childIface->state().invisible) {
if (childIface->rect().contains(x, y))
return childIface;
}
}
return 0;
}
QAccessible::State QQmlAccessible::state() const QAccessible::State QQmlAccessible::state() const
{ {
QAccessible::State state; QAccessible::State state;
......
...@@ -64,7 +64,6 @@ public: ...@@ -64,7 +64,6 @@ public:
void *interface_cast(QAccessible::InterfaceType t); void *interface_cast(QAccessible::InterfaceType t);
virtual QRect viewRect() const = 0; virtual QRect viewRect() const = 0;
QAccessibleInterface *childAt(int, int) const;
QAccessible::State state() const; QAccessible::State state() const;
QStringList actionNames() const; QStringList actionNames() const;
...@@ -72,7 +71,6 @@ public: ...@@ -72,7 +71,6 @@ public:
QStringList keyBindingsForAction(const QString &actionName) const; QStringList keyBindingsForAction(const QString &actionName) const;
protected: protected:
virtual bool clipsChildren() const = 0;
// For subclasses, use instantiateObject factory method outside the class. // For subclasses, use instantiateObject factory method outside the class.
QQmlAccessible(QObject *object); QQmlAccessible(QObject *object);
}; };
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment