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