Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm

Issue 605803002: [Mac] Redesign the new avatar button. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rachel comments Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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/profiles/avatar_button_controller.h" 5 #import "chrome/browser/ui/cocoa/profiles/avatar_button_controller.h"
6 6
7 #include "base/mac/foundation_util.h" 7 #include "base/mac/foundation_util.h"
8 #include "base/strings/sys_string_conversions.h" 8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile_manager.h"
9 #include "chrome/browser/profiles/profiles_state.h" 11 #include "chrome/browser/profiles/profiles_state.h"
10 #include "chrome/browser/themes/theme_service.h" 12 #include "chrome/browser/themes/theme_service.h"
11 #include "chrome/browser/themes/theme_service_factory.h" 13 #include "chrome/browser/themes/theme_service_factory.h"
12 #include "chrome/browser/ui/browser.h" 14 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_window.h" 15 #include "chrome/browser/ui/browser_window.h"
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" 16 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
15 #include "chrome/grit/generated_resources.h" 17 #include "chrome/grit/generated_resources.h"
16 #include "components/signin/core/browser/signin_error_controller.h" 18 #include "components/signin/core/browser/signin_error_controller.h"
17 #include "grit/theme_resources.h" 19 #include "grit/theme_resources.h"
18 #import "ui/base/cocoa/appkit_utils.h" 20 #import "ui/base/cocoa/appkit_utils.h"
19 #import "ui/base/cocoa/hover_image_button.h" 21 #import "ui/base/cocoa/hover_image_button.h"
20 #include "ui/base/l10n/l10n_util_mac.h" 22 #include "ui/base/l10n/l10n_util_mac.h"
21 #include "ui/base/nine_image_painter_factory.h" 23 #include "ui/base/nine_image_painter_factory.h"
22 #include "ui/base/resource/resource_bundle.h" 24 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/image/image_skia_operations.h" 25 #include "ui/gfx/image/image_skia_operations.h"
24 #include "ui/gfx/image/image_skia_util_mac.h" 26 #include "ui/gfx/image/image_skia_util_mac.h"
25 27
26 namespace { 28 namespace {
27 29
28 const CGFloat kButtonPadding = 12; 30 // NSButtons have a default padding of 5px. This button should have a padding
29 const CGFloat kButtonDefaultPadding = 5; 31 // of 8px.
30 const CGFloat kButtonHeight = 27; 32 const CGFloat kButtonExtraPadding = 8 - 5;
31 const CGFloat kButtonTitleImageSpacing = 10; 33 const CGFloat kButtonHeight = 28;
32 34
33 const ui::NinePartImageIds kNormalBorderImageIds = 35 const ui::NinePartImageIds kNormalBorderImageIds =
34 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_NORMAL); 36 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_NORMAL);
35 const ui::NinePartImageIds kHoverBorderImageIds = 37 const ui::NinePartImageIds kHoverBorderImageIds =
36 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_HOVER); 38 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_HOVER);
37 const ui::NinePartImageIds kPressedBorderImageIds = 39 const ui::NinePartImageIds kPressedBorderImageIds =
38 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_PRESSED); 40 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_PRESSED);
39 const ui::NinePartImageIds kThemedBorderImageIds = 41 const ui::NinePartImageIds kThemedBorderImageIds =
40 IMAGE_GRID(IDR_AVATAR_THEMED_MAC_BUTTON_NORMAL); 42 IMAGE_GRID(IDR_AVATAR_THEMED_MAC_BUTTON_NORMAL);
41 43
42 NSImage* GetImageFromResourceID(int resourceId) { 44 NSImage* GetImageFromResourceID(int resourceId) {
43 return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( 45 return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
44 resourceId).ToNSImage(); 46 resourceId).ToNSImage();
45 } 47 }
46 48
47 } // namespace 49 } // namespace
48 50
49 // Button cell with a custom border given by a set of nine-patch image grids. 51 // Button cell with a custom border given by a set of nine-patch image grids.
50 @interface CustomThemeButtonCell : NSButtonCell { 52 @interface CustomThemeButtonCell : NSButtonCell {
51 @private 53 @private
52 BOOL isThemedWindow_; 54 BOOL isThemedWindow_;
53 base::scoped_nsobject<NSImage> authenticationErrorImage_; 55 BOOL hasError_;
54 } 56 }
55 - (void)setIsThemedWindow:(BOOL)isThemedWindow; 57 - (void)setIsThemedWindow:(BOOL)isThemedWindow;
56 - (void)setHasError:(BOOL)hasError withTitle:(NSString*)title; 58 - (void)setHasError:(BOOL)hasError withTitle:(NSString*)title;
57 59
58 @end 60 @end
59 61
60 @implementation CustomThemeButtonCell 62 @implementation CustomThemeButtonCell
61 - (id)initWithThemedWindow:(BOOL)isThemedWindow { 63 - (id)initWithThemedWindow:(BOOL)isThemedWindow {
62 if ((self = [super init])) { 64 if ((self = [super init])) {
63 isThemedWindow_ = isThemedWindow; 65 isThemedWindow_ = isThemedWindow;
64 authenticationErrorImage_.reset(); 66 hasError_ = false;
65 } 67 }
66 return self; 68 return self;
67 } 69 }
68 70
69 - (NSSize)cellSize { 71 - (NSSize)cellSize {
70 NSSize buttonSize = [super cellSize]; 72 NSSize buttonSize = [super cellSize];
71 CGFloat errorWidth = [authenticationErrorImage_ size].width; 73
72 buttonSize.width += 2 * (kButtonPadding - kButtonDefaultPadding) + errorWidth; 74 // An image and no error means we are drawing the generic button, which
75 // is square. Otherwise, we are displaying the profile's name and an
76 // optional authentication error icon.
77 if ([self image] && !hasError_) {
78 buttonSize.width = kButtonHeight;
79 } else {
80 buttonSize.width += 2 * kButtonExtraPadding;
81 }
73 buttonSize.height = kButtonHeight; 82 buttonSize.height = kButtonHeight;
74 return buttonSize; 83 return buttonSize;
75 } 84 }
76 85
77 - (NSRect)drawTitle:(NSAttributedString*)title 86 - (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView*)controlView {
78 withFrame:(NSRect)frame 87 NSRect frameAfterPadding = NSInsetRect(frame, kButtonExtraPadding, 0);
79 inView:(NSView*)controlView { 88 [super drawInteriorWithFrame:frameAfterPadding inView:controlView];
80 frame.origin.x = kButtonPadding;
81
82 // If there's an auth error, draw a warning icon before the cell image.
83 if (authenticationErrorImage_) {
84 NSSize imageSize = [authenticationErrorImage_ size];
85 NSRect rect = NSMakeRect(
86 frame.size.width - imageSize.width,
87 (kButtonHeight - imageSize.height) / 2,
88 imageSize.width,
89 imageSize.height);
90 [authenticationErrorImage_ drawInRect:rect
91 fromRect:NSZeroRect
92 operation:NSCompositeSourceOver
93 fraction:1.0
94 respectFlipped:YES
95 hints:nil];
96 // Padding between the title and the error image.
97 frame.size.width -= kButtonTitleImageSpacing;
98 }
99
100 // Padding between the title (or error image, if it exists) and the
101 // button's drop down image.
102 frame.size.width -= kButtonTitleImageSpacing;
103 return [super drawTitle:title withFrame:frame inView:controlView];
104 } 89 }
105 90
106 - (void)drawImage:(NSImage*)image 91 - (void)drawImage:(NSImage*)image
107 withFrame:(NSRect)frame 92 withFrame:(NSRect)frame
108 inView:(NSView*)controlView { 93 inView:(NSView*)controlView {
109 // For the x-offset, we need to undo the default padding and apply the 94 // The image used in the generic button case needs to be shifted down
110 // new one. For the y-offset, increasing the button height means we need 95 // slightly to be centered correctly.
111 // to move the image a little down to align it nicely with the text; this 96 // TODO(noms): When the assets are fixed, remove this latter offset.
112 // was chosen by visual inspection. 97 if (!hasError_)
113 frame = NSOffsetRect(frame, kButtonDefaultPadding - kButtonPadding, 2); 98 frame = NSOffsetRect(frame, 0, 1);
114 [super drawImage:image withFrame:frame inView:controlView]; 99 [super drawImage:image withFrame:frame inView:controlView];
115 } 100 }
116 101
117 - (void)drawBezelWithFrame:(NSRect)frame 102 - (void)drawBezelWithFrame:(NSRect)frame
118 inView:(NSView*)controlView { 103 inView:(NSView*)controlView {
119 HoverState hoverState = 104 HoverState hoverState =
120 [base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState]; 105 [base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState];
121 ui::NinePartImageIds imageIds = kNormalBorderImageIds; 106 ui::NinePartImageIds imageIds = kNormalBorderImageIds;
122 if (isThemedWindow_) 107 if (isThemedWindow_)
123 imageIds = kThemedBorderImageIds; 108 imageIds = kThemedBorderImageIds;
124 109
125 if (hoverState == kHoverStateMouseDown) 110 if (hoverState == kHoverStateMouseDown)
126 imageIds = kPressedBorderImageIds; 111 imageIds = kPressedBorderImageIds;
127 else if (hoverState == kHoverStateMouseOver) 112 else if (hoverState == kHoverStateMouseOver)
128 imageIds = kHoverBorderImageIds; 113 imageIds = kHoverBorderImageIds;
129 ui::DrawNinePartImage(frame, imageIds, NSCompositeSourceOver, 1.0, true); 114 ui::DrawNinePartImage(frame, imageIds, NSCompositeSourceOver, 1.0, true);
130 } 115 }
131 116
132 - (void)setIsThemedWindow:(BOOL)isThemedWindow { 117 - (void)setIsThemedWindow:(BOOL)isThemedWindow {
133 isThemedWindow_ = isThemedWindow; 118 isThemedWindow_ = isThemedWindow;
134 } 119 }
135 120
136 - (void)setHasError:(BOOL)hasError withTitle:(NSString*)title { 121 - (void)setHasError:(BOOL)hasError withTitle:(NSString*)title {
122 hasError_ = hasError;
137 if (hasError) { 123 if (hasError) {
138 authenticationErrorImage_.reset(
139 [ui::ResourceBundle::GetSharedInstance().GetImageNamed(
140 IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR).ToNSImage() retain]);
141 [self accessibilitySetOverrideValue:l10n_util::GetNSStringF( 124 [self accessibilitySetOverrideValue:l10n_util::GetNSStringF(
142 IDS_PROFILES_ACCOUNT_BUTTON_AUTH_ERROR_ACCESSIBLE_NAME, 125 IDS_PROFILES_ACCOUNT_BUTTON_AUTH_ERROR_ACCESSIBLE_NAME,
143 base::SysNSStringToUTF16(title)) 126 base::SysNSStringToUTF16(title))
144 forAttribute:NSAccessibilityTitleAttribute]; 127 forAttribute:NSAccessibilityTitleAttribute];
145 } else { 128 } else {
146 authenticationErrorImage_.reset();
147 [self accessibilitySetOverrideValue:title 129 [self accessibilitySetOverrideValue:title
148 forAttribute:NSAccessibilityTitleAttribute]; 130 forAttribute:NSAccessibilityTitleAttribute];
149 } 131 }
150 } 132 }
151 133
152 @end 134 @end
153 135
154 @interface AvatarButtonController (Private) 136 @interface AvatarButtonController (Private)
155 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent; 137 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
156 - (void)updateErrorStatus:(BOOL)hasError; 138 - (void)updateErrorStatus:(BOOL)hasError;
157 - (void)dealloc; 139 - (void)dealloc;
158 - (void)themeDidChangeNotification:(NSNotification*)aNotification; 140 - (void)themeDidChangeNotification:(NSNotification*)aNotification;
159 @end 141 @end
160 142
161 @implementation AvatarButtonController 143 @implementation AvatarButtonController
162 144
163 - (id)initWithBrowser:(Browser*)browser { 145 - (id)initWithBrowser:(Browser*)browser {
164 if ((self = [super initWithBrowser:browser])) { 146 if ((self = [super initWithBrowser:browser])) {
165 ThemeService* themeService = 147 ThemeService* themeService =
166 ThemeServiceFactory::GetForProfile(browser->profile()); 148 ThemeServiceFactory::GetForProfile(browser->profile());
167 isThemedWindow_ = !themeService->UsingSystemTheme(); 149 isThemedWindow_ = !themeService->UsingSystemTheme();
168 150
169 HoverImageButton* hoverButton = 151 HoverImageButton* hoverButton =
170 [[HoverImageButton alloc] initWithFrame:NSZeroRect]; 152 [[HoverImageButton alloc] initWithFrame:NSZeroRect];
171 [hoverButton setDefaultImage:GetImageFromResourceID(
172 IDR_AVATAR_MAC_BUTTON_DROPARROW)];
173 [hoverButton setHoverImage:GetImageFromResourceID(
174 IDR_AVATAR_MAC_BUTTON_DROPARROW_HOVER)];
175 [hoverButton setPressedImage:GetImageFromResourceID(
176 IDR_AVATAR_MAC_BUTTON_DROPARROW_PRESSED)];
177
178 button_.reset(hoverButton); 153 button_.reset(hoverButton);
179 base::scoped_nsobject<CustomThemeButtonCell> cell( 154 base::scoped_nsobject<CustomThemeButtonCell> cell(
180 [[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]); 155 [[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]);
156 [button_ setCell:cell.get()];
157
158 // Check if the account already has an authentication error.
181 SigninErrorController* errorController = 159 SigninErrorController* errorController =
182 profiles::GetSigninErrorController(browser->profile()); 160 profiles::GetSigninErrorController(browser->profile());
183 161 hasError_ = errorController && errorController->HasError();
184 [button_ setCell:cell.get()]; 162 [cell setHasError:hasError_ withTitle:nil];
185
186 if (errorController)
187 [cell setHasError:errorController->HasError() withTitle:[button_ title]];
188 163
189 [button_ setWantsLayer:YES]; 164 [button_ setWantsLayer:YES];
190 [self setView:button_]; 165 [self setView:button_];
191 166
192 [button_ setBezelStyle:NSShadowlessSquareBezelStyle]; 167 [button_ setBezelStyle:NSShadowlessSquareBezelStyle];
193 [button_ setButtonType:NSMomentaryChangeButton]; 168 [button_ setButtonType:NSMomentaryChangeButton];
194 [button_ setBordered:YES]; 169 [button_ setBordered:YES];
195 // This is a workaround for an issue in the HoverImageButton where the 170
196 // button is initially sized incorrectly unless a default image is provided.
197 [button_ setImage:GetImageFromResourceID(IDR_AVATAR_MAC_BUTTON_DROPARROW)];
198 [button_ setImagePosition:NSImageRight];
199 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; 171 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
200 [button_ setTarget:self]; 172 [button_ setTarget:self];
201 [button_ setAction:@selector(buttonClicked:)]; 173 [button_ setAction:@selector(buttonClicked:)];
202 174
203 [self updateAvatarButtonAndLayoutParent:NO]; 175 [self updateAvatarButtonAndLayoutParent:NO];
204 176
205 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 177 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
206 [center addObserver:self 178 [center addObserver:self
207 selector:@selector(themeDidChangeNotification:) 179 selector:@selector(themeDidChangeNotification:)
208 name:kBrowserThemeDidChangeNotification 180 name:kBrowserThemeDidChangeNotification
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 foregroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.9]; 213 foregroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.9];
242 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.4]]; 214 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.4]];
243 } else if (!isThemedWindow_) { 215 } else if (!isThemedWindow_) {
244 foregroundColor = [NSColor blackColor]; 216 foregroundColor = [NSColor blackColor];
245 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.7]]; 217 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.7]];
246 } else { 218 } else {
247 foregroundColor = [NSColor blackColor]; 219 foregroundColor = [NSColor blackColor];
248 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.4]]; 220 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.4]];
249 } 221 }
250 222
251 NSString* buttonTitle = base::SysUTF16ToNSString( 223 const ProfileInfoCache& cache =
224 g_browser_process->profile_manager()->GetProfileInfoCache();
225 // If there is a single local profile, then use the generic avatar button
226 // instead of the profile name. Never use the generic button if the active
227 // profile is Guest.
228 bool useGenericButton = (!browser_->profile()->IsGuestSession() &&
229 cache.GetNumberOfProfiles() == 1 &&
230 cache.GetUserNameOfProfileAtIndex(0).empty());
231
232
233 NSString* buttonTitle = base::SysUTF16ToNSString(useGenericButton ?
234 base::string16() :
252 profiles::GetAvatarButtonTextForProfile(browser_->profile())); 235 profiles::GetAvatarButtonTextForProfile(browser_->profile()));
253 236
237 HoverImageButton* button =
238 base::mac::ObjCCastStrict<HoverImageButton>(button_);
239 if (useGenericButton) {
240 [button setDefaultImage:GetImageFromResourceID(
241 IDR_AVATAR_MAC_BUTTON_AVATAR)];
242 [button setHoverImage:GetImageFromResourceID(
243 IDR_AVATAR_MAC_BUTTON_AVATAR_HOVER)];
244 [button setPressedImage:GetImageFromResourceID(
245 IDR_AVATAR_MAC_BUTTON_AVATAR_PRESSED)];
246 // This is a workaround for an issue in the HoverImageButton where the
247 // button is initially sized incorrectly unless a default image is provided.
248 // See crbug.com/298501.
249 [button setImage:GetImageFromResourceID(IDR_AVATAR_MAC_BUTTON_AVATAR)];
250 [button setImagePosition:NSImageOnly];
251 } else if (hasError_) {
252 [button setDefaultImage:GetImageFromResourceID(
253 IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR)];
254 [button setHoverImage:nil];
255 [button setPressedImage:nil];
256 [button setImage:GetImageFromResourceID(
257 IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR)];
258 [button setImagePosition:NSImageRight];
259 } else {
260 [button setDefaultImage:nil];
261 [button setHoverImage:nil];
262 [button setPressedImage:nil];
263 [button setImagePosition:NSNoImage];
264 }
265
254 base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( 266 base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
255 [[NSMutableParagraphStyle alloc] init]); 267 [[NSMutableParagraphStyle alloc] init]);
256 [paragraphStyle setAlignment:NSLeftTextAlignment]; 268 [paragraphStyle setAlignment:NSLeftTextAlignment];
257 269
258 base::scoped_nsobject<NSAttributedString> attributedTitle( 270 base::scoped_nsobject<NSAttributedString> attributedTitle(
259 [[NSAttributedString alloc] 271 [[NSAttributedString alloc]
260 initWithString:buttonTitle 272 initWithString:buttonTitle
261 attributes:@{ NSShadowAttributeName : shadow.get(), 273 attributes:@{ NSShadowAttributeName : shadow.get(),
262 NSForegroundColorAttributeName : foregroundColor, 274 NSForegroundColorAttributeName : foregroundColor,
263 NSParagraphStyleAttributeName : paragraphStyle }]); 275 NSParagraphStyleAttributeName : paragraphStyle }]);
264 [button_ setAttributedTitle:attributedTitle]; 276 [button_ setAttributedTitle:attributedTitle];
265 [button_ sizeToFit]; 277 [button_ sizeToFit];
266 278
267 if (layoutParent) { 279 if (layoutParent) {
268 // Because the width of the button might have changed, the parent browser 280 // Because the width of the button might have changed, the parent browser
269 // frame needs to recalculate the button bounds and redraw it. 281 // frame needs to recalculate the button bounds and redraw it.
270 [[BrowserWindowController 282 [[BrowserWindowController
271 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()] 283 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()]
272 layoutSubviews]; 284 layoutSubviews];
273 } 285 }
274 } 286 }
275 287
276 - (void)updateErrorStatus:(BOOL)hasError { 288 - (void)updateErrorStatus:(BOOL)hasError {
289 hasError_ = hasError;
277 [[button_ cell] setHasError:hasError withTitle:[button_ title]]; 290 [[button_ cell] setHasError:hasError withTitle:[button_ title]];
278 [self updateAvatarButtonAndLayoutParent:YES]; 291 [self updateAvatarButtonAndLayoutParent:YES];
279 } 292 }
280 293
281 @end 294 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698