File: bookmark_button_cell.mm

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (439 lines) | stat: -rw-r--r-- 14,473 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"

#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controller.h"
#include "chrome/grit/generated_resources.h"
#import "components/bookmarks/browser/bookmark_model.h"
#include "content/public/browser/user_metrics.h"
#import "ui/base/cocoa/nsview_additions.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/resources/grit/ui_resources.h"

using base::UserMetricsAction;
using bookmarks::BookmarkNode;

namespace {

// Padding on the right side of the arrow icon.
const int kHierarchyButtonRightPadding = 4;

// Padding on the left side of the arrow icon.
const int kHierarchyButtonLeftPadding = 11;

const int kIconTextSpacer = 4;
const int kTextRightPadding = 4;
const int kIconLeftPadding = 4;

const int kDefaultFontSize = 12;

};  // namespace

@interface OffTheSideButtonCell : BookmarkButtonCell

- (NSString*)accessibilityTitle;

@end
@implementation OffTheSideButtonCell

- (BOOL)isOffTheSideButtonCell {
  return YES;
}

- (NSString*)accessibilityTitle {
  return l10n_util::GetNSString(IDS_ACCNAME_BOOKMARKS_CHEVRON);
}

- (NSRect)imageRectForBounds:(NSRect)theRect {
  NSRect imageRect = [super imageRectForBounds:theRect];
  // Make sure the chevron icon stays centered. Normally a bookmark bar item
  // with no label has its icon placed at a fixed x-position.
  CGFloat totalWidth = NSMaxX(theRect);
  imageRect.origin.x = (totalWidth - [self image].size.width) / 2;
  return imageRect;
}

@end

@interface BookmarkButtonCell(Private)
// Returns YES if the cell is the offTheSide button cell.
- (BOOL)isOffTheSideButtonCell;
- (void)configureBookmarkButtonCell;
- (void)applyTextColor;
// Returns the title the button cell displays. Note that a button cell can
// have a title string assigned but it won't be visible if its image position
// is NSImageOnly.
- (NSString*)visibleTitle;
// Returns the dictionary of attributes to associate with the button title.
- (NSDictionary*)titleTextAttributes;
@end


@implementation BookmarkButtonCell

@synthesize startingChildIndex = startingChildIndex_;
@synthesize drawFolderArrow = drawFolderArrow_;

+ (id)buttonCellForNode:(const BookmarkNode*)node
                   text:(NSString*)text
                  image:(NSImage*)image
         menuController:(BookmarkContextMenuCocoaController*)menuController {
  id buttonCell =
      [[[BookmarkButtonCell alloc] initForNode:node
                                          text:text
                                         image:image
                                menuController:menuController]
       autorelease];
  return buttonCell;
}

+ (id)buttonCellWithText:(NSString*)text
                   image:(NSImage*)image
          menuController:(BookmarkContextMenuCocoaController*)menuController {
  id buttonCell =
      [[[BookmarkButtonCell alloc] initWithText:text
                                          image:image
                                 menuController:menuController]
       autorelease];
  return buttonCell;
}

- (id)initForNode:(const BookmarkNode*)node
             text:(NSString*)text
            image:(NSImage*)image
   menuController:(BookmarkContextMenuCocoaController*)menuController {
  if ((self = [super initTextCell:text])) {
    menuController_ = menuController;
    [self configureBookmarkButtonCell];
    [self setTextColor:[NSColor blackColor]];
    [self setBookmarkNode:node];
    // When opening a bookmark folder, the default behavior is that the
    // favicon is greyed when menu item is hovered with the mouse cursor.
    // When using NSNoCellMask, the favicon won't be greyed when menu item
    // is hovered.
    // In the bookmark bar, the favicon is not greyed when the bookmark is
    // hovered with the mouse cursor.
    // It makes the behavior of the bookmark folder consistent with hovering
    // on the bookmark bar.
    [self setHighlightsBy:NSNoCellMask];

    if (node) {
      NSString* title = base::SysUTF16ToNSString(node->GetTitle());
      [self setBookmarkCellText:title image:image];
    } else {
      [self setEmpty:YES];
      [self setBookmarkCellText:l10n_util::GetNSString(IDS_MENU_EMPTY_SUBMENU)
                          image:nil];
    }
  }

  return self;
}

- (id)initWithText:(NSString*)text
             image:(NSImage*)image
    menuController:(BookmarkContextMenuCocoaController*)menuController {
  if ((self = [super initTextCell:text])) {
    menuController_ = menuController;
    [self configureBookmarkButtonCell];
    [self setTextColor:[NSColor blackColor]];
    [self setBookmarkNode:NULL];
    [self setBookmarkCellText:text image:image];
    // This is a custom button not attached to any node. It is no considered
    // empty even if its bookmark node is NULL.
    [self setEmpty:NO];
  }

  return self;
}

- (id)initTextCell:(NSString*)string {
  return [self initForNode:nil text:string image:nil menuController:nil];
}

