Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(223)

Side by Side Diff: ash/common/system/user/user_view.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/common/system/user/user_view.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "ash/common/multi_profile_uma.h"
11 #include "ash/common/session/session_state_delegate.h"
12 #include "ash/common/shell_delegate.h"
13 #include "ash/common/system/tray/system_tray.h"
14 #include "ash/common/system/tray/system_tray_controller.h"
15 #include "ash/common/system/tray/system_tray_delegate.h"
16 #include "ash/common/system/tray/tray_constants.h"
17 #include "ash/common/system/tray/tray_popup_item_style.h"
18 #include "ash/common/system/tray/tray_popup_utils.h"
19 #include "ash/common/system/user/button_from_view.h"
20 #include "ash/common/system/user/login_status.h"
21 #include "ash/common/system/user/rounded_image_view.h"
22 #include "ash/common/system/user/user_card_view.h"
23 #include "ash/common/wm_shell.h"
24 #include "ash/common/wm_window.h"
25 #include "ash/public/cpp/shell_window_ids.h"
26 #include "ash/resources/grit/ash_resources.h"
27 #include "ash/resources/vector_icons/vector_icons.h"
28 #include "ash/root_window_controller.h"
29 #include "ash/strings/grit/ash_strings.h"
30 #include "base/memory/ptr_util.h"
31 #include "components/signin/core/account_id/account_id.h"
32 #include "components/user_manager/user_info.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/resource/resource_bundle.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/gfx/geometry/insets.h"
37 #include "ui/gfx/paint_vector_icon.h"
38 #include "ui/views/controls/button/label_button.h"
39 #include "ui/views/controls/label.h"
40 #include "ui/views/controls/separator.h"
41 #include "ui/views/layout/fill_layout.h"
42 #include "ui/views/painter.h"
43
44 namespace ash {
45 namespace tray {
46
47 namespace {
48
49 // Switch to a user with the given |user_index|.
50 void SwitchUser(UserIndex user_index) {
51 // Do not switch users when the log screen is presented.
52 SessionStateDelegate* delegate = WmShell::Get()->GetSessionStateDelegate();
53 if (delegate->IsUserSessionBlocked())
54 return;
55
56 DCHECK(user_index > 0);
57 MultiProfileUMA::RecordSwitchActiveUser(
58 MultiProfileUMA::SWITCH_ACTIVE_USER_BY_TRAY);
59 delegate->SwitchActiveUser(delegate->GetUserInfo(user_index)->GetAccountId());
60 }
61
62 bool IsMultiProfileSupportedAndUserActive() {
63 return WmShell::Get()->delegate()->IsMultiProfilesEnabled() &&
64 !WmShell::Get()->GetSessionStateDelegate()->IsUserSessionBlocked();
65 }
66
67 // Creates the view shown in the user switcher popup ("AddUserMenuOption").
68 views::View* CreateAddUserView(AddUserSessionPolicy policy,
69 views::ButtonListener* listener) {
70 auto* view = new views::View;
71 const int icon_padding = (kMenuButtonSize - kMenuIconSize) / 2;
72 auto* layout =
73 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
74 kTrayPopupLabelHorizontalPadding + icon_padding);
75 layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight);
76 view->SetLayoutManager(layout);
77 view->set_background(
78 views::Background::CreateSolidBackground(kBackgroundColor));
79
80 int message_id = 0;
81 switch (policy) {
82 case AddUserSessionPolicy::ALLOWED: {
83 message_id = IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT;
84
85 auto* icon = new views::ImageView();
86 icon->SetImage(
87 gfx::CreateVectorIcon(kSystemMenuNewUserIcon, kMenuIconColor));
88 view->AddChildView(icon);
89 break;
90 }
91 case AddUserSessionPolicy::ERROR_NOT_ALLOWED_PRIMARY_USER:
92 message_id = IDS_ASH_STATUS_TRAY_MESSAGE_NOT_ALLOWED_PRIMARY_USER;
93 break;
94 case AddUserSessionPolicy::ERROR_MAXIMUM_USERS_REACHED:
95 message_id = IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER;
96 break;
97 case AddUserSessionPolicy::ERROR_NO_ELIGIBLE_USERS:
98 message_id = IDS_ASH_STATUS_TRAY_MESSAGE_OUT_OF_USERS;
99 break;
100 }
101
102 auto* command_label = new views::Label(l10n_util::GetStringUTF16(message_id));
103 command_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
104 command_label->SetMultiLine(true);
105
106 TrayPopupItemStyle label_style(
107 TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
108 int vertical_padding = kMenuSeparatorVerticalPadding;
109 if (policy != AddUserSessionPolicy::ALLOWED) {
110 label_style.set_font_style(TrayPopupItemStyle::FontStyle::CAPTION);
111 label_style.set_color_style(TrayPopupItemStyle::ColorStyle::INACTIVE);
112 vertical_padding += kMenuSeparatorVerticalPadding;
113 }
114 label_style.SetupLabel(command_label);
115 view->AddChildView(command_label);
116 view->SetBorder(views::CreateEmptyBorder(vertical_padding, icon_padding,
117 vertical_padding,
118 kTrayPopupLabelHorizontalPadding));
119 if (policy == AddUserSessionPolicy::ALLOWED) {
120 auto* button =
121 new ButtonFromView(view, listener, TrayPopupInkDropStyle::INSET_BOUNDS);
122 button->SetAccessibleName(
123 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT));
124 return button;
125 }
126
127 return view;
128 }
129
130 class UserViewMouseWatcherHost : public views::MouseWatcherHost {
131 public:
132 explicit UserViewMouseWatcherHost(const gfx::Rect& screen_area)
133 : screen_area_(screen_area) {}
134 ~UserViewMouseWatcherHost() override {}
135
136 // Implementation of MouseWatcherHost.
137 bool Contains(const gfx::Point& screen_point,
138 views::MouseWatcherHost::MouseEventType type) override {
139 return screen_area_.Contains(screen_point);
140 }
141
142 private:
143 gfx::Rect screen_area_;
144
145 DISALLOW_COPY_AND_ASSIGN(UserViewMouseWatcherHost);
146 };
147
148 // A view that acts as the contents of the widget that appears when clicking
149 // the active user. If the mouse exits this view or an otherwise unhandled
150 // click is detected, it will invoke a closure passed at construction time.
151 class AddUserWidgetContents : public views::View {
152 public:
153 explicit AddUserWidgetContents(const base::Closure& close_widget)
154 : close_widget_(close_widget) {
155 // Don't want to receive a mouse exit event when the cursor enters a child.
156 set_notify_enter_exit_on_child(true);
157 }
158
159 ~AddUserWidgetContents() override {}
160
161 bool OnMousePressed(const ui::MouseEvent& event) override { return true; }
162 void OnMouseReleased(const ui::MouseEvent& event) override {
163 close_widget_.Run();
164 }
165 void OnMouseExited(const ui::MouseEvent& event) override {
166 close_widget_.Run();
167 }
168 void OnGestureEvent(ui::GestureEvent* event) override { close_widget_.Run(); }
169
170 private:
171 base::Closure close_widget_;
172
173 DISALLOW_COPY_AND_ASSIGN(AddUserWidgetContents);
174 };
175
176 // This border reserves 4dp above and 8dp below and paints a horizontal
177 // separator 3dp below the host view.
178 class ActiveUserBorder : public views::Border {
179 public:
180 ActiveUserBorder() {}
181 ~ActiveUserBorder() override {}
182
183 // views::Border:
184 void Paint(const views::View& view, gfx::Canvas* canvas) override {
185 canvas->FillRect(
186 gfx::Rect(
187 0, view.height() - kMenuSeparatorVerticalPadding - kSeparatorWidth,
188 view.width(), kSeparatorWidth),
189 kMenuSeparatorColor);
190 }
191
192 gfx::Insets GetInsets() const override {
193 return gfx::Insets(kMenuSeparatorVerticalPadding,
194 kMenuExtraMarginFromLeftEdge,
195 kMenuSeparatorVerticalPadding * 2, 0);
196 }
197
198 gfx::Size GetMinimumSize() const override { return gfx::Size(); }
199
200 private:
201 DISALLOW_COPY_AND_ASSIGN(ActiveUserBorder);
202 };
203
204 } // namespace
205
206 UserView::UserView(SystemTrayItem* owner, LoginStatus login, UserIndex index)
207 : user_index_(index),
208 user_card_view_(nullptr),
209 owner_(owner),
210 is_user_card_button_(false),
211 logout_button_(nullptr),
212 add_user_enabled_(true),
213 focus_manager_(nullptr) {
214 CHECK_NE(LoginStatus::NOT_LOGGED_IN, login);
215 // The logout button must be added before the user card so that the user card
216 // can correctly calculate the remaining available width.
217 // Note that only the current multiprofile user gets a button.
218 if (IsActiveUser())
219 AddLogoutButton(login);
220 AddUserCard(login);
221
222 auto* layout = new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
223 SetLayoutManager(layout);
224 layout->set_cross_axis_alignment(
225 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
226 layout->SetFlexForView(user_card_view_, 1);
227
228 if (IsActiveUser())
229 SetBorder(base::MakeUnique<ActiveUserBorder>());
230 }
231
232 UserView::~UserView() {
233 RemoveAddUserMenuOption();
234 }
235
236 TrayUser::TestState UserView::GetStateForTest() const {
237 if (add_menu_option_)
238 return add_user_enabled_ ? TrayUser::ACTIVE : TrayUser::ACTIVE_BUT_DISABLED;
239
240 if (!is_user_card_button_)
241 return TrayUser::SHOWN;
242
243 return static_cast<ButtonFromView*>(user_card_view_)->is_hovered_for_test()
244 ? TrayUser::HOVERED
245 : TrayUser::SHOWN;
246 }
247
248 gfx::Rect UserView::GetBoundsInScreenOfUserButtonForTest() {
249 DCHECK(user_card_view_);
250 return user_card_view_->GetBoundsInScreen();
251 }
252
253 bool UserView::IsActiveUser() const {
254 return user_index_ == 0;
255 }
256
257 int UserView::GetHeightForWidth(int width) const {
258 return GetPreferredSize().height();
259 }
260
261 void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
262 if (sender == logout_button_) {
263 WmShell::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_SIGN_OUT);
264 RemoveAddUserMenuOption();
265 WmShell::Get()->system_tray_controller()->SignOut();
266 } else if (sender == user_card_view_ &&
267 IsMultiProfileSupportedAndUserActive()) {
268 if (IsActiveUser()) {
269 ToggleAddUserMenuOption();
270 } else {
271 RemoveAddUserMenuOption();
272 SwitchUser(user_index_);
273 // Since the user list is about to change the system menu should get
274 // closed.
275 owner_->system_tray()->CloseSystemBubble();
276 }
277 } else if (add_menu_option_ &&
278 sender->GetWidget() == add_menu_option_.get()) {
279 RemoveAddUserMenuOption();
280 // Let the user add another account to the session.
281 MultiProfileUMA::RecordSigninUser(MultiProfileUMA::SIGNIN_USER_BY_TRAY);
282 WmShell::Get()->system_tray_delegate()->ShowUserLogin();
283 owner_->system_tray()->CloseSystemBubble();
284 } else {
285 NOTREACHED();
286 }
287 }
288
289 void UserView::OnWillChangeFocus(View* focused_before, View* focused_now) {
290 if (focused_now)
291 RemoveAddUserMenuOption();
292 }
293
294 void UserView::OnDidChangeFocus(View* focused_before, View* focused_now) {
295 // Nothing to do here.
296 }
297
298 void UserView::AddLogoutButton(LoginStatus login) {
299 AddChildView(TrayPopupUtils::CreateVerticalSeparator());
300 logout_button_ = TrayPopupUtils::CreateTrayPopupBorderlessButton(
301 this, user::GetLocalizedSignOutStringForStatus(login, true));
302 AddChildView(logout_button_);
303 }
304
305 void UserView::AddUserCard(LoginStatus login) {
306 user_card_view_ = new UserCardView(login, -1, user_index_);
307 // The entry is clickable when no system modal dialog is open and the multi
308 // profile option is active.
309 bool clickable = !WmShell::Get()->IsSystemModalWindowOpen() &&
310 IsMultiProfileSupportedAndUserActive();
311 if (clickable) {
312 views::View* contents_view = user_card_view_;
313 auto* button =
314 new ButtonFromView(contents_view, this,
315 IsActiveUser() ? TrayPopupInkDropStyle::INSET_BOUNDS
316 : TrayPopupInkDropStyle::FILL_BOUNDS);
317 user_card_view_ = button;
318 is_user_card_button_ = true;
319 }
320 AddChildViewAt(user_card_view_, 0);
321 }
322
323 void UserView::ToggleAddUserMenuOption() {
324 if (add_menu_option_) {
325 RemoveAddUserMenuOption();
326 return;
327 }
328
329 // Note: We do not need to install a global event handler to delete this
330 // item since it will destroyed automatically before the menu / user menu item
331 // gets destroyed..
332 add_menu_option_.reset(new views::Widget);
333 views::Widget::InitParams params;
334 params.type = views::Widget::InitParams::TYPE_TOOLTIP;
335 params.keep_on_top = true;
336 params.accept_events = true;
337 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
338 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
339 params.name = "AddUserMenuOption";
340 WmWindow::Get(GetWidget()->GetNativeWindow())
341 ->GetRootWindowController()
342 ->ConfigureWidgetInitParamsForContainer(
343 add_menu_option_.get(), kShellWindowId_DragImageAndTooltipContainer,
344 &params);
345 add_menu_option_->Init(params);
346
347 const SessionStateDelegate* delegate =
348 WmShell::Get()->GetSessionStateDelegate();
349 const AddUserSessionPolicy add_user_policy =
350 delegate->GetAddUserSessionPolicy();
351 add_user_enabled_ = add_user_policy == AddUserSessionPolicy::ALLOWED;
352
353 // Position the widget on top of the user card view (which is still in the
354 // system menu). The top half of the widget will be transparent to allow
355 // the active user to show through.
356 gfx::Rect bounds = user_card_view_->GetBoundsInScreen();
357 bounds.set_width(bounds.width() + kSeparatorWidth);
358 int row_height = bounds.height();
359
360 views::View* container = new AddUserWidgetContents(
361 base::Bind(&UserView::RemoveAddUserMenuOption, base::Unretained(this)));
362 container->SetBorder(views::CreatePaddedBorder(
363 views::CreateSolidSidedBorder(0, 0, 0, kSeparatorWidth, kBackgroundColor),
364 gfx::Insets(row_height, 0, 0, 0)));
365 views::View* add_user_padding = new views::View();
366 add_user_padding->SetBorder(views::CreateSolidSidedBorder(
367 kMenuSeparatorVerticalPadding, 0, 0, 0, kBackgroundColor));
368 views::View* add_user_view = CreateAddUserView(add_user_policy, this);
369 add_user_padding->AddChildView(add_user_view);
370 add_user_padding->SetLayoutManager(new views::FillLayout());
371 container->AddChildView(add_user_padding);
372 container->SetLayoutManager(new views::FillLayout());
373 add_menu_option_->SetContentsView(container);
374
375 bounds.set_height(container->GetPreferredSize().height());
376 add_menu_option_->SetBounds(bounds);
377
378 // Show the content.
379 add_menu_option_->SetAlwaysOnTop(true);
380 add_menu_option_->Show();
381
382 // Install a listener to focus changes so that we can remove the card when
383 // the focus gets changed. When called through the destruction of the bubble,
384 // the FocusManager cannot be determined anymore and we remember it here.
385 focus_manager_ = user_card_view_->GetFocusManager();
386 focus_manager_->AddFocusChangeListener(this);
387 }
388
389 void UserView::RemoveAddUserMenuOption() {
390 if (!add_menu_option_)
391 return;
392 focus_manager_->RemoveFocusChangeListener(this);
393 focus_manager_ = nullptr;
394 if (user_card_view_->GetFocusManager())
395 user_card_view_->GetFocusManager()->ClearFocus();
396 add_menu_option_.reset();
397 }
398
399 } // namespace tray
400 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/system/user/user_view.h ('k') | ash/common/system/web_notification/ash_popup_alignment_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698