| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" | 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/strings/sys_string_conversions.h" | 8 #include "base/strings/sys_string_conversions.h" |
| 9 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h" | |
| 10 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" | 9 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" |
| 11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controlle
r.h" | 10 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controlle
r.h" |
| 12 #include "chrome/grit/generated_resources.h" | 11 #include "chrome/grit/generated_resources.h" |
| 13 #import "components/bookmarks/browser/bookmark_model.h" | 12 #import "components/bookmarks/browser/bookmark_model.h" |
| 14 #include "content/public/browser/user_metrics.h" | 13 #include "content/public/browser/user_metrics.h" |
| 15 #include "ui/base/l10n/l10n_util_mac.h" | 14 #include "ui/base/l10n/l10n_util_mac.h" |
| 16 #include "ui/base/material_design/material_design_controller.h" | |
| 17 #include "ui/base/resource/resource_bundle.h" | 15 #include "ui/base/resource/resource_bundle.h" |
| 18 #include "ui/resources/grit/ui_resources.h" | 16 #include "ui/resources/grit/ui_resources.h" |
| 19 | 17 |
| 20 using base::UserMetricsAction; | 18 using base::UserMetricsAction; |
| 21 using bookmarks::BookmarkNode; | 19 using bookmarks::BookmarkNode; |
| 22 | 20 |
| 23 namespace { | |
| 24 | |
| 25 const int kHierarchyButtonXMargin = 4; | 21 const int kHierarchyButtonXMargin = 4; |
| 26 const int kIconTextSpacer = 4; | |
| 27 const int kTextRightPadding = 1; | |
| 28 const int kIconLeftPadding = 3; | |
| 29 | |
| 30 const int kDefaultFontSize = 12; | |
| 31 | |
| 32 }; // namespace | |
| 33 | 22 |
| 34 @interface BookmarkButtonCell(Private) | 23 @interface BookmarkButtonCell(Private) |
| 35 - (void)configureBookmarkButtonCell; | 24 - (void)configureBookmarkButtonCell; |
| 36 - (void)applyTextColor; | 25 - (void)applyTextColor; |
| 37 // Returns the title the button cell displays. Note that a button cell can | |
| 38 // have a title string assigned but it won't be visible if its image position | |
| 39 // is NSImageOnly. | |
| 40 - (NSString*)visibleTitle; | |
| 41 // Returns the dictionary of attributes to associate with the button title. | |
| 42 - (NSDictionary*)titleTextAttributes; | |
| 43 @end | 26 @end |
| 44 | 27 |
| 45 | 28 |
| 46 @implementation BookmarkButtonCell | 29 @implementation BookmarkButtonCell |
| 47 | 30 |
| 48 @synthesize startingChildIndex = startingChildIndex_; | 31 @synthesize startingChildIndex = startingChildIndex_; |
| 49 @synthesize drawFolderArrow = drawFolderArrow_; | 32 @synthesize drawFolderArrow = drawFolderArrow_; |
| 50 | 33 |
| 51 + (id)buttonCellForNode:(const BookmarkNode*)node | 34 + (id)buttonCellForNode:(const BookmarkNode*)node |
| 52 text:(NSString*)text | 35 text:(NSString*)text |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 [self configureBookmarkButtonCell]; | 114 [self configureBookmarkButtonCell]; |
| 132 } | 115 } |
| 133 | 116 |
| 134 - (BOOL)isFolderButtonCell { | 117 - (BOOL)isFolderButtonCell { |
| 135 return NO; | 118 return NO; |
| 136 } | 119 } |
| 137 | 120 |
| 138 // Perform all normal init routines specific to the BookmarkButtonCell. | 121 // Perform all normal init routines specific to the BookmarkButtonCell. |
| 139 - (void)configureBookmarkButtonCell { | 122 - (void)configureBookmarkButtonCell { |
| 140 [self setButtonType:NSMomentaryPushInButton]; | 123 [self setButtonType:NSMomentaryPushInButton]; |
| 124 [self setBezelStyle:NSShadowlessSquareBezelStyle]; |
| 141 [self setShowsBorderOnlyWhileMouseInside:YES]; | 125 [self setShowsBorderOnlyWhileMouseInside:YES]; |
| 142 [self setControlSize:NSSmallControlSize]; | 126 [self setControlSize:NSSmallControlSize]; |
| 143 [self setAlignment:NSLeftTextAlignment]; | 127 [self setAlignment:NSLeftTextAlignment]; |
| 144 if (!ui::MaterialDesignController::IsModeMaterial()) { | 128 [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; |
| 145 [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; | |
| 146 [self setBezelStyle:NSShadowlessSquareBezelStyle]; | |
| 147 } else { | |
| 148 [self setFont:[NSFont systemFontOfSize:kDefaultFontSize]]; | |
| 149 [self setBordered:NO]; | |
| 150 [self setBezeled:NO]; | |
| 151 } | |
| 152 [self setWraps:NO]; | 129 [self setWraps:NO]; |
| 153 // NSLineBreakByTruncatingMiddle seems more common on OSX but let's | 130 // NSLineBreakByTruncatingMiddle seems more common on OSX but let's |
| 154 // try to match Windows for a bit to see what happens. | 131 // try to match Windows for a bit to see what happens. |
| 155 [self setLineBreakMode:NSLineBreakByTruncatingTail]; | 132 [self setLineBreakMode:NSLineBreakByTruncatingTail]; |
| 156 | 133 |
| 157 // The overflow button chevron bitmap is not 16 units high, so it'd be scaled | 134 // The overflow button chevron bitmap is not 16 units high, so it'd be scaled |
| 158 // at paint time without this. | 135 // at paint time without this. |
| 159 [self setImageScaling:NSImageScaleNone]; | 136 [self setImageScaling:NSImageScaleNone]; |
| 160 | 137 |
| 161 // Theming doesn't work for bookmark buttons yet (cell text is chucked). | 138 // Theming doesn't work for bookmark buttons yet (cell text is chucked). |
| 162 [super setShouldTheme:NO]; | 139 [super setShouldTheme:NO]; |
| 163 } | 140 } |
| 164 | 141 |
| 165 - (BOOL)empty { | 142 - (BOOL)empty { |
| 166 return empty_; | 143 return empty_; |
| 167 } | 144 } |
| 168 | 145 |
| 169 - (void)setEmpty:(BOOL)empty { | 146 - (void)setEmpty:(BOOL)empty { |
| 170 empty_ = empty; | 147 empty_ = empty; |
| 171 [self setShowsBorderOnlyWhileMouseInside:!empty]; | 148 [self setShowsBorderOnlyWhileMouseInside:!empty]; |
| 172 } | 149 } |
| 173 | 150 |
| 174 - (NSSize)cellSizeForBounds:(NSRect)aRect { | 151 - (NSSize)cellSizeForBounds:(NSRect)aRect { |
| 175 // There's no bezel or border in Material Design so return cellSize. | |
| 176 if (ui::MaterialDesignController::IsModeMaterial()) { | |
| 177 NSSize size = [self cellSize]; | |
| 178 size.width = std::min(aRect.size.width, size.width); | |
| 179 size.height = std::min(aRect.size.height, size.height); | |
| 180 return size; | |
| 181 } | |
| 182 NSSize size = [super cellSizeForBounds:aRect]; | 152 NSSize size = [super cellSizeForBounds:aRect]; |
| 183 // Cocoa seems to slightly underestimate how much space we need, so we | 153 // Cocoa seems to slightly underestimate how much space we need, so we |
| 184 // compensate here to avoid a clipped rendering. | 154 // compensate here to avoid a clipped rendering. |
| 185 size.width += 2; | 155 size.width += 2; |
| 186 size.height += 4; | 156 size.height += 4; |
| 187 return size; | 157 return size; |
| 188 } | 158 } |
| 189 | 159 |
| 190 - (void)setBookmarkCellText:(NSString*)title | 160 - (void)setBookmarkCellText:(NSString*)title |
| 191 image:(NSImage*)image { | 161 image:(NSImage*)image { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 | 215 |
| 246 - (void)setTextColor:(NSColor*)color { | 216 - (void)setTextColor:(NSColor*)color { |
| 247 if ([textColor_ isEqualTo:color]) | 217 if ([textColor_ isEqualTo:color]) |
| 248 return; | 218 return; |
| 249 textColor_.reset([color copy]); | 219 textColor_.reset([color copy]); |
| 250 [self applyTextColor]; | 220 [self applyTextColor]; |
| 251 } | 221 } |
| 252 | 222 |
| 253 // We must reapply the text color after any setTitle: call | 223 // We must reapply the text color after any setTitle: call |
| 254 - (void)applyTextColor { | 224 - (void)applyTextColor { |
| 225 base::scoped_nsobject<NSMutableParagraphStyle> style( |
| 226 [NSMutableParagraphStyle new]); |
| 227 [style setAlignment:NSLeftTextAlignment]; |
| 228 NSDictionary* dict = [NSDictionary |
| 229 dictionaryWithObjectsAndKeys:textColor_, |
| 230 NSForegroundColorAttributeName, |
| 231 [self font], NSFontAttributeName, |
| 232 style.get(), NSParagraphStyleAttributeName, |
| 233 [NSNumber numberWithFloat:0.2], NSKernAttributeName, |
| 234 nil]; |
| 255 base::scoped_nsobject<NSAttributedString> ats( | 235 base::scoped_nsobject<NSAttributedString> ats( |
| 256 [[NSAttributedString alloc] initWithString:[self title] | 236 [[NSAttributedString alloc] initWithString:[self title] attributes:dict]); |
| 257 attributes:[self titleTextAttributes]]); | |
| 258 [self setAttributedTitle:ats.get()]; | 237 [self setAttributedTitle:ats.get()]; |
| 259 } | 238 } |
| 260 | 239 |
| 261 // To implement "hover open a bookmark button to open the folder" | 240 // To implement "hover open a bookmark button to open the folder" |
| 262 // which feels like menus, we override NSButtonCell's mouseEntered: | 241 // which feels like menus, we override NSButtonCell's mouseEntered: |
| 263 // and mouseExited:, then and pass them along to our owning control. | 242 // and mouseExited:, then and pass them along to our owning control. |
| 264 // Note: as verified in a debugger, mouseEntered: does NOT increase | 243 // Note: as verified in a debugger, mouseEntered: does NOT increase |
| 265 // the retainCount of the cell or its owning control. | 244 // the retainCount of the cell or its owning control. |
| 266 - (void)mouseEntered:(NSEvent*)event { | 245 - (void)mouseEntered:(NSEvent*)event { |
| 267 [super mouseEntered:event]; | 246 [super mouseEntered:event]; |
| 268 [[self controlView] mouseEntered:event]; | 247 [[self controlView] mouseEntered:event]; |
| 269 } | 248 } |
| 270 | 249 |
| 271 // See comment above mouseEntered:, above. | 250 // See comment above mouseEntered:, above. |
| 272 - (void)mouseExited:(NSEvent*)event { | 251 - (void)mouseExited:(NSEvent*)event { |
| 273 [[self controlView] mouseExited:event]; | 252 [[self controlView] mouseExited:event]; |
| 274 [super mouseExited:event]; | 253 [super mouseExited:event]; |
| 275 } | 254 } |
| 276 | 255 |
| 277 - (void)setDrawFolderArrow:(BOOL)draw { | 256 - (void)setDrawFolderArrow:(BOOL)draw { |
| 278 drawFolderArrow_ = draw; | 257 drawFolderArrow_ = draw; |
| 279 if (draw && !arrowImage_) { | 258 if (draw && !arrowImage_) { |
| 280 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 259 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 281 arrowImage_.reset( | 260 arrowImage_.reset( |
| 282 [rb.GetNativeImageNamed(IDR_MENU_HIERARCHY_ARROW).ToNSImage() retain]); | 261 [rb.GetNativeImageNamed(IDR_MENU_HIERARCHY_ARROW).ToNSImage() retain]); |
| 283 } | 262 } |
| 284 } | 263 } |
| 285 | 264 |
| 286 - (NSDictionary*)titleTextAttributes { | |
| 287 base::scoped_nsobject<NSMutableParagraphStyle> style( | |
| 288 [NSMutableParagraphStyle new]); | |
| 289 [style setAlignment:NSNaturalTextAlignment]; | |
| 290 [style setLineBreakMode:NSLineBreakByTruncatingTail]; | |
| 291 NSColor* textColor = textColor_.get(); | |
| 292 if (!textColor) { | |
| 293 textColor = [NSColor blackColor]; | |
| 294 } | |
| 295 if (![self isEnabled]) { | |
| 296 textColor = [textColor colorWithAlphaComponent:0.5]; | |
| 297 } | |
| 298 NSFont* theFont = [self font]; | |
| 299 if (!theFont) { | |
| 300 theFont = [NSFont systemFontOfSize:kDefaultFontSize]; | |
| 301 } | |
| 302 | |
| 303 return @{ | |
| 304 NSFontAttributeName : theFont, | |
| 305 NSForegroundColorAttributeName : textColor, | |
| 306 NSParagraphStyleAttributeName : style.get(), | |
| 307 NSKernAttributeName : [NSNumber numberWithFloat:0.2] | |
| 308 }; | |
| 309 } | |
| 310 | |
| 311 - (NSString*)visibleTitle { | |
| 312 return [self imagePosition] != NSImageOnly ? [self title] : @""; | |
| 313 } | |
| 314 | |
| 315 // Add extra size for the arrow so it doesn't overlap the text. | 265 // Add extra size for the arrow so it doesn't overlap the text. |
| 316 // Does not sanity check to be sure this is actually a folder node. | 266 // Does not sanity check to be sure this is actually a folder node. |
| 317 - (NSSize)cellSize { | 267 - (NSSize)cellSize { |
| 318 NSSize cellSize = NSZeroSize; | 268 NSSize cellSize = [super cellSize]; |
| 319 if (!ui::MaterialDesignController::IsModeMaterial()) { | |
| 320 cellSize = [super cellSize]; | |
| 321 } else { | |
| 322 // Return the space needed to display the image and title, with a little | |
| 323 // distance between them. | |
| 324 cellSize = NSMakeSize(kIconLeftPadding + [[self image] size].width, | |
| 325 bookmarks::kMaterialBookmarkButtonHeight); | |
| 326 NSString* title = [self visibleTitle]; | |
| 327 if ([title length] > 0) { | |
| 328 CGFloat textWidth = | |
| 329 [title sizeWithAttributes:[self titleTextAttributes]].width; | |
| 330 cellSize.width += | |
| 331 kIconTextSpacer + std::ceil(textWidth) + kTextRightPadding; | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 if (drawFolderArrow_) { | 269 if (drawFolderArrow_) { |
| 336 cellSize.width += [arrowImage_ size].width + 2 * kHierarchyButtonXMargin; | 270 cellSize.width += [arrowImage_ size].width + 2 * kHierarchyButtonXMargin; |
| 337 } | 271 } |
| 338 return cellSize; | 272 return cellSize; |
| 339 } | 273 } |
| 340 | 274 |
| 341 - (NSRect)imageRectForBounds:(NSRect)theRect { | |
| 342 NSRect imageRect = [super imageRectForBounds:theRect]; | |
| 343 // In Material Design, add a little space between the image and the button's | |
| 344 // left edge, but only if there's a visible title. | |
| 345 if ([[self visibleTitle] length] && | |
| 346 ui::MaterialDesignController::IsModeMaterial()) { | |
| 347 imageRect.origin.x += kIconLeftPadding; | |
| 348 } | |
| 349 return imageRect; | |
| 350 } | |
| 351 | |
| 352 - (CGFloat)textStartXOffset { | |
| 353 if (!ui::MaterialDesignController::IsModeMaterial()) { | |
| 354 return [super textStartXOffset]; | |
| 355 } | |
| 356 return kIconLeftPadding + [[self image] size].width + kIconTextSpacer; | |
| 357 } | |
| 358 | |
| 359 // Override cell drawing to add a submenu arrow like a real menu. | 275 // Override cell drawing to add a submenu arrow like a real menu. |
| 360 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 276 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 361 // First draw "everything else". | 277 // First draw "everything else". |
| 362 [super drawInteriorWithFrame:cellFrame inView:controlView]; | 278 [super drawInteriorWithFrame:cellFrame inView:controlView]; |
| 363 | 279 |
| 364 // If asked to do so, and if a folder, draw the arrow. | 280 // If asked to do so, and if a folder, draw the arrow. |
| 365 if (!drawFolderArrow_) | 281 if (!drawFolderArrow_) |
| 366 return; | 282 return; |
| 367 BookmarkButton* button = static_cast<BookmarkButton*>([self controlView]); | 283 BookmarkButton* button = static_cast<BookmarkButton*>([self controlView]); |
| 368 DCHECK([button respondsToSelector:@selector(isFolder)]); | 284 DCHECK([button respondsToSelector:@selector(isFolder)]); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 382 respectFlipped:YES | 298 respectFlipped:YES |
| 383 hints:nil]; | 299 hints:nil]; |
| 384 } | 300 } |
| 385 } | 301 } |
| 386 | 302 |
| 387 - (int)verticalTextOffset { | 303 - (int)verticalTextOffset { |
| 388 return 0; | 304 return 0; |
| 389 } | 305 } |
| 390 | 306 |
| 391 @end | 307 @end |
| OLD | NEW |