// Used by the off-the-side menu, the only case where a
// BookmarkButtonCell is loaded from a nib.
- (void)awakeFromNib {
  [self configureBookmarkButtonCell];
}

- (BOOL)isFolderButtonCell {
  return NO;
}

- (BOOL)isOffTheSideButtonCell {
  return NO;
}

// Perform all normal init routines specific to the BookmarkButtonCell.
- (void)configureBookmarkButtonCell {
  [self setButtonType:NSMomentaryPushInButton];
  [self setShowsBorderOnlyWhileMouseInside:YES];
  [self setControlSize:NSSmallControlSize];
  [self setAlignment:NSLeftTextAlignment];
  [self setFont:[NSFont systemFontOfSize:kDefaultFontSize]];
  [self setBordered:NO];
  [self setBezeled:NO];
  [self setWraps:NO];
  // NSLineBreakByTruncatingMiddle seems more common on OSX but let's
  // try to match Windows for a bit to see what happens.
  [self setLineBreakMode:NSLineBreakByTruncatingTail];

  // The overflow button chevron bitmap is not 16 units high, so it'd be scaled
  // at paint time without this.
  [self setImageScaling:NSImageScaleNone];

  // Theming doesn't work for bookmark buttons yet (cell text is chucked).
  [super setShouldTheme:NO];
}

- (BOOL)empty {
  return empty_;
}

- (void)setEmpty:(BOOL)empty {
  empty_ = empty;
  [self setShowsBorderOnlyWhileMouseInside:!empty];
}

- (NSSize)cellSizeForBounds:(NSRect)aRect {
  // There's no bezel or border so return cellSize.
  NSSize size = [self cellSize];
  size.width = std::min(aRect.size.width, size.width);
  size.height = std::min(aRect.size.height, size.height);
  return size;
}

- (void)setBookmarkCellText:(NSString*)title
                      image:(NSImage*)image {
  title = [title stringByReplacingOccurrencesOfString:@"\n"
                                           withString:@" "];
  title = [title stringByReplacingOccurrencesOfString:@"\r"
                                           withString:@" "];

  if ([title length]) {
    [self setImagePosition:NSImageLeft];
    [self setTitle:title];
  } else if ([self isFolderButtonCell]) {
    // Left-align icons for bookmarks within folders, regardless of whether
    // there is a title.
    [self setImagePosition:NSImageLeft];
  } else {
    // For bookmarks without a title that aren't visible directly in the
    // bookmarks bar, squeeze things tighter by displaying only the image.
    // By default, Cocoa leaves extra space in an attempt to display an
    // empty title.
    [self setImagePosition:NSImageOnly];
  }

  if (image)
    [self setImage:image];
}

- (void)setBookmarkNode:(const BookmarkNode*)node {
  [self setRepresentedObject:[NSValue valueWithPointer:node]];
}

- (const BookmarkNode*)bookmarkNode {
  return static_cast<const BookmarkNode*>([[self representedObject]
                                            pointerValue]);
}

- (NSMenu*)menu {
  // If node is NULL, this is a custom button, the menu does not represent
  // anything.
  const BookmarkNode* node = [self bookmarkNode];

  if (node && node->parent() &&
      node->parent()->type() == BookmarkNode::FOLDER) {
    content::RecordAction(UserMetricsAction("BookmarkBarFolder_CtxMenu"));
  } else {
    content::RecordAction(UserMetricsAction("BookmarkBar_CtxMenu"));
  }
  return [menuController_ menuForBookmarkNode:node];
}

- (void)setTitle:(NSString*)title {
  if ([[self title] isEqualTo:title])
    return;
  [super setTitle:title];
  [self applyTextColor];
}

- (void)setTextColor:(NSColor*)color {
  if ([textColor_ isEqualTo:color])
    return;
  textColor_.reset([color copy]);
  [self applyTextColor];
}

// We must reapply the text color after any setTitle: call
- (void)applyTextColor {
  base::scoped_nsobject<NSAttributedString> ats(
      [[NSAttributedString alloc] initWithString:[self title]
                                      attributes:[self titleTextAttributes]]);
  [self setAttributedTitle:ats.get()];
}

// To implement "hover open a bookmark button to open the folder"
// which feels like menus, we override NSButtonCell's mouseEntered:
// and mouseExited:, then and pass them along to our owning control.
// Note: as verified in a debugger, mouseEntered: does NOT increase
// the retainCount of the cell or its owning control.
- (void)mouseEntered:(NSEvent*)event {
  [super mouseEntered:event];
  [[self controlView] mouseEntered:event];
}

// See comment above mouseEntered:, above.
- (void)mouseExited:(NSEvent*)event {
  [[self controlView] mouseExited:event];
  [super mouseExited:event];
}

- (void)setDrawFolderArrow:(BOOL)draw {
  drawFolderArrow_ = draw;
  if (draw && !arrowImage_) {
    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    arrowImage_.reset(
        [rb.GetNativeImageNamed(IDR_MENU_HIERARCHY_ARROW).ToNSImage() retain]);
  }
}

