From 7bd9f9817bbbbfeb9801fbf41dec412b566fc80c Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 14 Aug 2012 17:59:13 +0200 Subject: [PATCH] Improve tab trigger rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is using NSAttributedString’s NSTextTableBlocks: * Font is determined from current menu rather than hardcoded * Fixed: Invalid font size on bundle-item popup * Fixed: Both tab triggers and key equivalents can co exists on single menu item * No relayout calculations are needed after menu update (this is done automatically by NSLayoutManager when using NSTextTables) * Uses 10.5 compatible API --- .../OakAppKit/src/NSMenuItem Additions.mm | 121 ++++++++++++++++-- 1 file changed, 112 insertions(+), 9 deletions(-) diff --git a/Frameworks/OakAppKit/src/NSMenuItem Additions.mm b/Frameworks/OakAppKit/src/NSMenuItem Additions.mm index dd635843..bbf03b7c 100644 --- a/Frameworks/OakAppKit/src/NSMenuItem Additions.mm +++ b/Frameworks/OakAppKit/src/NSMenuItem Additions.mm @@ -83,6 +83,109 @@ static void set_legacy_key_equivalent (MenuRef aMenu, UInt16 anIndex, std::strin SetMenuItemModifiers(aMenu, anIndex, modifiers); } +@interface MenuMutableAttributedString : NSMutableAttributedString +{ + NSMutableAttributedString* contents; + CGSize size; +} +- (void)appendTableCellWithString:(NSString*)string table:(NSTextTable*)table textAlignment:(NSTextAlignment)textAlignment verticalAlignment:(NSTextBlockVerticalAlignment)verticalAlignment font:(NSFont*)font row:(int)row column:(int)column; +- (CGSize)size; +@end + +@implementation MenuMutableAttributedString + +// Methods to override in subclass + +- (id)init +{ + return [self initWithAttributedString:nil]; +} + +- (id)initWithAttributedString:(NSAttributedString*)attributedString +{ + if(self = [super init]) + contents = attributedString ? [attributedString mutableCopy] : [[NSMutableAttributedString alloc] init]; + return self; +} + +- (NSString*)string +{ + return [contents string]; +} + +- (NSDictionary*)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRange*)range +{ + return [contents attributesAtIndex:location effectiveRange:range]; +} + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString*)string +{ + [contents replaceCharactersInRange:range withString:string]; +} + +- (void)setAttributes:(NSDictionary*)attributes range:(NSRange)range +{ + [contents setAttributes:attributes range:range]; +} + +- (id)copyWithZone:(NSZone*)zone +{ + MenuMutableAttributedString* copy = [MenuMutableAttributedString allocWithZone:zone]; + copy->contents = [contents copyWithZone:zone]; + copy->size = size; + return copy; +} + +- (void)dealloc +{ + [contents release]; + [super dealloc]; +} + +// NOTE: AppKit additions produce invalid values here, provide our own implementation + +- (NSRect)boundingRectWithSize:(NSSize)aSize options:(NSStringDrawingOptions)options +{ + return NSMakeRect(0, 0, size.width, size.height); +} + +// Helper method for adding table cell into the attributed string + +- (void)appendTableCellWithString:(NSString*)string table:(NSTextTable*)table textAlignment:(NSTextAlignment)textAlignment verticalAlignment:(NSTextBlockVerticalAlignment)verticalAlignment font:(NSFont*)font row:(int)row column:(int)column; +{ + CGSize stringSize = [string sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]]; + + NSTextTableBlock* block = [[[NSTextTableBlock alloc] initWithTable:table startingRow:row rowSpan:1 startingColumn:column columnSpan:1] autorelease]; + + if(column > 0) + [block setContentWidth:stringSize.width type:NSTextBlockAbsoluteValueType]; + + block.verticalAlignment = verticalAlignment; + + NSMutableParagraphStyle* paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease]; + [paragraphStyle setTextBlocks:[NSArray arrayWithObjects:block, nil]]; + [paragraphStyle setAlignment:textAlignment]; + + string = [string stringByAppendingString:@"\n"]; + + NSMutableAttributedString* cellString = [[[NSMutableAttributedString alloc] initWithString:string] autorelease]; + [cellString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [cellString length])]; + [cellString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [cellString length])]; + + size.width += stringSize.width; + if(size.height < stringSize.height) + size.height = stringSize.height; + + [self appendAttributedString:cellString]; +} + +- (CGSize)size +{ + return size; +} + +@end + @implementation NSMenuItem (FileIcon) - (void)setIconForFile:(NSString*)path; { @@ -144,16 +247,16 @@ static void set_legacy_key_equivalent (MenuRef aMenu, UInt16 anIndex, std::strin if(aTabTrigger == NULL_STR) return; - if(MenuRef menu = _NSGetCarbonMenu([self menu])) - { - MenuItemIndex itemIndex = [[self menu] indexOfItem:self] + 1; + MenuMutableAttributedString* attributedTitle = [[[MenuMutableAttributedString alloc] init] autorelease]; + NSTextTable* table = [[[NSTextTable alloc] init] autorelease]; + [table setNumberOfColumns:2]; - ChangeMenuItemAttributes(menu, itemIndex, kMenuItemAttrCustomDraw, 0); - std::string const& tabTrigger(aTabTrigger + "⇥"); - size_t len = tabTrigger.size(); - SetMenuItemProperty(menu, itemIndex, 'TxMt', 'TbLn', sizeof(size_t), &len); - SetMenuItemProperty(menu, itemIndex, 'TxMt', 'TbTr', tabTrigger.size(), tabTrigger.data()); - } + NSFont* font = self.menu.font ?: [NSFont menuFontOfSize:0]; + [attributedTitle appendTableCellWithString:self.title table:table textAlignment:NSLeftTextAlignment verticalAlignment:NSTextBlockMiddleAlignment font:font row:0 column:0]; + [attributedTitle appendTableCellWithString:[NSString stringWithCxxString:(" "+aTabTrigger+"\u21E5")] table:table textAlignment:NSRightTextAlignment + verticalAlignment:font.pointSize >= 13 ? NSTextBlockBottomAlignment : NSTextBlockMiddleAlignment + font:[NSFont menuBarFontOfSize:floor(font.pointSize * 0.85)] row:0 column:1]; + self.attributedTitle = attributedTitle; } - (void)setModifiedState:(BOOL)flag