#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/avatar_menu.h"
#include "chrome/browser/profiles/avatar_menu_observer.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/signin/signin_header_helper.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/chrome_style.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
#import "chrome/browser/ui/cocoa/info_bubble_view.h"
#import "chrome/browser/ui/cocoa/info_bubble_window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/text_elider.h"
+#include "ui/native_theme/common_theme.h"
#include "ui/native_theme/native_theme.h"
namespace {
// Fixed size for embedded sign in pages as defined in Gaia.
const CGFloat kFixedGaiaViewWidth = 360;
-const CGFloat kFixedGaiaViewHeight = 400;
+const CGFloat kFixedGaiaViewHeight = 440;
// Fixed size for the account removal view.
const CGFloat kFixedAccountRemovalViewWidth = 280;
-// Fixed size for the end-preview view.
-const int kFixedEndPreviewViewWidth = 280;
-// Maximum number of times to show the welcome tutorial in the profile avatar
-// bubble.
-const int kProfileAvatarTutorialShowMax = 1;
+// Fixed size for the switch user view.
+const int kFixedSwitchUserViewWidth = 280;
// The tag number for the primary account.
const int kPrimaryProfileTag = -1;
return base::SysUTF16ToNSString(elidedEmail);
}
+NSString* ElideMessage(const base::string16& message, CGFloat width) {
+ return base::SysUTF16ToNSString(
+ gfx::ElideText(message, gfx::FontList(), width, gfx::ELIDE_TAIL));
+}
+
// Builds a label with the given |title| anchored at |frame_origin|. Sets the
-// text color and background color to the given colors if not null.
+// text color to |text_color| if not null.
NSTextField* BuildLabel(NSString* title,
NSPoint frame_origin,
- NSColor* background_color,
NSColor* text_color) {
base::scoped_nsobject<NSTextField> label(
[[NSTextField alloc] initWithFrame:NSZeroRect]);
[label setAlignment:NSLeftTextAlignment];
[label setBezeled:NO];
[label setFont:[NSFont labelFontOfSize:kTextFontSize]];
+ [label setDrawsBackground:NO];
[label setFrameOrigin:frame_origin];
[label sizeToFit];
- if (background_color)
- [[label cell] setBackgroundColor:background_color];
if (text_color)
[[label cell] setTextColor:text_color];
// Builds a title card with one back button right aligned and one label center
// aligned.
NSView* BuildTitleCard(NSRect frame_rect,
- int message_id,
+ const base::string16& message,
id back_button_target,
SEL back_button_action) {
base::scoped_nsobject<NSView> container(
[button setFrameSize:NSMakeSize(kProfileButtonHeight, kProfileButtonHeight)];
[button setFrameOrigin:NSMakePoint(kHorizontalSpacing, 0)];
- NSTextField* title_label =
- BuildLabel(l10n_util::GetNSString(message_id), NSZeroPoint,
- GetDialogBackgroundColor(), nil /* text_color */);
+ CGFloat max_label_width = frame_rect.size.width -
+ (kHorizontalSpacing * 2 + kProfileButtonHeight) * 2;
+ NSTextField* title_label = BuildLabel(
+ ElideMessage(message, max_label_width),
+ NSZeroPoint, nil);
[title_label setAlignment:NSCenterTextAlignment];
[title_label setFont:[NSFont labelFontOfSize:kTitleFontSize]];
[title_label sizeToFit];
// update.
profiles::BubbleViewMode viewMode = [controller_ viewMode];
if (viewMode == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ||
- viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN ||
viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT ||
viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) {
[controller_ initMenuContentsWithView:
- profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
+ switches::IsEnableAccountConsistency() ?
+ profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT :
+ profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
}
}
// AvatarMenuObserver:
virtual void OnAvatarMenuChanged(AvatarMenu* avatar_menu) OVERRIDE {
+ // Do not refresh the avatar menu if the user is on a signin related view.
+ profiles::BubbleViewMode viewMode = [controller_ viewMode];
+ if (viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN ||
+ viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT ||
+ viewMode == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) {
+ return;
+ }
+
// While the bubble is open, the avatar menu can only change from the
// profile chooser view by modifying the current profile's photo or name.
[controller_
// Hide the button until the image is hovered over.
[changePhotoButton_ setHidden:YES];
}
+
+ // Set the image cell's accessibility strings to be the same as the
+ // button's strings.
+ [[self cell] accessibilitySetOverrideValue:l10n_util::GetNSString(
+ editingAllowed ?
+ IDS_PROFILES_NEW_AVATAR_MENU_CHANGE_PHOTO_ACCESSIBLE_NAME :
+ IDS_PROFILES_NEW_AVATAR_MENU_PHOTO_ACCESSIBLE_NAME)
+ forAttribute:NSAccessibilityTitleAttribute];
+ [[self cell] accessibilitySetOverrideValue:
+ editingAllowed ? NSAccessibilityButtonRole : NSAccessibilityImageRole
+ forAttribute:NSAccessibilityRoleAttribute];
+ [[self cell] accessibilitySetOverrideValue:
+ NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil)
+ forAttribute:NSAccessibilityRoleDescriptionAttribute];
+
+ // The button and the cell should read the same thing.
+ [self accessibilitySetOverrideValue:l10n_util::GetNSString(
+ editingAllowed ?
+ IDS_PROFILES_NEW_AVATAR_MENU_CHANGE_PHOTO_ACCESSIBLE_NAME :
+ IDS_PROFILES_NEW_AVATAR_MENU_PHOTO_ACCESSIBLE_NAME)
+ forAttribute:NSAccessibilityTitleAttribute];
+ [self accessibilitySetOverrideValue:NSAccessibilityButtonRole
+ forAttribute:NSAccessibilityRoleAttribute];
+ [self accessibilitySetOverrideValue:
+ NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil)
+ forAttribute:NSAccessibilityRoleDescriptionAttribute];
}
return self;
}
[changePhotoButton_ setHidden:YES];
}
+// Make sure the element is focusable for accessibility.
+- (BOOL)canBecomeKeyView {
+ return YES;
+}
+
+- (BOOL)accessibilityIsIgnored {
+ return NO;
+}
+
+- (NSArray*)accessibilityActionNames {
+ NSArray* parentActions = [super accessibilityActionNames];
+ return [parentActions arrayByAddingObject:NSAccessibilityPressAction];
+}
+
+- (void)accessibilityPerformAction:(NSString*)action {
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ avatarMenu_->EditProfile(avatarMenu_->GetActiveProfileIndex());
+ }
+
+ [super accessibilityPerformAction:action];
+}
+
- (TransparentBackgroundButton*)changePhotoButtonWithRect:(NSRect)rect {
TransparentBackgroundButton* button =
[[TransparentBackgroundButton alloc] initWithFrame:rect];
[profileNameTextField_ setHidden:YES];
}
+ [[self cell] accessibilitySetOverrideValue:NSAccessibilityButtonRole
+ forAttribute:NSAccessibilityRoleAttribute];
+ [[self cell] accessibilitySetOverrideValue:
+ NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil)
+ forAttribute:NSAccessibilityRoleDescriptionAttribute];
+
[self setBordered:NO];
[self setFont:[NSFont labelFontOfSize:kTitleFontSize]];
[self setAlignment:NSCenterTextAlignment];
}
- (void)saveProfileName:(id)sender {
- NSString* text = [profileNameTextField_ stringValue];
+ base::string16 newProfileName =
+ base::SysNSStringToUTF16([profileNameTextField_ stringValue]);
+
// Empty profile names are not allowed, and are treated as a cancel.
- if ([text length] > 0) {
- profiles::UpdateProfileName(profile_, base::SysNSStringToUTF16(text));
+ base::TrimWhitespace(newProfileName, base::TRIM_ALL, &newProfileName);
+ if (!newProfileName.empty()) {
+ profiles::UpdateProfileName(profile_, newProfileName);
[controller_
postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME];
- [self setTitle:text];
+ [self setTitle:base::SysUTF16ToNSString(newProfileName)];
+ } else {
+ // Since the text is empty and not allowed, revert it from the textbox.
+ [profileNameTextField_ setStringValue:[self title]];
}
[profileNameTextField_ setHidden:YES];
}
backgroundColor:(NSColor*)backgroundColor {
if ((self = [super initWithFrame:frameRect])) {
backgroundColor_.reset([backgroundColor retain]);
- hoverColor_.reset([gfx::SkColorToCalibratedNSColor(
- ui::NativeTheme::instance()->GetSystemColor(
- ui::NativeTheme::kColorId_ButtonHoverBackgroundColor)) retain]);
+ // Use a color from the common theme, since this button is not trying to
+ // look like a native control.
+ SkColor hoverColor;
+ bool found = ui::CommonThemeGetSystemColor(
+ ui::NativeTheme::kColorId_ButtonHoverBackgroundColor, &hoverColor);
+ DCHECK(found);
+ hoverColor_.reset([gfx::SkColorToSRGBNSColor(hoverColor) retain]);
[self setBordered:NO];
[self setFont:[NSFont labelFontOfSize:kTextFontSize]];
// Builds the profile chooser view.
- (NSView*)buildProfileChooserView;
-// Builds a tutorial card with a title label using |titleMessageId|, a content
-// label using |contentMessageId|, and a bottom row with a right-aligned link
-// using |linkMessageId|, and a left aligned button using |buttonMessageId|.
-// On click, the link would execute |linkAction|, and the button would execute
-// |buttonAction|. It sets |tutorialMode_| to the given |mode|.
+// Builds a tutorial card with a title label using |titleMessage|, a content
+// label using |contentMessage|, a link using |linkMessage|, and a button using
+// |buttonMessage|. If |stackButton| is YES, places the button above the link.
+// Otherwise places both on the same row with the link left aligned and button
+// right aligned. On click, the link would execute |linkAction|, and the button
+// would execute |buttonAction|. It sets |tutorialMode_| to the given |mode|.
- (NSView*)tutorialViewWithMode:(profiles::TutorialMode)mode
- titleMessage:(int)titleMessageId
- contentMessage:(int)contentMessageId
- linkMessage:(int)linkMessageId
- buttonMessage:(int)buttonMessageId
+ titleMessage:(NSString*)titleMessage
+ contentMessage:(NSString*)contentMessage
+ linkMessage:(NSString*)linkMessage
+ buttonMessage:(NSString*)buttonMessage
+ stackButton:(BOOL)stackButton
+ hasCloseButton:(BOOL)hasCloseButton
linkAction:(SEL)linkAction
buttonAction:(SEL)buttonAction;
-// Builds a a tutorial card for new profile management preview if needed. if
-// new profile management is not enabled yet, then it prompts the user to try
-// out the feature. Otherwise, it notifies the user that the feature has been
-// enabled if needed.
-- (NSView*)buildPreviewTutorialIfNeeded:(const AvatarMenu::Item&)item;
+// Builds a tutorial card to introduce an upgrade user to the new avatar menu if
+// needed. |tutorial_shown| indicates if the tutorial has already been shown in
+// the previous active view. |avatar_item| refers to the current profile.
+- (NSView*)buildWelcomeUpgradeTutorialViewIfNeeded;
+
+// Builds a tutorial card to have the user confirm the last Chrome signin,
+// Chrome sync will be delayed until the user either dismisses the tutorial, or
+// configures sync through the "Settings" link.
+- (NSView*)buildSigninConfirmationView;
+
+// Builds a tutorial card to show the last signin error.
+- (NSView*)buildSigninErrorView;
// Creates the main profile card for the profile |item| at the top of
// the bubble.
// Creates the account removal view.
- (NSView*)buildAccountRemovalView;
-// Creates the end-preview view.
-- (NSView*)buildEndPreviewView;
+// Create a view that shows various options for an upgrade user who is not
+// the same person as the currently signed in user.
+- (NSView*)buildSwitchUserView;
// Creates a button with |text|, an icon given by |imageResourceId| and with
-// |action|. The icon |alternateImageResourceId| is displayed in the button's
-// hovered and pressed states.
+// |action|.
- (NSButton*)hoverButtonWithRect:(NSRect)rect
text:(NSString*)text
imageResourceId:(int)imageResourceId
- alternateImageResourceId:(int)alternateImageResourceId
action:(SEL)action;
// Creates a generic link button with |title| and an |action| positioned at
title:(const std::string&)title
tag:(int)tag
reauthRequired:(BOOL)reauthRequired;
+
+- (bool)shouldShowGoIncognito;
@end
@implementation ProfileChooserController
return viewMode_;
}
+- (void)setTutorialMode:(profiles::TutorialMode)tutorialMode {
+ tutorialMode_ = tutorialMode;
+}
+
- (IBAction)switchToProfile:(id)sender {
// Check the event flags to see if a new window should be created.
bool alwaysCreate = ui::WindowOpenDispositionFromNSEvent(
}
- (IBAction)showUserManager:(id)sender {
- profiles::ShowUserManagerMaybeWithTutorial(browser_->profile());
+ chrome::ShowUserManager(browser_->profile()->GetPath());
+ [self postActionPerformed:
+ ProfileMetrics::PROFILE_DESKTOP_MENU_OPEN_USER_MANAGER];
}
- (IBAction)exitGuest:(id)sender {
DCHECK(browser_->profile()->IsGuestSession());
- [self showUserManager:sender];
+ chrome::ShowUserManager(base::FilePath());
profiles::CloseGuestProfileWindows();
}
+- (IBAction)goIncognito:(id)sender {
+ DCHECK([self shouldShowGoIncognito]);
+ chrome::NewIncognitoWindow(browser_);
+}
+
- (IBAction)showAccountManagement:(id)sender {
[self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
}
[self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN];
}
-- (IBAction)showTabbedSigninPage:(id)sender {
- chrome::ShowBrowserSignin(browser_, signin::SOURCE_MENU);
-}
-
- (IBAction)addAccount:(id)sender {
[self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT];
[self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT];
- (IBAction)navigateBackFromSigninPage:(id)sender {
std::string primaryAccount = SigninManagerFactory::GetForProfile(
browser_->profile())->GetAuthenticatedUsername();
- [self initMenuContentsWithView:primaryAccount.empty() ?
- profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER :
- profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
+ bool hasAccountManagement = !primaryAccount.empty() &&
+ switches::IsEnableAccountConsistency();
+ [self initMenuContentsWithView:hasAccountManagement ?
+ profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT :
+ profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
}
- (IBAction)showAccountRemovalView:(id)sender {
[self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
}
-- (IBAction)openTutorialLearnMoreURL:(id)sender {
- ProfileMetrics::LogProfileUpgradeEnrollment(
- ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE);
- // TODO(guohui): update |learnMoreUrl| once it is decided.
- const GURL learnMoreUrl("https://support.google.com/chrome/?hl=en#to");
- chrome::NavigateParams params(browser_->profile(), learnMoreUrl,
- content::PAGE_TRANSITION_LINK);
- params.disposition = NEW_FOREGROUND_TAB;
- chrome::Navigate(¶ms);
+- (IBAction)seeWhatsNew:(id)sender {
+ chrome::ShowUserManagerWithTutorial(
+ profiles::USER_MANAGER_TUTORIAL_OVERVIEW);
+ ProfileMetrics::LogProfileNewAvatarMenuUpgrade(
+ ProfileMetrics::PROFILE_AVATAR_MENU_UPGRADE_WHATS_NEW);
}
-- (IBAction)enableNewProfileManagementPreview:(id)sender {
- ProfileMetrics::LogProfileUpgradeEnrollment(
- ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT);
- profiles::EnableNewProfileManagementPreview(browser_->profile());
+- (IBAction)showSwitchUserView:(id)sender {
+ [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_SWITCH_USER];
+ ProfileMetrics::LogProfileNewAvatarMenuUpgrade(
+ ProfileMetrics::PROFILE_AVATAR_MENU_UPGRADE_NOT_YOU);
}
-- (IBAction)dismissTutorial:(id)sender {
- ProfileMetrics::LogProfileUpgradeEnrollment(
- ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD);
- // If the user manually dismissed the tutorial, never show it again by setting
- // the number of times shown to the maximum plus 1, so that later we could
- // distinguish between the dismiss case and the case when the tutorial is
- // indeed shown for the maximum number of times.
- browser_->profile()->GetPrefs()->SetInteger(
- prefs::kProfileAvatarTutorialShown, kProfileAvatarTutorialShowMax + 1);
- [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
+- (IBAction)showLearnMorePage:(id)sender {
+ signin_ui_util::ShowSigninErrorLearnMorePage(browser_->profile());
}
-- (IBAction)showSendFeedbackTutorial:(id)sender {
- ProfileMetrics::LogProfileUpgradeEnrollment(
- ProfileMetrics::PROFILE_ENROLLMENT_SEND_FEEDBACK);
- tutorialMode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK;
+- (IBAction)configureSyncSettings:(id)sender {
+ tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
+ LoginUIServiceFactory::GetForProfile(browser_->profile())->
+ SyncConfirmationUIClosed(true);
+ ProfileMetrics::LogProfileNewAvatarMenuSignin(
+ ProfileMetrics::PROFILE_AVATAR_MENU_SIGNIN_SETTINGS);
+}
+
+- (IBAction)syncSettingsConfirmed:(id)sender {
+ tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
+ LoginUIServiceFactory::GetForProfile(browser_->profile())->
+ SyncConfirmationUIClosed(false);
+ ProfileMetrics::LogProfileNewAvatarMenuSignin(
+ ProfileMetrics::PROFILE_AVATAR_MENU_SIGNIN_OK);
[self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
}
-- (IBAction)showEndPreviewView:(id)sender {
- [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_END_PREVIEW];
+- (IBAction)disconnectProfile:(id)sender {
+ chrome::ShowSettings(browser_);
+ ProfileMetrics::LogProfileNewAvatarMenuNotYou(
+ ProfileMetrics::PROFILE_AVATAR_MENU_NOT_YOU_DISCONNECT);
+}
+
+- (IBAction)navigateBackFromSwitchUserView:(id)sender {
+ [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
+ ProfileMetrics::LogProfileNewAvatarMenuNotYou(
+ ProfileMetrics::PROFILE_AVATAR_MENU_NOT_YOU_BACK);
}
-- (IBAction)sendFeedback:(id)sender {
- chrome::OpenFeedbackDialog(browser_);
+- (IBAction)dismissTutorial:(id)sender {
+ // Never shows the upgrade tutorial again if manually closed.
+ if (tutorialMode_ == profiles::TUTORIAL_MODE_WELCOME_UPGRADE) {
+ browser_->profile()->GetPrefs()->SetInteger(
+ prefs::kProfileAvatarTutorialShown,
+ signin_ui_util::kUpgradeWelcomeTutorialShowMax + 1);
+ }
+
+ tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
+ [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
}
-- (IBAction)endPreviewAndRelaunch:(id)sender {
- profiles::DisableNewProfileManagementPreview(browser_->profile());
+- (void)windowWillClose:(NSNotification*)notification {
+ if (tutorialMode_ == profiles::TUTORIAL_MODE_CONFIRM_SIGNIN) {
+ LoginUIServiceFactory::GetForProfile(browser_->profile())->
+ SyncConfirmationUIClosed(false);
+ }
+
+ [super windowWillClose:notification];
}
- (void)cleanUpEmbeddedViewContents {
- (id)initWithBrowser:(Browser*)browser
anchoredAt:(NSPoint)point
- withMode:(profiles::BubbleViewMode)mode
- withServiceType:(signin::GAIAServiceType)serviceType {
+ viewMode:(profiles::BubbleViewMode)viewMode
+ tutorialMode:(profiles::TutorialMode)tutorialMode
+ serviceType:(signin::GAIAServiceType)serviceType {
base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc]
initWithContentRect:ui::kWindowSizeDeterminedLater
styleMask:NSBorderlessWindowMask
parentWindow:browser->window()->GetNativeWindow()
anchoredAt:point])) {
browser_ = browser;
- viewMode_ = mode;
- tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
+ viewMode_ = viewMode;
+ tutorialMode_ = tutorialMode;
observer_.reset(new ActiveProfileObserverBridge(self, browser_));
serviceType_ = serviceType;
// If view mode is PROFILE_CHOOSER but there is an auth error, force
// ACCOUNT_MANAGEMENT mode.
if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER &&
- HasAuthError(browser_->profile())) {
+ HasAuthError(browser_->profile()) &&
+ switches::IsEnableAccountConsistency() &&
+ avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex()).
+ signed_in) {
viewMode_ = profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT;
}
+ [window accessibilitySetOverrideValue:
+ l10n_util::GetNSString(IDS_PROFILES_NEW_AVATAR_MENU_ACCESSIBLE_NAME)
+ forAttribute:NSAccessibilityTitleAttribute];
+ [window accessibilitySetOverrideValue:
+ l10n_util::GetNSString(IDS_PROFILES_NEW_AVATAR_MENU_ACCESSIBLE_NAME)
+ forAttribute:NSAccessibilityHelpAttribute];
+
[[self bubble] setAlignment:info_bubble::kAlignRightEdgeToAnchorEdge];
[[self bubble] setArrowLocation:info_bubble::kNoArrow];
[[self bubble] setBackgroundColor:GetDialogBackgroundColor()];
}
- (void)initMenuContentsWithView:(profiles::BubbleViewMode)viewToDisplay {
+ if (browser_->profile()->IsSupervised() &&
+ (viewToDisplay == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT ||
+ viewToDisplay == profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL)) {
+ LOG(WARNING) << "Supervised user attempted to add/remove account";
+ return;
+ }
viewMode_ = viewToDisplay;
NSView* contentView = [[self window] contentView];
[contentView setSubviews:[NSArray array]];
case profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL:
subView = [self buildAccountRemovalView];
break;
- case profiles::BUBBLE_VIEW_MODE_END_PREVIEW:
- subView = [self buildEndPreviewView];
+ case profiles::BUBBLE_VIEW_MODE_SWITCH_USER:
+ subView = [self buildSwitchUserView];
break;
case profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER:
case profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT:
break;
}
+ // Clears tutorial mode for all non-profile-chooser views.
+ if (viewMode_ != profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER)
+ tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
+
[contentView addSubview:subView];
SetWindowSize([self window],
NSMakeSize(NSWidth([subView frame]), NSHeight([subView frame])));
[[NSMutableArray alloc] init]);
// Local and guest profiles cannot lock their profile.
bool enableLock = false;
- // Store the most recently displayed tutorial mode
- profiles::TutorialMode lastTutorialMode = tutorialMode_;
// Loop over the profiles in reverse, so that they are sorted by their
// y-coordinate, and separate them into active and "other" profiles.
for (int i = avatarMenu_->GetNumberOfItems() - 1; i >= 0; --i) {
const AvatarMenu::Item& item = avatarMenu_->GetItemAt(i);
if (item.active) {
- if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER)
- tutorialView = [self buildPreviewTutorialIfNeeded:item];
+ if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
+ switch (tutorialMode_) {
+ case profiles::TUTORIAL_MODE_NONE:
+ case profiles::TUTORIAL_MODE_WELCOME_UPGRADE:
+ tutorialView =
+ [self buildWelcomeUpgradeTutorialViewIfNeeded];
+ break;
+ case profiles::TUTORIAL_MODE_CONFIRM_SIGNIN:
+ tutorialView = [self buildSigninConfirmationView];
+ break;
+ case profiles::TUTORIAL_MODE_SHOW_ERROR:
+ tutorialView = [self buildSigninErrorView];
+ }
+ }
currentProfileView = [self createCurrentProfileView:item];
- enableLock = item.signed_in;
+ enableLock = switches::IsNewProfileManagement() && item.signed_in;
} else {
[otherProfiles addObject:[self createOtherProfileView:i]];
}
// overlap the bubble's rounded corners.
CGFloat yOffset = 1;
- // Option buttons. Only available with the new profile management flag.
- if (switches::IsNewProfileManagement()) {
- NSRect rect = NSMakeRect(0, yOffset, kFixedMenuWidth, 0);
- NSView* optionsView = [self createOptionsViewWithRect:rect
- enableLock:enableLock];
- [container addSubview:optionsView];
- rect.origin.y = NSMaxY([optionsView frame]);
+ // Option buttons.
+ NSRect rect = NSMakeRect(0, yOffset, kFixedMenuWidth, 0);
+ NSView* optionsView = [self createOptionsViewWithRect:rect
+ enableLock:enableLock];
+ [container addSubview:optionsView];
+ rect.origin.y = NSMaxY([optionsView frame]);
- NSBox* separator = [self horizontalSeparatorWithFrame:rect];
- [container addSubview:separator];
- yOffset = NSMaxY([separator frame]);
- }
+ NSBox* separator = [self horizontalSeparatorWithFrame:rect];
+ [container addSubview:separator];
+ yOffset = NSMaxY([separator frame]);
if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER &&
switches::IsFastUserSwitching()) {
[tutorialView setFrameOrigin:NSMakePoint(0, yOffset)];
[container addSubview:tutorialView];
yOffset = NSMaxY([tutorialView frame]);
- if (!switches::IsNewProfileManagement() &&
- tutorialMode_ != lastTutorialMode) {
- ProfileMetrics::LogProfileUpgradeEnrollment(
- ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO);
- }
+ //TODO(mlerman): update UMA stats for the new tutorials.
} else {
tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
}
return container.autorelease();
}
-- (NSView*)buildPreviewTutorialIfNeeded:(const AvatarMenu::Item&)item {
- if (!switches::IsNewProfileManagement()) {
- return [self tutorialViewWithMode:profiles::TUTORIAL_MODE_ENABLE_PREVIEW
- titleMessage:IDS_PROFILES_PREVIEW_TUTORIAL_TITLE
- contentMessage:IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT
- linkMessage:IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE
- buttonMessage:IDS_PROFILES_TUTORIAL_TRY_BUTTON
- linkAction:@selector(openTutorialLearnMoreURL:)
- buttonAction:
- @selector(enableNewProfileManagementPreview:)];
- }
-
- if (!switches::IsNewProfileManagementPreviewEnabled())
- return nil;
-
- if (tutorialMode_ == profiles::TUTORIAL_MODE_SEND_FEEDBACK) {
- return [self tutorialViewWithMode:profiles::TUTORIAL_MODE_SEND_FEEDBACK
- titleMessage:IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE
- contentMessage:
- IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT
- linkMessage:IDS_PROFILES_END_PREVIEW
- buttonMessage:IDS_PROFILES_SEND_FEEDBACK_BUTTON
- linkAction:@selector(showEndPreviewView:)
- buttonAction:@selector(sendFeedback:)];
- }
-
+- (NSView*)buildSigninConfirmationView {
+ ProfileMetrics::LogProfileNewAvatarMenuSignin(
+ ProfileMetrics::PROFILE_AVATAR_MENU_SIGNIN_VIEW);
+
+ NSString* titleMessage = l10n_util::GetNSString(
+ IDS_PROFILES_CONFIRM_SIGNIN_TUTORIAL_TITLE);
+ NSString* contentMessage = l10n_util::GetNSString(
+ IDS_PROFILES_CONFIRM_SIGNIN_TUTORIAL_CONTENT_TEXT);
+ NSString* linkMessage = l10n_util::GetNSString(
+ IDS_PROFILES_SYNC_SETTINGS_LINK);
+ NSString* buttonMessage = l10n_util::GetNSString(
+ IDS_PROFILES_TUTORIAL_OK_BUTTON);
+ return [self tutorialViewWithMode:profiles::TUTORIAL_MODE_CONFIRM_SIGNIN
+ titleMessage:titleMessage
+ contentMessage:contentMessage
+ linkMessage:linkMessage
+ buttonMessage:buttonMessage
+ stackButton:NO
+ hasCloseButton:NO
+ linkAction:@selector(configureSyncSettings:)
+ buttonAction:@selector(syncSettingsConfirmed:)];
+}
+
+- (NSView*)buildSigninErrorView {
+ NSString* titleMessage = l10n_util::GetNSString(
+ IDS_PROFILES_ERROR_TUTORIAL_TITLE);
+ LoginUIService* loginUiService =
+ LoginUIServiceFactory::GetForProfile(browser_->profile());
+ NSString* contentMessage =
+ base::SysUTF16ToNSString(loginUiService->GetLastLoginResult());
+ NSString* linkMessage = l10n_util::GetNSString(
+ IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE);
+ return [self tutorialViewWithMode:profiles::TUTORIAL_MODE_CONFIRM_SIGNIN
+ titleMessage:titleMessage
+ contentMessage:contentMessage
+ linkMessage:linkMessage
+ buttonMessage:nil
+ stackButton:NO
+ hasCloseButton:YES
+ linkAction:@selector(showLearnMorePage:)
+ buttonAction:nil];
+}
+
+- (NSView*)buildWelcomeUpgradeTutorialViewIfNeeded {
Profile* profile = browser_->profile();
+ const AvatarMenu::Item& avatarItem =
+ avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex());
+
const int showCount = profile->GetPrefs()->GetInteger(
prefs::kProfileAvatarTutorialShown);
// Do not show the tutorial if user has dismissed it.
- if (showCount > kProfileAvatarTutorialShowMax)
+ if (showCount > signin_ui_util::kUpgradeWelcomeTutorialShowMax)
return nil;
- if (tutorialMode_ != profiles::TUTORIAL_MODE_WELCOME) {
- if (showCount == kProfileAvatarTutorialShowMax)
+ if (tutorialMode_ != profiles::TUTORIAL_MODE_WELCOME_UPGRADE) {
+ if (showCount == signin_ui_util::kUpgradeWelcomeTutorialShowMax)
return nil;
profile->GetPrefs()->SetInteger(
prefs::kProfileAvatarTutorialShown, showCount + 1);
}
- return [self tutorialViewWithMode:profiles::TUTORIAL_MODE_WELCOME
- titleMessage:IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE
- contentMessage:
- IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT
- linkMessage:IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE
- buttonMessage:IDS_PROFILES_TUTORIAL_OK_BUTTON
- linkAction:@selector(openTutorialLearnMoreURL:)
- buttonAction:@selector(dismissTutorial:)];
+ ProfileMetrics::LogProfileNewAvatarMenuUpgrade(
+ ProfileMetrics::PROFILE_AVATAR_MENU_UPGRADE_VIEW);
+
+ NSString* titleMessage = l10n_util::GetNSString(
+ IDS_PROFILES_WELCOME_UPGRADE_TUTORIAL_TITLE);
+ NSString* contentMessage = l10n_util::GetNSString(
+ IDS_PROFILES_WELCOME_UPGRADE_TUTORIAL_CONTENT_TEXT);
+ // For local profiles, the "Not you" link doesn't make sense.
+ NSString* linkMessage = avatarItem.signed_in ?
+ ElideMessage(
+ l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU, avatarItem.name),
+ kFixedMenuWidth - 2 * kHorizontalSpacing) :
+ nil;
+ NSString* buttonMessage = l10n_util::GetNSString(
+ IDS_PROFILES_TUTORIAL_WHATS_NEW_BUTTON);
+ return [self tutorialViewWithMode:profiles::TUTORIAL_MODE_WELCOME_UPGRADE
+ titleMessage:titleMessage
+ contentMessage:contentMessage
+ linkMessage:linkMessage
+ buttonMessage:buttonMessage
+ stackButton:YES
+ hasCloseButton:YES
+ linkAction:@selector(showSwitchUserView:)
+ buttonAction:@selector(seeWhatsNew:)];
}
- (NSView*)tutorialViewWithMode:(profiles::TutorialMode)mode
- titleMessage:(int)titleMessageId
- contentMessage:(int)contentMessageId
- linkMessage:(int)linkMessageId
- buttonMessage:(int)buttonMessageId
+ titleMessage:(NSString*)titleMessage
+ contentMessage:(NSString*)contentMessage
+ linkMessage:(NSString*)linkMessage
+ buttonMessage:(NSString*)buttonMessage
+ stackButton:(BOOL)stackButton
+ hasCloseButton:(BOOL)hasCloseButton
linkAction:(SEL)linkAction
buttonAction:(SEL)buttonAction {
tutorialMode_ = mode;
initWithFrame:NSMakeRect(0, 0, kFixedMenuWidth, 0)
withColor:tutorialBackgroundColor]);
CGFloat availableWidth = kFixedMenuWidth - 2 * kHorizontalSpacing;
- CGFloat yOffset = kSmallVerticalSpacing;
+ CGFloat yOffset = kVerticalSpacing;
// Adds links and buttons at the bottom.
- base::scoped_nsobject<NSButton> tutorialOkButton([[HoverButton alloc]
- initWithFrame:NSZeroRect]);
- [tutorialOkButton setTitle:l10n_util::GetNSString(
- buttonMessageId)];
- [tutorialOkButton setBezelStyle:NSRoundedBezelStyle];
- [tutorialOkButton setTarget:self];
- [tutorialOkButton setAction:buttonAction];
- [tutorialOkButton sizeToFit];
- NSSize buttonSize = [tutorialOkButton frame].size;
- const CGFloat kTopBottomTextPadding = 6;
- const CGFloat kLeftRightTextPadding = 15;
- buttonSize.width += 2 * kLeftRightTextPadding;
- buttonSize.height += 2 * kTopBottomTextPadding;
- [tutorialOkButton setFrameSize:buttonSize];
- [tutorialOkButton setAlignment:NSCenterTextAlignment];
- [tutorialOkButton setFrameOrigin:NSMakePoint(
- kFixedMenuWidth - NSWidth([tutorialOkButton frame]) - kHorizontalSpacing,
- yOffset)];
- [container addSubview:tutorialOkButton];
-
- NSButton* learnMoreLink =
- [self linkButtonWithTitle:l10n_util::GetNSString(linkMessageId)
- frameOrigin:NSZeroPoint
- action:linkAction];
- [[learnMoreLink cell] setTextColor:[NSColor whiteColor]];
- CGFloat linkYOffset = yOffset + (NSHeight([tutorialOkButton frame]) -
- NSHeight([learnMoreLink frame])) / 2;
- [learnMoreLink setFrameOrigin:NSMakePoint(kHorizontalSpacing, linkYOffset)];
- [container addSubview:learnMoreLink];
-
- yOffset = std::max(NSMaxY([learnMoreLink frame]),
- NSMaxY([tutorialOkButton frame])) + kVerticalSpacing;
+ base::scoped_nsobject<NSButton> tutorialOkButton;
+ if (buttonMessage) {
+ tutorialOkButton.reset([[HoverButton alloc] initWithFrame:NSZeroRect]);
+ [tutorialOkButton setTitle:buttonMessage];
+ [tutorialOkButton setBezelStyle:NSRoundedBezelStyle];
+ [tutorialOkButton setTarget:self];
+ [tutorialOkButton setAction:buttonAction];
+ [tutorialOkButton setAlignment:NSCenterTextAlignment];
+ [tutorialOkButton sizeToFit];
+ }
+
+ NSButton* learnMoreLink = nil;
+ if (linkMessage) {
+ learnMoreLink = [self linkButtonWithTitle:linkMessage
+ frameOrigin:NSZeroPoint
+ action:linkAction];
+ [[learnMoreLink cell] setTextColor:[NSColor whiteColor]];
+ }
+
+ if (stackButton) {
+ if (linkMessage) {
+ [learnMoreLink setFrameOrigin:NSMakePoint(
+ (kFixedMenuWidth - NSWidth([learnMoreLink frame])) / 2, yOffset)];
+ }
+ [tutorialOkButton setFrameSize:NSMakeSize(
+ availableWidth, NSHeight([tutorialOkButton frame]))];
+ [tutorialOkButton setFrameOrigin:NSMakePoint(
+ kHorizontalSpacing,
+ yOffset + (learnMoreLink ? NSHeight([learnMoreLink frame]) : 0))];
+ } else {
+ if (buttonMessage) {
+ NSSize buttonSize = [tutorialOkButton frame].size;
+ const CGFloat kTopBottomTextPadding = 6;
+ const CGFloat kLeftRightTextPadding = 15;
+ buttonSize.width += 2 * kLeftRightTextPadding;
+ buttonSize.height += 2 * kTopBottomTextPadding;
+ [tutorialOkButton setFrameSize:buttonSize];
+ CGFloat buttonXOffset = kFixedMenuWidth -
+ NSWidth([tutorialOkButton frame]) - kHorizontalSpacing;
+ [tutorialOkButton setFrameOrigin:NSMakePoint(buttonXOffset, yOffset)];
+ }
+
+ if (linkMessage) {
+ CGFloat linkYOffset = yOffset;
+ if (buttonMessage) {
+ linkYOffset += (NSHeight([tutorialOkButton frame]) -
+ NSHeight([learnMoreLink frame])) / 2;
+ }
+ [learnMoreLink setFrameOrigin:NSMakePoint(
+ kHorizontalSpacing, linkYOffset)];
+ }
+ }
+
+ if (buttonMessage) {
+ [container addSubview:tutorialOkButton];
+ yOffset = NSMaxY([tutorialOkButton frame]);
+ }
+
+ if (linkMessage) {
+ [container addSubview:learnMoreLink];
+ yOffset = std::max(NSMaxY([learnMoreLink frame]), yOffset);
+ }
+
+ yOffset += kVerticalSpacing;
// Adds body content.
NSTextField* contentLabel = BuildLabel(
- l10n_util::GetNSString(contentMessageId),
+ contentMessage,
NSMakePoint(kHorizontalSpacing, yOffset),
- tutorialBackgroundColor,
gfx::SkColorToSRGBNSColor(profiles::kAvatarTutorialContentTextColor));
[contentLabel setFrameSize:NSMakeSize(availableWidth, 0)];
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel];
// Adds title.
NSTextField* titleLabel =
- BuildLabel(l10n_util::GetNSString(titleMessageId),
+ BuildLabel(titleMessage,
NSMakePoint(kHorizontalSpacing, yOffset),
- tutorialBackgroundColor,
[NSColor whiteColor] /* text_color */);
[titleLabel setFont:[NSFont labelFontOfSize:kTitleFontSize]];
- [titleLabel sizeToFit];
- [titleLabel setFrameSize:
- NSMakeSize(availableWidth, NSHeight([titleLabel frame]))];
- [container addSubview:titleLabel];
- yOffset = NSMaxY([titleLabel frame]) + kVerticalSpacing;
-
- [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)];
- // Adds caret at the bottom.
- NSImage* caretImage = ui::ResourceBundle::GetSharedInstance().
- GetNativeImageNamed(IDR_ICON_PROFILES_MENU_CARET).AsNSImage();
- base::scoped_nsobject<NSImageView> caretView(
- [[NSImageView alloc] initWithFrame:NSMakeRect(
- kHorizontalSpacing, 0, caretImage.size.width,
- caretImage.size.height)]);
- [caretView setImage:caretImage];
+ if (hasCloseButton) {
+ base::scoped_nsobject<HoverImageButton> closeButton(
+ [[HoverImageButton alloc] initWithFrame:NSZeroRect]);
+ [closeButton setBordered:NO];
- base::scoped_nsobject<NSView> containerWithCaret([[NSView alloc]
- initWithFrame:NSMakeRect(0, 0, kFixedMenuWidth, 0)]);
- [containerWithCaret addSubview:caretView];
+ ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
+ NSImage* closeImage = rb->GetNativeImageNamed(IDR_CLOSE_1).ToNSImage();
+ CGFloat closeImageWidth = [closeImage size].width;
+ [closeButton setDefaultImage:closeImage];
+ [closeButton setHoverImage:
+ rb->GetNativeImageNamed(IDR_CLOSE_1_H).ToNSImage()];
+ [closeButton setPressedImage:
+ rb->GetNativeImageNamed(IDR_CLOSE_1_P).ToNSImage()];
+ [closeButton setTarget:self];
+ [closeButton setAction:@selector(dismissTutorial:)];
+ [closeButton setFrameSize:[closeImage size]];
+ [closeButton setFrameOrigin:NSMakePoint(
+ kFixedMenuWidth - kHorizontalSpacing - closeImageWidth, yOffset)];
+ [container addSubview:closeButton];
+
+ [titleLabel setFrameSize:NSMakeSize(
+ availableWidth - closeImageWidth - kHorizontalSpacing, 0)];
+ } else {
+ [titleLabel setFrameSize:NSMakeSize(availableWidth, 0)];
+ }
- [container setFrameOrigin:NSMakePoint(0, caretImage.size.height)];
- [containerWithCaret addSubview:container];
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel];
+ [container addSubview:titleLabel];
+ yOffset = NSMaxY([titleLabel frame]) + kVerticalSpacing;
- [containerWithCaret setFrameSize:
- NSMakeSize(kFixedMenuWidth, NSMaxY([container frame]))];
- return containerWithCaret.autorelease();
+ [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)];
+ [container setFrameOrigin:NSZeroPoint];
+ return container.autorelease();
}
- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item {
!browser_->profile()->IsSupervised();
base::scoped_nsobject<EditableProfileNameButton> profileName(
[[EditableProfileNameButton alloc]
- initWithFrame:NSMakeRect(xOffset, yOffset,
+ initWithFrame:NSMakeRect(xOffset,
+ yOffset,
availableTextWidth,
kProfileButtonHeight)
profile:browser_->profile()
profileName:base::SysUTF16ToNSString(
- profiles::GetAvatarNameForProfile(browser_->profile()))
+ profiles::GetAvatarNameForProfile(
+ browser_->profile()->GetPath()))
editingAllowed:editingAllowed
withController:self]);
[container addSubview:profileName];
- yOffset = NSMaxY([profileName frame]);
+ yOffset = NSMaxY([profileName frame]) + 4; // Adds a small vertical padding.
// Profile icon, centered.
xOffset = (kFixedMenuWidth - kLargeImageSide) / 2;
[container addSubview:supervisedIcon];
}
- if (switches::IsNewProfileManagementPreviewEnabled()) {
- base::scoped_nsobject<HoverImageButton> questionButton(
- [[HoverImageButton alloc] initWithFrame:NSZeroRect]);
- [questionButton setBordered:NO];
- ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
- [questionButton setDefaultImage:rb->GetNativeImageNamed(
- IDR_ICON_PROFILES_MENU_QUESTION_STABLE).ToNSImage()];
- [questionButton setHoverImage:rb->GetNativeImageNamed(
- IDR_ICON_PROFILES_MENU_QUESTION_HOVER).ToNSImage()];
- [questionButton setPressedImage:rb->GetNativeImageNamed(
- IDR_ICON_PROFILES_MENU_QUESTION_SELECT).ToNSImage()];
- [questionButton setTarget:self];
- [questionButton setAction:@selector(showSendFeedbackTutorial:)];
- [questionButton sizeToFit];
- const CGFloat size = NSHeight([questionButton frame]) + 2;
- [questionButton setFrame:
- NSMakeRect(kHorizontalSpacing, yOffset - size, size, size)];
- [container addSubview:questionButton];
- }
-
[container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)];
return container.autorelease();
}
rect.origin.x = 0;
// The available links depend on the type of profile that is active.
- NSButton* link;
if (item.signed_in) {
- if (switches::IsNewProfileManagement()) {
+ rect.size.height = kBlueButtonHeight / 2;
+ // Signed in profiles with no authentication errors do not have a clickable
+ // email link.
+ NSButton* link = nil;
+ if (switches::IsEnableAccountConsistency()) {
NSString* linkTitle = l10n_util::GetNSString(
viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER ?
IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON :
link = [self linkButtonWithTitle:base::SysUTF16ToNSString(item.sync_state)
frameOrigin:rect.origin
action:nil];
- [[link cell] setTextColor:[NSColor blackColor]];
+ if (HasAuthError(browser_->profile())) {
+ [link setImage:ui::ResourceBundle::GetSharedInstance().
+ GetNativeImageNamed(IDR_ICON_PROFILES_ACCOUNT_BUTTON_ERROR).
+ ToNSImage()];
+ [link setImagePosition:NSImageRight];
+ [link setTarget:self];
+ [link setAction:@selector(showAccountReauthenticationView:)];
+ [link setTag:kPrimaryProfileTag];
+ [[link cell]
+ accessibilitySetOverrideValue:l10n_util::GetNSStringF(
+ IDS_PROFILES_ACCOUNT_BUTTON_AUTH_ERROR_ACCESSIBLE_NAME,
+ item.sync_state)
+ forAttribute:NSAccessibilityTitleAttribute];
+ } else {
+ [link setEnabled:NO];
+ }
}
// -linkButtonWithTitle sizeToFit's the link, so re-stretch it so that it
// can be centered correctly in the view.
- rect.size.height = NSMaxY([link frame]);
- [link setFrame:rect];
[link setAlignment:NSCenterTextAlignment];
+ [link setFrame:rect];
+ [container addSubview:link];
+ [container setFrameSize:rect.size];
} else {
rect.size.height = kBlueButtonHeight;
- link = [[BlueLabelButton alloc] initWithFrame:rect];
+ NSButton* signinButton = [[BlueLabelButton alloc] initWithFrame:rect];
// Manually elide the button text so that the contents fit inside the bubble
// This is needed because the BlueLabelButton cell resets the style on
l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
gfx::FontList(), rect.size.width, gfx::ELIDE_TAIL));
- [link setTitle:elidedButtonText];
- [link setTarget:self];
- [link setAction:switches::IsNewProfileManagement() ?
- @selector(showInlineSigninPage:) : @selector(showTabbedSigninPage:)];
+ [signinButton setTitle:elidedButtonText];
+ [signinButton setTarget:self];
+ [signinButton setAction:@selector(showInlineSigninPage:)];
+ [container addSubview:signinButton];
+
+ // Sign-in promo text.
+ NSTextField* promo = BuildLabel(
+ l10n_util::GetNSString(IDS_PROFILES_SIGNIN_PROMO),
+ NSMakePoint(0, NSMaxY([signinButton frame]) + kVerticalSpacing),
+ nil);
+ [promo setFrameSize:NSMakeSize(rect.size.width, 0)];
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:promo];
+ [container addSubview:promo];
+
+ [container setFrameSize:NSMakeSize(
+ rect.size.width,
+ NSMaxY([promo frame]) + 4)]; // Adds a small vertical padding.
}
- [container addSubview:link];
- [container setFrameSize:rect.size];
return container.autorelease();
}
NSTextField* disclaimer = BuildLabel(
base::SysUTF16ToNSString(avatarMenu_->GetSupervisedUserInformation()),
- NSMakePoint(kHorizontalSpacing, yOffset),
- nil /* background_color */,
- nil /* text_color */);
+ NSMakePoint(kHorizontalSpacing, yOffset), nil);
[disclaimer setFrameSize:NSMakeSize(availableTextWidth, 0)];
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:disclaimer];
yOffset = NSMaxY([disclaimer frame]);
- (NSButton*)createOtherProfileView:(int)itemIndex {
const AvatarMenu::Item& item = avatarMenu_->GetItemAt(itemIndex);
- NSRect rect = NSMakeRect(0, 0, kFixedMenuWidth,
- kBlueButtonHeight + kSmallVerticalSpacing);
+ NSRect rect = NSMakeRect(0, 0, kFixedMenuWidth, kBlueButtonHeight);
base::scoped_nsobject<BackgroundColorHoverButton> profileButton(
[[BackgroundColorHoverButton alloc]
initWithFrame:rect
- (NSView*)createOptionsViewWithRect:(NSRect)rect
enableLock:(BOOL)enableLock {
- int widthOfLockButton = enableLock ? 2 * kHorizontalSpacing + 14 : 0;
NSRect viewRect = NSMakeRect(0, 0,
- rect.size.width - widthOfLockButton,
- kBlueButtonHeight + kVerticalSpacing);
- NSString* text = isGuestSession_ ?
- l10n_util::GetNSString(IDS_PROFILES_EXIT_GUEST) :
- l10n_util::GetNSStringF(IDS_PROFILES_NOT_YOU_BUTTON,
- profiles::GetAvatarNameForProfile(browser_->profile()));
- NSButton* notYouButton =
- [self hoverButtonWithRect:viewRect
- text:text
- imageResourceId:IDR_ICON_PROFILES_MENU_AVATAR
- alternateImageResourceId:IDR_ICON_PROFILES_MENU_AVATAR
- action:isGuestSession_? @selector(exitGuest:) :
- @selector(showUserManager:)];
-
- rect.size.height = NSMaxY([notYouButton frame]);
+ rect.size.width,
+ kBlueButtonHeight + kSmallVerticalSpacing);
base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]);
- [container addSubview:notYouButton];
if (enableLock) {
- viewRect.origin.x = NSMaxX([notYouButton frame]);
- NSBox* separator = [self verticalSeparatorWithFrame:viewRect];
- [container addSubview:separator];
-
- viewRect.origin.x = NSMaxX([separator frame]);
- viewRect.size.width = widthOfLockButton;
NSButton* lockButton =
[self hoverButtonWithRect:viewRect
- text:@""
+ text:l10n_util::GetNSString(
+ IDS_PROFILES_PROFILE_SIGNOUT_BUTTON)
imageResourceId:IDR_ICON_PROFILES_MENU_LOCK
- alternateImageResourceId:IDR_ICON_PROFILES_MENU_LOCK
action:@selector(lockProfile:)];
[container addSubview:lockButton];
+ viewRect.origin.y = NSMaxY([lockButton frame]);
+
+ NSBox* separator = [self horizontalSeparatorWithFrame:viewRect];
+ [container addSubview:separator];
+ viewRect.origin.y = NSMaxY([separator frame]);
}
+ if ([self shouldShowGoIncognito]) {
+ NSButton* goIncognitoButton =
+ [self hoverButtonWithRect:viewRect
+ text:l10n_util::GetNSString(
+ IDS_PROFILES_GO_INCOGNITO_BUTTON)
+ imageResourceId:IDR_ICON_PROFILES_MENU_INCOGNITO
+ action:@selector(goIncognito:)];
+ viewRect.origin.y = NSMaxY([goIncognitoButton frame]);
+ [container addSubview:goIncognitoButton];
+
+ NSBox* separator = [self horizontalSeparatorWithFrame:viewRect];
+ [container addSubview:separator];
+ viewRect.origin.y = NSMaxY([separator frame]);
+ }
+
+ NSString* text = isGuestSession_ ?
+ l10n_util::GetNSString(IDS_PROFILES_EXIT_GUEST) :
+ l10n_util::GetNSString(IDS_PROFILES_SWITCH_USERS_BUTTON);
+ NSButton* switchUsersButton =
+ [self hoverButtonWithRect:viewRect
+ text:text
+ imageResourceId:IDR_ICON_PROFILES_MENU_AVATAR
+ action:isGuestSession_? @selector(exitGuest:) :
+ @selector(showUserManager:)];
+ viewRect.origin.y = NSMaxY([switchUsersButton frame]);
+ [container addSubview:switchUsersButton];
+
+ [container setFrameSize:NSMakeSize(rect.size.width, viewRect.origin.y)];
return container.autorelease();
}
initWithFrame:rect
withColor:backgroundColor]);
- // Manually elide the button text so that the contents fit inside the bubble.
- // This is needed because the BlueLabelButton cell resets the style on
- // every call to -cellSize, which prevents setting a custom lineBreakMode.
- NSString* elidedButtonText = base::SysUTF16ToNSString(gfx::ElideText(
- l10n_util::GetStringFUTF16(
- IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, item.name),
- gfx::FontList(), rect.size.width, gfx::ELIDE_TAIL));
-
- NSButton* addAccountsButton =
- [self linkButtonWithTitle:elidedButtonText
- frameOrigin:NSMakePoint(
- kHorizontalSpacing, kSmallVerticalSpacing)
- action:@selector(addAccount:)];
- [container addSubview:addAccountsButton];
+ rect.origin.y = 0;
+ if (!browser_->profile()->IsSupervised()) {
+ // Manually elide the button text so the contents fit inside the bubble.
+ // This is needed because the BlueLabelButton cell resets the style on
+ // every call to -cellSize, which prevents setting a custom lineBreakMode.
+ NSString* elidedButtonText = base::SysUTF16ToNSString(gfx::ElideText(
+ l10n_util::GetStringFUTF16(
+ IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, item.name),
+ gfx::FontList(), rect.size.width, gfx::ELIDE_TAIL));
+
+ NSButton* addAccountsButton =
+ [self linkButtonWithTitle:elidedButtonText
+ frameOrigin:NSMakePoint(
+ kHorizontalSpacing, kSmallVerticalSpacing)
+ action:@selector(addAccount:)];
+ [container addSubview:addAccountsButton];
+ rect.origin.y += kAccountButtonHeight;
+ }
NSView* accountEmails = [self createAccountsListWithRect:NSMakeRect(
- 0, kAccountButtonHeight, rect.size.width, kAccountButtonHeight)];
+ 0, rect.origin.y, rect.size.width, kAccountButtonHeight)];
[container addSubview:accountEmails];
[container setFrameSize:NSMakeSize(rect.size.width,
NSView* titleView = BuildTitleCard(
NSMakeRect(0, yOffset, kFixedGaiaViewWidth, 0),
- messageId,
+ l10n_util::GetStringUTF16(messageId),
self /* backButtonTarget*/,
@selector(navigateBackFromSigninPage:) /* backButtonAction */);
[container addSubview:titleView];
} else {
NSString* contentStr =
l10n_util::GetNSString(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT);
- NSTextField* contentLabel = BuildLabel(contentStr, contentFrameOrigin,
- GetDialogBackgroundColor(), nil /* text_color */);
+ NSTextField* contentLabel = BuildLabel(contentStr, contentFrameOrigin, nil);
[contentLabel setFrameSize:NSMakeSize(availableWidth, 0)];
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel];
contentView = contentLabel;
NSView* titleView = BuildTitleCard(
NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth,0),
- IDS_PROFILES_ACCOUNT_REMOVAL_TITLE,
+ l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE),
self /* backButtonTarget*/,
@selector(showAccountManagement:) /* backButtonAction */);
[container addSubview:titleView];
return container.autorelease();
}
-- (NSView*)buildEndPreviewView {
+- (NSView*)buildSwitchUserView {
+ ProfileMetrics::LogProfileNewAvatarMenuNotYou(
+ ProfileMetrics::PROFILE_AVATAR_MENU_NOT_YOU_VIEW);
base::scoped_nsobject<NSView> container(
[[NSView alloc] initWithFrame:NSZeroRect]);
CGFloat availableWidth =
- kFixedEndPreviewViewWidth - 2 * kHorizontalSpacing;
- CGFloat yOffset = kVerticalSpacing;
+ kFixedSwitchUserViewWidth - 2 * kHorizontalSpacing;
+ CGFloat yOffset = 0;
+ NSRect viewRect = NSMakeRect(0, yOffset,
+ kFixedSwitchUserViewWidth,
+ kBlueButtonHeight + kSmallVerticalSpacing);
- // Adds the "end preview and relaunch" button at the bottom.
- base::scoped_nsobject<NSButton> endPreviewAndRelaunchButton(
- [[BlueLabelButton alloc] initWithFrame:NSZeroRect]);
- [endPreviewAndRelaunchButton setTitle:l10n_util::GetNSString(
- IDS_PROFILES_END_PREVIEW_AND_RELAUNCH)];
- [endPreviewAndRelaunchButton setTarget:self];
- [endPreviewAndRelaunchButton setAction:@selector(endPreviewAndRelaunch:)];
- [endPreviewAndRelaunchButton sizeToFit];
- [endPreviewAndRelaunchButton setAlignment:NSCenterTextAlignment];
- CGFloat xOffset = (kFixedEndPreviewViewWidth -
- NSWidth([endPreviewAndRelaunchButton frame])) / 2;
- [endPreviewAndRelaunchButton setFrameOrigin:NSMakePoint(xOffset, yOffset)];
- [container addSubview:endPreviewAndRelaunchButton];
- yOffset = NSMaxY([endPreviewAndRelaunchButton frame]) + kVerticalSpacing;
-
- // Adds the main text label.
- NSPoint contentFrameOrigin = NSMakePoint(kHorizontalSpacing, yOffset);
- NSString* contentStr =
- l10n_util::GetNSString(IDS_PROFILES_END_PREVIEW_TEXT);
- NSTextField* contentLabel = BuildLabel(contentStr, contentFrameOrigin,
- GetDialogBackgroundColor(), nil /* text_color */);
+ const AvatarMenu::Item& avatarItem =
+ avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex());
+
+ // Adds "Disconnect your Google Account" button at the bottom.
+ NSButton* disconnectButton =
+ [self hoverButtonWithRect:viewRect
+ text:l10n_util::GetNSString(
+ IDS_PROFILES_DISCONNECT_BUTTON)
+ imageResourceId:IDR_ICON_PROFILES_MENU_DISCONNECT
+ action:@selector(disconnectProfile:)];
+ [container addSubview:disconnectButton];
+ yOffset = NSMaxY([disconnectButton frame]);
+
+ NSBox* separator = [self horizontalSeparatorWithFrame:
+ NSMakeRect(0, yOffset, kFixedMenuWidth, 0)];
+ [container addSubview:separator];
+ yOffset = NSMaxY([separator frame]);
+
+ // Adds "Add person" button.
+ viewRect.origin.y = yOffset;
+ NSButton* addPersonButton =
+ [self hoverButtonWithRect:viewRect
+ text:l10n_util::GetNSString(
+ IDS_PROFILES_ADD_PERSON_BUTTON)
+ imageResourceId:IDR_ICON_PROFILES_MENU_AVATAR
+ action:@selector(showUserManager:)];
+ [container addSubview:addPersonButton];
+ yOffset = NSMaxY([addPersonButton frame]);
+
+ separator = [self horizontalSeparatorWithFrame:
+ NSMakeRect(0, yOffset, kFixedMenuWidth, 0)];
+ [container addSubview:separator];
+ yOffset = NSMaxY([separator frame]);
+
+ // Adds the content text.
+ base::string16 elidedName(gfx::ElideText(
+ avatarItem.name, gfx::FontList(), availableWidth, gfx::ELIDE_TAIL));
+ NSTextField* contentLabel = BuildLabel(
+ l10n_util::GetNSStringF(IDS_PROFILES_NOT_YOU_CONTENT_TEXT, elidedName),
+ NSMakePoint(kHorizontalSpacing, yOffset + kVerticalSpacing),
+ nil);
[contentLabel setFrameSize:NSMakeSize(availableWidth, 0)];
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel];
[container addSubview:contentLabel];
yOffset = NSMaxY([contentLabel frame]) + kVerticalSpacing;
// Adds the title card.
- NSBox* separator = [self horizontalSeparatorWithFrame:
- NSMakeRect(0, yOffset, kFixedEndPreviewViewWidth, 0)];
+ separator = [self horizontalSeparatorWithFrame:
+ NSMakeRect(0, yOffset, kFixedSwitchUserViewWidth, 0)];
[container addSubview:separator];
yOffset = NSMaxY([separator frame]) + kSmallVerticalSpacing;
NSView* titleView = BuildTitleCard(
- NSMakeRect(0, yOffset, kFixedEndPreviewViewWidth, 0),
- IDS_PROFILES_END_PREVIEW,
+ NSMakeRect(0, yOffset, kFixedSwitchUserViewWidth,0),
+ l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU, avatarItem.name),
self /* backButtonTarget*/,
- @selector(showSendFeedbackTutorial:) /* backButtonAction */);
+ @selector(navigateBackFromSwitchUserView:) /* backButtonAction */);
[container addSubview:titleView];
yOffset = NSMaxY([titleView frame]);
- [container setFrameSize:NSMakeSize(kFixedEndPreviewViewWidth, yOffset)];
+ [container setFrameSize:NSMakeSize(kFixedAccountRemovalViewWidth, yOffset)];
return container.autorelease();
}
- (NSButton*)hoverButtonWithRect:(NSRect)rect
text:(NSString*)text
imageResourceId:(int)imageResourceId
- alternateImageResourceId:(int)alternateImageResourceId
action:(SEL)action {
base::scoped_nsobject<BackgroundColorHoverButton> button(
[[BackgroundColorHoverButton alloc]
[button setTitle:text];
ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
- NSImage* alternateImage = rb->GetNativeImageNamed(
- alternateImageResourceId).ToNSImage();
- [button setDefaultImage:rb->GetNativeImageNamed(imageResourceId).ToNSImage()];
- [button setHoverImage:alternateImage];
- [button setPressedImage:alternateImage];
+ NSImage* image = rb->GetNativeImageNamed(imageResourceId).ToNSImage();
+ [button setDefaultImage:image];
+ [button setHoverImage:image];
+ [button setPressedImage:image];
[button setImagePosition:NSImageLeft];
[button setAlignment:NSLeftTextAlignment];
[button setBordered:NO];
}
// Delete button.
- NSRect buttonRect;
- NSDivideRect(rect, &buttonRect, &rect,
- deleteImageWidth + kHorizontalSpacing, NSMaxXEdge);
- buttonRect.origin.y = 0;
-
- base::scoped_nsobject<HoverImageButton> deleteButton(
- [[HoverImageButton alloc] initWithFrame:buttonRect]);
- [deleteButton setBordered:NO];
- [deleteButton setDefaultImage:deleteImage];
- [deleteButton setHoverImage:rb->GetNativeImageNamed(
- IDR_CLOSE_1_H).ToNSImage()];
- [deleteButton setPressedImage:rb->GetNativeImageNamed(
- IDR_CLOSE_1_P).ToNSImage()];
- [deleteButton setTarget:self];
- [deleteButton setAction:@selector(showAccountRemovalView:)];
- [deleteButton setTag:tag];
-
- [button addSubview:deleteButton];
+ if (!browser_->profile()->IsSupervised()) {
+ NSRect buttonRect;
+ NSDivideRect(rect, &buttonRect, &rect,
+ deleteImageWidth + kHorizontalSpacing, NSMaxXEdge);
+ buttonRect.origin.y = 0;
+
+ base::scoped_nsobject<HoverImageButton> deleteButton(
+ [[HoverImageButton alloc] initWithFrame:buttonRect]);
+ [deleteButton setBordered:NO];
+ [deleteButton setDefaultImage:deleteImage];
+ [deleteButton setHoverImage:rb->GetNativeImageNamed(
+ IDR_CLOSE_1_H).ToNSImage()];
+ [deleteButton setPressedImage:rb->GetNativeImageNamed(
+ IDR_CLOSE_1_P).ToNSImage()];
+ [deleteButton setTarget:self];
+ [deleteButton setAction:@selector(showAccountRemovalView:)];
+ [deleteButton setTag:tag];
+
+ [button addSubview:deleteButton];
+ }
return button.autorelease();
}
serviceType_ = signin::GAIA_SERVICE_TYPE_NONE;
}
+- (bool)shouldShowGoIncognito {
+ bool incognitoAvailable =
+ IncognitoModePrefs::GetAvailability(browser_->profile()->GetPrefs()) !=
+ IncognitoModePrefs::DISABLED;
+ return incognitoAvailable && !browser_->profile()->IsGuestSession();
+}
+
@end