From b2ec0da95641d9cec006fa9699e6d082ad35db0b Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Thu, 3 Jan 2013 17:18:40 +0100 Subject: Cache QAccessibleInterfaces. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since there already is a one-to-one relationship between QObject and QAccessibleInterface it makes little sense to create and destroy the interfaces on each call to queryAccessibleInterface. Add a cache and keep created interfaces around for the lifetime of the corresponding QObject. This changes the memory management rules: accessible interfaces must no longer be deleted. If you get an QAccessibleIntrface pointer that pointer will stay valid as long as the corresponding QObject is not deleted. This also re-enables accessibility for Mac. We limit the range of the IDs so that they are useable for Windows directly. That means we can get rid of the event cache there. This is based on: Iebf2f374916fc70a9dd29e95f45a6444b85f6cee Change-Id: I9fe6531812c0dbc5b41101ac05830a6dd75e13a3 Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/cocoa.pro | 3 - src/plugins/platforms/cocoa/qcocoaaccessibility.h | 7 +- src/plugins/platforms/cocoa/qcocoaaccessibility.mm | 15 +-- .../platforms/cocoa/qcocoaaccessibilityelement.h | 12 +-- .../platforms/cocoa/qcocoaaccessibilityelement.mm | 119 ++++++++++++--------- src/plugins/platforms/cocoa/qcocoaintegration.h | 2 +- src/plugins/platforms/cocoa/qcocoaintegration.mm | 4 +- .../platforms/cocoa/qnsviewaccessibility.mm | 13 ++- 8 files changed, 90 insertions(+), 85 deletions(-) (limited to 'src/plugins/platforms/cocoa') diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index 6ed26f9e6c..66c4e3c49c 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -102,8 +102,5 @@ OTHER_FILES += cocoa.json # DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR # include ($$PWD/../../../../util/accessibilityinspector/accessibilityinspector.pri) -# Accessibility is currently unstable and disabled. -DEFINES += QT_NO_COCOA_ACCESSIBILITY - # Window debug support #DEFINES += QT_COCOA_ENABLE_WINDOW_DEBUG diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h index 0f1f96814f..3f0367d36a 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h @@ -44,9 +44,6 @@ #include #include - -#ifndef QT_NO_COCOA_ACCESSIBILITY - #include class QCococaAccessibility : public QPlatformAccessibility @@ -81,7 +78,7 @@ namespace QCocoaAccessible { */ NSString *macRole(QAccessibleInterface *interface); -bool shouldBeIgnrored(QAccessibleInterface *interface); +bool shouldBeIgnored(QAccessibleInterface *interface); NSString *getTranslatedAction(const QString &qtAction); NSMutableArray *createTranslatedActionsList(const QStringList &qtActions); QString translateAction(NSString *nsAction); @@ -90,6 +87,4 @@ id getValueAttribute(QAccessibleInterface *interface); } -#endif // QT_NO_COCOA_ACCESSIBILITY - #endif diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index adeb423cf9..34192e85b0 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -44,8 +44,6 @@ #include #include -#ifndef QT_NO_COCOA_ACCESSIBILITY - QCococaAccessibility::QCococaAccessibility() { @@ -62,7 +60,7 @@ void QCococaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) if (!object) return; - QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object); + QAccessibleInterface *interface = event->accessibleInterface(); if (!interface) return; @@ -71,13 +69,11 @@ void QCococaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::TextInserted : case QAccessible::TextRemoved : case QAccessible::TextUpdated : { - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithInterface : interface parent : nil]; + QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId : QAccessible::uniqueId(interface) parent : nil]; [element autorelease]; NSAccessibilityPostNotification(element, NSAccessibilityValueChangedNotification); break; } - - default: - delete interface; + default: break; } } @@ -178,7 +174,7 @@ NSString *macRole(QAccessibleInterface *interface) the elements are still present in the accessibility tree but is not used by the screen reader. */ -bool shouldBeIgnrored(QAccessibleInterface *interface) +bool shouldBeIgnored(QAccessibleInterface *interface) { // Mac accessibility does not have an attribute that corresponds to the Invisible/Offscreen // state. Ignore interfaces with those flags set. @@ -280,6 +276,7 @@ QString translateAction(NSString *nsAction) bool hasValueAttribute(QAccessibleInterface *interface) { + Q_ASSERT(interface); const QAccessible::Role qtrole = interface->role(); if (qtrole == QAccessible::EditableText || interface->valueInterface()) { @@ -318,5 +315,3 @@ id getValueAttribute(QAccessibleInterface *interface) } } // namespace QCocoaAccessible - -#endif // QT_NO_COCOA_ACCESSIBILITY diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index 3b1fbe042d..c207cbee2d 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -46,22 +46,20 @@ #import #import -#ifndef QT_NO_COCOA_ACCESSIBILITY +#import @class QCocoaAccessibleElement; @interface QCocoaAccessibleElement : NSObject { NSString *role; - NSObject * parent; - void *accessibleInterface; + NSObject *parent; + QAccessible::Id axid; } -- (id)initWithInterface:(void *)anQAccessibleInterface parent:(id)aParent; -+ (QCocoaAccessibleElement *)createElementWithInterface:(void *)anQAccessibleInterface parent:(id)aParent; +- (id)initWithId:(QAccessible::Id)anId parent:(id)aParent; ++ (QCocoaAccessibleElement *)createElementWithId:(QAccessible::Id)anId parent:(id)aParent; @end -#endif // QT_NO_COCOA_ACCESSIBILITY - #endif diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index df496a413b..1d6797e51a 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -47,48 +47,43 @@ #import -#ifndef QT_NO_COCOA_ACCESSIBILITY - -static QAccessibleInterface *acast(void *ptr) -{ - return reinterpret_cast(ptr); -} - @implementation QCocoaAccessibleElement -- (id)initWithInterface:(void *)anQAccessibleInterface parent:(id)aParent +- (id)initWithId:(QAccessible::Id)anId parent:(id)aParent { + Q_ASSERT((int)anId < 0); self = [super init]; if (self) { - accessibleInterface = anQAccessibleInterface; - role = QCocoaAccessible::macRole(acast(accessibleInterface)); + axid = anId; + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + Q_ASSERT(iface); + role = QCocoaAccessible::macRole(iface); parent = aParent; } return self; } -+ (QCocoaAccessibleElement *)createElementWithInterface:(void *)anQAccessibleInterface parent:(id)aParent ++ (QCocoaAccessibleElement *)createElementWithId:(QAccessible::Id)anId parent:(id)aParent { - return [[self alloc] initWithInterface:anQAccessibleInterface parent:aParent]; + return [[self alloc] initWithId:anId parent:aParent]; } - (void)dealloc { [super dealloc]; - delete acast(accessibleInterface); } - (BOOL)isEqual:(id)object { if ([object isKindOfClass:[QCocoaAccessibleElement class]]) { QCocoaAccessibleElement *other = object; - return acast(other->accessibleInterface)->object() == acast(accessibleInterface)->object(); + return other->axid == axid; } else { return NO; } } - (NSUInteger)hash { - return qHash(acast(accessibleInterface)->object()); + return axid; } // @@ -99,6 +94,11 @@ static QAccessibleInterface *acast(void *ptr) - (NSArray *)accessibilityAttributeNames { static NSArray *defaultAttributes = nil; + + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return defaultAttributes; + if (defaultAttributes == nil) { defaultAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute, @@ -118,7 +118,7 @@ static QAccessibleInterface *acast(void *ptr) NSMutableArray *attributes = [[NSMutableArray alloc] initWithCapacity : [defaultAttributes count]]; [attributes addObjectsFromArray : defaultAttributes]; - if (QCocoaAccessible::hasValueAttribute(acast(accessibleInterface))) { + if (QCocoaAccessible::hasValueAttribute(iface)) { [attributes addObject : NSAccessibilityValueAttribute]; } @@ -126,22 +126,33 @@ static QAccessibleInterface *acast(void *ptr) } - (id)accessibilityAttributeValue:(NSString *)attribute { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) { + qWarning() << "Called attribute on invalid object: " << axid; + return nil; + } + if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { return role; } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { return NSAccessibilityRoleDescription(role, nil); } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - int numKids = acast(accessibleInterface)->childCount(); + + int numKids = iface->childCount(); + // qDebug() << "Children for: " << axid << iface << " are: " << numKids; NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids]; for (int i = 0; i < numKids; ++i) { - QAccessibleInterface *childInterface = acast(accessibleInterface)->child(i); - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithInterface:(void*)childInterface parent:self]; + QAccessibleInterface *child = iface->child(i); + Q_ASSERT(child); + QAccessible::Id childId = QAccessible::uniqueId(child); + //qDebug() << " kid: " << childId << child; + QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childId parent:self]; [kids addObject: element]; [element release]; } - return kids; + } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { // Just check if the app thinks we're focused. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute]; @@ -155,23 +166,23 @@ static QAccessibleInterface *acast(void *ptr) // We're in the same top level element as our parent. return [parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { - QPoint qtPosition = acast(accessibleInterface)->rect().topLeft(); - QSize qtSize = acast(accessibleInterface)->rect().size(); + QPoint qtPosition = iface->rect().topLeft(); + QSize qtSize = iface->rect().size(); return [NSValue valueWithPoint: NSMakePoint(qtPosition.x(), qt_mac_flipYCoordinate(qtPosition.y() + qtSize.height()))]; } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) { - QSize qtSize = acast(accessibleInterface)->rect().size(); + QSize qtSize = iface->rect().size(); return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())]; } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { - return QCFString::toNSString(acast(accessibleInterface)->text(QAccessible::Name)); + return QCFString::toNSString(iface->text(QAccessible::Name)); } else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) { - return [NSNumber numberWithBool:!acast(accessibleInterface)->state().disabled]; + return [NSNumber numberWithBool:!iface->state().disabled]; } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { // VoiceOver asks for the value attribute for all elements. Return nil // if we don't want the element to have a value attribute. - if (!QCocoaAccessible::hasValueAttribute(acast(accessibleInterface))) + if (!QCocoaAccessible::hasValueAttribute(iface)) return nil; - return QCocoaAccessible::getValueAttribute(acast(accessibleInterface)); + return QCocoaAccessible::getValueAttribute(iface); } return nil; @@ -196,8 +207,11 @@ static QAccessibleInterface *acast(void *ptr) - (NSArray *)accessibilityActionNames { NSMutableArray * nsActions = [NSMutableArray new]; + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return nsActions; - QAccessibleActionInterface *actionInterface = acast(accessibleInterface)->actionInterface(); + QAccessibleActionInterface *actionInterface = iface->actionInterface(); if (actionInterface) { QStringList supportedActionNames = actionInterface->actionNames(); @@ -212,48 +226,58 @@ static QAccessibleInterface *acast(void *ptr) } - (NSString *)accessibilityActionDescription:(NSString *)action { - QAccessibleActionInterface *actionInterface = acast(accessibleInterface)->actionInterface(); - QString qtAction = QCocoaAccessible::translateAction(action); + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return nil; // FIXME is that the right return type?? + QAccessibleActionInterface *actionInterface = iface->actionInterface(); + if (actionInterface) { + QString qtAction = QCocoaAccessible::translateAction(action); - // Return a description from the action interface if this action is not known to the OS. - if (qtAction.isEmpty()) { - QString description = actionInterface->localizedActionDescription(qtAction); - return QCFString::toNSString(description); + // Return a description from the action interface if this action is not known to the OS. + if (qtAction.isEmpty()) { + QString description = actionInterface->localizedActionDescription(qtAction); + return QCFString::toNSString(description); + } } return NSAccessibilityActionDescription(action); } - (void)accessibilityPerformAction:(NSString *)action { - QAccessibleActionInterface *actionInterface = acast(accessibleInterface)->actionInterface(); - if (actionInterface) { - actionInterface->doAction(QCocoaAccessible::translateAction(action)); + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (iface) { + QAccessibleActionInterface *actionInterface = iface->actionInterface(); + if (actionInterface) { + actionInterface->doAction(QCocoaAccessible::translateAction(action)); + } } } // misc - (BOOL)accessibilityIsIgnored { - return QCocoaAccessible::shouldBeIgnrored(acast(accessibleInterface)); + return false; //QCocoaAccessible::shouldBeIgnored(QAccessible::accessibleInterface(id)); } - (id)accessibilityHitTest:(NSPoint)point { - - if (!accessibleInterface) - return NSAccessibilityUnignoredAncestor(self); - - if (!acast(accessibleInterface)->isValid()) + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) { +// qDebug() << "Hit test: INVALID"; return NSAccessibilityUnignoredAncestor(self); + } - QAccessibleInterface *childInterface = acast(accessibleInterface)->childAt(point.x, qt_mac_flipYCoordinate(point.y)); + QAccessibleInterface *childInterface = iface->childAt(point.x, qt_mac_flipYCoordinate(point.y)); // No child found, meaning we hit this element. if (!childInterface) { - return NSAccessibilityUnignoredAncestor(self); +// qDebug() << "Hit test returns: " << id << iface; + return self; + //return NSAccessibilityUnignoredAncestor(self); } + QAccessible::Id childId = QAccessible::uniqueId(childInterface); // hit a child, forward to child accessible interface. - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithInterface:childInterface parent:self]; + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childId parent:self]; [accessibleElement autorelease]; return [accessibleElement accessibilityHitTest:point]; @@ -264,6 +288,3 @@ static QAccessibleInterface *acast(void *ptr) } @end - -#endif // QT_NO_COCOA_ACCESSIBILITY - diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index d667ac4a86..6e690dd51e 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -132,7 +132,7 @@ private: QAbstractEventDispatcher *mEventDispatcher; QScopedPointer mInputContext; -#ifndef QT_NO_COCOA_ACCESSIBILITY +#ifndef QT_NO_ACCESSIBILITY QScopedPointer mAccessibility; #endif QScopedPointer mPlatformTheme; diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 3312de6e3f..e2d867e623 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -216,7 +216,7 @@ QCocoaIntegration::QCocoaIntegration() : mFontDb(new QCoreTextFontDatabase()) , mEventDispatcher(new QCocoaEventDispatcher()) , mInputContext(new QCocoaInputContext) -#ifndef QT_NO_COCOA_ACCESSIBILITY +#ifndef QT_NO_ACCESSIBILITY , mAccessibility(new QCococaAccessibility) #endif , mCocoaClipboard(new QCocoaClipboard) @@ -395,7 +395,7 @@ QPlatformInputContext *QCocoaIntegration::inputContext() const QPlatformAccessibility *QCocoaIntegration::accessibility() const { -#ifndef QT_NO_COCOA_ACCESSIBILITY +#ifndef QT_NO_ACCESSIBILITY return mAccessibility.data(); #else return 0; diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index e05ffbe343..e3b8cf6532 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -51,8 +51,6 @@ #import -#ifndef QT_NO_COCOA_ACCESSIBILITY - @implementation QNSView (QNSViewAccessibility) // The QNSView is a container that the user does not interact directly with: @@ -77,7 +75,10 @@ int numKids = m_accessibleRoot->childCount(); NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids]; for (int i = 0; i < numKids; ++i) { - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithInterface: m_accessibleRoot->child(i) parent:self ]; + QAccessibleInterface *child = m_accessibleRoot->child(i); + Q_ASSERT(child); + QAccessible::Id childAxid = QAccessible::uniqueId(child); + QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId:childAxid parent:self]; [kids addObject: element]; [element release]; } @@ -99,12 +100,10 @@ } // Hit a child, forward to child accessible interface. - - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithInterface: childInterface parent:self ]; + QAccessible::Id childAxid = QAccessible::uniqueId(childInterface); + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childAxid parent:self ]; [accessibleElement autorelease]; return [accessibleElement accessibilityHitTest:point]; } @end - -#endif // QT_NO_COCOA_ACCESSIBILITY -- cgit v1.2.3