Commit 95b89c26 authored by anthonyvd's avatar anthonyvd Committed by Commit bot

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}
parent 1e2555db
...@@ -33,6 +33,7 @@ const CGFloat kMenuXOffsetAdjust = 2.0; ...@@ -33,6 +33,7 @@ const CGFloat kMenuXOffsetAdjust = 2.0;
@interface AvatarBaseController (Private) @interface AvatarBaseController (Private)
// Shows the avatar bubble. // Shows the avatar bubble.
- (IBAction)buttonClicked:(id)sender; - (IBAction)buttonClicked:(id)sender;
- (IBAction)buttonRightClicked:(id)sender;
- (void)bubbleWillClose:(NSNotification*)notif; - (void)bubbleWillClose:(NSNotification*)notif;
...@@ -213,8 +214,14 @@ class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver, ...@@ -213,8 +214,14 @@ class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver,
BrowserWindow::AvatarBubbleMode mode = BrowserWindow::AvatarBubbleMode mode =
BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT; BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT;
if ([NSEvent modifierFlags] & NSCommandKeyMask) [self showAvatarBubbleAnchoredAt:button_
mode = BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH; 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_ [self showAvatarBubbleAnchoredAt:button_
withMode:mode withMode:mode
......
// 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_
// 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
...@@ -14,11 +14,11 @@ ...@@ -14,11 +14,11 @@
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.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 "chrome/grit/generated_resources.h"
#include "components/signin/core/browser/signin_error_controller.h" #include "components/signin/core/browser/signin_error_controller.h"
#include "grit/theme_resources.h" #include "grit/theme_resources.h"
#import "ui/base/cocoa/appkit_utils.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/l10n/l10n_util_mac.h"
#include "ui/base/nine_image_painter_factory.h" #include "ui/base/nine_image_painter_factory.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
...@@ -102,7 +102,7 @@ NSImage* GetImageFromResourceID(int resourceId) { ...@@ -102,7 +102,7 @@ NSImage* GetImageFromResourceID(int resourceId) {
- (void)drawBezelWithFrame:(NSRect)frame - (void)drawBezelWithFrame:(NSRect)frame
inView:(NSView*)controlView { inView:(NSView*)controlView {
HoverState hoverState = HoverState hoverState =
[base::mac::ObjCCastStrict<HoverImageButton>(controlView) hoverState]; [base::mac::ObjCCastStrict<AvatarButton>(controlView) hoverState];
ui::NinePartImageIds imageIds = kNormalBorderImageIds; ui::NinePartImageIds imageIds = kNormalBorderImageIds;
if (isThemedWindow_) if (isThemedWindow_)
imageIds = kThemedBorderImageIds; imageIds = kThemedBorderImageIds;
...@@ -146,28 +146,29 @@ NSImage* GetImageFromResourceID(int resourceId) { ...@@ -146,28 +146,29 @@ NSImage* GetImageFromResourceID(int resourceId) {
ThemeServiceFactory::GetForProfile(browser->profile()); ThemeServiceFactory::GetForProfile(browser->profile());
isThemedWindow_ = !themeService->UsingSystemTheme(); isThemedWindow_ = !themeService->UsingSystemTheme();
HoverImageButton* hoverButton = AvatarButton* avatarButton =
[[HoverImageButton alloc] initWithFrame:NSZeroRect]; [[AvatarButton alloc] initWithFrame:NSZeroRect];
button_.reset(hoverButton); button_.reset(avatarButton);
base::scoped_nsobject<CustomThemeButtonCell> cell( base::scoped_nsobject<CustomThemeButtonCell> cell(
[[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]); [[CustomThemeButtonCell alloc] initWithThemedWindow:isThemedWindow_]);
[button_ setCell:cell.get()]; [avatarButton setCell:cell.get()];
// Check if the account already has an authentication error. // Check if the account already has an authentication error.
SigninErrorController* errorController = SigninErrorController* errorController =
profiles::GetSigninErrorController(browser->profile()); profiles::GetSigninErrorController(browser->profile());
hasError_ = errorController && errorController->HasError(); hasError_ = errorController && errorController->HasError();
[button_ setWantsLayer:YES]; [avatarButton setWantsLayer:YES];
[self setView:button_]; [self setView:avatarButton];
[button_ setBezelStyle:NSShadowlessSquareBezelStyle]; [avatarButton setBezelStyle:NSShadowlessSquareBezelStyle];
[button_ setButtonType:NSMomentaryChangeButton]; [avatarButton setButtonType:NSMomentaryChangeButton];
[button_ setBordered:YES]; [avatarButton setBordered:YES];
[button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; [avatarButton setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
[button_ setTarget:self]; [avatarButton setTarget:self];
[button_ setAction:@selector(buttonClicked:)]; [avatarButton setAction:@selector(buttonClicked:)];
[avatarButton setRightAction:@selector(buttonRightClicked:)];
[self updateAvatarButtonAndLayoutParent:NO]; [self updateAvatarButtonAndLayoutParent:NO];
...@@ -232,8 +233,8 @@ NSImage* GetImageFromResourceID(int resourceId) { ...@@ -232,8 +233,8 @@ NSImage* GetImageFromResourceID(int resourceId) {
profiles::GetAvatarButtonTextForProfile(browser_->profile())); profiles::GetAvatarButtonTextForProfile(browser_->profile()));
[[button_ cell] setHasError:hasError_ withTitle:buttonTitle]; [[button_ cell] setHasError:hasError_ withTitle:buttonTitle];
HoverImageButton* button = AvatarButton* button =
base::mac::ObjCCastStrict<HoverImageButton>(button_); base::mac::ObjCCastStrict<AvatarButton>(button_);
if (useGenericButton) { if (useGenericButton) {
[button setDefaultImage:GetImageFromResourceID( [button setDefaultImage:GetImageFromResourceID(
IDR_AVATAR_MAC_BUTTON_AVATAR)]; IDR_AVATAR_MAC_BUTTON_AVATAR)];
......
// 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);
}
...@@ -623,6 +623,8 @@ ...@@ -623,6 +623,8 @@
'browser/ui/cocoa/presentation_mode_controller.mm', 'browser/ui/cocoa/presentation_mode_controller.mm',
'browser/ui/cocoa/profiles/avatar_base_controller.h', 'browser/ui/cocoa/profiles/avatar_base_controller.h',
'browser/ui/cocoa/profiles/avatar_base_controller.mm', '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.h',
'browser/ui/cocoa/profiles/avatar_button_controller.mm', 'browser/ui/cocoa/profiles/avatar_button_controller.mm',
'browser/ui/cocoa/profiles/avatar_icon_controller.h', 'browser/ui/cocoa/profiles/avatar_icon_controller.h',
......
...@@ -469,6 +469,7 @@ ...@@ -469,6 +469,7 @@
'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm', '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.h',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.mm', '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_button_controller_unittest.mm',
'browser/ui/cocoa/profiles/avatar_icon_controller_unittest.mm', 'browser/ui/cocoa/profiles/avatar_icon_controller_unittest.mm',
'browser/ui/cocoa/profiles/avatar_label_button_unittest.mm', 'browser/ui/cocoa/profiles/avatar_label_button_unittest.mm',
......
...@@ -18,19 +18,28 @@ namespace cocoa_test_event_utils { ...@@ -18,19 +18,28 @@ namespace cocoa_test_event_utils {
// basic, flesh out as needed. Points are all in window coordinates; // basic, flesh out as needed. Points are all in window coordinates;
// where the window is not specified, coordinate system is undefined // where the window is not specified, coordinate system is undefined
// (but will be repeated when the event is queried). // (but will be repeated when the event is queried).
NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers);
NSEvent* MouseEventAtPoint(NSPoint point, NSEventType type, NSEvent* MouseEventAtPoint(NSPoint point, NSEventType type,
NSUInteger modifiers); NSUInteger modifiers);
NSEvent* LeftMouseDownAtPoint(NSPoint point); NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers);
NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window); NSEvent* MouseEventAtPointInWindow(NSPoint point,
NSEventType type,
NSWindow* window,
NSUInteger clickCount);
NSEvent* RightMouseDownAtPoint(NSPoint point); NSEvent* RightMouseDownAtPoint(NSPoint point);
NSEvent* RightMouseDownAtPointInWindow(NSPoint point, NSWindow* window); 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 // Return a mouse down and an up event with the given |clickCount| at
// |view|'s midpoint. // |view|'s midpoint.
std::pair<NSEvent*, NSEvent*> MouseClickInView(NSView* view, std::pair<NSEvent*, NSEvent*> MouseClickInView(NSView* view,
NSUInteger clickCount); 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. // Returns a key event with the given character.
NSEvent* KeyEventWithCharacter(unichar c); NSEvent* KeyEventWithCharacter(unichar c);
......
...@@ -72,10 +72,10 @@ NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers) { ...@@ -72,10 +72,10 @@ NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers) {
return MouseEventAtPoint(NSZeroPoint, type, modifiers); return MouseEventAtPoint(NSZeroPoint, type, modifiers);
} }
static NSEvent* MouseEventAtPointInWindow(NSPoint point, NSEvent* MouseEventAtPointInWindow(NSPoint point,
NSEventType type, NSEventType type,
NSWindow* window, NSWindow* window,
NSUInteger clickCount) { NSUInteger clickCount) {
return [NSEvent mouseEventWithType:type return [NSEvent mouseEventWithType:type
location:point location:point
modifierFlags:0 modifierFlags:0
...@@ -114,6 +114,17 @@ std::pair<NSEvent*,NSEvent*> MouseClickInView(NSView* view, ...@@ -114,6 +114,17 @@ std::pair<NSEvent*,NSEvent*> MouseClickInView(NSView* view,
return std::make_pair(down, up); 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) { NSEvent* KeyEventWithCharacter(unichar c) {
return KeyEventWithKeyCode(0, c, NSKeyDown, 0); return KeyEventWithKeyCode(0, c, NSKeyDown, 0);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment