Revision: 27904
          http://sourceforge.net/p/bibdesk/svn/27904
Author:   hofman
Date:     2022-09-18 14:52:38 +0000 (Sun, 18 Sep 2022)
Log Message:
-----------
Make icons in status bar accessible using custom accessibility elements, also 
used as a data element

Modified Paths:
--------------
    trunk/bibdesk/BDSKStatusBar.m

Modified: trunk/bibdesk/BDSKStatusBar.m
===================================================================
--- trunk/bibdesk/BDSKStatusBar.m       2022-09-18 06:30:29 UTC (rev 27903)
+++ trunk/bibdesk/BDSKStatusBar.m       2022-09-18 14:52:38 UTC (rev 27904)
@@ -48,7 +48,20 @@
 #define MARGIN_BETWEEN_ITEMS   2.0
 #define VERTICAL_OFFSET         0.0
 
+@interface BDSKStatusBarIcon : NSObject {
+    NSImage *image;
+    NSString *identifier;
+    NSString *toolTip;
+    id parent;
+}
+- (id)initWithIdentifier:(NSString *)anIdentnfier image:(NSImage *)anImage 
toolTip:(NSString *)aToolTip parent:(id)aParent;
+@property (nonatomic, readonly) NSImage *image;
+@property (nonatomic, readonly) NSString *identifier;
+@property (nonatomic, readonly) NSString *toolTip;
+@end
 
+#pragma mark -
+
 @implementation BDSKStatusBar
 
 @synthesize leftMargin, rightMargin, progressIndicator, delegate;
@@ -116,18 +129,18 @@
     if (progressIndicator)
         fullRightMargin += NSWidth([progressIndicator frame]) + 
MARGIN_BETWEEN_ITEMS;
        NSRect rect = BDSKShrinkRect([self bounds], fullRightMargin, 
NSMaxXEdge);
-       NSImage *icon;
+       NSImage *image;
        NSRect iconRect;    
        NSSize size;
        
-       for (NSDictionary *dict in icons) {
-               icon = [dict objectForKey:@"icon"];
-        size = [self cellSizeForIcon:icon];
+       for (BDSKStatusBarIcon *icon in icons) {
+               image = [icon image];
+        size = [self cellSizeForIcon:image];
         NSDivideRect(rect, &iconRect, &rect, size.width, NSMaxXEdge);
         rect = BDSKShrinkRect(rect, MARGIN_BETWEEN_ITEMS, NSMaxXEdge);
         iconRect = BDSKCenterRectVertically(iconRect, size.height, NO);
         iconRect.origin.y += VERTICAL_OFFSET;
-               [iconCell setImage:icon];
+               [iconCell setImage:image];
                [iconCell drawWithFrame:iconRect inView:self];
        }
 }
@@ -142,8 +155,8 @@
     if (progressIndicator)
         fullRightMargin += NSWidth([progressIndicator frame]) + 
MARGIN_BETWEEN_ITEMS;
     
-    for (NSDictionary *dict in icons)
-        fullRightMargin += [self cellSizeForIcon:[dict 
objectForKey:@"icon"]].width + MARGIN_BETWEEN_ITEMS;
+    for (BDSKStatusBarIcon *icon in icons)
+        fullRightMargin += [self cellSizeForIcon:[icon image]].width + 
MARGIN_BETWEEN_ITEMS;
     
     return fullRightMargin;
 }
