a11y atspi: Add support for ATSPI_COORD_TYPE_PARENT

So far, only screen coordinates (`ATSPI_COORD_TYPE_SCREEN`)
and coordinates relative to the widget's top-level window
(`ATSPI_COORD_TYPE_WINDOW`) were supported.

In at-spi 2.30, a third coordinate type, describing
coordinates relative to the widget's immediate parent,
was added: `ATSPI_COORD_TYPE_PARENT` (commit: [1])

This commit adds the handling to convert to and
from that coord type as well and unifies the
existing handling to convert to/from screen coordinates
by introducing two static helper methods
(`AtSpiAdaptor::translateToScreenCoordinates` and
`AtSpiAdaptor::translateFromScreenCoordinates`) and
making use of those wherever conversion needs to
be done.

In addition, also add a check to only handle
requests using a coordinate type that is one of those
that is actually supported instead of returning
incorrect values (for the case that new coord types
should be introduced in AT-SPI in the future).

[1] 737c9853b6

Fixes: QTBUG-105313
Change-Id: Ia8752bd6a35328cc2de33ecad08f2964735f41ae
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Michael Weghorn 2022-08-03 07:42:10 +02:00
parent 4d6decf628
commit a32a3aa388
2 changed files with 66 additions and 56 deletions

View File

