Let platform plugin decide if accessibility is active

Change-Id: I881a8ff3fedf3db73ee37046a4363c70960a92a6
Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>
This commit is contained in:
Frederik Gladhorn 2013-03-25 18:07:17 +01:00 committed by The Qt Project
parent c2059ac80d
commit 0d57da067b
16 changed files with 93 additions and 57 deletions

View File

@ -2,7 +2,7 @@
DBUS_ADAPTORS = $$PWD/xml/Cache.xml $$PWD/xml/DeviceEventController.xml
QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS = -i struct_marshallers_p.h
DBUS_INTERFACES = $$PWD/xml/Socket.xml
DBUS_INTERFACES = $$PWD/xml/Socket.xml $$PWD/xml/Bus.xml
QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i struct_marshallers_p.h
INCLUDEPATH += $$PWD

17
src/3rdparty/atspi2/xml/Bus.xml vendored Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.a11y.Status">
<property type="b" name="IsEnabled" access="readwrite">
</property>
<property type="b" name="ScreenReaderEnabled" access="readwrite">
</property>
</interface>
<interface name="org.a11y.Bus">
<method name="GetAddress">
<arg type="s" name="address" direction="out">
</arg>
</method>
</interface>
</node>

View File

@ -441,7 +441,6 @@ Q_GLOBAL_STATIC(QAccessiblePluginsHash, qAccessiblePlugins);
QAccessible::UpdateHandler QAccessible::updateHandler = 0;
QAccessible::RootObjectHandler QAccessible::rootObjectHandler = 0;
static bool accessibility_active = false;
static bool cleanupAdded = false;
#ifndef QT_NO_ACCESSIBILITY
@ -584,7 +583,6 @@ Q_GLOBAL_STATIC(QAccessibleCache, qAccessibleCache)
*/
QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
{
accessibility_active = true;
if (!object)
return 0;
@ -699,19 +697,26 @@ QAccessibleInterface *QAccessible::accessibleInterface(Id id)
/*!
Returns true if an accessibility implementation has been requested
during the runtime of the application; otherwise returns false.
Returns true if the platform requested accessibility information.
Use this function to prevent potentially expensive notifications via
updateAccessibility().
This function will return false until a tool such as a screen reader
accessed the accessibility framework. It is still possible to use
\l QAccessible::queryAccessibleInterface even if accessibility is not
active. But there will be no notifications sent to the platform.
It is recommended to use this function to prevent expensive notifications
via updateAccessibility() when they are not needed.
*/
bool QAccessible::isActive()
{
return accessibility_active;
#ifndef QT_NO_ACCESSIBILITY
if (QPlatformAccessibility *pfAccessibility = platformAccessibility())
return pfAccessibility->isActive();
#endif
return false;
}
/*!
Sets the root object of the accessible objects of this application
to \a object. All other accessible objects are reachable using object

View File

@ -73,6 +73,7 @@ Q_GLOBAL_STATIC(QVector<QAccessibleBridge *>, bridges)
\sa QAccessible
*/
QPlatformAccessibility::QPlatformAccessibility()
: m_active(false)
{
}

View File

@ -69,6 +69,11 @@ public:
virtual void initialize();
virtual void cleanup();
inline bool isActive() const { return m_active; }
inline void setActive(bool active) { m_active = active; }
private:
bool m_active;
};
QT_END_NAMESPACE

View File

