Index: chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm |
diff --git a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm |
index 071852409383bbe5f4fef87a5fcb1bd4eb3528dd..28486eee57bf033633367df5058be2a0d4f17951 100644 |
--- a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm |
+++ b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm |
@@ -26,7 +26,17 @@ |
@interface AvatarMenuBubbleController (Private) |
- (AvatarMenuModel*)model; |
-- (NSButton*)configureNewUserButton:(CGFloat)yOffset; |
+- (NSTextView*)configureManagedUserInformation:(CGFloat)yOffset; |
+- (NSButton*)configureNewUserButton:(CGFloat)yOffset |
+ updateWidthAdjust:(CGFloat*)widthAdjust; |
+- (NSButton*)configureSwitchUserButton:(CGFloat)yOffset |
+ updateWidthAdjust:(CGFloat*)widthAdjust; |
+- (AvatarMenuItemController*)initAvatarItem:(int)itemIndex |
+ updateWidthAdjust:(CGFloat*)widthAdjust |
+ setYOffset:(CGFloat)yOffset; |
+- (void)setWindowFrame:(CGFloat)yOffset widthAdjust:(CGFloat)width; |
+- (void)initMenuContents; |
+- (void)initManagedUserContents; |
- (void)keyDown:(NSEvent*)theEvent; |
- (void)moveDown:(id)sender; |
- (void)moveUp:(id)sender; |
@@ -84,6 +94,11 @@ const CGFloat kLabelInset = 49.0; |
model_->EditProfile([sender modelIndex]); |
} |
+- (IBAction)switchProfile:(id)sender { |
+ expanded_ = YES; |
+ [self performLayout]; |
+} |
+ |
// Private ///////////////////////////////////////////////////////////////////// |
- (id)initWithModel:(AvatarMenuModel*)model |
@@ -115,22 +130,77 @@ const CGFloat kLabelInset = 49.0; |
return self; |
} |
-- (void)performLayout { |
+- (AvatarMenuItemController*)initAvatarItem:(int)itemIndex |
+ updateWidthAdjust:(CGFloat*)widthAdjust |
+ setYOffset:(CGFloat)yOffset { |
ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
- NSView* contentView = [[self window] contentView]; |
+ const AvatarMenuModel::Item& item = model_->GetItemAt(itemIndex); |
+ // Create the item view controller. Autorelease it because it will be owned |
+ // by the |items_| array. |
+ AvatarMenuItemController* itemView = |
+ [[[AvatarMenuItemController alloc] initWithModelIndex:item.model_index |
+ menuController:self] autorelease]; |
+ itemView.iconView.image = item.icon.ToNSImage(); |
+ |
+ // Adjust the name field to fit the string. If it overflows, record by how |
+ // much the window needs to grow to accomodate the new size of the field. |
+ NSTextField* nameField = itemView.nameField; |
+ nameField.stringValue = base::SysUTF16ToNSString(item.name); |
+ NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:nameField]; |
+ if (delta.width > 0) |
+ *widthAdjust = std::max(*widthAdjust, delta.width); |
+ |
+ // Repeat for the sync state/email. |
+ NSTextField* emailField = itemView.emailField; |
+ emailField.stringValue = base::SysUTF16ToNSString(item.sync_state); |
+ delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:emailField]; |
+ if (delta.width > 0) |
+ *widthAdjust = std::max(*widthAdjust, delta.width); |
+ |
+ if (!item.active) { |
+ // In the inactive case, hide additional UI. |
+ [itemView.activeView setHidden:YES]; |
+ [itemView.editButton setHidden:YES]; |
+ } else { |
+ // Otherwise, set up the edit button and its three interaction states. |
+ itemView.activeView.image = |
+ rb.GetImageNamed(IDR_PROFILE_SELECTED).ToNSImage(); |
+ } |
- // Reset the array of controllers and remove all the views. |
- items_.reset([[NSMutableArray alloc] init]); |
- [contentView setSubviews:[NSArray array]]; |
+ // Add the item to the content view. |
+ [[itemView view] setFrameOrigin:NSMakePoint(0, yOffset)]; |
+ |
+ // Keep track of the view controller. |
+ [items_ addObject:itemView]; |
+ return itemView; |
+} |
+ |
+- (void)setWindowFrame:(CGFloat)yOffset widthAdjust:(CGFloat)width { |
+ // Set the window frame, clamping the width at a sensible max. |
+ NSRect frame = [[self window] frame]; |
+ // Adjust the origin after we have switched from the managed user menu to the |
+ // regular menu. |
+ if (expanded_) |
+ frame.origin.y += frame.size.height - yOffset; |
+ frame.size.height = yOffset; |
+ frame.size.width = kBubbleMinWidth + width; |
+ frame.size.width = std::min(NSWidth(frame), kBubbleMaxWidth); |
+ [[self window] setFrame:frame display:YES]; |
+} |
+ |
+- (void)initMenuContents { |
+ NSView* contentView = [[self window] contentView]; |
// |yOffset| is the next position at which to draw in contentView coordinates. |
// Use a little more vertical spacing because the items have padding built- |
// into the xib, and this gives a little more space to visually match. |
CGFloat yOffset = kLinkSpacing; |
+ CGFloat widthAdjust = 0; |
if (model_->ShouldShowAddNewProfileLink()) { |
// Since drawing happens bottom-up, start with the "New User" link. |
- NSButton* newButton = [self configureNewUserButton:yOffset]; |
+ NSButton* newButton = |
+ [self configureNewUserButton:yOffset updateWidthAdjust:&widthAdjust]; |
[contentView addSubview:newButton]; |
yOffset += NSHeight([newButton frame]) + kVerticalSpacing; |
@@ -145,65 +215,111 @@ const CGFloat kLabelInset = 49.0; |
} |
// Loop over the profiles in reverse, constructing the menu items. |
- CGFloat widthAdjust = 0; |
for (int i = model_->GetNumberOfItems() - 1; i >= 0; --i) { |
- const AvatarMenuModel::Item& item = model_->GetItemAt(i); |
- |
- // Create the item view controller. Autorelease it because it will be owned |
- // by the |items_| array. |
- AvatarMenuItemController* itemView = |
- [[[AvatarMenuItemController alloc] initWithModelIndex:item.model_index |
- menuController:self] autorelease]; |
- itemView.iconView.image = item.icon.ToNSImage(); |
- |
- // Adjust the name field to fit the string. If it overflows, record by how |
- // much the window needs to grow to accomodate the new size of the field. |
- NSTextField* nameField = itemView.nameField; |
- nameField.stringValue = base::SysUTF16ToNSString(item.name); |
- NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:nameField]; |
- if (delta.width > 0) |
- widthAdjust = std::max(widthAdjust, delta.width); |
- |
- // Repeat for the sync state/email. |
- NSTextField* emailField = itemView.emailField; |
- emailField.stringValue = base::SysUTF16ToNSString(item.sync_state); |
- delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:emailField]; |
- if (delta.width > 0) |
- widthAdjust = std::max(widthAdjust, delta.width); |
- |
- if (!item.active) { |
- // In the inactive case, hide additional UI. |
- [itemView.activeView setHidden:YES]; |
- [itemView.editButton setHidden:YES]; |
- } else { |
- // Otherwise, set up the edit button and its three interaction states. |
- itemView.activeView.image = |
- rb.GetImageNamed(IDR_PROFILE_SELECTED).ToNSImage(); |
- } |
- |
- // Add the item to the content view. |
- [[itemView view] setFrameOrigin:NSMakePoint(0, yOffset)]; |
+ AvatarMenuItemController* itemView = [self initAvatarItem:i |
+ updateWidthAdjust:&widthAdjust |
+ setYOffset:yOffset]; |
[contentView addSubview:[itemView view]]; |
yOffset += NSHeight([[itemView view] frame]); |
- |
- // Keep track of the view controller. |
- [items_ addObject:itemView]; |
} |
yOffset += kVerticalSpacing * 1.5; |
+ [self setWindowFrame:yOffset widthAdjust:widthAdjust]; |
+} |
- // Set the window frame, clamping the width at a sensible max. |
- NSRect frame = [[self window] frame]; |
- frame.size.height = yOffset; |
- frame.size.width = kBubbleMinWidth + widthAdjust; |
- frame.size.width = std::min(NSWidth(frame), kBubbleMaxWidth); |
- [[self window] setFrame:frame display:YES]; |
+- (void)initManagedUserContents { |
+ NSView* contentView = [[self window] contentView]; |
+ |
+ // |yOffset| is the next position at which to draw in contentView coordinates. |
+ // Use a little more vertical spacing because the items have padding built- |
+ // into the xib, and this gives a little more space to visually match. |
+ CGFloat yOffset = kLinkSpacing; |
+ CGFloat widthAdjust = 0; |
+ |
+ // Since drawing happens bottom-up, start with the "Switch User" link. |
+ NSButton* newButton = |
+ [self configureSwitchUserButton:yOffset updateWidthAdjust:&widthAdjust]; |
+ [contentView addSubview:newButton]; |
+ yOffset += NSHeight([newButton frame]) + kVerticalSpacing; |
+ |
+ NSBox* separator = [self separatorWithFrame: |
+ NSMakeRect(10, yOffset, NSWidth([contentView frame]) - 20, 0)]; |
+ [separator setAutoresizingMask:NSViewWidthSizable]; |
+ [contentView addSubview:separator]; |
+ |
+ yOffset += NSHeight([separator frame]) + kVerticalSpacing; |
+ |
+ // First init the active profile in order to determine the required width. We |
+ // will have to adjust its frame later after adding general information about |
+ // managed users. |
+ AvatarMenuItemController* itemView = |
+ [self initAvatarItem:model_->GetActiveProfileIndex() |
+ updateWidthAdjust:&widthAdjust |
+ setYOffset:yOffset]; |
+ |
+ // Don't increase the width too much (the total size should be at most |
+ // |kBubbleMaxWidth|). |
+ widthAdjust = std::min(widthAdjust, kBubbleMaxWidth - kBubbleMinWidth); |
+ CGFloat newWidth = kBubbleMinWidth + widthAdjust; |
+ |
+ // Add general information about managed users. |
+ NSTextView* info = [self configureManagedUserInformation:yOffset |
+ setWidth:newWidth]; |
+ [contentView addSubview:info]; |
+ yOffset += NSHeight([info frame]) + kVerticalSpacing; |
+ |
+ separator = [self separatorWithFrame: |
+ NSMakeRect(10, yOffset, NSWidth([contentView frame]) - 20, 0)]; |
+ [separator setAutoresizingMask:NSViewWidthSizable]; |
+ [contentView addSubview:separator]; |
+ |
+ yOffset += NSHeight([separator frame]); |
+ |
+ // Now update the frame of the active profile and add it. |
+ NSRect frame = [[itemView view] frame]; |
+ frame.origin.y = yOffset; |
+ [[itemView view] setFrame:frame]; |
+ [contentView addSubview:[itemView view]]; |
+ |
+ yOffset += NSHeight(frame) + kVerticalSpacing * 1.5; |
+ [self setWindowFrame:yOffset widthAdjust:widthAdjust]; |
} |
-- (NSButton*)configureNewUserButton:(CGFloat)yOffset { |
+- (void)performLayout { |
+ NSView* contentView = [[self window] contentView]; |
+ |
+ // Reset the array of controllers and remove all the views. |
+ items_.reset([[NSMutableArray alloc] init]); |
+ [contentView setSubviews:[NSArray array]]; |
+ |
+ if (model_->GetManagedUserInformation().empty() || expanded_) |
+ [self initMenuContents]; |
+ else |
+ [self initManagedUserContents]; |
+} |
+ |
+- (NSTextView*)configureManagedUserInformation:(CGFloat)yOffset |
+ setWidth:(CGFloat)width { |
+ NSString* info = |
+ base::SysUTF16ToNSString(model_->GetManagedUserInformation()); |
+ scoped_nsobject<NSAttributedString> attrString( |
+ [[NSAttributedString alloc] initWithString:info attributes:nil]); |
+ NSTextView* label = |
+ [[[NSTextView alloc] initWithFrame: |
+ NSMakeRect(5, yOffset, width - 10, 0)] autorelease]; |
+ [label setFont:[NSFont labelFontOfSize:12.0]]; |
+ [[label textStorage] setAttributedString:attrString]; |
+ [label setHorizontallyResizable:NO]; |
+ [label setEditable:NO]; |
+ [label sizeToFit]; |
+ return label; |
+} |
+ |
+- (NSButton*)configureNewUserButton:(CGFloat)yOffset |
+ updateWidthAdjust:(CGFloat*)widthAdjust { |
scoped_nsobject<NSButton> newButton( |
- [[NSButton alloc] initWithFrame:NSMakeRect(kLabelInset, yOffset, |
- 90, 16)]); |
+ [[NSButton alloc] initWithFrame:NSMakeRect( |
+ kLabelInset, yOffset, kBubbleMinWidth - kLabelInset, 16)]); |
scoped_nsobject<HyperlinkButtonCell> buttonCell( |
[[HyperlinkButtonCell alloc] initTextCell: |
l10n_util::GetNSString(IDS_PROFILES_CREATE_NEW_PROFILE_LINK)]); |
@@ -212,8 +328,29 @@ const CGFloat kLabelInset = 49.0; |
[newButton setBezelStyle:NSRegularSquareBezelStyle]; |
[newButton setTarget:self]; |
[newButton setAction:@selector(newProfile:)]; |
- [GTMUILocalizerAndLayoutTweaker sizeToFitView:newButton]; |
- return [newButton.release() autorelease]; |
+ NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:newButton]; |
+ if (delta.width > 0) |
+ *widthAdjust = std::max(*widthAdjust, delta.width); |
+ return newButton.autorelease(); |
+} |
+ |
+- (NSButton*)configureSwitchUserButton:(CGFloat)yOffset |
+ updateWidthAdjust:(CGFloat*)widthAdjust { |
+ scoped_nsobject<NSButton> newButton( |
+ [[NSButton alloc] initWithFrame:NSMakeRect( |
+ kLabelInset, yOffset, kBubbleMinWidth - kLabelInset, 16)]); |
+ scoped_nsobject<HyperlinkButtonCell> buttonCell( |
+ [[HyperlinkButtonCell alloc] initTextCell: |
+ l10n_util::GetNSString(IDS_PROFILES_SWITCH_PROFILE_LINK)]); |
+ [newButton setCell:buttonCell.get()]; |
+ [newButton setFont:[NSFont labelFontOfSize:12.0]]; |
+ [newButton setBezelStyle:NSRegularSquareBezelStyle]; |
+ [newButton setTarget:self]; |
+ [newButton setAction:@selector(switchProfile:)]; |
+ NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:newButton]; |
+ if (delta.width > 0) |
+ *widthAdjust = std::max(*widthAdjust, delta.width); |
+ return newButton.autorelease(); |
} |
- (NSMutableArray*)items { |