OLD | NEW |
---|---|
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/profiles/profiles_state.h" | 9 #include "chrome/browser/profiles/profiles_state.h" |
10 #include "chrome/browser/themes/theme_service.h" | 10 #include "chrome/browser/themes/theme_service.h" |
11 #include "chrome/browser/themes/theme_service_factory.h" | 11 #include "chrome/browser/themes/theme_service_factory.h" |
12 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
13 #include "chrome/browser/ui/browser_window.h" | 13 #include "chrome/browser/ui/browser_window.h" |
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
15 #include "components/signin/core/browser/signin_error_controller.h" | |
15 #include "grit/generated_resources.h" | 16 #include "grit/generated_resources.h" |
16 #include "grit/theme_resources.h" | 17 #include "grit/theme_resources.h" |
17 #import "ui/base/cocoa/appkit_utils.h" | 18 #import "ui/base/cocoa/appkit_utils.h" |
18 #import "ui/base/cocoa/hover_image_button.h" | 19 #import "ui/base/cocoa/hover_image_button.h" |
19 #include "ui/base/l10n/l10n_util_mac.h" | 20 #include "ui/base/l10n/l10n_util_mac.h" |
20 #include "ui/base/nine_image_painter_factory.h" | 21 #include "ui/base/nine_image_painter_factory.h" |
21 #include "ui/base/resource/resource_bundle.h" | 22 #include "ui/base/resource/resource_bundle.h" |
23 #include "ui/gfx/image/image_skia_operations.h" | |
24 #include "ui/gfx/image/image_skia_util_mac.h" | |
22 #include "ui/gfx/text_elider.h" | 25 #include "ui/gfx/text_elider.h" |
23 | 26 |
24 namespace { | 27 namespace { |
25 | 28 |
26 const CGFloat kButtonPadding = 12; | 29 const CGFloat kButtonPadding = 12; |
27 const CGFloat kButtonDefaultPadding = 5; | 30 const CGFloat kButtonDefaultPadding = 5; |
28 const CGFloat kButtonHeight = 27; | 31 const CGFloat kButtonHeight = 27; |
29 const CGFloat kButtonTitleImageSpacing = 10; | 32 const CGFloat kButtonTitleImageSpacing = 10; |
30 const CGFloat kMaxButtonContentWidth = 100; | 33 const CGFloat kMaxButtonContentWidth = 100; |
34 const CGFloat kAuthErrorIconWidth = 13; | |
35 const CGFloat kAuthErrorIconHeight = 11; | |
31 | 36 |
32 const ui::NinePartImageIds kNormalBorderImageIds = | 37 const ui::NinePartImageIds kNormalBorderImageIds = |
33 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_NORMAL); | 38 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_NORMAL); |
34 const ui::NinePartImageIds kHoverBorderImageIds = | 39 const ui::NinePartImageIds kHoverBorderImageIds = |
35 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_HOVER); | 40 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_HOVER); |
36 const ui::NinePartImageIds kPressedBorderImageIds = | 41 const ui::NinePartImageIds kPressedBorderImageIds = |
37 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_PRESSED); | 42 IMAGE_GRID(IDR_AVATAR_MAC_BUTTON_PRESSED); |
38 const ui::NinePartImageIds kThemedBorderImageIds = | 43 const ui::NinePartImageIds kThemedBorderImageIds = |
39 IMAGE_GRID(IDR_AVATAR_THEMED_MAC_BUTTON_NORMAL); | 44 IMAGE_GRID(IDR_AVATAR_THEMED_MAC_BUTTON_NORMAL); |
40 | 45 |
41 NSImage* GetImageFromResourceID(int resourceId) { | 46 NSImage* GetImageFromResourceID(int resourceId) { |
42 return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( | 47 return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( |
43 resourceId).ToNSImage(); | 48 resourceId).ToNSImage(); |
44 } | 49 } |
45 | 50 |
46 } // namespace | 51 } // namespace |
47 | 52 |
48 // Button cell with a custom border given by a set of nine-patch image grids. | 53 // Button cell with a custom border given by a set of nine-patch image grids. |
49 @interface CustomThemeButtonCell : NSButtonCell { | 54 @interface CustomThemeButtonCell : NSButtonCell { |
50 @private | 55 @private |
51 BOOL isThemedWindow_; | 56 BOOL isThemedWindow_; |
57 BOOL hasError_; | |
58 base::scoped_nsobject<NSImage> authErrorImage_; | |
groby-ooo-7-16
2014/06/10 19:28:14
authenticationErrorImage_, please. (This is Cocoa.
noms (inactive)
2014/06/17 17:07:32
Done.
| |
52 } | 59 } |
53 - (void)setIsThemedWindow:(BOOL)isThemedWindow; | 60 - (void)setIsThemedWindow:(BOOL)isThemedWindow; |
61 - (void)setHasError:(BOOL)hasError; | |
62 | |
54 @end | 63 @end |
55 | 64 |
56 @implementation CustomThemeButtonCell | 65 @implementation CustomThemeButtonCell |
57 - (id)initWithThemedWindow:(BOOL)isThemedWindow { | 66 - (id)initWithThemedWindow:(BOOL)isThemedWindow { |
58 if ((self = [super init])) { | 67 if ((self = [super init])) { |
59 isThemedWindow_ = isThemedWindow; | 68 isThemedWindow_ = isThemedWindow; |
69 hasError_ = NO; | |
70 | |
71 gfx::ImageSkia icon = gfx::ImageSkiaOperations::CreateResizedImage( | |
groby-ooo-7-16
2014/06/10 19:28:14
Why are we resizing a static resource? Can we inst
noms (inactive)
2014/06/11 15:12:46
Mike didn't want me to check in duplicate, just re
msw
2014/06/11 16:42:36
Don't block this CL on that, you can use the dupli
groby-ooo-7-16
2014/06/11 17:19:09
Not a blocking issue for me, either - I'm just sad
msw
2014/06/11 17:33:59
My concern wasn't entirely about binary size, we h
noms (inactive)
2014/06/17 17:07:32
Done.
| |
72 *ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
73 IDR_ICON_PROFILES_ACCOUNT_BUTTON_ERROR).ToImageSkia(), | |
74 skia::ImageOperations::RESIZE_BEST, | |
75 gfx::Size(kAuthErrorIconWidth, kAuthErrorIconHeight)); | |
76 authErrorImage_.reset([gfx::NSImageFromImageSkia(icon) retain]); | |
60 } | 77 } |
61 return self; | 78 return self; |
62 } | 79 } |
63 | 80 |
64 - (NSSize)cellSize { | 81 - (NSSize)cellSize { |
65 NSSize buttonSize = [super cellSize]; | 82 NSSize buttonSize = [super cellSize]; |
66 buttonSize.width += 2 * kButtonPadding - 2 * kButtonDefaultPadding; | 83 CGFloat errorWidth = hasError_ ? kAuthErrorIconWidth : 0; |
84 buttonSize.width += 2 * (kButtonPadding - kButtonDefaultPadding) + errorWidth; | |
67 buttonSize.height = kButtonHeight; | 85 buttonSize.height = kButtonHeight; |
68 return buttonSize; | 86 return buttonSize; |
69 } | 87 } |
70 | 88 |
71 - (NSRect)drawTitle:(NSAttributedString*)title | 89 - (NSRect)drawTitle:(NSAttributedString*)title |
72 withFrame:(NSRect)frame | 90 withFrame:(NSRect)frame |
73 inView:(NSView*)controlView { | 91 inView:(NSView*)controlView { |
74 frame.origin.x = kButtonPadding; | 92 frame.origin.x = kButtonPadding; |
75 // Ensure there's always a padding between the text and the image. | 93 // Ensure there's always a padding between the text and the image. |
76 frame.size.width -= kButtonTitleImageSpacing; | 94 frame.size.width -= hasError_ ? 2 * kButtonTitleImageSpacing : |
95 kButtonTitleImageSpacing; | |
77 return [super drawTitle:title withFrame:frame inView:controlView]; | 96 return [super drawTitle:title withFrame:frame inView:controlView]; |
78 } | 97 } |
79 | 98 |
80 - (void)drawImage:(NSImage*)image | 99 - (void)drawImage:(NSImage*)image |
81 withFrame:(NSRect)frame | 100 withFrame:(NSRect)frame |
82 inView:(NSView*)controlView { | 101 inView:(NSView*)controlView { |
102 // If there's an auth error, draw a warning icon before the cell image. | |
103 if (hasError_) { | |
104 NSSize imageSize = [authErrorImage_ size]; | |
105 NSRect rect = NSMakeRect( | |
106 frame.origin.x - imageSize.width - kButtonTitleImageSpacing, | |
groby-ooo-7-16
2014/06/10 19:28:14
Am I misreading this, or are you planning to draw
noms (inactive)
2014/06/17 17:07:32
Hmm, so the layout of the button is:
[padding][ti
groby-ooo-7-16
2014/06/17 17:16:41
It looked at first glance... let me recheck
| |
107 (kButtonHeight - imageSize.height) / 2, | |
108 imageSize.width, | |
109 imageSize.height); | |
110 [authErrorImage_ drawInRect:rect | |
111 fromRect:NSZeroRect | |
112 operation:NSCompositeSourceOver | |
113 fraction:1.0 | |
114 respectFlipped:YES | |
115 hints:nil]; | |
116 } | |
83 // For the x-offset, we need to undo the default padding and apply the | 117 // For the x-offset, we need to undo the default padding and apply the |
84 // new one. For the y-offset, increasing the button height means we need | 118 // new one. For the y-offset, increasing the button height means we need |
85 // to move the image a little down to align it nicely with the text; this | 119 // to move the image a little down to align it nicely with the text; this |
86 // was chosen by visual inspection. | 120 // was chosen by visual inspection. |
87 frame = NSOffsetRect(frame, kButtonDefaultPadding - kButtonPadding, 2); | 121 frame = NSOffsetRect(frame, kButtonDefaultPadding - kButtonPadding, 2); |
88 [super drawImage:image withFrame:frame inView:controlView]; | 122 [super drawImage:image withFrame:frame inView:controlView]; |
89 } | 123 } |
90 | 124 |
91 - (void)drawBezelWithFrame:(NSRect)frame | 125 - (void)drawBezelWithFrame:(NSRect)frame |
92 inView:(NSView*)controlView { | 126 inView:(NSView*)controlView { |
93 HoverState hoverState = | 127 HoverState hoverState = |
94 [base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState]; | 128 [base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState]; |
95 ui::NinePartImageIds imageIds = kNormalBorderImageIds; | 129 ui::NinePartImageIds imageIds = kNormalBorderImageIds; |
96 if (isThemedWindow_) | 130 if (isThemedWindow_) |
97 imageIds = kThemedBorderImageIds; | 131 imageIds = kThemedBorderImageIds; |
98 | 132 |
99 if (hoverState == kHoverStateMouseDown) | 133 if (hoverState == kHoverStateMouseDown) |
100 imageIds = kPressedBorderImageIds; | 134 imageIds = kPressedBorderImageIds; |
101 else if (hoverState == kHoverStateMouseOver) | 135 else if (hoverState == kHoverStateMouseOver) |
102 imageIds = kHoverBorderImageIds; | 136 imageIds = kHoverBorderImageIds; |
103 ui::DrawNinePartImage(frame, imageIds, NSCompositeSourceOver, 1.0, true); | 137 ui::DrawNinePartImage(frame, imageIds, NSCompositeSourceOver, 1.0, true); |
104 } | 138 } |
105 | 139 |
106 - (void)setIsThemedWindow:(BOOL)isThemedWindow { | 140 - (void)setIsThemedWindow:(BOOL)isThemedWindow { |
107 isThemedWindow_ = isThemedWindow; | 141 isThemedWindow_ = isThemedWindow; |
108 } | 142 } |
143 | |
144 - (void)setHasError:(BOOL)hasError { | |
groby-ooo-7-16
2014/06/10 19:28:14
Assuming that having an error on an account is rar
noms (inactive)
2014/06/17 17:07:32
Done.
| |
145 hasError_ = hasError; | |
146 } | |
147 | |
109 @end | 148 @end |
110 | 149 |
111 @interface AvatarButtonController (Private) | 150 @interface AvatarButtonController (Private) |
112 - (base::string16)getElidedAvatarName; | 151 - (base::string16)getElidedAvatarName; |
113 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent; | 152 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent; |
153 - (void)updateErrorStatus:(BOOL)hasError; | |
114 - (void)dealloc; | 154 - (void)dealloc; |
115 - (void)themeDidChangeNotification:(NSNotification*)aNotification; | 155 - (void)themeDidChangeNotification:(NSNotification*)aNotification; |
116 @end | 156 @end |
117 | 157 |
118 @implementation AvatarButtonController | 158 @implementation AvatarButtonController |
119 | 159 |
120 - (id)initWithBrowser:(Browser*)browser { | 160 - (id)initWithBrowser:(Browser*)browser { |
121 if ((self = [super initWithBrowser:browser])) { | 161 if ((self = [super initWithBrowser:browser])) { |
122 ThemeService* themeService = | 162 ThemeService* themeService = |
123 ThemeServiceFactory::GetForProfile(browser->profile()); | 163 ThemeServiceFactory::GetForProfile(browser->profile()); |
124 isThemedWindow_ = !themeService->UsingSystemTheme(); | 164 isThemedWindow_ = !themeService->UsingSystemTheme(); |
125 | 165 |
126 HoverImageButton* hoverButton = | 166 HoverImageButton* hoverButton = |
127 [[HoverImageButton alloc] initWithFrame:NSZeroRect]; | 167 [[HoverImageButton alloc] initWithFrame:NSZeroRect]; |
128 [hoverButton setDefaultImage:GetImageFromResourceID( | 168 [hoverButton setDefaultImage:GetImageFromResourceID( |
129 IDR_AVATAR_MAC_BUTTON_DROPARROW)]; | 169 IDR_AVATAR_MAC_BUTTON_DROPARROW)]; |
130 [hoverButton setHoverImage:GetImageFromResourceID( | 170 [hoverButton setHoverImage:GetImageFromResourceID( |
131 IDR_AVATAR_MAC_BUTTON_DROPARROW_HOVER)]; | 171 IDR_AVATAR_MAC_BUTTON_DROPARROW_HOVER)]; |
132 [hoverButton setPressedImage:GetImageFromResourceID( | 172 [hoverButton setPressedImage:GetImageFromResourceID( |
133 IDR_AVATAR_MAC_BUTTON_DROPARROW_PRESSED)]; | 173 IDR_AVATAR_MAC_BUTTON_DROPARROW_PRESSED)]; |
134 | 174 |
135 button_.reset(hoverButton); | 175 button_.reset(hoverButton); |
136 base::scoped_nsobject<CustomThemeButtonCell> cell( | 176 base::scoped_nsobject<CustomThemeButtonCell> cell( |
137 [[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]); | 177 [[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]); |
178 SigninErrorController* error = | |
179 profiles::GetSigninErrorController(browser->profile()); | |
180 if (error) | |
181 [cell setHasError:error->HasError()]; | |
groby-ooo-7-16
2014/06/10 19:28:14
I'd actually remove this - it gets rid of the depe
noms (inactive)
2014/06/17 17:07:32
Hmm, but then it adds that dependency to the Brows
| |
138 [button_ setCell:cell.get()]; | 182 [button_ setCell:cell.get()]; |
139 [self setView:button_]; | 183 [self setView:button_]; |
140 | 184 |
141 [button_ setBezelStyle:NSShadowlessSquareBezelStyle]; | 185 [button_ setBezelStyle:NSShadowlessSquareBezelStyle]; |
142 [button_ setButtonType:NSMomentaryChangeButton]; | 186 [button_ setButtonType:NSMomentaryChangeButton]; |
143 [button_ setBordered:YES]; | 187 [button_ setBordered:YES]; |
144 // This is a workaround for an issue in the HoverImageButton where the | 188 // This is a workaround for an issue in the HoverImageButton where the |
145 // button is initially sized incorrectly unless a default image is provided. | 189 // button is initially sized incorrectly unless a default image is provided. |
146 [button_ setImage:GetImageFromResourceID(IDR_AVATAR_MAC_BUTTON_DROPARROW)]; | 190 [button_ setImage:GetImageFromResourceID(IDR_AVATAR_MAC_BUTTON_DROPARROW)]; |
147 [button_ setImagePosition:NSImageRight]; | 191 [button_ setImagePosition:NSImageRight]; |
148 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; | 192 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; |
149 [button_ setTarget:self]; | 193 [button_ setTarget:self]; |
150 [button_ setAction:@selector(buttonClicked:)]; | 194 [button_ setAction:@selector(buttonClicked:)]; |
151 | 195 |
152 [self updateAvatarButtonAndLayoutParent:NO]; | 196 [self updateAvatarButtonAndLayoutParent:NO]; |
153 | 197 |
154 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | 198 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
155 [center addObserver:self | 199 [center addObserver:self |
156 selector:@selector(themeDidChangeNotification:) | 200 selector:@selector(themeDidChangeNotification:) |
157 name:kBrowserThemeDidChangeNotification | 201 name:kBrowserThemeDidChangeNotification |
158 object:nil]; | 202 object:nil]; |
159 | |
160 } | 203 } |
161 return self; | 204 return self; |
162 } | 205 } |
163 | 206 |
164 - (void)dealloc { | 207 - (void)dealloc { |
165 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 208 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
166 [super dealloc]; | 209 [super dealloc]; |
167 } | 210 } |
168 | 211 |
169 - (void)themeDidChangeNotification:(NSNotification*)aNotification { | 212 - (void)themeDidChangeNotification:(NSNotification*)aNotification { |
(...skipping 12 matching lines...) Expand all Loading... | |
182 base::string16 name = profiles::GetAvatarNameForProfile(browser_->profile()); | 225 base::string16 name = profiles::GetAvatarNameForProfile(browser_->profile()); |
183 int maxTextWidth = kMaxButtonContentWidth - [[button_ image] size].width; | 226 int maxTextWidth = kMaxButtonContentWidth - [[button_ image] size].width; |
184 return gfx::ElideText(name, gfx::FontList(gfx::Font([button_ font])), | 227 return gfx::ElideText(name, gfx::FontList(gfx::Font([button_ font])), |
185 maxTextWidth, gfx::ELIDE_TAIL); | 228 maxTextWidth, gfx::ELIDE_TAIL); |
186 } | 229 } |
187 | 230 |
188 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent { | 231 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent { |
189 // The button text has a black foreground and a white drop shadow for regular | 232 // The button text has a black foreground and a white drop shadow for regular |
190 // windows, and a light text with a dark drop shadow for guest windows | 233 // windows, and a light text with a dark drop shadow for guest windows |
191 // which are themed with a dark background. | 234 // which are themed with a dark background. |
192 // TODO(noms): Figure out something similar for themed windows, if possible. | |
193 base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); | 235 base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); |
194 [shadow setShadowOffset:NSMakeSize(0, -1)]; | 236 [shadow setShadowOffset:NSMakeSize(0, -1)]; |
195 [shadow setShadowBlurRadius:0]; | 237 [shadow setShadowBlurRadius:0]; |
196 | 238 |
197 NSColor* foregroundColor; | 239 NSColor* foregroundColor; |
198 if (browser_->profile()->IsGuestSession()) { | 240 if (browser_->profile()->IsGuestSession()) { |
199 foregroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.9]; | 241 foregroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.9]; |
200 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.4]]; | 242 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.4]]; |
201 } else if (!isThemedWindow_) { | 243 } else if (!isThemedWindow_) { |
202 foregroundColor = [NSColor blackColor]; | 244 foregroundColor = [NSColor blackColor]; |
(...skipping 29 matching lines...) Expand all Loading... | |
232 | 274 |
233 if (layoutParent) { | 275 if (layoutParent) { |
234 // Because the width of the button might have changed, the parent browser | 276 // Because the width of the button might have changed, the parent browser |
235 // frame needs to recalculate the button bounds and redraw it. | 277 // frame needs to recalculate the button bounds and redraw it. |
236 [[BrowserWindowController | 278 [[BrowserWindowController |
237 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()] | 279 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()] |
238 layoutSubviews]; | 280 layoutSubviews]; |
239 } | 281 } |
240 } | 282 } |
241 | 283 |
284 - (void)updateErrorStatus:(BOOL)hasError { | |
285 [[button_ cell] setHasError:hasError]; | |
286 [self updateAvatarButtonAndLayoutParent:YES]; | |
287 } | |
288 | |
242 @end | 289 @end |
OLD | NEW |