@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
*/
QSpiAccessibleBridge::QSpiAccessibleBridge()
: cache(0), dec(0), dbusAdaptor(0), m_enabled(false)
: cache(0), dec(0), dbusAdaptor(0)
{
dbusConnection = new DBusConnection();
connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool)));
@ -70,7 +70,7 @@ QSpiAccessibleBridge::QSpiAccessibleBridge()
void QSpiAccessibleBridge::enabledChanged(bool enabled)
{
m_enabled = enabled;
setActive(enabled);
updateStatus();
}
@ -87,7 +87,7 @@ QDBusConnection QSpiAccessibleBridge::dBusConnection() const
void QSpiAccessibleBridge::updateStatus()
{
// create the adaptor to handle everything if we are in enabled state
if (!dbusAdaptor && m_enabled) {
if (!dbusAdaptor && isActive()) {
qSpiInitializeStructTypes();
initializeConstantMappings();
@ -106,7 +106,7 @@ void QSpiAccessibleBridge::notifyAccessibilityUpdate(QAccessibleEvent *event)
{
if (!dbusAdaptor)
return;
if (m_enabled)
if (isActive())
dbusAdaptor->notify(event);
}

View File

@ -76,7 +76,6 @@ private:
DeviceEventControllerAdaptor *dec;
AtSpiAdaptor *dbusAdaptor;
DBusConnection* dbusConnection;
bool m_enabled;
};
QT_END_NAMESPACE

View File

@ -48,6 +48,7 @@
#include <qdebug.h>
#include <QDBusConnectionInterface>
#include "bus_interface.h"
QT_BEGIN_NAMESPACE
@ -81,21 +82,24 @@ void DBusConnection::serviceRegistered()
{
// listen to enabled changes
QDBusConnection c = QDBusConnection::sessionBus();
// FXIME check for changes of enabled state
// if (!c.connect(A11Y_SERVICE, A11Y_PATH, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"), this, SLOT(enabledStateChanged(QDBusVariant))))
// qWarning() << "Could not listen to accessibility enabled state changes.";
OrgA11yStatusInterface *a11yStatus = new OrgA11yStatusInterface(A11Y_SERVICE, A11Y_PATH, c, this);
// check if it's enabled right away
QDBusMessage enabledMessage = QDBusMessage::createMethodCall(A11Y_SERVICE, A11Y_PATH, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get"));
QList<QVariant> args;
args << QStringLiteral("org.a11y.Status") << QStringLiteral("IsEnabled");
enabledMessage.setArguments(args);
c.callWithCallback(enabledMessage, this, SLOT(enabledStateCallback(QDBusVariant)), SLOT(dbusError(QDBusError)));
}
// a11yStatus->isEnabled() returns always true (since Gnome 3.6)
bool enabled = a11yStatus->screenReaderEnabled();
if (enabled != m_enabled) {
m_enabled = enabled;
if (m_a11yConnection.isConnected()) {
emit enabledChanged(m_enabled);
} else {
QDBusConnection c = QDBusConnection::sessionBus();
QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.Bus"),
QLatin1String("/org/a11y/bus"),
QLatin1String("org.a11y.Bus"), QLatin1String("GetAddress"));
c.callWithCallback(m, this, SLOT(connectA11yBus(QString)), SLOT(dbusError(QDBusError)));
}
}
void DBusConnection::dbusError(const QDBusError &error)
{
qWarning() << "Accessibility encountered a DBus error:" << error;
// connect(a11yStatus, ); QtDbus doesn't support notifications for property changes yet
}
void DBusConnection::serviceUnregistered()
@ -103,20 +107,6 @@ void DBusConnection::serviceUnregistered()
emit enabledChanged(false);
}
void DBusConnection::enabledStateCallback(const QDBusVariant &enabled)
{
m_enabled = enabled.variant().toBool();
if (m_a11yConnection.isConnected()) {
emit enabledChanged(m_enabled);
} else {
QDBusConnection c = QDBusConnection::sessionBus();
QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.Bus"),
QLatin1String("/org/a11y/bus"),
QLatin1String("org.a11y.Bus"), QLatin1String("GetAddress"));
c.callWithCallback(m, this, SLOT(connectA11yBus(QString)), SLOT(dbusError(QDBusError)));
}
}
void DBusConnection::connectA11yBus(const QString &address)
{
if (address.isEmpty()) {
@ -129,6 +119,11 @@ void DBusConnection::connectA11yBus(const QString &address)
emit enabledChanged(true);
}
void DBusConnection::dbusError(const QDBusError &error)
{
qWarning() << "Accessibility encountered a DBus error:" << error;
}
/*!
Returns the DBus connection that got established.
Or an invalid connection if not yet connected.

View File

@ -67,8 +67,6 @@ Q_SIGNALS:
private Q_SLOTS:
void serviceRegistered();
void serviceUnregistered();
void enabledStateCallback(const QDBusVariant &enabled);
// void enabledStateChanged(const QDBusVariant &);
void connectA11yBus(const QString &address);
void dbusError(const QDBusError &error);

View File

@ -63,7 +63,6 @@ QT_END_NAMESPACE
QWindow *m_window;
QCocoaWindow *m_platformWindow;
Qt::MouseButtons m_buttons;
QAccessibleInterface *m_accessibleRoot;
QString m_composingText;
bool m_sendKeyEvent;
QStringList *currentCustomDragTypes;

View File

@ -121,7 +121,6 @@ static QTouchDevice *touchDevice = 0;
m_window = window;
m_platformWindow = platformWindow;
m_accessibleRoot = 0;
m_sendKeyEvent = false;
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
@ -130,15 +129,13 @@ static QTouchDevice *touchDevice = 0;
static bool skipAccessibilityForInspectorWindows = false;
if (!skipAccessibilityForInspectorWindows) {
m_accessibleRoot = window->accessibleRoot();
// m_accessibleRoot = window->accessibleRoot();
AccessibilityInspector *inspector = new AccessibilityInspector(window);
skipAccessibilityForInspectorWindows = true;
inspector->inspectWindow(window);
skipAccessibilityForInspectorWindows = false;
}
#else
m_accessibleRoot = window->accessibleRoot();
#endif
[self registerDragTypes];

View File

@ -45,6 +45,7 @@
#include "qcocoahelpers.h"
#include "qcocoaaccessibility.h"
#include "qcocoaaccessibilityelement.h"
#include <qpa/qplatformintegration.h>
#include <QtGui/private/qaccessible2_p.h>
#include <QtCore/QDebug>
@ -60,22 +61,26 @@
}
- (id)accessibilityAttributeValue:(NSString *)attribute {
// activate accessibility updates
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
if (m_accessibleRoot)
return QCocoaAccessible::macRole(m_accessibleRoot);
if (m_window->accessibleRoot())
return QCocoaAccessible::macRole(m_window->accessibleRoot());
return NSAccessibilityUnknownRole;
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
return NSAccessibilityRoleDescriptionForUIElement(self);
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
if (!m_accessibleRoot)
if (!m_window->accessibleRoot())
return [super accessibilityAttributeValue:attribute];
// Create QCocoaAccessibleElements for each child if the
// root accessible interface.
int numKids = m_accessibleRoot->childCount();
int numKids = m_window->accessibleRoot()->childCount();
NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids];
for (int i = 0; i < numKids; ++i) {
QAccessibleInterface *child = m_accessibleRoot->child(i);
QAccessibleInterface *child = m_window->accessibleRoot()->child(i);
Q_ASSERT(child);
QAccessible::Id childAxid = QAccessible::uniqueId(child);
QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childAxid parent:self];
@ -90,10 +95,10 @@
}
- (id)accessibilityHitTest:(NSPoint)point {
if (!m_accessibleRoot)
if (!m_window->accessibleRoot())
return [super accessibilityHitTest:point];
QAccessibleInterface *childInterface = m_accessibleRoot->childAt(point.x, qt_mac_flipYCoordinate(point.y));
QAccessibleInterface *childInterface = m_window->accessibleRoot()->childAt(point.x, qt_mac_flipYCoordinate(point.y));
// No child found, meaning we hit the NSView
if (!childInterface) {
return [super accessibilityHitTest:point];

View File

@ -52,7 +52,9 @@
#include <QtCore/qsettings.h>
#include <QtGui/qaccessible.h>
#include <QtGui/private/qaccessible2_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#include <QtGui/qwindow.h>
#include <QtGui/qguiapplication.h>
@ -245,6 +247,8 @@ bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, W
if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) {
/* For UI Automation */
} else if ((DWORD)lParam == DWORD(OBJID_CLIENT)) {
// Start handling accessibility internally
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
#if 1
// Ignoring all requests while starting up
// ### Maybe QPA takes care of this???

View File

@ -1,7 +1,7 @@
CONFIG += testcase
TARGET = tst_qaccessibility
requires(contains(QT_CONFIG,accessibility))
QT += testlib gui-private widgets-private
QT += testlib core-private gui-private widgets-private
SOURCES += tst_qaccessibility.cpp
unix:!mac:LIBS+=-lm

View File

@ -63,6 +63,9 @@
#include <QtWidgets/private/qaccessiblewidget_p.h>
#include <math.h>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformaccessibility.h>
#include <QtGui/private/qguiapplication_p.h>
#if defined(Q_OS_WIN) && defined(interface)
# undef interface
@ -311,6 +314,8 @@ void tst_QAccessibility::onClicked()
void tst_QAccessibility::initTestCase()
{
QTestAccessibility::initialize();
QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration();
pfIntegration->accessibility()->setActive(true);
}
void tst_QAccessibility::cleanupTestCase()

View File

@ -55,6 +55,7 @@
#include <QDBusReply>
#include "atspi/atspi-constants.h"
#include "bus_interface.h"
#include "dbusconnection_p.h"
#include "struct_marshallers_p.h"
@ -154,16 +155,21 @@ QDBusInterface *tst_QAccessibilityLinux::getInterface(const QString &path, const
return new QDBusInterface(address, path, interfaceName, dbus.connection(), this);
}
void tst_QAccessibilityLinux::initTestCase()
{
// Oxygen style creates many extra items, it's simply unusable here
qApp->setStyle("fusion");
qApp->setApplicationName("tst_QAccessibilityLinux app");
// Pretend we are a screen reader
QDBusConnection c = QDBusConnection::sessionBus();
OrgA11yStatusInterface *a11yStatus = new OrgA11yStatusInterface(QStringLiteral("org.a11y.Bus"), QStringLiteral("/org/a11y/bus"), c, this);
a11yStatus->setScreenReaderEnabled(true);
QTRY_VERIFY(dbus.isEnabled());
QTRY_VERIFY(dbus.connection().isConnected());
address = dbus.connection().baseService().toLatin1().data();
QVERIFY(!address.isEmpty());
m_window = new AccessibleTestWindow();
m_window->show();