Use events for accessibility updates.

The problem with the old updates is that it was impossible
to send details about the update.
For the Linux implementation to work properly with AT-SPI I need to know
which state changed (currently I only get a generic "state changed" event)
or which part of the text was changed for long texts (imagine a word processor sending updates).

This also gives us more options when updating with events generated from not QObject based objects.

Change-Id: If0b6c83c523092565eb48e79f3f6de5a1b905ea8
Reviewed-by: Jan-Arve Sæther <jan-arve.saether@nokia.com>
This commit is contained in:
Frederik Gladhorn 2012-01-11 16:31:35 +01:00 committed by Qt by Nokia
parent cf52c268b0
commit bf9bdf5328
8 changed files with 104 additions and 45 deletions

View File

@ -654,13 +654,29 @@ void QAccessible::setRootObject(QObject *object)
}
/*!
Notifies accessibility clients about a change in \a object's
accessibility information.
\deprecated
\a reason specifies the cause of the change, for example,
\c ValueChange when the position of a slider has been changed. \a
child is the (1-based) index of the child element that has changed.
When \a child is 0, the object itself has changed.
Use the version with a single \l QAccessibleEvent paremeter instead.
*/
void QAccessible::updateAccessibility(QObject *object, int child, Event reason)
{
Q_ASSERT(object);
qWarning("QAccessible::updateAccessibility is deprecated.");
QAccessibleEvent event = QAccessibleEvent(reason, object, child);
updateAccessibility(event);
}
/*!
Notifies about a change that might be relevant for accessibility clients.
\a event gives the details about the change.
This includes the source of the change and what the actual change is.
There should be sufficient details delivered with this event to give meaningful notifications.
For example, the type \c ValueChange indicates that the position of
a slider has been changed.
Call this function whenever the state of your accessible object or
one of its sub-elements has been changed either programmatically
@ -671,12 +687,10 @@ void QAccessible::setRootObject(QObject *object)
the parameters of the call is expensive you can test isActive() to
avoid unnecessary computations.
*/
void QAccessible::updateAccessibility(QObject *object, int child, Event reason)
void QAccessible::updateAccessibility(const QAccessibleEvent &event)
{
Q_ASSERT(object);
if (updateHandler) {
updateHandler(object, child, reason);
updateHandler(event);
return;
}
@ -684,9 +698,43 @@ void QAccessible::updateAccessibility(QObject *object, int child, Event reason)
return;
if (QPlatformAccessibility *pfAccessibility = platformAccessibility())
pfAccessibility->notifyAccessibilityUpdate(object, child, reason);
pfAccessibility->notifyAccessibilityUpdate(event);
}
/*!
\class QAccessibleEvent
\brief The QAccessibleEvent is use to notify about changes that are
relevant for accessibility in the application.
\ingroup accessibility
\inmodule QtGui
This class should be created on the stack and used as parameter for
\l QAccessible::updateAccessibility().
*/
/*!
Returns the QAccessibleInterface associated with the event.
The caller of this function takes ownership of the returned interface.
*/
QAccessibleInterface *QAccessibleEvent::accessibleInterface() const
{
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(m_object);
if (!iface)
return 0;
if (m_child >= 0) {
QAccessibleInterface *child = iface->child(m_child);
if (child) {
delete iface;
iface = child;
} else {
qWarning() << "Cannot creat accessible child interface for object: " << m_object << " index: " << m_child;
}
}
return iface;
}
/*!
\class QAccessibleInterface

View File

@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_ACCESSIBILITY
class QAccessibleInterface;
class QAccessibleEvent;
class QWindow;
// We need to inherit QObject to expose the enums to QML.
@ -326,7 +327,7 @@ public:
};
typedef QAccessibleInterface*(*InterfaceFactory)(const QString &key, QObject*);
typedef void(*UpdateHandler)(QObject*, int who, Event reason);
typedef void(*UpdateHandler)(const QAccessibleEvent &event);
typedef void(*RootObjectHandler)(QObject*);
static void installFactory(InterfaceFactory);
@ -335,7 +336,10 @@ public:
static RootObjectHandler installRootObjectHandler(RootObjectHandler);
static QAccessibleInterface *queryAccessibleInterface(QObject *);
static void updateAccessibility(QObject *object, int child, Event reason);
static void updateAccessibility(const QAccessibleEvent &event);
static bool isActive();
static void setRootObject(QObject *object);
@ -425,6 +429,28 @@ public:
private:
};
class Q_GUI_EXPORT QAccessibleEvent
{
public:
inline QAccessibleEvent(QAccessible::Event type, QObject *object, int child = -1)
: m_type(type), m_object(object), m_child(child)
{
Q_ASSERT(object);
}
QAccessible::Event type() const { return m_type; }
QObject *object() const { return m_object; }
int child() const { return m_child; }
QAccessibleInterface *accessibleInterface() const;
private:
QAccessible::Event m_type;
QObject *m_object;
int m_child;
};
#define QAccessibleInterface_iid "org.qt-project.Qt.QAccessibleInterface"
Q_DECLARE_INTERFACE(QAccessibleInterface, QAccessibleInterface_iid)

View File

@ -53,13 +53,14 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_ACCESSIBILITY
class QAccessibleInterface;
class QAccessibleEvent;
class QAccessibleBridge
{
public:
virtual ~QAccessibleBridge() {}
virtual void setRootObject(QAccessibleInterface *) = 0;
virtual void notifyAccessibilityUpdate(int, QAccessibleInterface*, int) = 0;
virtual void notifyAccessibilityUpdate(const QAccessibleEvent &event) = 0;
};
struct Q_GUI_EXPORT QAccessibleBridgeFactoryInterface : public QFactoryInterface

View File

@ -45,6 +45,8 @@
#include "qaccessiblebridge.h"
#include <QtGui/QGuiApplication>
#include <QDebug>
QT_BEGIN_NAMESPACE
/* accessiblebridge plugin discovery stuff */
@ -73,33 +75,15 @@ QPlatformAccessibility::~QPlatformAccessibility()
{
}
void QPlatformAccessibility::notifyAccessibilityUpdate(QObject *o,
int who,
QAccessible::Event reason)
void QPlatformAccessibility::notifyAccessibilityUpdate(const QAccessibleEvent &event)
{
initialize();
if (!bridges() || bridges()->isEmpty())
return;
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o);
if (!iface)
return;
// updates for List/Table/Tree should send child
if (who) {
QAccessibleInterface *child = iface->child(who - 1);
if (child) {
delete iface;
iface = child;
who = 0;
}
}
for (int i = 0; i < bridges()->count(); ++i)
bridges()->at(i)->notifyAccessibilityUpdate(reason, iface, who);
delete iface;
bridges()->at(i)->notifyAccessibilityUpdate(event);
}
void QPlatformAccessibility::setRootObject(QObject *o)