@ -28,6 +28,11 @@
It sends notifications coming from Qt via dbus and listens to incoming dbus requests.
*/
// ATSPI_COORD_TYPE_PARENT was added in at-spi 2.30, define here for older versions
#if ATSPI_COORD_TYPE_COUNT < 3
#define ATSPI_COORD_TYPE_PARENT 2
#endif
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@ -1534,23 +1539,6 @@ static QAccessibleInterface * getWindow(QAccessibleInterface * interface)
return parent;
}
static QRect getRelativeRect(QAccessibleInterface *interface)
{
QAccessibleInterface * window;
QRect wr, cr;
cr = interface->rect();
window = getWindow(interface);
if (window) {
wr = window->rect();
cr.setX(cr.x() - wr.x());
cr.setY(cr.y() - wr.y());
}
return cr;
}
bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
{
if (function == "Contains"_L1) {
@ -1558,28 +1546,22 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
int x = message.arguments().at(0).toInt();
int y = message.arguments().at(1).toInt();
uint coordType = message.arguments().at(2).toUInt();
if (coordType == ATSPI_COORD_TYPE_SCREEN)
ret = interface->rect().contains(x, y);
else
ret = getRelativeRect(interface).contains(x, y);
if (!isValidCoordType(coordType))
return false;
ret = getExtents(interface, coordType).contains(x, y);
sendReply(connection, message, ret);
} else if (function == "GetAccessibleAtPoint"_L1) {
int x = message.arguments().at(0).toInt();
int y = message.arguments().at(1).toInt();
QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
uint coordType = message.arguments().at(2).toUInt();
if (coordType == ATSPI_COORD_TYPE_WINDOW) {
QWindow * window = interface->window();
if (window) {
x += window->position().x();
y += window->position().y();
}
}
if (!isValidCoordType(coordType))
return false;
QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
QAccessibleInterface * childInterface(interface->childAt(x, y));
QAccessibleInterface * childInterface(interface->childAt(screenPos.x(), screenPos.y()));
QAccessibleInterface * iface = nullptr;
while (childInterface) {
iface = childInterface;
childInterface = iface->childAt(x, y);
childInterface = iface->childAt(screenPos.x(), screenPos.y());
}
if (iface) {
QString path = pathForInterface(iface);
@ -1593,6 +1575,8 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
sendReply(connection, message, (double) 1.0);
} else if (function == "GetExtents"_L1) {
uint coordType = message.arguments().at(0).toUInt();
if (!isValidCoordType(coordType))
return false;
sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType)));
} else if (function == "GetLayer"_L1) {
sendReply(connection, message, QVariant::fromValue((uint)1));
@ -1600,11 +1584,9 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
sendReply(connection, message, QVariant::fromValue((short)0));
} else if (function == "GetPosition"_L1) {
uint coordType = message.arguments().at(0).toUInt();
QRect rect;
if (coordType == ATSPI_COORD_TYPE_SCREEN)
rect = interface->rect();
else
rect = getRelativeRect(interface);
if (!isValidCoordType(coordType))
return false;
QRect rect = getExtents(interface, coordType);
QVariantList pos;
pos << rect.x() << rect.y();
connection.send(message.createReply(pos));
@ -1649,7 +1631,7 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType)
{
return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface);
return translateFromScreenCoordinates(interface, interface->rect(), coordType);
}
// Action interface
@ -1802,11 +1784,10 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
Q_ASSERT(!message.signature().isEmpty());
QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
uint coordType = message.arguments().at(2).toUInt();
if (coordType == ATSPI_COORD_TYPE_WINDOW) {
QWindow *win = interface->window();
point += QPoint(win->x(), win->y());
}
int offset = interface->textInterface()->offsetAtPoint(point);
if (!isValidCoordType(coordType))
return false;
QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
int offset = interface->textInterface()->offsetAtPoint(screenPos);
sendReply(connection, message, offset);
} else if (function == "GetRangeExtents"_L1) {
int startOffset = message.arguments().at(0).toInt();
@ -2053,10 +2034,7 @@ QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, in
QList<QVariant> AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const
{
QRect rect = interface->textInterface()->characterRect(offset);
if (coordType == ATSPI_COORD_TYPE_WINDOW)
rect = translateRectToWindowCoordinates(interface, rect);
rect = translateFromScreenCoordinates(interface, rect, coordType);
return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
}
@ -2074,22 +2052,52 @@ QList<QVariant> AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface,
for (int i=startOffset + 1; i <= endOffset; i++)
rect = rect | textInterface->characterRect(i);
// relative to window
if (coordType == ATSPI_COORD_TYPE_WINDOW)
rect = translateRectToWindowCoordinates(interface, rect);
rect = translateFromScreenCoordinates(interface, rect, coordType);
return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
}
QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect)
bool AtSpiAdaptor::isValidCoordType(uint coordType)
{
QAccessibleInterface * window = getWindow(interface);
if (window)
return rect.translated(-window->rect().x(), -window->rect().y());
if (coordType == ATSPI_COORD_TYPE_SCREEN || coordType == ATSPI_COORD_TYPE_WINDOW || coordType == ATSPI_COORD_TYPE_PARENT)
return true;
qCWarning(lcAccessibilityAtspi) << "unknown value" << coordType << "for AT-SPI coord type";
return false;
}
QRect AtSpiAdaptor::translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &screenRect, uint targetCoordType)
{
Q_ASSERT(isValidCoordType(targetCoordType));
QAccessibleInterface *upper = nullptr;
if (targetCoordType == ATSPI_COORD_TYPE_WINDOW)
upper = getWindow(interface);
else if (targetCoordType == ATSPI_COORD_TYPE_PARENT)
upper = interface->parent();
QRect rect = screenRect;
if (upper)
rect.translate(-upper->rect().x(), -upper->rect().y());
return rect;
}
QPoint AtSpiAdaptor::translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType)
{
Q_ASSERT(isValidCoordType(fromCoordType));
QAccessibleInterface *upper = nullptr;
if (fromCoordType == ATSPI_COORD_TYPE_WINDOW)
upper = getWindow(interface);
else if (fromCoordType == ATSPI_COORD_TYPE_PARENT)
upper = interface->parent();
QPoint screenPos = pos;
if (upper)
screenPos += upper->rect().topLeft();
return screenPos;
}
// Editable Text interface
static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset)

View File

@ -94,7 +94,9 @@ private:
// component helper functions
static QRect getExtents(QAccessibleInterface *interface, uint coordType);
static QRect translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect);
static bool isValidCoordType(uint coordType);
static QRect translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &rect, uint targetCoordType);
static QPoint translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType);
// action helper functions
QSpiActionArray getActions(QAccessibleInterface *interface) const;