OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #import "chrome/browser/ui/cocoa/browser/new_avatar_button_controller.h" | |
6 | |
7 #include "base/strings/sys_string_conversions.h" | |
8 #include "chrome/app/chrome_command_ids.h" | |
9 #include "chrome/browser/chrome_notification_types.h" | |
10 #include "chrome/browser/profiles/profile_metrics.h" | |
11 #include "chrome/browser/profiles/profiles_state.h" | |
12 #include "chrome/browser/ui/browser.h" | |
13 #include "chrome/browser/ui/browser_commands.h" | |
14 #include "chrome/browser/ui/browser_window.h" | |
15 #import "chrome/browser/ui/cocoa/browser/profile_chooser_controller.h" | |
16 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | |
17 #include "content/public/browser/notification_service.h" | |
18 #include "grit/generated_resources.h" | |
19 #include "grit/theme_resources.h" | |
20 #include "ui/base/l10n/l10n_util_mac.h" | |
21 #include "ui/base/resource/resource_bundle.h" | |
22 #include "ui/gfx/text_elider.h" | |
23 | |
24 namespace { | |
25 | |
26 NSString* GetElidedProfileName(const base::string16& name) { | |
27 // Maximum characters the button can be before the text will get elided. | |
28 const int kMaxCharactersToDisplay = 15; | |
29 | |
30 gfx::FontList font_list = ui::ResourceBundle::GetSharedInstance().GetFontList( | |
31 ui::ResourceBundle::BaseFont); | |
32 return base::SysUTF16ToNSString(gfx::ElideText( | |
33 name, | |
34 font_list, | |
35 font_list.GetExpectedTextWidth(kMaxCharactersToDisplay), | |
36 gfx::ELIDE_AT_END)); | |
37 } | |
38 | |
39 } // namespace | |
40 | |
41 @interface NewAvatarButtonController (Private) | |
42 // Shows the ProfileMenuController. | |
43 - (IBAction)buttonClicked:(id)sender; | |
44 | |
45 - (void)bubbleWillClose:(NSNotification*)notif; | |
46 | |
47 // Updates the profile name displayed by the avatar button. If |layoutParent| is | |
48 // yes, then the BrowserWindowController is notified to relayout the subviews, | |
49 // as the button needs to be repositioned. | |
50 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent; | |
51 @end | |
52 | |
53 class ProfileInfoUpdateObserverBridge : public content::NotificationObserver { | |
Nico
2014/01/07 23:20:33
In general, we try to not use notifications in new
noms (inactive)
2014/01/08 15:05:31
I can also make this an AvatarMenuObserver (like t
| |
54 public: | |
55 ProfileInfoUpdateObserverBridge(NewAvatarButtonController* avatarButton) | |
56 : avatarButton_(avatarButton) { | |
57 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, | |
58 content::NotificationService::AllSources()); | |
59 } | |
60 | |
61 virtual ~ProfileInfoUpdateObserverBridge() { | |
62 registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, | |
63 content::NotificationService::AllSources()); | |
Nico
2014/01/07 23:20:33
Do you need to do this? I thought the point of Not
| |
64 } | |
65 | |
66 // content::NotificationObserver: | |
67 virtual void Observe(int type, | |
68 const content::NotificationSource& source, | |
69 const content::NotificationDetails& details) OVERRIDE { | |
70 switch (type) { | |
71 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: | |
72 [avatarButton_ updateAvatarButtonAndLayoutParent:YES]; | |
73 break; | |
74 default: | |
75 NOTREACHED(); | |
76 break; | |
77 } | |
78 } | |
79 | |
80 private: | |
81 content::NotificationRegistrar registrar_; | |
82 | |
83 NewAvatarButtonController* avatarButton_; // Weak; owns this. | |
84 | |
85 DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserverBridge); | |
86 }; | |
87 | |
88 @implementation NewAvatarButtonController | |
89 | |
90 - (id)initWithBrowser:(Browser*)browser { | |
91 if ((self = [super init])) { | |
92 browser_ = browser; | |
93 profileInfoObserver_.reset(new ProfileInfoUpdateObserverBridge(self)); | |
94 | |
95 base::scoped_nsobject<NSView> container([[NSView alloc] | |
96 initWithFrame:NSZeroRect]); | |
97 [self setView:container]; | |
98 | |
99 button_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); | |
100 [button_ setBezelStyle:NSTexturedRoundedBezelStyle]; | |
101 [button_ setImage:ui::ResourceBundle::GetSharedInstance(). | |
102 GetNativeImageNamed(IDR_APP_DROPARROW).ToNSImage()]; | |
103 [button_ setImagePosition:NSImageRight]; | |
104 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; | |
105 [button_ setTarget:self]; | |
106 [button_ setAction:@selector(buttonClicked:)]; | |
107 | |
108 [self updateAvatarButtonAndLayoutParent:NO]; | |
109 | |
110 [[self view] addSubview:button_]; | |
Nico
2014/01/07 23:20:33
Since this is the only view, can button_ just be t
noms (inactive)
2014/01/08 15:05:31
Not really. The only reason I added the container
| |
111 } | |
112 return self; | |
113 } | |
114 | |
115 - (void)dealloc { | |
116 [[NSNotificationCenter defaultCenter] | |
117 removeObserver:self | |
118 name:NSWindowWillCloseNotification | |
119 object:[menuController_ window]]; | |
120 [super dealloc]; | |
121 } | |
122 | |
123 - (NSButton*)buttonView { | |
124 return button_.get(); | |
125 } | |
126 | |
127 - (void)showAvatarBubble:(NSView*)anchor { | |
128 if (menuController_) | |
129 return; | |
130 | |
131 DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU)); | |
132 | |
133 NSWindowController* wc = | |
134 [browser_->window()->GetNativeWindow() windowController]; | |
135 if ([wc isKindOfClass:[BrowserWindowController class]]) { | |
136 [static_cast<BrowserWindowController*>(wc) | |
137 lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; | |
138 } | |
139 | |
140 NSPoint point = NSMakePoint(NSMidX([anchor bounds]), | |
141 NSMaxY([anchor bounds])); | |
142 point = [anchor convertPoint:point toView:nil]; | |
143 point = [[anchor window] convertBaseToScreen:point]; | |
144 | |
145 menuController_ = [[ProfileChooserController alloc] initWithBrowser:browser_ | |
146 anchoredAt:point]; | |
147 [[NSNotificationCenter defaultCenter] | |
148 addObserver:self | |
149 selector:@selector(bubbleWillClose:) | |
150 name:NSWindowWillCloseNotification | |
151 object:[menuController_ window]]; | |
152 [menuController_ showWindow:self]; | |
153 | |
154 ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE); | |
155 } | |
156 | |
157 - (IBAction)buttonClicked:(id)sender { | |
158 DCHECK(sender == button_.get()); | |
159 [self showAvatarBubble:button_]; | |
160 } | |
161 | |
162 - (void)bubbleWillClose:(NSNotification*)notif { | |
163 NSWindowController* wc = | |
164 [browser_->window()->GetNativeWindow() windowController]; | |
165 if ([wc isKindOfClass:[BrowserWindowController class]]) { | |
166 [static_cast<BrowserWindowController*>(wc) | |
167 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; | |
168 } | |
169 menuController_ = nil; | |
170 } | |
171 | |
172 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent { | |
173 [button_ setTitle:GetElidedProfileName( | |
174 profiles::GetActiveProfileDisplayName(browser_))]; | |
175 [button_ sizeToFit]; | |
176 | |
177 // Resize the container. | |
178 [[self view] setFrameSize:[button_ frame].size]; | |
179 [button_ setFrameOrigin:NSMakePoint(0, 0)]; | |
180 | |
181 if (layoutParent) { | |
182 // Because the width of the button might have changed, the parent browser | |
183 // frame needs to recalculate the button bounds and redraw it. | |
184 [[BrowserWindowController | |
185 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()] | |
186 layoutSubviews]; | |
187 } | |
188 } | |
189 | |
190 - (ProfileChooserController*)menuController { | |
191 return menuController_; | |
192 } | |
193 | |
194 @end | |
OLD | NEW |