summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/cocoa
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-30 00:16:07 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-30 00:16:07 +0000
commit38edb9ea6c406b8745935c82ebd57fa9825be6b2 (patch)
tree223afc9e4ac9f4d1afef86f58b06538126cffc9e /chrome/browser/ui/cocoa
parent0de0ad2eaeb50a0cdc37c5ca1c6106c0731e8e54 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/ui/cocoa/base_bubble_controller.h4
-rw-r--r--chrome/browser/ui/cocoa/base_bubble_controller.mm9
-rw-r--r--chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h18
-rw-r--r--chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.mm121
-rw-r--r--chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm90
-rw-r--r--chrome/browser/ui/cocoa/page_info_bubble_controller.mm13
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