- (NSDictionary*)titleTextAttributes {
  base::scoped_nsobject<NSMutableParagraphStyle> style(
      [NSMutableParagraphStyle new]);
  [style setAlignment:NSNaturalTextAlignment];
  [style setLineBreakMode:NSLineBreakByTruncatingTail];
  NSColor* textColor = textColor_.get();
  if (!textColor) {
    textColor = [NSColor blackColor];
  }
  if (![self isEnabled]) {
    textColor = [textColor colorWithAlphaComponent:0.5];
  }
  NSFont* theFont = [self font];
  if (!theFont) {
    theFont = [NSFont systemFontOfSize:kDefaultFontSize];
  }

  return @{
     NSFontAttributeName : theFont,
     NSForegroundColorAttributeName : textColor,
     NSParagraphStyleAttributeName : style.get(),
     NSKernAttributeName : [NSNumber numberWithFloat:0.2]
  };
}

- (NSString*)visibleTitle {
  return [self imagePosition] != NSImageOnly ? [self title] : @"";
}

// Add extra size for the arrow so it doesn't overlap the text.
// Does not sanity check to be sure this is actually a folder node.
- (NSSize)cellSize {
  NSSize cellSize = NSZeroSize;
  // Return the space needed to display the image and title, with a little
  // distance between them.
  cellSize = NSMakeSize(kIconLeftPadding + [[self image] size].width,
                        bookmarks::kBookmarkButtonHeight);
  NSString* title = [self visibleTitle];
  if ([title length] > 0) {
    CGFloat textWidth =
        [title sizeWithAttributes:[self titleTextAttributes]].width;
    cellSize.width +=
        kIconTextSpacer + std::ceil(textWidth) + kTextRightPadding;
  } else {
    // Make buttons without visible titles 20pts wide (18 plus padding).
    cellSize.width += kIconLeftPadding;
  }

  if (drawFolderArrow_) {
    cellSize.width += [arrowImage_ size].width +
                      kHierarchyButtonLeftPadding +
                      kHierarchyButtonRightPadding;
  }
  return cellSize;
}

- (NSRect)imageRectForBounds:(NSRect)theRect {
  NSRect imageRect = [super imageRectForBounds:theRect];
  // Add a little space between the image and the button's left edge, but only
  // if there's a visible title.
  imageRect.origin.y -= 1;
  imageRect.origin.x = kIconLeftPadding;
  return imageRect;
}

- (CGFloat)textStartXOffset {
  return kIconLeftPadding + [[self image] size].width + kIconTextSpacer;
}

- (void)drawFocusRingMaskWithFrame:(NSRect)cellFrame
                            inView:(NSView*)controlView {
  // We have to adjust the focus ring slightly for the chevron and regular
  // bookmark icons.
  if ([self isOffTheSideButtonCell]) {
    cellFrame.origin.y -= 2;
  } else if ([self visibleTitle].length > 0) {
    cellFrame.origin.x += 4;
  }
  if ([controlView cr_lineWidth] < 1) {
    cellFrame.origin.y -= 0.5;
  }
  [super drawFocusRingMaskWithFrame:cellFrame inView:controlView];
}

// Override cell drawing to add a submenu arrow like a real menu.
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
  // First draw "everything else".
  [super drawInteriorWithFrame:cellFrame inView:controlView];

  // If asked to do so, and if a folder, draw the arrow.
  if (!drawFolderArrow_)
    return;
  BookmarkButton* button = static_cast<BookmarkButton*>([self controlView]);
  DCHECK([button respondsToSelector:@selector(isFolder)]);
  if ([button isFolder]) {
    NSRect imageRect = NSZeroRect;
    imageRect.size = [arrowImage_ size];
    const CGFloat kArrowOffset = 1.0;  // Required for proper centering.
    CGFloat dX =
        NSWidth(cellFrame) - NSWidth(imageRect) - kHierarchyButtonRightPadding;
    CGFloat dY = (NSHeight(cellFrame) / 2.0) - (NSHeight(imageRect) / 2.0) +
        kArrowOffset;
    NSRect drawRect = NSOffsetRect(imageRect, dX, dY);
    [arrowImage_ drawInRect:drawRect
                    fromRect:imageRect
                   operation:NSCompositeSourceOver
                    fraction:[self isEnabled] ? 1.0 : 0.5
              respectFlipped:YES
                       hints:nil];
  }
}

- (int)verticalTextOffset {
  return -1;
}

- (CGFloat)hoverBackgroundVerticalOffsetInControlView:(NSView*)controlView {
  // In Material Design on Retina, and not in a folder menu, nudge the hover
  // background by 1px.
  const CGFloat kLineWidth = [controlView cr_lineWidth];
  if ([self isMaterialDesignButtonType] && ![self isFolderButtonCell] &&
      kLineWidth < 1) {
    return -kLineWidth;
  }
  return 0.0;
}


@end