@@ -312,11 +325,7 @@
 #pragma mark Icons
 
 - (NSArray *)iconIdentifiers {
-       NSMutableArray *IDs = [NSMutableArray arrayWithCapacity:[icons count]];
-       for (NSDictionary *dict in icons) {
-               [IDs addObject:[dict objectForKey:@"identifier"]];
-       }
-       return IDs;
+       return [icons valueForKey:@"identifier"];
 }
 
 - (void)addIcon:(NSImage *)icon withIdentifier:(NSString *)identifier{
@@ -327,10 +336,7 @@
 - (void)addIcon:(NSImage *)icon withIdentifier:(NSString *)identifier 
toolTip:(NSString *)toolTip {
     if (icon == nil || identifier == nil)
         return;
-       NSMutableDictionary *dict = [NSMutableDictionary 
dictionaryWithObjectsAndKeys:icon, @"icon", identifier, @"identifier", nil];
-       if (toolTip != nil)
-               [dict setObject:toolTip forKey:@"toolTip"];
-       [icons addObject:dict];
+       [icons addObject:[[[BDSKStatusBarIcon alloc] 
initWithIdentifier:identifier image:icon toolTip:toolTip parent:self] 
autorelease]];
        [self rebuildToolTips];
        [self setNeedsDisplay:YES];
     [[self constraintWithSecondItem:textField 
secondAttribute:NSLayoutAttributeTrailing] setConstant:[self fullRightMargin]];
@@ -339,7 +345,7 @@
 - (void)removeIconWithIdentifier:(NSString *)identifier {
        NSUInteger i = [icons count];
        while (i--) {
-               if ([[[icons objectAtIndex:i] objectForKey:@"identifier"] 
isEqualToString:identifier]) {
+               if ([[[icons objectAtIndex:i] identifier] 
isEqualToString:identifier]) {
                        [icons removeObjectAtIndex:i];
                        [self rebuildToolTips];
                        [self setNeedsDisplay:YES];
@@ -356,9 +362,9 @@
     if ([delegate 
respondsToSelector:@selector(statusBar:toolTipForIdentifier:)])
                return [delegate statusBar:self toolTipForIdentifier:(NSString 
*)userData];
        
-       for (NSDictionary *dict in icons) {
-               if ([[dict objectForKey:@"identifier"] 
isEqualToString:(NSString *)userData]) {
-                       return [dict objectForKey:@"toolTip"];
+       for (BDSKStatusBarIcon *icon in icons) {
+               if ([[icon identifier] isEqualToString:(NSString *)userData]) {
+                       return [icon toolTip];
                }
        }
        return nil;
@@ -374,13 +380,13 @@
        
        [self removeAllToolTips];
        
-       for (NSDictionary *dict in icons) {
-        size = [self cellSizeForIcon:[dict objectForKey:@"icon"]];
+       for (BDSKStatusBarIcon *icon in icons) {
+        size = [self cellSizeForIcon:[icon image]];
         NSDivideRect(rect, &iconRect, &rect, size.width, NSMaxXEdge);
         rect = BDSKShrinkRect(rect, MARGIN_BETWEEN_ITEMS, NSMaxXEdge);
         iconRect = BDSKCenterRectVertically(iconRect, size.height, NO);
         iconRect.origin.y += VERTICAL_OFFSET;
-               [self addToolTipRect:iconRect owner:self userData:[dict 
objectForKey:@"identifier"]];
+               [self addToolTipRect:iconRect owner:self userData:[icon 
identifier]];
        }
 }
 
@@ -448,7 +454,7 @@
 #pragma mark Accessibility
 
 - (NSArray *)accessibilityAttributeNames {
-    return [[super accessibilityAttributeNames] 
arrayByAddingObject:NSAccessibilityLabelValueAttribute];
+    return [[super accessibilityAttributeNames] 
arrayByAddingObject:NSAccessibilityTitleAttribute];
 }
 
 - (id)accessibilityAttributeValue:(NSString *)attribute {
@@ -456,7 +462,9 @@
         return NSAccessibilityGroupRole;
     else if ([attribute 
isEqualToString:NSAccessibilityRoleDescriptionAttribute])
         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
-    else if ([attribute isEqualToString:NSAccessibilityLabelValueAttribute])
+    else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
+        return NSAccessibilityUnignoredChildren([[self subviews] 
arrayByAddingObjectsFromArray:icons]);
+    else if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
         return NSLocalizedString(@"status bar", @"Accessibility description");
     return [super accessibilityAttributeValue:attribute];
 }
@@ -486,7 +494,7 @@
 }
 
 - (NSArray *)accessibilityChildren {
-    return NSAccessibilityUnignoredChildren([self subviews]);
+    return NSAccessibilityUnignoredChildren([[self subviews] 
arrayByAddingObjectsFromArray:icons]);
 }
 
 - (NSString *)accessibilityLabel {
@@ -493,4 +501,129 @@
     return NSLocalizedString(@"status bar", @"Accessibility description");
 }
 
+- (NSRect)screenRectForIcon:(BDSKStatusBarIcon *)icon {
+    CGFloat fullRightMargin = rightMargin;
+    if (progressIndicator)
+        fullRightMargin += NSWidth([progressIndicator frame]) + 
MARGIN_BETWEEN_ITEMS;
+    NSRect rect = BDSKShrinkRect([self bounds], fullRightMargin, NSMaxXEdge);
+    NSRect iconRect;
+    NSSize size;
+    
+    for (BDSKStatusBarIcon *anIcon in icons) {
+        size = [self cellSizeForIcon:[anIcon image]];
+        NSDivideRect(rect, &iconRect, &rect, size.width, NSMaxXEdge);
+        rect = BDSKShrinkRect(rect, MARGIN_BETWEEN_ITEMS, NSMaxXEdge);
+        if (anIcon == icon)
+            return [self 
convertRectToScreen:NSOffsetRect(BDSKCenterRectVertically(iconRect, 
size.height, NO), 0.0, VERTICAL_OFFSET)];
+    }
+    return [self convertRectToScreen:NSZeroRect];
+}
+
+- (NSString *)toolTipForIcon:(BDSKStatusBarIcon *)icon {
+    if ([delegate 
respondsToSelector:@selector(statusBar:toolTipForIdentifier:)])
+        return [delegate statusBar:self toolTipForIdentifier:[icon 
identifier]];
+    return [icon toolTip];
+}
+
 @end
+
+#pragma mark -
+
+@implementation BDSKStatusBarIcon
+
+@synthesize image, identifier, toolTip;
+
+- (id)initWithIdentifier:(NSString *)anIdentnfier image:(NSImage *)anImage 
toolTip:(NSString *)aToolTip parent:(id)aParent {
+    self = [super init];
+    if (self) {
+        identifier = [anIdentnfier retain];
+        image = [anImage retain];
+        toolTip = [aToolTip retain];
+        parent = aParent;
+    }
+    return self;
+}
+
+- (void)dealloc {
+    parent = nil;
+    BDSKDESTROY(identifier);
+    BDSKDESTROY(image);
+    BDSKDESTROY(toolTip);
+    [super dealloc];
+}
+
+#pragma mark Accessibility
+
+- (NSArray *)accessibilityAttributeNames {
+    static NSArray *attributes = nil;
+    if (attributes == nil) {
+        attributes = [[NSArray alloc] initWithObjects:
+            NSAccessibilityRoleAttribute,
+            NSAccessibilityRoleDescriptionAttribute,
+            NSAccessibilityParentAttribute,
+            NSAccessibilityWindowAttribute,
+            NSAccessibilityTopLevelUIElementAttribute,
+            NSAccessibilityPositionAttribute,
+            NSAccessibilitySizeAttribute,
+            NSAccessibilityTitleAttribute,
+            NSAccessibilityHelpAttribute,
+            nil];
+    }
+    return attributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+    if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
+        return NSAccessibilityImageRole;
+    else if ([attribute 
isEqualToString:NSAccessibilityRoleDescriptionAttribute])
+        return NSAccessibilityRoleDescription(NSAccessibilityImageRole, nil);
+    else if ([attribute isEqualToString:NSAccessibilityParentAttribute])
+        return NSAccessibilityUnignoredAncestor(parent);
+    else if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
+        return [NSAccessibilityUnignoredAncestor(parent) 
accessibilityAttributeValue:NSAccessibilityWindowAttribute];
+    else if ([attribute 
isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
+        return [NSAccessibilityUnignoredAncestor(parent) 
accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
+    else if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
+        [NSValue valueWithPoint:[parent screenRectForIcon:self].origin];
+    else if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
+        [NSValue valueWithSize:[parent screenRectForIcon:self].size];
+    else if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
+        return [self toolTip];
+    else if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
+        return [parent toolTipForIcon:self];
+    return [super accessibilityAttributeValue:attribute];
+}
+
+- (BOOL)accessibilityIsIgnored {
+    return NO;
+}
+
+- (BOOL)isAccessibilityElement {
+    return YES;
+}
+
+- (NSString *)accessibilityRole {
+    return NSAccessibilityImageRole;
+}
+
+- (NSString *)accessibilityRoleDescription {
+    return NSAccessibilityRoleDescription(NSAccessibilityImageRole, nil);
+}
+
+- (NSRect)accessibilityFrame {
+    return [parent screenRectForIcon:self];
+}
+
+- (id)accessibilityParent {
+    return NSAccessibilityUnignoredAncestor(parent);
+}
+
+- (NSString *)accessibilityLabel {
+    return [self toolTip];
+}
+
+- (NSString *)accessibilityHelp {
+    return [parent toolTipForIcon:self];
+}
+
+@end

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit

Reply via email to