1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/browser/avatar_button_controller.h"
7 #include "base/mac/foundation_util.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/browser/profiles/profiles_state.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_window.h"
12 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
13 #include "grit/generated_resources.h"
14 #include "grit/theme_resources.h"
15 #import "ui/base/cocoa/appkit_utils.h"
16 #import "ui/base/cocoa/hover_image_button.h"
17 #include "ui/base/l10n/l10n_util_mac.h"
18 #include "ui/base/nine_image_painter_factory.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/text_elider.h"
24 const CGFloat kButtonPadding = 12;
25 const CGFloat kButtonDefaultPadding = 5;
26 const CGFloat kButtonHeight = 27;
27 const CGFloat kButtonTitleImageSpacing = 10;
28 const CGFloat kMaxButtonWidth = 120;
30 const ui::NinePartImageIds kNormalBorderImageIds =
31 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_NORMAL);
32 const ui::NinePartImageIds kHoverBorderImageIds =
33 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_HOVER);
34 const ui::NinePartImageIds kPressedBorderImageIds =
35 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_PRESSED);
37 NSImage* GetImageFromResourceID(int resourceId) {
38 return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
39 resourceId).ToNSImage();
44 // Button cell with a custom border given by a set of nine-patch image grids.
45 @interface CustomThemeButtonCell : NSButtonCell
48 @implementation CustomThemeButtonCell
50 NSSize buttonSize = [super cellSize];
51 buttonSize.width += 2 * kButtonPadding - 2 * kButtonDefaultPadding;
52 buttonSize.height = kButtonHeight;
56 - (NSRect)drawTitle:(NSAttributedString*)title
57 withFrame:(NSRect)frame
58 inView:(NSView*)controlView {
59 frame.origin.x = kButtonPadding;
60 // Ensure there's always a padding between the text and the image.
61 frame.size.width -= kButtonTitleImageSpacing;
62 return [super drawTitle:title withFrame:frame inView:controlView];
65 - (void)drawImage:(NSImage*)image
66 withFrame:(NSRect)frame
67 inView:(NSView*)controlView {
68 // For the x-offset, we need to undo the default padding and apply the
69 // new one. For the y-offset, increasing the button height means we need
70 // to move the image a little down to align it nicely with the text; this
71 // was chosen by visual inspection.
72 frame = NSOffsetRect(frame, kButtonDefaultPadding - kButtonPadding, 2);
73 [super drawImage:image withFrame:frame inView:controlView];
76 - (void)drawBezelWithFrame:(NSRect)frame
77 inView:(NSView*)controlView {
78 HoverState hoverState =
79 [base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState];
80 ui::NinePartImageIds imageIds = kNormalBorderImageIds;
82 if (hoverState == kHoverStateMouseDown)
83 imageIds = kPressedBorderImageIds;
84 else if (hoverState == kHoverStateMouseOver)
85 imageIds = kHoverBorderImageIds;
86 ui::DrawNinePartImage(frame, imageIds, NSCompositeSourceOver, 1.0, true);
90 @interface AvatarButtonController (Private)
91 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
94 @implementation AvatarButtonController
96 - (id)initWithBrowser:(Browser*)browser {
97 if ((self = [super initWithBrowser:browser])) {
98 HoverImageButton* hoverButton =
99 [[HoverImageButton alloc] initWithFrame:NSZeroRect];
100 [hoverButton setDefaultImage:GetImageFromResourceID(
101 IDR_AVATAR_MAC_BUTTON_DROPARROW)];
102 [hoverButton setHoverImage:GetImageFromResourceID(
103 IDR_AVATAR_MAC_BUTTON_DROPARROW_HOVER)];
104 [hoverButton setPressedImage:GetImageFromResourceID(
105 IDR_AVATAR_MAC_BUTTON_DROPARROW_PRESSED)];
107 button_.reset(hoverButton);
108 base::scoped_nsobject<CustomThemeButtonCell> cell(
109 [[CustomThemeButtonCell alloc] init]);
110 [button_ setCell:cell.get()];
111 [self setView:button_];
113 [button_ setBezelStyle:NSShadowlessSquareBezelStyle];
114 [button_ setButtonType:NSMomentaryChangeButton];
115 [button_ setBordered:YES];
116 // This is a workaround for an issue in the HoverImageButton where the
117 // button is initially sized incorrectly unless a default image is provided.
118 [button_ setImage:GetImageFromResourceID(IDR_AVATAR_MAC_BUTTON_DROPARROW)];
119 [button_ setImagePosition:NSImageRight];
120 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
121 [button_ setTarget:self];
122 [button_ setAction:@selector(buttonClicked:)];
124 [self updateAvatarButtonAndLayoutParent:NO];
129 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
130 // The button text has a white drop shadow.
131 base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
132 [shadow setShadowOffset:NSMakeSize(0, -1)];
133 [shadow setShadowBlurRadius:0];
134 [shadow setShadowColor:[NSColor whiteColor]];
136 base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
137 [[NSMutableParagraphStyle alloc] init]);
138 [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
139 [paragraphStyle setAlignment:NSLeftTextAlignment];
141 NSString* buttonTitle = base::SysUTF16ToNSString(
142 profiles::GetActiveProfileDisplayName(browser_));
144 base::scoped_nsobject<NSAttributedString> attributedTitle(
145 [[NSAttributedString alloc]
146 initWithString:buttonTitle
147 attributes:@{ NSShadowAttributeName : shadow.get(),
148 NSParagraphStyleAttributeName : paragraphStyle }]);
149 [button_ setAttributedTitle:attributedTitle];
152 // Truncate the title if needed.
153 if (NSWidth([button_ bounds]) > kMaxButtonWidth)
154 [button_ setFrameSize:NSMakeSize(kMaxButtonWidth, kButtonHeight)];
157 // Because the width of the button might have changed, the parent browser
158 // frame needs to recalculate the button bounds and redraw it.
159 [[BrowserWindowController
160 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()]