Index: chrome/browser/ui/cocoa/browser/new_avatar_button_controller.mm |
diff --git a/chrome/browser/ui/cocoa/browser/new_avatar_button_controller.mm b/chrome/browser/ui/cocoa/browser/new_avatar_button_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..be1667db75752533780a43938de20734108395ac |
--- /dev/null |
+++ b/chrome/browser/ui/cocoa/browser/new_avatar_button_controller.mm |
@@ -0,0 +1,194 @@ |
+// Copyright 2014 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/browser/new_avatar_button_controller.h" |
+ |
+#include "base/strings/sys_string_conversions.h" |
+#include "chrome/app/chrome_command_ids.h" |
+#include "chrome/browser/chrome_notification_types.h" |
+#include "chrome/browser/profiles/profile_metrics.h" |
+#include "chrome/browser/profiles/profiles_state.h" |
+#include "chrome/browser/ui/browser.h" |
+#include "chrome/browser/ui/browser_commands.h" |
+#include "chrome/browser/ui/browser_window.h" |
+#import "chrome/browser/ui/cocoa/browser/profile_chooser_controller.h" |
+#import "chrome/browser/ui/cocoa/browser_window_controller.h" |
+#include "content/public/browser/notification_service.h" |
+#include "grit/generated_resources.h" |
+#include "grit/theme_resources.h" |
+#include "ui/base/l10n/l10n_util_mac.h" |
+#include "ui/base/resource/resource_bundle.h" |
+#include "ui/gfx/text_elider.h" |
+ |
+namespace { |
+ |
+NSString* GetElidedProfileName(const base::string16& name) { |
+ // Maximum characters the button can be before the text will get elided. |
+ const int kMaxCharactersToDisplay = 15; |
+ |
+ gfx::FontList font_list = ui::ResourceBundle::GetSharedInstance().GetFontList( |
+ ui::ResourceBundle::BaseFont); |
+ return base::SysUTF16ToNSString(gfx::ElideText( |
+ name, |
+ font_list, |
+ font_list.GetExpectedTextWidth(kMaxCharactersToDisplay), |
+ gfx::ELIDE_AT_END)); |
+} |
+ |
+} // namespace |
+ |
+@interface NewAvatarButtonController (Private) |
+// Shows the ProfileMenuController. |
+- (IBAction)buttonClicked:(id)sender; |
+ |
+- (void)bubbleWillClose:(NSNotification*)notif; |
+ |
+// Updates the profile name displayed by the avatar button. If |layoutParent| is |
+// yes, then the BrowserWindowController is notified to relayout the subviews, |
+// as the button needs to be repositioned. |
+- (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent; |
+@end |
+ |
+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
|
+ public: |
+ ProfileInfoUpdateObserverBridge(NewAvatarButtonController* avatarButton) |
+ : avatarButton_(avatarButton) { |
+ registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, |
+ content::NotificationService::AllSources()); |
+ } |
+ |
+ virtual ~ProfileInfoUpdateObserverBridge() { |
+ registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, |
+ content::NotificationService::AllSources()); |
Nico
2014/01/07 23:20:33
Do you need to do this? I thought the point of Not
|
+ } |
+ |
+ // content::NotificationObserver: |
+ virtual void Observe(int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) OVERRIDE { |
+ switch (type) { |
+ case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: |
+ [avatarButton_ updateAvatarButtonAndLayoutParent:YES]; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ } |
+ |
+ private: |
+ content::NotificationRegistrar registrar_; |
+ |
+ NewAvatarButtonController* avatarButton_; // Weak; owns this. |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserverBridge); |
+}; |
+ |
+@implementation NewAvatarButtonController |
+ |
+- (id)initWithBrowser:(Browser*)browser { |
+ if ((self = [super init])) { |
+ browser_ = browser; |
+ profileInfoObserver_.reset(new ProfileInfoUpdateObserverBridge(self)); |
+ |
+ base::scoped_nsobject<NSView> container([[NSView alloc] |
+ initWithFrame:NSZeroRect]); |
+ [self setView:container]; |
+ |
+ button_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); |
+ [button_ setBezelStyle:NSTexturedRoundedBezelStyle]; |
+ [button_ setImage:ui::ResourceBundle::GetSharedInstance(). |
+ GetNativeImageNamed(IDR_APP_DROPARROW).ToNSImage()]; |
+ [button_ setImagePosition:NSImageRight]; |
+ [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; |
+ [button_ setTarget:self]; |
+ [button_ setAction:@selector(buttonClicked:)]; |
+ |
+ [self updateAvatarButtonAndLayoutParent:NO]; |
+ |
+ [[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
|
+ } |
+ return self; |
+} |
+ |
+- (void)dealloc { |
+ [[NSNotificationCenter defaultCenter] |
+ removeObserver:self |
+ name:NSWindowWillCloseNotification |
+ object:[menuController_ window]]; |
+ [super dealloc]; |
+} |
+ |
+- (NSButton*)buttonView { |
+ return button_.get(); |
+} |
+ |
+- (void)showAvatarBubble:(NSView*)anchor { |
+ if (menuController_) |
+ return; |
+ |
+ DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU)); |
+ |
+ NSWindowController* wc = |
+ [browser_->window()->GetNativeWindow() windowController]; |
+ if ([wc isKindOfClass:[BrowserWindowController class]]) { |
+ [static_cast<BrowserWindowController*>(wc) |
+ lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; |
+ } |
+ |
+ NSPoint point = NSMakePoint(NSMidX([anchor bounds]), |
+ NSMaxY([anchor bounds])); |
+ point = [anchor convertPoint:point toView:nil]; |
+ point = [[anchor window] convertBaseToScreen:point]; |
+ |
+ menuController_ = [[ProfileChooserController alloc] initWithBrowser:browser_ |
+ anchoredAt:point]; |
+ [[NSNotificationCenter defaultCenter] |
+ addObserver:self |
+ selector:@selector(bubbleWillClose:) |
+ name:NSWindowWillCloseNotification |
+ object:[menuController_ window]]; |
+ [menuController_ showWindow:self]; |
+ |
+ ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE); |
+} |
+ |
+- (IBAction)buttonClicked:(id)sender { |
+ DCHECK(sender == button_.get()); |
+ [self showAvatarBubble:button_]; |
+} |
+ |
+- (void)bubbleWillClose:(NSNotification*)notif { |
+ NSWindowController* wc = |
+ [browser_->window()->GetNativeWindow() windowController]; |
+ if ([wc isKindOfClass:[BrowserWindowController class]]) { |
+ [static_cast<BrowserWindowController*>(wc) |
+ releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; |
+ } |
+ menuController_ = nil; |
+} |
+ |
+- (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent { |
+ [button_ setTitle:GetElidedProfileName( |
+ profiles::GetActiveProfileDisplayName(browser_))]; |
+ [button_ sizeToFit]; |
+ |
+ // Resize the container. |
+ [[self view] setFrameSize:[button_ frame].size]; |
+ [button_ setFrameOrigin:NSMakePoint(0, 0)]; |
+ |
+ if (layoutParent) { |
+ // Because the width of the button might have changed, the parent browser |
+ // frame needs to recalculate the button bounds and redraw it. |
+ [[BrowserWindowController |
+ browserWindowControllerForWindow:browser_->window()->GetNativeWindow()] |
+ layoutSubviews]; |
+ } |
+} |
+ |
+- (ProfileChooserController*)menuController { |
+ return menuController_; |
+} |
+ |
+@end |