Commit 92164ac3 authored by Jacob Dufault's avatar Jacob Dufault Committed by Commit Bot

cros: Views-login UI for removing a user.

Bug: 809637
Change-Id: Id06bf1b73bed7e2338488b68a1702f13582331f0
Reviewed-on: https://chromium-review.googlesource.com/930242
Commit-Queue: Jacob Dufault <jdufault@chromium.org>
Reviewed-by: default avatarXiaoyin Hu <xiaoyinh@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#539519}
parent 43a5cd79
...@@ -1156,7 +1156,18 @@ This file contains the strings for ash. ...@@ -1156,7 +1156,18 @@ This file contains the strings for ash.
<message name="IDS_ASH_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME" desc="Text to be spoken when the focus is set to the menu button of the user pod on the login screen."> <message name="IDS_ASH_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME" desc="Text to be spoken when the focus is set to the menu button of the user pod on the login screen.">
Options menu for <ph name="USER_EMAIL_ADDRESS">$1<ex>john.doe@example.com</ex></ph> Options menu for <ph name="USER_EMAIL_ADDRESS">$1<ex>john.doe@example.com</ex></ph>
</message> </message>
<message name="IDS_ASH_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME" desc="Text to be spoken when 'Remove user' item is selected on the pod menu.">
Remove this user
</message>
<message name="IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING" desc="Text shown as a warning when attempting to remove legacy supervised user.">
All files and local data associated with the supervised user will be permanently deleted once this supervised user is removed. Visited websites and settings for this supervised user may still be visible by the manager at <ph name="MANAGEMENT_URL">$1<ex>www.example.com</ex></ph>.
</message>
<message name="IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_1" desc="Text shown as a warning when attempting to remove non-owner user. Also see IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_2.">
All files and local data associated with this user will be permanently deleted once this user is removed.
</message>
<message name="IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_2" desc="Text shown as a warning when attempting to remove non-owner user. Also see IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_1.">
$1 can still sign in later.
</message>
<!-- Multi-profiles intro dialog --> <!-- Multi-profiles intro dialog -->
<message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents."> <message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents.">
......
...@@ -17,6 +17,7 @@ class View; ...@@ -17,6 +17,7 @@ class View;
namespace ash { namespace ash {
// Runs a callback whenever a view has gained or lost mouse hover. // Runs a callback whenever a view has gained or lost mouse hover.
// TODO(jdufault): see if we can replace this class with views::MouseWatcher.
class HoverNotifier : public ui::EventHandler { class HoverNotifier : public ui::EventHandler {
public: public:
using OnHover = base::RepeatingCallback<void(bool has_hover)>; using OnHover = base::RepeatingCallback<void(bool has_hover)>;
......
...@@ -9,9 +9,6 @@ ...@@ -9,9 +9,6 @@
namespace ash { namespace ash {
namespace { namespace {
// Spacing between the child view inside the bubble view.
constexpr int kBubbleBetweenChildSpacing = 6;
// Total width of the bubble view. // Total width of the bubble view.
constexpr int kBubbleTotalWidthDp = 178; constexpr int kBubbleTotalWidthDp = 178;
...@@ -28,9 +25,6 @@ constexpr int kBubbleBottomMarginDp = 18; ...@@ -28,9 +25,6 @@ constexpr int kBubbleBottomMarginDp = 18;
LoginBaseBubbleView::LoginBaseBubbleView(views::View* anchor_view) LoginBaseBubbleView::LoginBaseBubbleView(views::View* anchor_view)
: BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE) { : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE) {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::kVertical, gfx::Insets(), kBubbleBetweenChildSpacing));
set_margins(gfx::Insets(kBubbleTopMarginDp, kBubbleHorizontalMarginDp, set_margins(gfx::Insets(kBubbleTopMarginDp, kBubbleHorizontalMarginDp,
kBubbleBottomMarginDp, kBubbleHorizontalMarginDp)); kBubbleBottomMarginDp, kBubbleHorizontalMarginDp));
set_color(SK_ColorBLACK); set_color(SK_ColorBLACK);
...@@ -49,7 +43,7 @@ int LoginBaseBubbleView::GetDialogButtons() const { ...@@ -49,7 +43,7 @@ int LoginBaseBubbleView::GetDialogButtons() const {
} }
gfx::Size LoginBaseBubbleView::CalculatePreferredSize() const { gfx::Size LoginBaseBubbleView::CalculatePreferredSize() const {
gfx::Size size = views::View::CalculatePreferredSize(); gfx::Size size = views::BubbleDialogDelegateView::CalculatePreferredSize();
size.set_width(kBubbleTotalWidthDp); size.set_width(kBubbleTotalWidthDp);
return size; return size;
} }
......
...@@ -20,10 +20,10 @@ class ASH_EXPORT LoginBaseBubbleView : public views::BubbleDialogDelegateView { ...@@ -20,10 +20,10 @@ class ASH_EXPORT LoginBaseBubbleView : public views::BubbleDialogDelegateView {
// views::BubbleDialogDelegateView: // views::BubbleDialogDelegateView:
int GetDialogButtons() const override; int GetDialogButtons() const override;
private:
// views::View: // views::View:
gfx::Size CalculatePreferredSize() const override; gfx::Size CalculatePreferredSize() const override;
private:
DISALLOW_COPY_AND_ASSIGN(LoginBaseBubbleView); DISALLOW_COPY_AND_ASSIGN(LoginBaseBubbleView);
}; };
......
...@@ -4,35 +4,67 @@ ...@@ -4,35 +4,67 @@
#include "ash/login/ui/login_bubble.h" #include "ash/login/ui/login_bubble.h"
#include "ash/ash_constants.h"
#include "ash/focus_cycler.h"
#include "ash/login/ui/layout_util.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/login/ui/lock_window.h"
#include "ash/login/ui/login_button.h" #include "ash/login/ui/login_button.h"
#include "ash/login/ui/non_accessible_view.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer_animator.h" #include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/font.h" #include "ui/gfx/font.h"
#include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/controls/separator.h"
#include "ui/views/controls/styled_label.h" #include "ui/views/controls/styled_label.h"
#include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_manager.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/coordinate_conversion.h"
namespace ash { namespace ash {
namespace { namespace {
constexpr char kLegacySupervisedUserManagementDisplayURL[] =
"www.chrome.com/manage";
// Spacing between the child view inside the bubble view.
constexpr int kBubbleBetweenChildSpacingDp = 6;
// The size of the alert icon in the error bubble. // The size of the alert icon in the error bubble.
constexpr int kAlertIconSizeDp = 20; constexpr int kAlertIconSizeDp = 20;
// Vertical spacing between the anchor view and error bubble.
constexpr int kAnchorViewErrorBubbleVerticalSpacingDp = 48;
// An alpha value for the sub message in the user menu. // An alpha value for the sub message in the user menu.
constexpr SkAlpha kSubMessageColorAlpha = 0x89; constexpr SkAlpha kSubMessageColorAlpha = 0x89;
// Color of the "Remove user" text.
constexpr SkColor kRemoveUserInitialColor = SkColorSetRGB(0x7B, 0xAA, 0xF7);
constexpr SkColor kRemoveUserConfirmColor = SkColorSetRGB(0xE6, 0x7C, 0x73);
// Margin/inset of the entries for the user menu.
constexpr int kUserMenuMarginWidth = 14;
constexpr int kUserMenuMarginHeight = 18;
// Distance above/below the separator.
constexpr int kUserMenuMarginAroundSeparatorDp = 18;
// Distance between labels.
constexpr int kUserMenuVerticalDistanceBetweenLabelsDp = 18;
// Margin around remove user button.
constexpr int kUserMenuMarginAroundRemoveUserButtonDp = 4;
// Vertical spacing between the anchor view and error bubble.
constexpr int kAnchorViewErrorBubbleVerticalSpacingDp = 48;
// Horizontal spacing with the anchor view. // Horizontal spacing with the anchor view.
constexpr int kAnchorViewHorizontalSpacingDp = 105; constexpr int kAnchorViewUserMenuHorizontalSpacingDp = 98;
// Vertical spacing between the anchor view and user menu. // Vertical spacing between the anchor view and user menu.
constexpr int kAnchorViewUserMenuVerticalSpacingDp = 4; constexpr int kAnchorViewUserMenuVerticalSpacingDp = 4;
...@@ -56,12 +88,15 @@ class LoginErrorBubbleView : public LoginBaseBubbleView { ...@@ -56,12 +88,15 @@ class LoginErrorBubbleView : public LoginBaseBubbleView {
public: public:
LoginErrorBubbleView(views::StyledLabel* label, views::View* anchor_view) LoginErrorBubbleView(views::StyledLabel* label, views::View* anchor_view)
: LoginBaseBubbleView(anchor_view) { : LoginBaseBubbleView(anchor_view) {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::kVertical, gfx::Insets(),
kBubbleBetweenChildSpacingDp));
set_anchor_view_insets( set_anchor_view_insets(
gfx::Insets(kAnchorViewErrorBubbleVerticalSpacingDp, 0)); gfx::Insets(kAnchorViewErrorBubbleVerticalSpacingDp, 0));
views::View* alert_view = new views::View(); auto* alert_view = new NonAccessibleView("AlertIconContainer");
alert_view->SetLayoutManager(std::make_unique<views::BoxLayout>( alert_view->SetLayoutManager(
views::BoxLayout::kHorizontal, gfx::Insets())); std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal));
views::ImageView* alert_icon = new views::ImageView(); views::ImageView* alert_icon = new views::ImageView();
alert_icon->SetPreferredSize(gfx::Size(kAlertIconSizeDp, kAlertIconSizeDp)); alert_icon->SetPreferredSize(gfx::Size(kAlertIconSizeDp, kAlertIconSizeDp));
alert_icon->SetImage( alert_icon->SetImage(
...@@ -85,30 +120,207 @@ class LoginErrorBubbleView : public LoginBaseBubbleView { ...@@ -85,30 +120,207 @@ class LoginErrorBubbleView : public LoginBaseBubbleView {
DISALLOW_COPY_AND_ASSIGN(LoginErrorBubbleView); DISALLOW_COPY_AND_ASSIGN(LoginErrorBubbleView);
}; };
class LoginUserMenuView : public LoginBaseBubbleView { // A button that holds a child view.
class ButtonWithContent : public views::Button {
public: public:
LoginUserMenuView(const base::string16& message, ButtonWithContent(views::ButtonListener* listener, views::View* content)
const base::string16& sub_message, : views::Button(listener) {
SetLayoutManager(std::make_unique<views::FillLayout>());
AddChildView(content);
// Increase the size of the button so that the focus is not rendered next to
// the text.
SetBorder(views::CreateEmptyBorder(
gfx::Insets(kUserMenuMarginAroundRemoveUserButtonDp,
kUserMenuMarginAroundRemoveUserButtonDp)));
SetFocusPainter(views::Painter::CreateSolidFocusPainter(
kFocusBorderColor, kFocusBorderThickness, gfx::InsetsF()));
}
~ButtonWithContent() override = default;
private:
DISALLOW_COPY_AND_ASSIGN(ButtonWithContent);
};
class LoginUserMenuView : public LoginBaseBubbleView,
public views::ButtonListener {
public:
LoginUserMenuView(LoginBubble* bubble,
const base::string16& username,
const base::string16& email,
user_manager::UserType type,
bool is_owner,
views::View* anchor_view, views::View* anchor_view,
bool show_remove_user) bool show_remove_user,
: LoginBaseBubbleView(anchor_view) { base::OnceClosure do_remove_user)
views::Label* label = CreateLabel(message, SK_ColorWHITE); : LoginBaseBubbleView(anchor_view),
views::Label* sub_label = CreateLabel( bubble_(bubble),
sub_message, SkColorSetA(SK_ColorWHITE, kSubMessageColorAlpha)); do_remove_user_(std::move(do_remove_user)) {
AddChildView(label); // This view has content the user can interact with if the remove user
AddChildView(sub_label); // button is displayed.
set_anchor_view_insets(gfx::Insets(kAnchorViewUserMenuVerticalSpacingDp, set_can_activate(show_remove_user);
kAnchorViewHorizontalSpacingDp));
// TODO: Show remove user in the menu in login screen. set_anchor_view_insets(gfx::Insets(kAnchorViewUserMenuVerticalSpacingDp,
kAnchorViewUserMenuHorizontalSpacingDp));
// LoginUserMenuView does not use the parent margins. Further, because the
// splitter spans the entire view set_margins cannot be used.
set_margins(gfx::Insets());
// The bottom margin is less the margin around the remove user button, which
// is always visible.
gfx::Insets margins(
kUserMenuMarginHeight, kUserMenuMarginWidth,
kUserMenuMarginHeight - kUserMenuMarginAroundRemoveUserButtonDp,
kUserMenuMarginWidth);
auto create_and_add_horizontal_margin_container = [&]() {
auto* container = new NonAccessibleView("MarginContainer");
container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::kVertical,
gfx::Insets(0, margins.left(), 0, margins.right())));
AddChildView(container);
return container;
};
// Add vertical whitespace.
auto add_space = [](views::View* root, int amount) {
auto* spacer = new NonAccessibleView("Whitespace");
spacer->SetPreferredSize(gfx::Size(1, amount));
root->AddChildView(spacer);
};
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::kVertical,
gfx::Insets(margins.top(), 0, margins.bottom(), 0)));
// User information.
{
base::string16 display_username =
is_owner ? l10n_util::GetStringFUTF16(IDS_ASH_LOGIN_POD_OWNER_USER,
username)
: username;
views::View* container = create_and_add_horizontal_margin_container();
container->AddChildView(CreateLabel(display_username, SK_ColorWHITE));
add_space(container, kBubbleBetweenChildSpacingDp);
container->AddChildView(CreateLabel(
email, SkColorSetA(SK_ColorWHITE, kSubMessageColorAlpha)));
}
// Remove user.
if (show_remove_user) {
DCHECK(!is_owner);
// Add separator.
add_space(this, kUserMenuMarginAroundSeparatorDp);
auto* separator = new views::Separator();
separator->SetColor(SkColorSetA(SK_ColorWHITE, 0x2B));
AddChildView(separator);
// The space below the separator is less the margin around remove user;
// this is readded if showing confirmation.
add_space(this, kUserMenuMarginAroundSeparatorDp -
kUserMenuMarginAroundRemoveUserButtonDp);
auto make_label = [this](const base::string16& text) {
views::Label* label = CreateLabel(text, SK_ColorWHITE);
label->SetMultiLine(true);
// Make sure to set a maximum label width, otherwise text wrapping will
// significantly increase width and layout may not work correctly if
// the input string is very long.
label->SetMaximumWidth(GetPreferredSize().width());
return label;
};
remove_user_confirm_data_ = create_and_add_horizontal_margin_container();
remove_user_confirm_data_->SetVisible(false);
base::string16 part1 = l10n_util::GetStringUTF16(
IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_1);
if (type == user_manager::UserType::USER_TYPE_SUPERVISED) {
part1 = l10n_util::GetStringFUTF16(
IDS_ASH_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING,
base::UTF8ToUTF16(ash::kLegacySupervisedUserManagementDisplayURL));
}
// Account for margin that was removed below the separator for the add
// user button.
add_space(remove_user_confirm_data_,
kUserMenuMarginAroundRemoveUserButtonDp);
remove_user_confirm_data_->AddChildView(make_label(part1));
add_space(remove_user_confirm_data_,
kUserMenuVerticalDistanceBetweenLabelsDp);
remove_user_confirm_data_->AddChildView(
make_label(l10n_util::GetStringFUTF16(
IDS_ASH_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING_PART_2, email)));
// Reduce margin since the remove user button comes next.
add_space(remove_user_confirm_data_,
kUserMenuVerticalDistanceBetweenLabelsDp -
kUserMenuMarginAroundRemoveUserButtonDp);
auto* container = create_and_add_horizontal_margin_container();
remove_user_label_ =
CreateLabel(l10n_util::GetStringUTF16(
IDS_ASH_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME),
kRemoveUserInitialColor);
remove_user_button_ = new ButtonWithContent(this, remove_user_label_);
remove_user_button_->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
remove_user_button_->set_id(
LoginBubble::kUserMenuRemoveUserButtonIdForTest);
container->AddChildView(remove_user_button_);
}
// The user menu is focusable so that the we can detect when to refocus the
// lock window from tab navigation, otherwise focus will be trapped inside
// of the bubble.
SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
} }
~LoginUserMenuView() override = default; ~LoginUserMenuView() override = default;
// views::View: // views::View:
const char* GetClassName() const override { return "LoginUserMenuView"; } const char* GetClassName() const override { return "LoginUserMenuView"; }
gfx::Size CalculatePreferredSize() const override {
gfx::Size size = LoginBaseBubbleView::CalculatePreferredSize();
// We don't use margins() directly which means that we need to account for
// the margin width here. Margin height is accounted for by the layout code.
size.Enlarge(kUserMenuMarginWidth, 0);
return size;
}
void OnFocus() override {
// This view has no actual interesting contents to focus, so immediately
// forward to the button.
remove_user_button_->RequestFocus();
}
void AboutToRequestFocusFromTabTraversal(bool reverse) override {
// Redirect the focus event to the lock screen.
Shell::Get()->focus_cycler()->FocusWidget(LockScreen::Get()->window());
LockScreen::Get()->window()->GetFocusManager()->AdvanceFocus(reverse);
}
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override {
// Show confirmation warning. The user has to click the button again before
// we actually allow the exit.
if (!remove_user_confirm_data_->visible()) {
remove_user_confirm_data_->SetVisible(true);
remove_user_label_->SetEnabledColor(kRemoveUserConfirmColor);
SizeToContents();
GetWidget()->SetSize(size());
Layout();
return;
}
if (do_remove_user_)
std::move(do_remove_user_).Run();
bubble_->Close();
}
private: private:
LoginBubble* bubble_ = nullptr;
base::OnceClosure do_remove_user_;
views::View* remove_user_confirm_data_ = nullptr;
views::Label* remove_user_label_ = nullptr;
ButtonWithContent* remove_user_button_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(LoginUserMenuView); DISALLOW_COPY_AND_ASSIGN(LoginUserMenuView);
}; };
...@@ -116,6 +328,8 @@ class LoginTooltipView : public LoginBaseBubbleView { ...@@ -116,6 +328,8 @@ class LoginTooltipView : public LoginBaseBubbleView {
public: public:
LoginTooltipView(const base::string16& message, views::View* anchor_view) LoginTooltipView(const base::string16& message, views::View* anchor_view)
: LoginBaseBubbleView(anchor_view) { : LoginBaseBubbleView(anchor_view) {
SetLayoutManager(
std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
views::Label* text = CreateLabel(message, SK_ColorWHITE); views::Label* text = CreateLabel(message, SK_ColorWHITE);
text->SetMultiLine(true); text->SetMultiLine(true);
AddChildView(text); AddChildView(text);
...@@ -132,6 +346,9 @@ class LoginTooltipView : public LoginBaseBubbleView { ...@@ -132,6 +346,9 @@ class LoginTooltipView : public LoginBaseBubbleView {
} // namespace } // namespace
// static
const int LoginBubble::kUserMenuRemoveUserButtonIdForTest = 1;
LoginBubble::LoginBubble() { LoginBubble::LoginBubble() {
Shell::Get()->AddPreTargetHandler(this); Shell::Get()->AddPreTargetHandler(this);
} }
...@@ -153,18 +370,29 @@ void LoginBubble::ShowErrorBubble(views::StyledLabel* label, ...@@ -153,18 +370,29 @@ void LoginBubble::ShowErrorBubble(views::StyledLabel* label,
Show(); Show();
} }
void LoginBubble::ShowUserMenu(const base::string16& message, void LoginBubble::ShowUserMenu(const base::string16& username,
const base::string16& sub_message, const base::string16& email,
user_manager::UserType type,
bool is_owner,
views::View* anchor_view, views::View* anchor_view,
LoginButton* bubble_opener, LoginButton* bubble_opener,
bool show_remove_user) { bool show_remove_user,
base::OnceClosure do_remove_user) {
if (bubble_view_) if (bubble_view_)
CloseImmediately(); CloseImmediately();
bubble_opener_ = bubble_opener; bubble_opener_ = bubble_opener;
bubble_view_ = new LoginUserMenuView(message, sub_message, anchor_view, bubble_view_ =
show_remove_user); new LoginUserMenuView(this, username, email, type, is_owner, anchor_view,
show_remove_user, std::move(do_remove_user));
bool had_focus = bubble_opener_->HasFocus();
Show(); Show();
if (had_focus) {
// Try to focus the bubble view only if the tooltip was focused.
bubble_view_->RequestFocus();
}
} }
void LoginBubble::ShowTooltip(const base::string16& message, void LoginBubble::ShowTooltip(const base::string16& message,
...@@ -215,6 +443,10 @@ void LoginBubble::OnKeyEvent(ui::KeyEvent* event) { ...@@ -215,6 +443,10 @@ void LoginBubble::OnKeyEvent(ui::KeyEvent* event) {
if (bubble_opener_ && bubble_opener_->HasFocus()) if (bubble_opener_ && bubble_opener_->HasFocus())
return; return;
// If |bubble_view_| is interactive do not close it.
if (bubble_view_->GetWidget()->IsActive())
return;
Close(); Close();
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/login/ui/login_base_bubble_view.h" #include "ash/login/ui/login_base_bubble_view.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "components/user_manager/user_type.h"
#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_observer.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/widget/widget_observer.h" #include "ui/views/widget/widget_observer.h"
...@@ -26,6 +27,8 @@ class ASH_EXPORT LoginBubble : public views::WidgetObserver, ...@@ -26,6 +27,8 @@ class ASH_EXPORT LoginBubble : public views::WidgetObserver,
public ui::EventHandler, public ui::EventHandler,
public ui::LayerAnimationObserver { public ui::LayerAnimationObserver {
public: public:
static const int kUserMenuRemoveUserButtonIdForTest;
LoginBubble(); LoginBubble();
~LoginBubble() override; ~LoginBubble() override;
...@@ -38,11 +41,14 @@ class ASH_EXPORT LoginBubble : public views::WidgetObserver, ...@@ -38,11 +41,14 @@ class ASH_EXPORT LoginBubble : public views::WidgetObserver,
// |bubble_opener| is a view that could open/close the bubble. // |bubble_opener| is a view that could open/close the bubble.
// |show_remove_user| indicate whether or not we show the // |show_remove_user| indicate whether or not we show the
// "Remove this user" action. // "Remove this user" action.
void ShowUserMenu(const base::string16& message, void ShowUserMenu(const base::string16& username,
const base::string16& sub_message, const base::string16& email,
user_manager::UserType type,
bool is_owner,
views::View* anchor_view, views::View* anchor_view,
LoginButton* bubble_opener, LoginButton* bubble_opener,
bool show_remove_user); bool show_remove_user,
base::OnceClosure do_remove_user);
// Shows a tooltip. // Shows a tooltip.
void ShowTooltip(const base::string16& message, views::View* anchor_view); void ShowTooltip(const base::string16& message, views::View* anchor_view);
......
...@@ -66,6 +66,14 @@ class LoginBubbleTest : public LoginTestBase { ...@@ -66,6 +66,14 @@ class LoginBubbleTest : public LoginTestBase {
LoginTestBase::TearDown(); LoginTestBase::TearDown();
} }
void ShowUserMenu(base::OnceClosure on_remove) {
bool show_remove_user = !on_remove.is_null();
bubble_->ShowUserMenu(
base::string16() /*username*/, base::string16() /*email*/,
user_manager::UserType::USER_TYPE_REGULAR, false /*is_owner*/,
container_, bubble_opener_, show_remove_user, std::move(on_remove));
}
// Owned by test widget view hierarchy. // Owned by test widget view hierarchy.
views::View* container_ = nullptr; views::View* container_ = nullptr;
// Owned by test widget view hierarchy. // Owned by test widget view hierarchy.
...@@ -83,8 +91,7 @@ class LoginBubbleTest : public LoginTestBase { ...@@ -83,8 +91,7 @@ class LoginBubbleTest : public LoginTestBase {
// Verifies the base bubble settings. // Verifies the base bubble settings.
TEST_F(LoginBubbleTest, BaseBubbleSettings) { TEST_F(LoginBubbleTest, BaseBubbleSettings) {
bubble_->ShowUserMenu(base::string16(), base::string16(), container_, bubble_->ShowTooltip(base::string16(), bubble_opener_);
bubble_opener_, false /*show_remove_user*/);
EXPECT_TRUE(bubble_->IsVisible()); EXPECT_TRUE(bubble_->IsVisible());
LoginBaseBubbleView* bubble_view = bubble_->bubble_view_for_test(); LoginBaseBubbleView* bubble_view = bubble_->bubble_view_for_test();
...@@ -108,8 +115,7 @@ TEST_F(LoginBubbleTest, BubbleKeyEventHandling) { ...@@ -108,8 +115,7 @@ TEST_F(LoginBubbleTest, BubbleKeyEventHandling) {
EXPECT_FALSE(bubble_->IsVisible()); EXPECT_FALSE(bubble_->IsVisible());
// Verifies that key event on the bubble opener view won't close the bubble. // Verifies that key event on the bubble opener view won't close the bubble.
bubble_->ShowUserMenu(base::string16(), base::string16(), container_, ShowUserMenu(base::OnceClosure());
bubble_opener_, false /*show_remove_user*/);
EXPECT_TRUE(bubble_->IsVisible()); EXPECT_TRUE(bubble_->IsVisible());
bubble_opener_->RequestFocus(); bubble_opener_->RequestFocus();
generator.PressKey(ui::KeyboardCode::VKEY_A, ui::EF_NONE); generator.PressKey(ui::KeyboardCode::VKEY_A, ui::EF_NONE);
...@@ -132,8 +138,7 @@ TEST_F(LoginBubbleTest, BubbleMouseEventHandling) { ...@@ -132,8 +138,7 @@ TEST_F(LoginBubbleTest, BubbleMouseEventHandling) {
EXPECT_FALSE(bubble_->IsVisible()); EXPECT_FALSE(bubble_->IsVisible());
// Verifies that mouse event on the bubble opener view won't close the bubble. // Verifies that mouse event on the bubble opener view won't close the bubble.
bubble_->ShowUserMenu(base::string16(), base::string16(), container_, ShowUserMenu(base::OnceClosure());
bubble_opener_, false /*show_remove_user*/);
EXPECT_TRUE(bubble_->IsVisible()); EXPECT_TRUE(bubble_->IsVisible());
generator.MoveMouseTo(bubble_opener_->GetBoundsInScreen().CenterPoint()); generator.MoveMouseTo(bubble_opener_->GetBoundsInScreen().CenterPoint());
generator.ClickLeftButton(); generator.ClickLeftButton();
...@@ -162,8 +167,7 @@ TEST_F(LoginBubbleTest, BubbleGestureEventHandling) { ...@@ -162,8 +167,7 @@ TEST_F(LoginBubbleTest, BubbleGestureEventHandling) {
// Verifies that gesture event on the bubble opener view won't close the // Verifies that gesture event on the bubble opener view won't close the
// bubble. // bubble.
bubble_->ShowUserMenu(base::string16(), base::string16(), container_, ShowUserMenu(base::OnceClosure());
bubble_opener_, false /*show_remove_user*/);
EXPECT_TRUE(bubble_->IsVisible()); EXPECT_TRUE(bubble_->IsVisible());
generator.GestureTapAt(bubble_opener_->GetBoundsInScreen().CenterPoint()); generator.GestureTapAt(bubble_opener_->GetBoundsInScreen().CenterPoint());
EXPECT_TRUE(bubble_->IsVisible()); EXPECT_TRUE(bubble_->IsVisible());
...@@ -185,8 +189,7 @@ TEST_F(LoginBubbleTest, LoginButtonRipple) { ...@@ -185,8 +189,7 @@ TEST_F(LoginBubbleTest, LoginButtonRipple) {
views::InkDropHostView::InkDropMode::ON); views::InkDropHostView::InkDropMode::ON);
// Show the bubble to activate the ripple effect. // Show the bubble to activate the ripple effect.
bubble_->ShowUserMenu(base::string16(), base::string16(), container_, ShowUserMenu(base::OnceClosure());
bubble_opener_, false /*show_remove_user*/);
EXPECT_TRUE(bubble_->IsVisible()); EXPECT_TRUE(bubble_->IsVisible());
EXPECT_TRUE(ink_drop_api.HasInkDrop()); EXPECT_TRUE(ink_drop_api.HasInkDrop());
EXPECT_EQ(ink_drop_api.GetInkDrop()->GetTargetInkDropState(), EXPECT_EQ(ink_drop_api.GetInkDrop()->GetTargetInkDropState(),
...@@ -206,4 +209,31 @@ TEST_F(LoginBubbleTest, LoginButtonRipple) { ...@@ -206,4 +209,31 @@ TEST_F(LoginBubbleTest, LoginButtonRipple) {
EXPECT_FALSE(ink_drop_api.GetInkDrop()->IsHighlightFadingInOrVisible()); EXPECT_FALSE(ink_drop_api.GetInkDrop()->IsHighlightFadingInOrVisible());
} }
// Verifies that clicking remove user requires two clicks before firing the
// callback.
TEST_F(LoginBubbleTest, RemoveUserRequiresTwoActivations) {
// Show the user menu.
bool remove_called = false;
ShowUserMenu(base::BindOnce(
[](bool* remove_called) { *remove_called = true; }, &remove_called));
EXPECT_TRUE(bubble_->IsVisible());
// Focus the remove user button.
views::View* remove_user_button =
bubble_->bubble_view_for_test()->GetViewByID(
LoginBubble::kUserMenuRemoveUserButtonIdForTest);
remove_user_button->RequestFocus();
EXPECT_TRUE(remove_user_button->HasFocus());
// Click it twice. Verify only the second click fires the callback.
auto click = [&]() {
EXPECT_TRUE(remove_user_button->HasFocus());
GetEventGenerator().PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
};
EXPECT_NO_FATAL_FAILURE(click());
EXPECT_FALSE(remove_called);
EXPECT_NO_FATAL_FAILURE(click());
EXPECT_TRUE(remove_called);
}
} // namespace ash } // namespace ash
...@@ -405,17 +405,13 @@ void LoginUserView::ButtonPressed(views::Button* sender, ...@@ -405,17 +405,13 @@ void LoginUserView::ButtonPressed(views::Button* sender,
if (sender == user_dropdown_) { if (sender == user_dropdown_) {
DCHECK(user_dropdown_); DCHECK(user_dropdown_);
if (!user_menu_->IsVisible()) { if (!user_menu_->IsVisible()) {
base::string16 display_name =
base::UTF8ToUTF16(current_user_->basic_user_info->display_name);
user_menu_->ShowUserMenu( user_menu_->ShowUserMenu(
current_user_->is_device_owner base::UTF8ToUTF16(current_user_->basic_user_info->display_name),
? l10n_util::GetStringFUTF16(IDS_ASH_LOGIN_POD_OWNER_USER,
display_name)
: display_name,
base::UTF8ToUTF16(current_user_->basic_user_info->display_email), base::UTF8ToUTF16(current_user_->basic_user_info->display_email),
current_user_->basic_user_info->type, current_user_->is_device_owner,
user_dropdown_ /*anchor_view*/, user_dropdown_ /*bubble_opener*/, user_dropdown_ /*anchor_view*/, user_dropdown_ /*bubble_opener*/,
false /*show_remove_user*/); current_user_->can_remove /*show_remove_user*/,
base::OnceClosure() /*do_remove_user*/);
} else { } else {
user_menu_->Close(); user_menu_->Close();
} }
......
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