diff options
author | anthonyvd <anthonyvd@chromium.org> | 2015-02-23 11:56:38 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-23 19:57:27 +0000 |
commit | 95b89c2672ce7b0ed1a6803b59acee369b232a3c (patch) | |
tree | 89098c833b07891b550d8b88a93b23d5d4fa4ec7 | |
parent | 1e2555db3bd31e72396b24fbf7e9fdb93daf016e (diff) | |
download | chromium_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.mm | 11 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/profiles/avatar_button.h | 23 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/profiles/avatar_button.mm | 48 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/profiles/avatar_button_controller.mm | 33 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/profiles/avatar_button_unittest.mm | 93 | ||||
-rw-r--r-- | chrome/chrome_browser_ui.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 1 | ||||
-rw-r--r-- | ui/events/test/cocoa_test_event_utils.h | 15 | ||||
-rw-r--r-- | ui/events/test/cocoa_test_event_utils.mm | 19 |
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); } |