diff options
author | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-30 00:16:07 +0000 |
---|---|---|
committer | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-30 00:16:07 +0000 |
commit | 38edb9ea6c406b8745935c82ebd57fa9825be6b2 (patch) | |
tree | 223afc9e4ac9f4d1afef86f58b06538126cffc9e /chrome/browser/ui/cocoa | |
parent | 0de0ad2eaeb50a0cdc37c5ca1c6106c0731e8e54 (diff) | |
download | chromium_src-38edb9ea6c406b8745935c82ebd57fa9825be6b2.zip chromium_src-38edb9ea6c406b8745935c82ebd57fa9825be6b2.tar.gz chromium_src-38edb9ea6c406b8745935c82ebd57fa9825be6b2.tar.bz2 |
[Mac] Slightly redesigned avatar menu.
XIB Changes:
* Add email field and change the edit button to a hyperlink button that overlaps the email
* Add ChromeUILocalizer object to translate the edit string
* Change username font to 13pt
* Remove the invisible tracking button (SwitchProfileButtonCell) and make the root view AvatarMenuItemView
BUG=93837,94930
TEST=Visual and no more zombie crashes.
Review URL: http://codereview.chromium.org/8083010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@103391 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/cocoa')
6 files changed, 135 insertions, 120 deletions
diff --git a/chrome/browser/ui/cocoa/base_bubble_controller.h b/chrome/browser/ui/cocoa/base_bubble_controller.h index 27d750e..773ec58 100644 --- a/chrome/browser/ui/cocoa/base_bubble_controller.h +++ b/chrome/browser/ui/cocoa/base_bubble_controller.h @@ -64,4 +64,8 @@ class Bridge; parentWindow:(NSWindow*)parentWindow anchoredAt:(NSPoint)anchoredAt; +// Creates an autoreleased separator view with a given frame. The height of the +// frame is ignored. +- (NSBox*)separatorWithFrame:(NSRect)frame; + @end diff --git a/chrome/browser/ui/cocoa/base_bubble_controller.mm b/chrome/browser/ui/cocoa/base_bubble_controller.mm index 808ec8e..69160d7 100644 --- a/chrome/browser/ui/cocoa/base_bubble_controller.mm +++ b/chrome/browser/ui/cocoa/base_bubble_controller.mm @@ -133,6 +133,15 @@ class Bridge : public NotificationObserver { [self updateOriginFromAnchor]; } +- (NSBox*)separatorWithFrame:(NSRect)frame { + frame.size.height = 1.0; + scoped_nsobject<NSBox> spacer([[NSBox alloc] initWithFrame:frame]); + [spacer setBoxType:NSBoxSeparator]; + [spacer setBorderType:NSLineBorder]; + [spacer setAlphaValue:0.2]; + return [spacer.release() autorelease]; +} + - (void)parentWindowWillClose:(NSNotification*)notification { [self close]; } diff --git a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h index d78e434..aa3191f 100644 --- a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h +++ b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h @@ -10,6 +10,7 @@ #include "base/memory/scoped_nsobject.h" #include "base/memory/scoped_ptr.h" #import "chrome/browser/ui/cocoa/base_bubble_controller.h" +#import "chrome/browser/ui/cocoa/tracking_area.h" class AvatarMenuModel; class AvatarMenuModelObserver; @@ -61,13 +62,18 @@ class Browser; __weak NSImageView* iconView_; __weak NSImageView* activeView_; __weak NSTextField* nameField_; - __weak HoverImageButton* editButton_; + // These two views sit on top of each other, and only one is visible at a + // time. The editButton_ is visible when the mouse is over the item and the + // emailField_ is visible otherwise. + __weak NSTextField* emailField_; + __weak NSButton* editButton_; } @property(readonly, nonatomic) size_t modelIndex; @property(assign, nonatomic) IBOutlet NSImageView* iconView; @property(assign, nonatomic) IBOutlet NSImageView* activeView; @property(assign, nonatomic) IBOutlet NSTextField* nameField; -@property(assign, nonatomic) IBOutlet HoverImageButton* editButton; +@property(assign, nonatomic) IBOutlet NSTextField* emailField; +@property(assign, nonatomic) IBOutlet NSButton* editButton; // Designated initializer. - (id)initWithModelIndex:(size_t)modelIndex @@ -89,10 +95,16 @@ class Browser; // view controller for changing highlight style of the item subviews. This is // an invisible button that underlays most of the menu item and is responsible // for performing the switch profile action. -@interface SwitchProfileButtonCell : NSButtonCell { +@interface AvatarMenuItemView : NSView { @private // The controller that manages this. __weak AvatarMenuItemController* viewController_; + + // Used to highlight the background on hover. + ScopedCrTrackingArea trackingArea_; + + // Whether the mouse is inside the bounds of this view. + BOOL mouseInside_; } @property(assign, nonatomic) IBOutlet AvatarMenuItemController* viewController; @end 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 2a21437..f14b522 100644 --- a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm +++ b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm @@ -27,7 +27,6 @@ @interface AvatarMenuBubbleController (Private) - (AvatarMenuModel*)model; - (NSButton*)configureNewUserButton:(CGFloat)yOffset; -- (void)configureEditButton:(HoverImageButton*)button; @end namespace AvatarMenuInternal { @@ -132,7 +131,14 @@ const CGFloat kLabelInset = 49.0; // Since drawing happens bottom-up, start with the "New User" link. NSButton* newButton = [self configureNewUserButton:yOffset]; [contentView addSubview:newButton]; - yOffset += NSHeight([newButton frame]) + (kLinkSpacing - kVerticalSpacing); + 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]); // Loop over the profiles in reverse, constructing the menu items. CGFloat widthAdjust = 0; @@ -154,20 +160,23 @@ const CGFloat kLabelInset = 49.0; 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. - [self configureEditButton:itemView.editButton]; itemView.activeView.image = rb.GetImageNamed(IDR_PROFILE_SELECTED).ToNSImage(); } - // Force a highlight of the default state to get the text colored correctly. - [itemView highlightForEventType:NSLeftMouseUp]; - // Add the item to the content view. [[itemView view] setFrameOrigin:NSMakePoint(0, yOffset)]; [contentView addSubview:[itemView view]]; @@ -177,7 +186,7 @@ const CGFloat kLabelInset = 49.0; [items_ addObject:itemView]; } - yOffset += kVerticalSpacing; + yOffset += kVerticalSpacing * 1.5; // Set the window frame, clamping the width at a sensible max. NSRect frame = [[self window] frame]; @@ -203,23 +212,6 @@ const CGFloat kLabelInset = 49.0; return [newButton.release() autorelease]; } -- (void)configureEditButton:(HoverImageButton*)button { - [button setTrackingEnabled:YES]; - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - [button setDefaultImage:rb.GetImageNamed(IDR_PROFILE_EDIT).ToNSImage()]; - [button setDefaultOpacity:1.0]; - - [button setHoverImage:rb.GetImageNamed(IDR_PROFILE_EDIT_HOVER).ToNSImage()]; - [button setHoverOpacity:1.0]; - - [button setPressedImage: - rb.GetImageNamed(IDR_PROFILE_EDIT_PRESSED).ToNSImage()]; - [button setPressedOpacity:1.0]; - - [[button cell] setHighlightsBy:NSNoCellMask]; -} - - (NSMutableArray*)items { return items_.get(); } @@ -234,6 +226,7 @@ const CGFloat kLabelInset = 49.0; @synthesize iconView = iconView_; @synthesize activeView = activeView_; @synthesize nameField = nameField_; +@synthesize emailField = emailField_; @synthesize editButton = editButton_; - (id)initWithModelIndex:(size_t)modelIndex @@ -247,6 +240,16 @@ const CGFloat kLabelInset = 49.0; return self; } +- (void)dealloc { + static_cast<AvatarMenuItemView*>(self.view).viewController = nil; + [super dealloc]; +} + +- (void)awakeFromNib { + [GTMUILocalizerAndLayoutTweaker sizeToFitView:self.editButton]; + self.editButton.hidden = YES; +} + - (IBAction)switchToProfile:(id)sender { [controller_ switchToProfile:self]; } @@ -257,70 +260,80 @@ const CGFloat kLabelInset = 49.0; - (void)highlightForEventType:(NSEventType)type { BOOL active = !self.activeView.isHidden; - NSColor* color = nil; switch (type) { case NSMouseEntered: - case NSLeftMouseDown: if (active) { - color = [NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1]; - } else { - color = [NSColor colorWithCalibratedRed:0.25 - green:0.25 - blue:0.25 - alpha:1]; + self.editButton.hidden = NO; + self.emailField.hidden = YES; } break; case NSMouseExited: - case NSLeftMouseUp: - if (active) { - color = [NSColor colorWithCalibratedRed:0.12 - green:0.12 - blue:0.12 - alpha:1]; - } else { - color = [NSColor colorWithCalibratedRed:0.5 - green:0.5 - blue:0.5 - alpha:1]; - } + self.editButton.hidden = YES; + self.emailField.hidden = NO; break; default: NOTREACHED(); }; - - DCHECK(color); - self.nameField.textColor = color; } @end // Profile Switch Button /////////////////////////////////////////////////////// -@implementation SwitchProfileButtonCell +@implementation AvatarMenuItemView @synthesize viewController = viewController_; - (void)awakeFromNib { - // Needed to get entered and exited events. - self.showsBorderOnlyWhileMouseInside = YES; + [self updateTrackingAreas]; +} + +- (void)updateTrackingAreas { + if (trackingArea_.get()) + [self removeTrackingArea:trackingArea_.get()]; + + trackingArea_.reset( + [[CrTrackingArea alloc] initWithRect:[self bounds] + options:NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow + proxiedOwner:self + userInfo:nil]); + [self addTrackingArea:trackingArea_.get()]; + + [super updateTrackingAreas]; } - (void)mouseEntered:(id)sender { [viewController_ highlightForEventType:[[NSApp currentEvent] type]]; + mouseInside_ = YES; + [self setNeedsDisplay:YES]; } - (void)mouseExited:(id)sender { [viewController_ highlightForEventType:[[NSApp currentEvent] type]]; + mouseInside_ = NO; + [self setNeedsDisplay:YES]; } -- (void)mouseDown:(id)sender { - [viewController_ highlightForEventType:[[NSApp currentEvent] type]]; +- (void)mouseUp:(id)sender { + [viewController_ switchToProfile:self]; } -- (void)mouseUp:(id)sender { - [viewController_ highlightForEventType:[[NSApp currentEvent] type]]; +- (void)drawRect:(NSRect)dirtyRect { + NSColor* backgroundColor = nil; + if (mouseInside_) { + backgroundColor = [NSColor colorWithCalibratedRed:223.0/255 + green:238.0/255 + blue:246.0/255 + alpha:1.0]; + } else { + backgroundColor = [NSColor whiteColor]; + } + + [backgroundColor set]; + NSRectFill([self bounds]); } @end diff --git a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm index b4b2e27..ba017c2 100644 --- a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm @@ -64,9 +64,9 @@ class AvatarMenuBubbleControllerTest : public CocoaTest { TEST_F(AvatarMenuBubbleControllerTest, InitialLayout) { [controller() showWindow:nil]; - // Two profiles means two item views and the new button. + // Two profiles means two item views and the new button with separator. NSView* contents = [[controller() window] contentView]; - EXPECT_EQ(3U, [[contents subviews] count]); + EXPECT_EQ(4U, [[contents subviews] count]); // Loop over the itmes and match the viewController views to subviews. NSMutableArray* subviews = @@ -81,14 +81,27 @@ TEST_F(AvatarMenuBubbleControllerTest, InitialLayout) { } // The one remaining subview should be the new user button. - EXPECT_EQ(1U, [subviews count]); - - NSButton* newUser = [subviews lastObject]; - ASSERT_TRUE([newUser isKindOfClass:[NSButton class]]); - - EXPECT_EQ(@selector(newProfile:), [newUser action]); - EXPECT_EQ(controller(), [newUser target]); - EXPECT_TRUE([[newUser cell] isKindOfClass:[HyperlinkButtonCell class]]); + EXPECT_EQ(2U, [subviews count]); + + BOOL hasButton = NO; + BOOL hasSeparator = NO; + for (NSView* subview in subviews) { + if ([subview isKindOfClass:[NSButton class]]) { + EXPECT_FALSE(hasButton); + hasButton = YES; + + NSButton* button = static_cast<NSButton*>(subview); + EXPECT_EQ(@selector(newProfile:), [button action]); + EXPECT_EQ(controller(), [button target]); + EXPECT_TRUE([[button cell] isKindOfClass:[HyperlinkButtonCell class]]); + } else if ([subview isKindOfClass:[NSBox class]]) { + EXPECT_FALSE(hasSeparator); + hasSeparator = YES; + } else { + EXPECT_FALSE(subview) << "Unexpected subview: " + << [[subview description] UTF8String]; + } + } [controller() close]; } @@ -97,7 +110,7 @@ TEST_F(AvatarMenuBubbleControllerTest, PerformLayout) { [controller() showWindow:nil]; NSView* contents = [[controller() window] contentView]; - EXPECT_EQ(3U, [[contents subviews] count]); + EXPECT_EQ(4U, [[contents subviews] count]); scoped_nsobject<NSMutableArray> oldItems([[controller() items] copy]); @@ -107,7 +120,7 @@ TEST_F(AvatarMenuBubbleControllerTest, PerformLayout) { // Testing the bridge is not worth the effort... [controller() performLayout]; - EXPECT_EQ(4U, [[contents subviews] count]); + EXPECT_EQ(5U, [[contents subviews] count]); // Make sure that none of the old items exit. NSArray* newItems = [controller() items]; @@ -123,60 +136,29 @@ TEST_F(AvatarMenuBubbleControllerTest, HighlightForEventType) { scoped_nsobject<AvatarMenuItemController> item( [[AvatarMenuItemController alloc] initWithModelIndex:0 menuController:nil]); - NSTextField* field = [item nameField]; // Test non-active states first. [[item activeView] setHidden:YES]; - NSColor* startColor = [NSColor controlTextColor]; - NSColor* upColor = nil; - NSColor* downColor = nil; - NSColor* enterColor = nil; - NSColor* exitColor = nil; - - EXPECT_NSEQ(startColor, field.textColor); - - // The controller does this in |-performLayout|. - [item highlightForEventType:NSLeftMouseUp]; - EXPECT_NSNE(startColor, field.textColor); - startColor = field.textColor; - - [item highlightForEventType:NSLeftMouseDown]; - downColor = field.textColor; + NSView* editButton = [item editButton]; + NSView* emailField = [item emailField]; + // The edit link remains hidden. [item highlightForEventType:NSMouseEntered]; - enterColor = field.textColor; + EXPECT_TRUE(editButton.isHidden); + EXPECT_FALSE(emailField.isHidden); [item highlightForEventType:NSMouseExited]; - exitColor = field.textColor; - - [item highlightForEventType:NSLeftMouseUp]; - upColor = field.textColor; - - // Use transitivity to determine that all colors for each state are correct. - EXPECT_NSEQ(startColor, upColor); - EXPECT_NSEQ(upColor, exitColor); - EXPECT_NSEQ(downColor, enterColor); - EXPECT_NSNE(enterColor, exitColor); + EXPECT_TRUE(editButton.isHidden); + EXPECT_FALSE(emailField.isHidden); // Make the item "active" and re-test. [[item activeView] setHidden:NO]; - [item highlightForEventType:NSLeftMouseUp]; - EXPECT_NSNE(startColor, field.textColor); - upColor = field.textColor; - - [item highlightForEventType:NSLeftMouseDown]; - EXPECT_NSNE(downColor, field.textColor); - downColor = field.textColor; [item highlightForEventType:NSMouseEntered]; - EXPECT_NSNE(enterColor, field.textColor); - enterColor = field.textColor; + EXPECT_FALSE(editButton.isHidden); + EXPECT_TRUE(emailField.isHidden); [item highlightForEventType:NSMouseExited]; - EXPECT_NSNE(exitColor, field.textColor); - exitColor = field.textColor; - - EXPECT_NSEQ(upColor, exitColor); - EXPECT_NSEQ(downColor, enterColor); - EXPECT_NSNE(upColor, downColor); + EXPECT_TRUE(editButton.isHidden); + EXPECT_FALSE(emailField.isHidden); } diff --git a/chrome/browser/ui/cocoa/page_info_bubble_controller.mm b/chrome/browser/ui/cocoa/page_info_bubble_controller.mm index bdb0389..3ae8aac 100644 --- a/chrome/browser/ui/cocoa/page_info_bubble_controller.mm +++ b/chrome/browser/ui/cocoa/page_info_bubble_controller.mm @@ -453,15 +453,10 @@ void ShowPageInfoBubble(gfx::NativeWindow parent, // next offset. - (CGFloat)addSeparatorToSubviews:(NSMutableArray*)subviews atOffset:(CGFloat)offset { - const CGFloat kSpacerHeight = 1.0; - NSRect frame = NSMakeRect(kFramePadding, offset, - kWindowWidth - 2 * kFramePadding, kSpacerHeight); - scoped_nsobject<NSBox> spacer([[NSBox alloc] initWithFrame:frame]); - [spacer setBoxType:NSBoxSeparator]; - [spacer setBorderType:NSLineBorder]; - [spacer setAlphaValue:0.2]; - [subviews addObject:spacer.get()]; - return kVerticalSpacing + kSpacerHeight; + NSBox* spacer = [self separatorWithFrame:NSMakeRect(kFramePadding, offset, + kWindowWidth - 2 * kFramePadding, 0)]; + [subviews addObject:spacer]; + return kVerticalSpacing + NSHeight([spacer frame]); } // Takes in the bubble's height and the parent window, which should be a |