summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoranthonyvd <anthonyvd@chromium.org>2015-02-23 11:56:38 -0800
committerCommit bot <commit-bot@chromium.org>2015-02-23 19:57:27 +0000
commit95b89c2672ce7b0ed1a6803b59acee369b232a3c (patch)
tree89098c833b07891b550d8b88a93b23d5d4fa4ec7
parent1e2555db3bd31e72396b24fbf7e9fdb93daf016e (diff)
downloadchromium_src-95b89c2672ce7b0ed1a6803b59acee369b232a3c.zip
chromium_src-95b89c2672ce7b0ed1a6803b59acee369b232a3c.tar.gz
chromium_src-95b89c2672ce7b0ed1a6803b59acee369b232a3c.tar.bz2
Bring up fast user switcher on right-click of the avatar menu on Mac.
Change the behavior of fast user switching on Mac from Command click to right click to be consistent with Windows and Linux. BUG=458755 TEST= 1. Disable #enable-fast-user-switcher and enable #new-avatar-menu flags in chrome://flags 2. Relaunch Chrome 3. Right click on the Avatar Button, the fast user switcher should be shown 4. Command+Click on the Avatar Button, nothing should happen Review URL: https://codereview.chromium.org/916523003 Cr-Commit-Position: refs/heads/master@{#317631}
-rw-r--r--chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm11
-rw-r--r--chrome/browser/ui/cocoa/profiles/avatar_button.h23
-rw-r--r--chrome/browser/ui/cocoa/profiles/avatar_button.mm48
-rw-r--r--chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm33
-rw-r--r--chrome/browser/ui/cocoa/profiles/avatar_button_unittest.mm93
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--ui/events/test/cocoa_test_event_utils.h15
-rw-r--r--ui/events/test/cocoa_test_event_utils.mm19
9 files changed, 220 insertions, 25 deletions
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm b/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm
index 66a424a..00cb1b3 100644
--- a/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/avatar_base_controller.mm
@@ -33,6 +33,7 @@ const CGFloat kMenuXOffsetAdjust = 2.0;
@interface AvatarBaseController (Private)
// Shows the avatar bubble.
- (IBAction)buttonClicked:(id)sender;
+- (IBAction)buttonRightClicked:(id)sender;
- (void)bubbleWillClose:(NSNotification*)notif;
@@ -213,8 +214,14 @@ class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver,
BrowserWindow::AvatarBubbleMode mode =
BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT;
- if ([NSEvent modifierFlags] & NSCommandKeyMask)
- mode = BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH;
+ [self showAvatarBubbleAnchoredAt:button_
+ withMode:mode
+ withServiceType:signin::GAIA_SERVICE_TYPE_NONE];
+}
+
+- (IBAction)buttonRightClicked:(id)sender {
+ BrowserWindow::AvatarBubbleMode mode =
+ BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH;
[self showAvatarBubbleAnchoredAt:button_
withMode:mode
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_button.h b/chrome/browser/ui/cocoa/profiles/avatar_button.h
new file mode 100644
index 0000000..444110d
--- /dev/null
+++ b/chrome/browser/ui/cocoa/profiles/avatar_button.h
@@ -0,0 +1,23 @@
+// Copyright 2015 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.
+
+#ifndef CHROME_BROWSER_UI_COCOA_PROFILES_AVATAR_BUTTON_H_
+#define CHROME_BROWSER_UI_COCOA_PROFILES_AVATAR_BUTTON_H_
+
+#import <Cocoa/Cocoa.h>
+
+#import "ui/base/cocoa/hover_image_button.h"
+
+// A subclass of HoverImageButton that sends a target-action on right click.
+@interface AvatarButton : HoverImageButton {
+ @private
+ SEL rightAction_;
+}
+
+// Sets the action that will be called when this button is right clicked.
+- (void)setRightAction:(SEL)selector;
+
+@end
+
+#endif //CHROME_BROWSER_UI_COCOA_PROFILES_AVATAR_BUTTON_H_
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_button.mm b/chrome/browser/ui/cocoa/profiles/avatar_button.mm
new file mode 100644
index 0000000..a1d5c12
--- /dev/null
+++ b/chrome/browser/ui/cocoa/profiles/avatar_button.mm
@@ -0,0 +1,48 @@
+// Copyright 2015 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/profiles/avatar_button.h"
+
+@interface AvatarButton (Private)
+
+- (void)rightMouseDown:(NSEvent*)event;
+- (void)performRightClick;
+
+@end
+
+@implementation AvatarButton
+
+// Overrides -rightMouseDown and implements a custom mouse tracking loop.
+- (void)rightMouseDown:(NSEvent*)event {
+ NSEvent* nextEvent = event;
+ BOOL mouseInBounds = NO;
+ hoverState_ = kHoverStateMouseDown;
+
+ do {
+ nextEvent = [[self window]
+ nextEventMatchingMask:NSRightMouseDraggedMask |
+ NSRightMouseUpMask];
+
+ mouseInBounds = NSPointInRect(
+ [self convertPoint:[nextEvent locationInWindow] fromView:nil],
+ [self convertRect:[self frame] fromView:nil]);
+ } while (NSRightMouseUp != [nextEvent type]);
+
+ hoverState_ = kHoverStateNone;
+
+ if (mouseInBounds) {
+ hoverState_ = kHoverStateMouseOver;
+ [self performRightClick];
+ }
+}
+
+- (void)performRightClick {
+ [[super target] performSelector:rightAction_ withObject:self];
+}
+
+- (void)setRightAction:(SEL)selector {
+ rightAction_ = selector;
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm b/chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm
index 0ed4d4b..b5df7c7 100644
--- a/chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm
@@ -14,11 +14,11 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
+#import "chrome/browser/ui/cocoa/profiles/avatar_button.h"
#include "chrome/grit/generated_resources.h"
#include "components/signin/core/browser/signin_error_controller.h"
#include "grit/theme_resources.h"
#import "ui/base/cocoa/appkit_utils.h"
-#import "ui/base/cocoa/hover_image_button.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/nine_image_painter_factory.h"
#include "ui/base/resource/resource_bundle.h"
@@ -102,7 +102,7 @@ NSImage* GetImageFromResourceID(int resourceId) {
- (void)drawBezelWithFrame:(NSRect)frame
inView:(NSView*)controlView {
HoverState hoverState =
- [base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState];
+ [base::mac::ObjCCastStrict<AvatarButton>(controlView) hoverState];
ui::NinePartImageIds imageIds = kNormalBorderImageIds;
if (isThemedWindow_)
imageIds = kThemedBorderImageIds;
@@ -146,28 +146,29 @@ NSImage* GetImageFromResourceID(int resourceId) {
ThemeServiceFactory::GetForProfile(browser->profile());
isThemedWindow_ = !themeService->UsingSystemTheme();
- HoverImageButton* hoverButton =
- [[HoverImageButton alloc] initWithFrame:NSZeroRect];
- button_.reset(hoverButton);
+ AvatarButton* avatarButton =
+ [[AvatarButton alloc] initWithFrame:NSZeroRect];
+ button_.reset(avatarButton);
base::scoped_nsobject<CustomThemeButtonCell> cell(
[[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]);
- [button_ setCell:cell.get()];
+ [avatarButton setCell:cell.get()];
// Check if the account already has an authentication error.
SigninErrorController* errorController =
profiles::GetSigninErrorController(browser->profile());
hasError_ = errorController && errorController->HasError();
- [button_ setWantsLayer:YES];
- [self setView:button_];
+ [avatarButton setWantsLayer:YES];
+ [self setView:avatarButton];
- [button_ setBezelStyle:NSShadowlessSquareBezelStyle];
- [button_ setButtonType:NSMomentaryChangeButton];
- [button_ setBordered:YES];
+ [avatarButton setBezelStyle:NSShadowlessSquareBezelStyle];
+ [avatarButton setButtonType:NSMomentaryChangeButton];
+ [avatarButton setBordered:YES];
- [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
- [button_ setTarget:self];
- [button_ setAction:@selector(buttonClicked:)];
+ [avatarButton setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
+ [avatarButton setTarget:self];
+ [avatarButton setAction:@selector(buttonClicked:)];
+ [avatarButton setRightAction:@selector(buttonRightClicked:)];
[self updateAvatarButtonAndLayoutParent:NO];
@@ -232,8 +233,8 @@ NSImage* GetImageFromResourceID(int resourceId) {
profiles::GetAvatarButtonTextForProfile(browser_->profile()));
[[button_ cell] setHasError:hasError_ withTitle:buttonTitle];
- HoverImageButton* button =
- base::mac::ObjCCastStrict<HoverImageButton>(button_);
+ AvatarButton* button =
+ base::mac::ObjCCastStrict<AvatarButton>(button_);
if (useGenericButton) {
[button setDefaultImage:GetImageFromResourceID(
IDR_AVATAR_MAC_BUTTON_AVATAR)];
diff --git a/chrome/browser/ui/cocoa/profiles/avatar_button_unittest.mm b/chrome/browser/ui/cocoa/profiles/avatar_button_unittest.mm
new file mode 100644
index 0000000..cca632f
--- /dev/null
+++ b/chrome/browser/ui/cocoa/profiles/avatar_button_unittest.mm
@@ -0,0 +1,93 @@
+// Copyright 2015 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/profiles/avatar_button.h"
+
+#import "base/mac/scoped_nsobject.h"
+#import "ui/events/test/cocoa_test_event_utils.h"
+#import "ui/gfx/test/ui_cocoa_test_helper.h"
+
+@interface AvatarButton (ExposedForTesting)
+- (void)performRightClick;
+@end
+
+@interface AvatarButtonTestObserver : NSObject
+@property BOOL clicked;
+
+- (void)buttonRightClicked;
+@end
+
+@implementation AvatarButtonTestObserver
+@synthesize clicked = clicked_;
+
+- (void)buttonRightClicked {
+ self.clicked = YES;
+}
+@end
+
+class AvatarButtonTest : public ui::CocoaTest {
+ public:
+ AvatarButtonTest() {
+ NSRect content_frame = [[test_window() contentView] frame];
+ base::scoped_nsobject<AvatarButton> button(
+ [[AvatarButton alloc] initWithFrame:content_frame]);
+ button_ = button.get();
+ [[test_window() contentView] addSubview:button_];
+ }
+
+ AvatarButton* button_;
+};
+
+TEST_F(AvatarButtonTest, RightClick) {
+ base::scoped_nsobject<AvatarButtonTestObserver> observer(
+ [[AvatarButtonTestObserver alloc] init]);
+ [button_ setTarget:observer.get()];
+ [button_ setRightAction:@selector(buttonRightClicked)];
+
+ ASSERT_FALSE(observer.get().clicked);
+
+ [button_ performRightClick];
+ ASSERT_TRUE(observer.get().clicked);
+}
+
+TEST_F(AvatarButtonTest, RightClickInView) {
+ base::scoped_nsobject<AvatarButtonTestObserver> observer(
+ [[AvatarButtonTestObserver alloc] init]);
+ [button_ setTarget:observer.get()];
+ [button_ setRightAction:@selector(buttonRightClicked)];
+
+ ASSERT_FALSE(observer.get().clicked);
+
+ std::pair<NSEvent*, NSEvent*> events =
+ cocoa_test_event_utils::RightMouseClickInView(button_, 1);
+
+ [NSApp postEvent:events.second atStart:YES];
+ [NSApp sendEvent:events.first];
+
+ ASSERT_TRUE(observer.get().clicked);
+}
+
+TEST_F(AvatarButtonTest, RightMouseUpOutOfView) {
+ base::scoped_nsobject<AvatarButtonTestObserver> observer(
+ [[AvatarButtonTestObserver alloc] init]);
+ [button_ setTarget:observer.get()];
+ [button_ setRightAction:@selector(buttonRightClicked)];
+
+ ASSERT_FALSE(observer.get().clicked);
+
+ const NSRect bounds = [button_ convertRect:[button_ bounds] toView:nil];
+ const NSPoint downLocation = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
+ NSEvent* down = cocoa_test_event_utils::MouseEventAtPointInWindow(
+ downLocation, NSRightMouseDown, [button_ window], 1);
+
+ const NSPoint upLocation = NSMakePoint(downLocation.x + bounds.size.width,
+ downLocation.y + bounds.size.height);
+ NSEvent* up = cocoa_test_event_utils::MouseEventAtPointInWindow(
+ upLocation, NSRightMouseUp, [button_ window], 1);
+
+ [NSApp postEvent:up atStart:YES];
+ [NSApp sendEvent:down];
+
+ ASSERT_FALSE(observer.get().clicked);
+}
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index e520c87..01b0b29 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -623,6 +623,8 @@
'browser/ui/cocoa/presentation_mode_controller.mm',
'browser/ui/cocoa/profiles/avatar_base_controller.h',
'browser/ui/cocoa/profiles/avatar_base_controller.mm',
+ 'browser/ui/cocoa/profiles/avatar_button.h',
+ 'browser/ui/cocoa/profiles/avatar_button.mm',
'browser/ui/cocoa/profiles/avatar_button_controller.h',
'browser/ui/cocoa/profiles/avatar_button_controller.mm',
'browser/ui/cocoa/profiles/avatar_icon_controller.h',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 02ce0bd..c697526 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -469,6 +469,7 @@
'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.h',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.mm',
+ 'browser/ui/cocoa/profiles/avatar_button_unittest.mm',
'browser/ui/cocoa/profiles/avatar_button_controller_unittest.mm',
'browser/ui/cocoa/profiles/avatar_icon_controller_unittest.mm',
'browser/ui/cocoa/profiles/avatar_label_button_unittest.mm',
diff --git a/ui/events/test/cocoa_test_event_utils.h b/ui/events/test/cocoa_test_event_utils.h
index 6f6beb0..ea29e4b 100644
--- a/ui/events/test/cocoa_test_event_utils.h
+++ b/ui/events/test/cocoa_test_event_utils.h
@@ -18,19 +18,28 @@ namespace cocoa_test_event_utils {
// basic, flesh out as needed. Points are all in window coordinates;
// where the window is not specified, coordinate system is undefined
// (but will be repeated when the event is queried).
-NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers);
NSEvent* MouseEventAtPoint(NSPoint point, NSEventType type,
NSUInteger modifiers);
-NSEvent* LeftMouseDownAtPoint(NSPoint point);
-NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window);
+NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers);
+NSEvent* MouseEventAtPointInWindow(NSPoint point,
+ NSEventType type,
+ NSWindow* window,
+ NSUInteger clickCount);
NSEvent* RightMouseDownAtPoint(NSPoint point);
NSEvent* RightMouseDownAtPointInWindow(NSPoint point, NSWindow* window);
+NSEvent* LeftMouseDownAtPoint(NSPoint point);
+NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window);
// Return a mouse down and an up event with the given |clickCount| at
// |view|'s midpoint.
std::pair<NSEvent*, NSEvent*> MouseClickInView(NSView* view,
NSUInteger clickCount);
+// Return a right mouse down and an up event with the given |clickCount| at
+// |view|'s midpoint.
+std::pair<NSEvent*, NSEvent*> RightMouseClickInView(NSView* view,
+ NSUInteger clickCount);
+
// Returns a key event with the given character.
NSEvent* KeyEventWithCharacter(unichar c);
diff --git a/ui/events/test/cocoa_test_event_utils.mm b/ui/events/test/cocoa_test_event_utils.mm
index b27cd12..bbc7ac3 100644
--- a/ui/events/test/cocoa_test_event_utils.mm
+++ b/ui/events/test/cocoa_test_event_utils.mm
@@ -72,10 +72,10 @@ NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers) {
return MouseEventAtPoint(NSZeroPoint, type, modifiers);
}
-static NSEvent* MouseEventAtPointInWindow(NSPoint point,
- NSEventType type,
- NSWindow* window,
- NSUInteger clickCount) {
+NSEvent* MouseEventAtPointInWindow(NSPoint point,
+ NSEventType type,
+ NSWindow* window,
+ NSUInteger clickCount) {
return [NSEvent mouseEventWithType:type
location:point
modifierFlags:0
@@ -114,6 +114,17 @@ std::pair<NSEvent*,NSEvent*> MouseClickInView(NSView* view,
return std::make_pair(down, up);
}
+std::pair<NSEvent*, NSEvent*> RightMouseClickInView(NSView* view,
+ NSUInteger clickCount) {
+ const NSRect bounds = [view convertRect:[view bounds] toView:nil];
+ const NSPoint mid_point = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
+ NSEvent* down = MouseEventAtPointInWindow(mid_point, NSRightMouseDown,
+ [view window], clickCount);
+ NSEvent* up = MouseEventAtPointInWindow(mid_point, NSRightMouseUp,
+ [view window], clickCount);
+ return std::make_pair(down, up);
+}
+
NSEvent* KeyEventWithCharacter(unichar c) {
return KeyEventWithKeyCode(0, c, NSKeyDown, 0);
}