View File

@ -57,7 +57,7 @@ public:
QPlatformAccessibility();
virtual ~QPlatformAccessibility();
virtual void notifyAccessibilityUpdate(QObject *o, int who, QAccessible::Event reason);
virtual void notifyAccessibilityUpdate(const QAccessibleEvent &event);
virtual void setRootObject(QObject *o);
virtual void initialize();
virtual void cleanup();

View File

@ -1296,10 +1296,10 @@ QWindowsAccessibility::QWindowsAccessibility()
}
void QWindowsAccessibility::notifyAccessibilityUpdate(QObject *o, int who, QAccessible::Event reason)
void QWindowsAccessibility::notifyAccessibilityUpdate(const QAccessibleEvent &event)
{
QString soundName;
switch (reason) {
switch (event.type()) {
case QAccessible::PopupMenuStart:
soundName = QLatin1String("MenuPopup");
break;
@ -1370,8 +1370,9 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QObject *o, int who, QAcce
// An event has to be associated with a window,
// so find the first parent that is a widget and that has a WId
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o);
QAccessibleInterface *iface = event.accessibleInterface();
QWindow *window = iface ? window_helper(iface) : 0;
delete iface;
if (!window) {
window = QGuiApplication::activeWindow();
@ -1382,18 +1383,17 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QObject *o, int who, QAcce
QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
HWND hWnd = (HWND)platform->nativeResourceForWindow("handle", window);
if (reason != QAccessible::MenuCommand) { // MenuCommand is faked
if (event.type() != QAccessible::MenuCommand) { // MenuCommand is faked
// See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE"
eventNum %= 50; //[0..49]
int eventId = - eventNum - 1;
qAccessibleRecentSentEvents()->insert(eventId, qMakePair(o, who));
ptrNotifyWinEvent(reason, hWnd, OBJID_CLIENT, eventId );
qAccessibleRecentSentEvents()->insert(eventId, qMakePair(event.object(), event.child()));
ptrNotifyWinEvent(event.type(), hWnd, OBJID_CLIENT, eventId );
++eventNum;
}
#endif // Q_WS_WINCE
}
/*

View File

@ -50,12 +50,12 @@ class QWindowsAccessibility : public QPlatformAccessibility
public:
QWindowsAccessibility();
static bool handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult);
virtual void notifyAccessibilityUpdate(QObject *o, int who, QAccessible::Event reason);
virtual void notifyAccessibilityUpdate(const QAccessibleEvent &event);
/*
virtual void setRootObject(QObject *o);
virtual void initialize();
virtual void cleanup();
*/
*/
};
#endif // QWINDOWSACCESSIBILITY_H

View File

@ -134,10 +134,10 @@ private:
}
}
static void updateHandler(QObject *o, int c, QAccessible::Event e)
static void updateHandler(const QAccessibleEvent &event)
{
// qDebug("updateHandler called: %p %d %d", o, c, (int)e);
eventList().append(QTestAccessibilityEvent(o, c, (int)e));
eventList().append(QTestAccessibilityEvent(event.object(), event.child(), (int)event.type()));
}
static EventList &eventList()