diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/accessible/accessible.pri | 2 | ||||
-rw-r--r-- | src/gui/accessible/qaccessible.cpp | 28 | ||||
-rw-r--r-- | src/gui/accessible/qaccessiblecache.cpp | 13 | ||||
-rw-r--r-- | src/gui/accessible/qaccessiblecache_mac.mm | 67 | ||||
-rw-r--r-- | src/gui/accessible/qaccessiblecache_p.h | 29 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaaccessibility.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaaccessibility.mm | 43 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm | 96 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsviewaccessibility.mm | 3 |
10 files changed, 226 insertions, 62 deletions
diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri index 615323dbec..86ed4c3a71 100644 --- a/src/gui/accessible/accessible.pri +++ b/src/gui/accessible/accessible.pri @@ -16,4 +16,6 @@ contains(QT_CONFIG, accessibility) { HEADERS += accessible/qaccessiblebridge.h SOURCES += accessible/qaccessiblebridge.cpp + + OBJECTIVE_SOURCES += accessible/qaccessiblecache_mac.mm } diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index dffdfa889a..776320a517 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -591,8 +591,6 @@ QAccessible::RootObjectHandler QAccessible::installRootObjectHandler(RootObjectH return old; } -Q_GLOBAL_STATIC(QAccessibleCache, qAccessibleCache) - /*! If a QAccessibleInterface implementation exists for the given \a object, this function returns a pointer to the implementation; otherwise it @@ -616,8 +614,8 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) if (!object) return 0; - if (Id id = qAccessibleCache->objectToId.value(object)) - return qAccessibleCache->interfaceForId(id); + if (Id id = QAccessibleCache::instance()->objectToId.value(object)) + return QAccessibleCache::instance()->interfaceForId(id); // Create a QAccessibleInterface for the object class. Start by the most // derived class and walk up the class hierarchy. @@ -629,8 +627,8 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) for (int i = qAccessibleFactories()->count(); i > 0; --i) { InterfaceFactory factory = qAccessibleFactories()->at(i - 1); if (QAccessibleInterface *iface = factory(cn, object)) { - qAccessibleCache->insert(object, iface); - Q_ASSERT(qAccessibleCache->objectToId.contains(object)); + QAccessibleCache::instance()->insert(object, iface); + Q_ASSERT(QAccessibleCache::instance()->objectToId.contains(object)); return iface; } } @@ -652,8 +650,8 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) if (factory) { QAccessibleInterface *result = factory->create(cn, object); if (result) { // Need this condition because of QDesktopScreenWidget - qAccessibleCache->insert(object, result); - Q_ASSERT(qAccessibleCache->objectToId.contains(object)); + QAccessibleCache::instance()->insert(object, result); + Q_ASSERT(QAccessibleCache::instance()->objectToId.contains(object)); } return result; } @@ -665,8 +663,8 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) #ifndef QT_NO_ACCESSIBILITY if (object == qApp) { QAccessibleInterface *appInterface = new QAccessibleApplication; - qAccessibleCache->insert(object, appInterface); - Q_ASSERT(qAccessibleCache->objectToId.contains(qApp)); + QAccessibleCache::instance()->insert(object, appInterface); + Q_ASSERT(QAccessibleCache::instance()->objectToId.contains(qApp)); return appInterface; } #endif @@ -691,7 +689,7 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) QAccessible::Id QAccessible::registerAccessibleInterface(QAccessibleInterface *iface) { Q_ASSERT(iface); - return qAccessibleCache->insert(iface->object(), iface); + return QAccessibleCache::instance()->insert(iface->object(), iface); } /*! @@ -701,7 +699,7 @@ QAccessible::Id QAccessible::registerAccessibleInterface(QAccessibleInterface *i */ void QAccessible::deleteAccessibleInterface(Id id) { - qAccessibleCache->deleteInterface(id); + QAccessibleCache::instance()->deleteInterface(id); } /*! @@ -709,7 +707,7 @@ void QAccessible::deleteAccessibleInterface(Id id) */ QAccessible::Id QAccessible::uniqueId(QAccessibleInterface *iface) { - Id id = qAccessibleCache->idToInterface.key(iface); + Id id = QAccessibleCache::instance()->idToInterface.key(iface); if (!id) id = registerAccessibleInterface(iface); return id; @@ -722,7 +720,7 @@ QAccessible::Id QAccessible::uniqueId(QAccessibleInterface *iface) */ QAccessibleInterface *QAccessible::accessibleInterface(Id id) { - return qAccessibleCache->idToInterface.value(id); + return QAccessibleCache::instance()->idToInterface.value(id); } diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp index fe66c6e19d..09c155515e 100644 --- a/src/gui/accessible/qaccessiblecache.cpp +++ b/src/gui/accessible/qaccessiblecache.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -49,6 +49,13 @@ QT_BEGIN_NAMESPACE \brief Maintains a cache of accessible interfaces. */ +Q_GLOBAL_STATIC(QAccessibleCache, qAccessibleCache) + +QAccessibleCache *QAccessibleCache::instance() +{ + return qAccessibleCache; +} + /* The ID is always in the range [INT_MAX+1, UINT_MAX]. This makes it easy on windows to reserve the positive integer range @@ -113,6 +120,10 @@ void QAccessibleCache::deleteInterface(QAccessible::Id id, QObject *obj) if (obj) objectToId.remove(obj); delete iface; + +#ifdef Q_OS_MACX + removeCocoaElement(id); +#endif } QT_END_NAMESPACE diff --git a/src/gui/accessible/qaccessiblecache_mac.mm b/src/gui/accessible/qaccessiblecache_mac.mm new file mode 100644 index 0000000000..861423af7d --- /dev/null +++ b/src/gui/accessible/qaccessiblecache_mac.mm @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblecache_p.h" + +#ifdef Q_OS_OSX + +QT_BEGIN_NAMESPACE + +void QAccessibleCache::insertElement(QAccessible::Id axid, QCocoaAccessibleElement *element) const +{ + cocoaElements[axid] = element; +} + +void QAccessibleCache::removeCocoaElement(QAccessible::Id axid) +{ + QCocoaAccessibleElement *element = elementForId(axid); + [element invalidate]; + cocoaElements.remove(axid); +} + +QCocoaAccessibleElement *QAccessibleCache::elementForId(QAccessible::Id axid) const +{ + return cocoaElements.value(axid); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/accessible/qaccessiblecache_p.h b/src/gui/accessible/qaccessiblecache_p.h index 32f9c443ba..30b023cfbd 100644 --- a/src/gui/accessible/qaccessiblecache_p.h +++ b/src/gui/accessible/qaccessiblecache_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -42,24 +42,42 @@ #ifndef QACCESSIBLECACHE_P #define QACCESSIBLECACHE_P +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include <QtCore/qglobal.h> #include <QtCore/qobject.h> #include <QtCore/qhash.h> #include "qaccessible.h" -QT_BEGIN_NAMESPACE +Q_FORWARD_DECLARE_OBJC_CLASS(QCocoaAccessibleElement); +QT_BEGIN_NAMESPACE -class QAccessibleCache :public QObject +class Q_GUI_EXPORT QAccessibleCache :public QObject { Q_OBJECT public: + static QAccessibleCache *instance(); QAccessibleInterface *interfaceForId(QAccessible::Id id) const; QAccessible::Id insert(QObject *object, QAccessibleInterface *iface) const; void deleteInterface(QAccessible::Id id, QObject *obj = 0); +#ifdef Q_OS_OSX + QCocoaAccessibleElement *elementForId(QAccessible::Id axid) const; + void insertElement(QAccessible::Id axid, QCocoaAccessibleElement *element) const; +#endif + private Q_SLOTS: void objectDestroyed(QObject *obj); @@ -69,6 +87,11 @@ private: mutable QHash<QAccessible::Id, QAccessibleInterface *> idToInterface; mutable QHash<QObject *, QAccessible::Id> objectToId; +#ifdef Q_OS_OSX + void removeCocoaElement(QAccessible::Id axid); + mutable QHash<QAccessible::Id, QCocoaAccessibleElement *> cocoaElements; +#endif + friend class QAccessible; friend class QAccessibleInterface; }; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h index 86bb5323a7..a78901bfb1 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h @@ -79,7 +79,7 @@ namespace QCocoaAccessible { NSString *macRole(QAccessibleInterface *interface); bool shouldBeIgnored(QAccessibleInterface *interface); -NSArray *unignoredChildren(id parentObject, QAccessibleInterface *interface); +NSArray *unignoredChildren(QAccessibleInterface *interface); NSString *getTranslatedAction(const QString &qtAction); NSMutableArray *createTranslatedActionsList(const QStringList &qtActions); QString translateAction(NSString *nsAction); diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 990acd5301..72045a1bbb 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -55,19 +55,31 @@ QCocoaAccessibility::~QCocoaAccessibility() void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { - QAccessible::Id interfaceId = event->uniqueId(); - if (!interfaceId) + QCocoaAccessibleElement *element = [QCocoaAccessibleElement elementWithId: event->uniqueId()]; + if (!element) { + qWarning() << "QCocoaAccessibility::notifyAccessibilityUpdate: invalid element"; return; + } switch (event->type()) { - case QAccessible::ValueChanged: - case QAccessible::TextInserted : - case QAccessible::TextRemoved : - case QAccessible::TextUpdated : { - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId : interfaceId parent : nil]; - [element autorelease]; - NSAccessibilityPostNotification(element, NSAccessibilityValueChangedNotification); - break; } + case QAccessible::Focus: { + NSAccessibilityPostNotification(element, NSAccessibilityFocusedUIElementChangedNotification); + break; + } + case QAccessible::StateChanged: + case QAccessible::ValueChanged: + case QAccessible::TextInserted: + case QAccessible::TextRemoved: + case QAccessible::TextUpdated: + NSAccessibilityPostNotification(element, NSAccessibilityValueChangedNotification); + break; + case QAccessible::TextCaretMoved: + case QAccessible::TextSelectionChanged: + NSAccessibilityPostNotification(element, NSAccessibilitySelectedTextChangedNotification); + break; + case QAccessible::NameChanged: + NSAccessibilityPostNotification(element, NSAccessibilityTitleChangedNotification); + break; default: break; } @@ -218,7 +230,7 @@ bool shouldBeIgnored(QAccessibleInterface *interface) return false; } -NSArray *unignoredChildren(id parentObject, QAccessibleInterface *interface) +NSArray *unignoredChildren(QAccessibleInterface *interface) { int numKids = interface->childCount(); // qDebug() << "Children for: " << axid << iface << " are: " << numKids; @@ -231,9 +243,12 @@ NSArray *unignoredChildren(id parentObject, QAccessibleInterface *interface) QAccessible::Id childId = QAccessible::uniqueId(child); //qDebug() << " kid: " << childId << child; - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childId parent:parentObject]; - [kids addObject: element]; - [element release]; + + QCocoaAccessibleElement *element = [QCocoaAccessibleElement elementWithId: childId]; + if (element) + [kids addObject: element]; + else + qWarning() << "QCocoaAccessibility: invalid child"; } return NSAccessibilityUnignoredChildren(kids); } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index c207cbee2d..babaab5ae2 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -52,12 +52,11 @@ @interface QCocoaAccessibleElement : NSObject { NSString *role; - NSObject *parent; QAccessible::Id axid; } -- (id)initWithId:(QAccessible::Id)anId parent:(id)aParent; -+ (QCocoaAccessibleElement *)createElementWithId:(QAccessible::Id)anId parent:(id)aParent; +- (id)initWithId:(QAccessible::Id)anId; ++ (QCocoaAccessibleElement *)elementWithId:(QAccessible::Id)anId; @end diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index d9bfdbd55d..1df4230385 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -41,6 +41,8 @@ #include "qcocoaaccessibilityelement.h" #include "qcocoaaccessibility.h" #include "qcocoahelpers.h" +#include "qcocoawindow.h" +#include "private/qaccessiblecache_p.h" #include <QtGui/qaccessible.h> @@ -48,7 +50,7 @@ @implementation QCocoaAccessibleElement -- (id)initWithId:(QAccessible::Id)anId parent:(id)aParent +- (id)initWithId:(QAccessible::Id)anId { Q_ASSERT((int)anId < 0); self = [super init]; @@ -57,15 +59,35 @@ QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); Q_ASSERT(iface); role = QCocoaAccessible::macRole(iface); - parent = aParent; } return self; } -+ (QCocoaAccessibleElement *)createElementWithId:(QAccessible::Id)anId parent:(id)aParent ++ (id)elementWithId:(QAccessible::Id)anId { - return [[self alloc] initWithId:anId parent:aParent]; + Q_ASSERT(anId); + if (!anId) + return nil; + + QAccessibleCache *cache = QAccessibleCache::instance(); + + QCocoaAccessibleElement *element = cache->elementForId(anId); + if (!element) { + QAccessibleInterface *iface = QAccessible::accessibleInterface(anId); + Q_ASSERT(iface); + if (!iface) + return nil; + element = [[self alloc] initWithId:anId]; + cache->insertElement(anId, element); + } + return element; +} + +- (void)invalidate { + axid = 0; + NSAccessibilityPostNotification(self, NSAccessibilityUIElementDestroyedNotification); + [self release]; } - (void)dealloc { @@ -98,6 +120,10 @@ return [NSNumber numberWithInt: newlines]; } +- (BOOL) accessibilityNotifiesWhenDestroyed { + return YES; +} + - (NSArray *)accessibilityAttributeNames { static NSArray *defaultAttributes = nil; @@ -145,6 +171,26 @@ return [attributes autorelease]; } +- (id)parentElement { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return nil; + + if (QWindow *window = iface->window()) { + QCocoaWindow *win = static_cast<QCocoaWindow*>(window->handle()); + return win->qtView(); + } + + QAccessibleInterface *parent = iface->parent(); + if (!parent) { + qWarning() << "INVALID PARENT FOR INTERFACE: " << iface; + return nil; + } + + QAccessible::Id parentId = QAccessible::uniqueId(parent); + return [QCocoaAccessibleElement elementWithId: parentId]; +} + - (id)accessibilityAttributeValue:(NSString *)attribute { QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); if (!iface) { @@ -157,19 +203,19 @@ } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { return NSAccessibilityRoleDescription(role, nil); } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - return QCocoaAccessible::unignoredChildren(self, iface); + return QCocoaAccessible::unignoredChildren(iface); } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { // Just check if the app thinks we're focused. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute]; return [NSNumber numberWithBool:[focusedElement isEqual:self]]; } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) { - return NSAccessibilityUnignoredAncestor(parent); + return NSAccessibilityUnignoredAncestor([self parentElement]); } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) { // We're in the same window as our parent. - return [parent accessibilityAttributeValue:NSAccessibilityWindowAttribute]; + return [[self parentElement] accessibilityAttributeValue:NSAccessibilityWindowAttribute]; } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) { // We're in the same top level element as our parent. - return [parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; + return [[self parentElement] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { QPoint qtPosition = iface->rect().topLeft(); QSize qtSize = iface->rect().size(); @@ -403,20 +449,26 @@ return NSAccessibilityUnignoredAncestor(self); } - QAccessibleInterface *childInterface = iface->childAt(point.x, qt_mac_flipYCoordinate(point.y)); - + int y = qt_mac_flipYCoordinate(point.y); + QAccessibleInterface *childInterface = iface->childAt(point.x, y); // No child found, meaning we hit this element. - if (!childInterface) { -// qDebug() << "Hit test returns: " << id << iface; + if (!childInterface) return NSAccessibilityUnignoredAncestor(self); - } + + // find the deepest child at the point + QAccessibleInterface *childOfChildInterface = 0; + do { + childOfChildInterface = childInterface->childAt(point.x, y); + if (childOfChildInterface) + childInterface = childOfChildInterface; + } while (childOfChildInterface); QAccessible::Id childId = QAccessible::uniqueId(childInterface); // hit a child, forward to child accessible interface. - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childId parent:self]; - [accessibleElement autorelease]; - - return [accessibleElement accessibilityHitTest:point]; + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithId:childId]; + if (accessibleElement) + return NSAccessibilityUnignoredAncestor(accessibleElement); + return NSAccessibilityUnignoredAncestor(self); } - (id)accessibilityFocusedUIElement { @@ -426,17 +478,15 @@ qWarning() << "FocusedUIElement for INVALID"; return nil; } + QAccessibleInterface *childInterface = iface->focusChild(); if (childInterface) { QAccessible::Id childAxid = QAccessible::uniqueId(childInterface); - // FIXME: parent could be wrong - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childAxid parent:self]; - [accessibleElement autorelease]; - return accessibleElement; + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement elementWithId:childAxid]; + return NSAccessibilityUnignoredAncestor(accessibleElement); } - // no focus found - return nil; + return NSAccessibilityUnignoredAncestor(self); } @end diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index 31e3e343b9..d18a01b11c 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -59,8 +59,7 @@ return nil; QAccessible::Id childId = QAccessible::uniqueId(m_window->accessibleRoot()); - QCocoaAccessibleElement *child = [QCocoaAccessibleElement createElementWithId: childId parent: self]; - return [child autorelease]; + return [QCocoaAccessibleElement elementWithId: childId]; } // The QNSView is a container that the user does not interact directly with: |