1 // Copyright (c) 2012 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.
5 #include "ash/system/user/tray_user.h"
11 #include "ash/ash_switches.h"
12 #include "ash/metrics/user_metrics_recorder.h"
13 #include "ash/multi_profile_uma.h"
14 #include "ash/popup_message.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/session_state_delegate.h"
17 #include "ash/shelf/shelf_layout_manager.h"
18 #include "ash/shell.h"
19 #include "ash/shell_delegate.h"
20 #include "ash/system/tray/system_tray.h"
21 #include "ash/system/tray/system_tray_delegate.h"
22 #include "ash/system/tray/system_tray_notifier.h"
23 #include "ash/system/tray/tray_constants.h"
24 #include "ash/system/tray/tray_item_view.h"
25 #include "ash/system/tray/tray_popup_label_button.h"
26 #include "ash/system/tray/tray_popup_label_button_border.h"
27 #include "ash/system/tray/tray_utils.h"
28 #include "base/i18n/rtl.h"
29 #include "base/logging.h"
30 #include "base/memory/scoped_vector.h"
31 #include "base/strings/string16.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/utf_string_conversions.h"
34 #include "grit/ash_resources.h"
35 #include "grit/ash_strings.h"
36 #include "skia/ext/image_operations.h"
37 #include "third_party/skia/include/core/SkCanvas.h"
38 #include "third_party/skia/include/core/SkPaint.h"
39 #include "third_party/skia/include/core/SkPath.h"
40 #include "ui/aura/window.h"
41 #include "ui/base/l10n/l10n_util.h"
42 #include "ui/base/resource/resource_bundle.h"
43 #include "ui/gfx/canvas.h"
44 #include "ui/gfx/font_list.h"
45 #include "ui/gfx/image/image.h"
46 #include "ui/gfx/image/image_skia_operations.h"
47 #include "ui/gfx/insets.h"
48 #include "ui/gfx/range/range.h"
49 #include "ui/gfx/rect.h"
50 #include "ui/gfx/render_text.h"
51 #include "ui/gfx/size.h"
52 #include "ui/gfx/skia_util.h"
53 #include "ui/gfx/text_elider.h"
54 #include "ui/gfx/text_utils.h"
55 #include "ui/views/border.h"
56 #include "ui/views/bubble/tray_bubble_view.h"
57 #include "ui/views/controls/button/button.h"
58 #include "ui/views/controls/button/custom_button.h"
59 #include "ui/views/controls/image_view.h"
60 #include "ui/views/controls/label.h"
61 #include "ui/views/controls/link.h"
62 #include "ui/views/controls/link_listener.h"
63 #include "ui/views/corewm/shadow_types.h"
64 #include "ui/views/layout/box_layout.h"
65 #include "ui/views/layout/fill_layout.h"
66 #include "ui/views/mouse_watcher.h"
67 #include "ui/views/painter.h"
68 #include "ui/views/view.h"
69 #include "ui/views/widget/widget.h"
73 const int kUserDetailsVerticalPadding = 5;
74 const int kUserCardVerticalPadding = 10;
75 const int kProfileRoundedCornerRadius = 2;
76 const int kUserIconSize = 27;
77 const int kUserIconLargeSize = 32;
78 const int kUserIconLargeCornerRadius = 2;
79 const int kUserLabelToIconPadding = 5;
80 // When using multi login, this spacing is added between user icons.
81 const int kTrayLabelSpacing = 1;
83 // When a hover border is used, it is starting this many pixels before the icon
85 const int kTrayUserTileHoverBorderInset = 10;
87 // The border color of the user button.
88 const SkColor kBorderColor = 0xffdcdcdc;
90 // The invisible word joiner character, used as a marker to indicate the start
91 // and end of the user's display name in the public account user card's text.
92 const base::char16 kDisplayNameMark[] = { 0x2060, 0 };
94 const int kPublicAccountLogoutButtonBorderImagesNormal[] = {
95 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
96 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
97 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
98 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
99 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
100 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
101 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
102 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
103 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
106 const int kPublicAccountLogoutButtonBorderImagesHovered[] = {
107 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
108 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
109 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
110 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
111 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND,
112 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
113 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
114 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
115 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
118 // Offsetting the popup message relative to the tray menu.
119 const int kPopupMessageOffset = 25;
121 // Switch to a user with the given |user_index|.
122 void SwitchUser(ash::MultiProfileIndex user_index) {
123 // Do not switch users when the log screen is presented.
124 if (ash::Shell::GetInstance()->session_state_delegate()->
125 IsUserSessionBlocked())
128 DCHECK(user_index > 0);
129 ash::SessionStateDelegate* delegate =
130 ash::Shell::GetInstance()->session_state_delegate();
131 ash::MultiProfileUMA::RecordSwitchActiveUser(
132 ash::MultiProfileUMA::SWITCH_ACTIVE_USER_BY_TRAY);
133 delegate->SwitchActiveUser(delegate->GetUserID(user_index));
143 // A custom image view with rounded edges.
144 class RoundedImageView : public views::View {
146 // Constructs a new rounded image view with rounded corners of radius
147 // |corner_radius|. If |active_user| is set, the icon will be drawn in
148 // full colors - otherwise it will fade into the background.
149 RoundedImageView(int corner_radius, bool active_user);
150 virtual ~RoundedImageView();
152 // Set the image that should be displayed. The image contents is copied to the
154 void SetImage(const gfx::ImageSkia& img, const gfx::Size& size);
156 // Set the radii of the corners independently.
157 void SetCornerRadii(int top_left,
163 // Overridden from views::View.
164 virtual gfx::Size GetPreferredSize() OVERRIDE;
165 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
167 gfx::ImageSkia image_;
168 gfx::ImageSkia resized_;
169 gfx::Size image_size_;
170 int corner_radius_[4];
172 // True if the given user is the active user and the icon should get
173 // painted as active.
176 DISALLOW_COPY_AND_ASSIGN(RoundedImageView);
179 // An inactive user view which can be clicked to make active. Note that this
180 // "button" does not show as a button any click or hover changes.
181 class UserSwitcherView : public RoundedImageView {
183 UserSwitcherView(int corner_radius, MultiProfileIndex user_index);
184 virtual ~UserSwitcherView() {}
186 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
187 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
190 // The user index to activate when the item was clicked. Note that this
191 // index refers to the LRU list of logged in users.
192 MultiProfileIndex user_index_;
194 DISALLOW_COPY_AND_ASSIGN(UserSwitcherView);
197 // The user details shown in public account mode. This is essentially a label
198 // but with custom painting code as the text is styled with multiple colors and
200 class PublicAccountUserDetails : public views::View,
201 public views::LinkListener {
203 PublicAccountUserDetails(SystemTrayItem* owner, int used_width);
204 virtual ~PublicAccountUserDetails();
207 // Overridden from views::View.
208 virtual void Layout() OVERRIDE;
209 virtual gfx::Size GetPreferredSize() OVERRIDE;
210 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
212 // Overridden from views::LinkListener.
213 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
215 // Calculate a preferred size that ensures the label text and the following
216 // link do not wrap over more than three lines in total for aesthetic reasons
218 void CalculatePreferredSize(SystemTrayItem* owner, int used_width);
220 base::string16 text_;
221 views::Link* learn_more_;
222 gfx::Size preferred_size_;
223 ScopedVector<gfx::RenderText> lines_;
225 DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
228 // The button which holds the user card in case of multi profile.
229 class UserCard : public views::CustomButton {
231 UserCard(views::ButtonListener* listener, bool active_user);
234 // Called when the border should remain even in the non highlighted state.
235 void ForceBorderVisible(bool show);
237 // Overridden from views::View
238 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
239 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
241 // Check if the item is hovered.
242 bool is_hovered_for_test() {return button_hovered_; }
245 // Change the hover/active state of the "button" when the status changes.
248 // True if this is the active user.
249 bool is_active_user_;
251 // True if button is hovered.
252 bool button_hovered_;
254 // True if the border should be visible.
257 DISALLOW_COPY_AND_ASSIGN(UserCard);
260 class UserViewMouseWatcherHost : public views::MouseWatcherHost {
262 explicit UserViewMouseWatcherHost(const gfx::Rect& screen_area)
263 : screen_area_(screen_area) {}
264 virtual ~UserViewMouseWatcherHost() {}
266 // Implementation of MouseWatcherHost.
267 virtual bool Contains(const gfx::Point& screen_point,
268 views::MouseWatcherHost::MouseEventType type) OVERRIDE {
269 return screen_area_.Contains(screen_point);
273 gfx::Rect screen_area_;
275 DISALLOW_COPY_AND_ASSIGN(UserViewMouseWatcherHost);
278 // The view of a user item.
279 class UserView : public views::View,
280 public views::ButtonListener,
281 public views::MouseWatcherListener {
283 UserView(SystemTrayItem* owner,
284 ash::user::LoginStatus login,
285 MultiProfileIndex index);
288 // Overridden from MouseWatcherListener:
289 virtual void MouseMovedOutOfHost() OVERRIDE;
291 TrayUser::TestState GetStateForTest() const;
292 gfx::Rect GetBoundsInScreenOfUserButtonForTest();
295 // Overridden from views::View.
296 virtual gfx::Size GetPreferredSize() OVERRIDE;
297 virtual int GetHeightForWidth(int width) OVERRIDE;
298 virtual void Layout() OVERRIDE;
300 // Overridden from views::ButtonListener.
301 virtual void ButtonPressed(views::Button* sender,
302 const ui::Event& event) OVERRIDE;
304 void AddLogoutButton(user::LoginStatus login);
305 void AddUserCard(SystemTrayItem* owner, user::LoginStatus login);
307 // Create a user icon representation for the user card.
308 views::View* CreateIconForUserCard(user::LoginStatus login);
310 // Create the additional user card content for the retail logged in mode.
311 void AddLoggedInRetailModeUserCardContent();
313 // Create the additional user card content for the public mode.
314 void AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner);
316 // Create the menu option to add another user. If |disabled| is set the user
317 // cannot actively click on the item.
318 void ToggleAddUserMenuOption();
320 // Returns true when multi profile is supported.
321 bool SupportsMultiProfile();
323 MultiProfileIndex multiprofile_index_;
324 // The view of the user card.
325 views::View* user_card_view_;
327 // This is the owner system tray item of this view.
328 SystemTrayItem* owner_;
330 // True if |user_card_view_| is a |UserView| - otherwise it is only a
333 views::View* logout_button_;
334 scoped_ptr<PopupMessage> popup_message_;
335 scoped_ptr<views::Widget> add_menu_option_;
337 // True when the add user panel is visible but not activatable.
338 bool add_user_visible_but_disabled_;
340 // The mouse watcher which takes care of out of window hover events.
341 scoped_ptr<views::MouseWatcher> mouse_watcher_;
343 DISALLOW_COPY_AND_ASSIGN(UserView);
346 // The menu item view which gets shown when the user clicks in multi profile
347 // mode onto the user item.
348 class AddUserView : public views::CustomButton,
349 public views::ButtonListener {
351 // The |owner| is the view for which this view gets created. The |listener|
352 // will get notified when this item gets clicked.
353 AddUserView(UserCard* owner, views::ButtonListener* listener);
354 virtual ~AddUserView();
356 // Get the anchor view for a message.
357 views::View* anchor() { return anchor_; }
359 // Overridden from views::ButtonListener.
360 virtual void ButtonPressed(views::Button* sender,
361 const ui::Event& event) OVERRIDE;
364 // Overridden from views::View.
365 virtual gfx::Size GetPreferredSize() OVERRIDE;
366 virtual int GetHeightForWidth(int width) OVERRIDE;
367 virtual void Layout() OVERRIDE;
369 // Create the additional client content for this item.
372 // This is the content we create and show.
373 views::View* add_user_;
375 // This listener will get informed when someone clicks on this button.
376 views::ButtonListener* listener_;
378 // This is the owner view of this item.
381 // The anchor view for targetted bubble messages.
382 views::View* anchor_;
384 DISALLOW_COPY_AND_ASSIGN(AddUserView);
387 RoundedImageView::RoundedImageView(int corner_radius, bool active_user)
388 : active_user_(active_user) {
389 for (int i = 0; i < 4; ++i)
390 corner_radius_[i] = corner_radius;
393 RoundedImageView::~RoundedImageView() {}
395 void RoundedImageView::SetImage(const gfx::ImageSkia& img,
396 const gfx::Size& size) {
400 // Try to get the best image quality for the avatar.
401 resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_,
402 skia::ImageOperations::RESIZE_BEST, size);
403 if (GetWidget() && visible()) {
404 PreferredSizeChanged();
409 void RoundedImageView::SetCornerRadii(int top_left,
413 corner_radius_[0] = top_left;
414 corner_radius_[1] = top_right;
415 corner_radius_[2] = bottom_right;
416 corner_radius_[3] = bottom_left;
419 gfx::Size RoundedImageView::GetPreferredSize() {
420 return gfx::Size(image_size_.width() + GetInsets().width(),
421 image_size_.height() + GetInsets().height());
424 void RoundedImageView::OnPaint(gfx::Canvas* canvas) {
425 View::OnPaint(canvas);
426 gfx::Rect image_bounds(size());
427 image_bounds.ClampToCenteredSize(GetPreferredSize());
428 image_bounds.Inset(GetInsets());
429 const SkScalar kRadius[8] = {
430 SkIntToScalar(corner_radius_[0]),
431 SkIntToScalar(corner_radius_[0]),
432 SkIntToScalar(corner_radius_[1]),
433 SkIntToScalar(corner_radius_[1]),
434 SkIntToScalar(corner_radius_[2]),
435 SkIntToScalar(corner_radius_[2]),
436 SkIntToScalar(corner_radius_[3]),
437 SkIntToScalar(corner_radius_[3])
440 path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius);
442 paint.setAntiAlias(true);
443 paint.setXfermodeMode(active_user_ ? SkXfermode::kSrcOver_Mode :
444 SkXfermode::kLuminosity_Mode);
445 canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(),
449 UserSwitcherView::UserSwitcherView(int corner_radius,
450 MultiProfileIndex user_index)
451 : RoundedImageView(corner_radius, false),
452 user_index_(user_index) {
456 void UserSwitcherView::OnMouseEvent(ui::MouseEvent* event) {
457 if (event->type() == ui::ET_MOUSE_PRESSED) {
458 SwitchUser(user_index_);
463 void UserSwitcherView::OnTouchEvent(ui::TouchEvent* event) {
464 if (event->type() == ui::ET_TOUCH_PRESSED) {
465 SwitchUser(user_index_);
470 PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner,
472 : learn_more_(NULL) {
473 const int inner_padding =
474 kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
475 const bool rtl = base::i18n::IsRTL();
476 SetBorder(views::Border::CreateEmptyBorder(kUserDetailsVerticalPadding,
477 rtl ? 0 : inner_padding,
478 kUserDetailsVerticalPadding,
479 rtl ? inner_padding : 0));
481 // Retrieve the user's display name and wrap it with markers.
482 // Note that since this is a public account it always has to be the primary
484 base::string16 display_name =
485 Shell::GetInstance()->session_state_delegate()->GetUserDisplayName(0);
486 base::RemoveChars(display_name, kDisplayNameMark, &display_name);
487 display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
488 // Retrieve the domain managing the device and wrap it with markers.
489 base::string16 domain = base::UTF8ToUTF16(
490 Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
491 base::RemoveChars(domain, kDisplayNameMark, &domain);
492 base::i18n::WrapStringWithLTRFormatting(&domain);
493 // Retrieve the label text, inserting the display name and domain.
494 text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
495 display_name, domain);
497 learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
498 learn_more_->SetUnderline(false);
499 learn_more_->set_listener(this);
500 AddChildView(learn_more_);
502 CalculatePreferredSize(owner, used_width);
505 PublicAccountUserDetails::~PublicAccountUserDetails() {}
507 void PublicAccountUserDetails::Layout() {
509 const gfx::Rect contents_area = GetContentsBounds();
510 if (contents_area.IsEmpty())
513 // Word-wrap the label text.
514 const gfx::FontList font_list;
515 std::vector<base::string16> lines;
516 gfx::ElideRectangleText(text_, font_list, contents_area.width(),
517 contents_area.height(), gfx::ELIDE_LONG_WORDS,
519 // Loop through the lines, creating a renderer for each.
520 gfx::Point position = contents_area.origin();
521 gfx::Range display_name(gfx::Range::InvalidRange());
522 for (std::vector<base::string16>::const_iterator it = lines.begin();
523 it != lines.end(); ++it) {
524 gfx::RenderText* line = gfx::RenderText::CreateInstance();
525 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
527 const gfx::Size size(contents_area.width(), line->GetStringSize().height());
528 line->SetDisplayRect(gfx::Rect(position, size));
529 position.set_y(position.y() + size.height());
531 // Set the default text color for the line.
532 line->SetColor(kPublicAccountUserCardTextColor);
534 // If a range of the line contains the user's display name, apply a custom
536 if (display_name.is_empty())
537 display_name.set_start(it->find(kDisplayNameMark));
538 if (!display_name.is_empty()) {
539 display_name.set_end(
540 it->find(kDisplayNameMark, display_name.start() + 1));
541 gfx::Range line_range(0, it->size());
542 line->ApplyColor(kPublicAccountUserCardNameColor,
543 display_name.Intersect(line_range));
544 // Update the range for the next line.
545 if (display_name.end() >= line_range.end())
546 display_name.set_start(0);
548 display_name = gfx::Range::InvalidRange();
551 lines_.push_back(line);
554 // Position the link after the label text, separated by a space. If it does
555 // not fit onto the last line of the text, wrap the link onto its own line.
556 const gfx::Size last_line_size = lines_.back()->GetStringSize();
557 const int space_width =
558 gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list);
559 const gfx::Size link_size = learn_more_->GetPreferredSize();
560 if (contents_area.width() - last_line_size.width() >=
561 space_width + link_size.width()) {
562 position.set_x(position.x() + last_line_size.width() + space_width);
563 position.set_y(position.y() - last_line_size.height());
565 position.set_y(position.y() - learn_more_->GetInsets().top());
566 gfx::Rect learn_more_bounds(position, link_size);
567 learn_more_bounds.Intersect(contents_area);
568 if (base::i18n::IsRTL()) {
569 const gfx::Insets insets = GetInsets();
570 learn_more_bounds.Offset(insets.right() - insets.left(), 0);
572 learn_more_->SetBoundsRect(learn_more_bounds);
575 gfx::Size PublicAccountUserDetails::GetPreferredSize() {
576 return preferred_size_;
579 void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) {
580 for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin();
581 it != lines_.end(); ++it) {
584 views::View::OnPaint(canvas);
587 void PublicAccountUserDetails::LinkClicked(views::Link* source,
589 DCHECK_EQ(source, learn_more_);
590 Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo();
593 void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner,
595 const gfx::FontList font_list;
596 const gfx::Size link_size = learn_more_->GetPreferredSize();
597 const int space_width =
598 gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list);
599 const gfx::Insets insets = GetInsets();
600 views::TrayBubbleView* bubble_view =
601 owner->system_tray()->GetSystemBubble()->bubble_view();
602 int min_width = std::max(
604 bubble_view->GetPreferredSize().width() - (used_width + insets.width()));
605 int max_width = std::min(
606 gfx::GetStringWidth(text_, font_list) + space_width + link_size.width(),
607 bubble_view->GetMaximumSize().width() - (used_width + insets.width()));
608 // Do a binary search for the minimum width that ensures no more than three
609 // lines are needed. The lower bound is the minimum of the current bubble
610 // width and the width of the link (as no wrapping is permitted inside the
611 // link). The upper bound is the maximum of the largest allowed bubble width
612 // and the sum of the label text and link widths when put on a single line.
613 std::vector<base::string16> lines;
614 while (min_width < max_width) {
616 const int width = (min_width + max_width) / 2;
617 const bool too_narrow =
618 gfx::ElideRectangleText(text_, font_list, width, INT_MAX,
619 gfx::TRUNCATE_LONG_WORDS, &lines) != 0;
620 int line_count = lines.size();
621 if (!too_narrow && line_count == 3 &&
622 width - gfx::GetStringWidth(lines.back(), font_list) <=
623 space_width + link_size.width())
625 if (too_narrow || line_count > 3)
626 min_width = width + 1;
631 // Calculate the corresponding height and set the preferred size.
633 gfx::ElideRectangleText(
634 text_, font_list, min_width, INT_MAX, gfx::TRUNCATE_LONG_WORDS, &lines);
635 int line_count = lines.size();
636 if (min_width - gfx::GetStringWidth(lines.back(), font_list) <=
637 space_width + link_size.width()) {
640 const int line_height = font_list.GetHeight();
641 const int link_extra_height = std::max(
642 link_size.height() - learn_more_->GetInsets().top() - line_height, 0);
643 preferred_size_ = gfx::Size(
644 min_width + insets.width(),
645 line_count * line_height + link_extra_height + insets.height());
647 bubble_view->SetWidth(preferred_size_.width() + used_width);
650 UserCard::UserCard(views::ButtonListener* listener, bool active_user)
651 : CustomButton(listener),
652 is_active_user_(active_user),
653 button_hovered_(false),
654 show_border_(false) {
655 if (is_active_user_) {
657 views::Background::CreateSolidBackground(kBackgroundColor));
662 UserCard::~UserCard() {}
664 void UserCard::ForceBorderVisible(bool show) {
669 void UserCard::OnMouseEntered(const ui::MouseEvent& event) {
670 if (is_active_user_) {
671 button_hovered_ = true;
672 background()->SetNativeControlColor(kHoverBackgroundColor);
677 void UserCard::OnMouseExited(const ui::MouseEvent& event) {
678 if (is_active_user_) {
679 button_hovered_ = false;
680 background()->SetNativeControlColor(kBackgroundColor);
685 void UserCard::ShowActive() {
686 int width = button_hovered_ || show_border_ ? 1 : 0;
687 SetBorder(views::Border::CreateSolidSidedBorder(
688 width, width, width, 1, kBorderColor));
692 UserView::UserView(SystemTrayItem* owner,
693 user::LoginStatus login,
694 MultiProfileIndex index)
695 : multiprofile_index_(index),
696 user_card_view_(NULL),
698 is_user_card_(false),
699 logout_button_(NULL),
700 add_user_visible_but_disabled_(false) {
701 CHECK_NE(user::LOGGED_IN_NONE, login);
703 // Only the logged in user will have a background. All other users will have
704 // to allow the TrayPopupContainer highlighting the menu line.
705 set_background(views::Background::CreateSolidBackground(
706 login == user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor :
709 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
710 kTrayPopupPaddingBetweenItems));
711 // The logout button must be added before the user card so that the user card
712 // can correctly calculate the remaining available width.
713 // Note that only the current multiprofile user gets a button.
714 if (!multiprofile_index_)
715 AddLogoutButton(login);
716 AddUserCard(owner, login);
719 UserView::~UserView() {}
721 void UserView::MouseMovedOutOfHost() {
722 popup_message_.reset();
723 mouse_watcher_.reset();
724 add_menu_option_.reset();
727 TrayUser::TestState UserView::GetStateForTest() const {
728 if (add_menu_option_.get()) {
729 return add_user_visible_but_disabled_ ? TrayUser::ACTIVE_BUT_DISABLED :
734 return TrayUser::SHOWN;
736 return static_cast<UserCard*>(user_card_view_)->is_hovered_for_test() ?
737 TrayUser::HOVERED : TrayUser::SHOWN;
740 gfx::Rect UserView::GetBoundsInScreenOfUserButtonForTest() {
741 DCHECK(user_card_view_);
742 return user_card_view_->GetBoundsInScreen();
745 gfx::Size UserView::GetPreferredSize() {
746 gfx::Size size = views::View::GetPreferredSize();
747 // Only the active user panel will be forced to a certain height.
748 if (!multiprofile_index_) {
749 size.set_height(std::max(size.height(),
750 kTrayPopupItemHeight + GetInsets().height()));
755 int UserView::GetHeightForWidth(int width) {
756 return GetPreferredSize().height();
759 void UserView::Layout() {
760 gfx::Rect contents_area(GetContentsBounds());
761 if (user_card_view_ && logout_button_) {
762 // Give the logout button the space it requests.
763 gfx::Rect logout_area = contents_area;
764 logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize());
765 logout_area.set_x(contents_area.right() - logout_area.width());
767 // Give the remaining space to the user card.
768 gfx::Rect user_card_area = contents_area;
769 int remaining_width = contents_area.width() - logout_area.width();
770 if (SupportsMultiProfile()) {
771 // In multiprofile case |user_card_view_| and |logout_button_| have to
772 // have the same height.
773 int y = std::min(user_card_area.y(), logout_area.y());
774 int height = std::max(user_card_area.height(), logout_area.height());
775 logout_area.set_y(y);
776 logout_area.set_height(height);
777 user_card_area.set_y(y);
778 user_card_area.set_height(height);
780 // In multiprofile mode we have also to increase the size of the card by
781 // the size of the border to make it overlap with the logout button.
782 user_card_area.set_width(std::max(0, remaining_width + 1));
784 // To make the logout button symmetrical with the user card we also make
785 // the button longer by the same size the hover area in front of the icon
787 logout_area.set_width(logout_area.width() +
788 kTrayUserTileHoverBorderInset);
790 // In all other modes we have to make sure that there is enough spacing
792 remaining_width -= kTrayPopupPaddingBetweenItems;
794 user_card_area.set_width(remaining_width);
795 user_card_view_->SetBoundsRect(user_card_area);
796 logout_button_->SetBoundsRect(logout_area);
797 } else if (user_card_view_) {
798 user_card_view_->SetBoundsRect(contents_area);
799 } else if (logout_button_) {
800 logout_button_->SetBoundsRect(contents_area);
804 void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
805 if (sender == logout_button_) {
806 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
807 ash::UMA_STATUS_AREA_SIGN_OUT);
808 Shell::GetInstance()->system_tray_delegate()->SignOut();
809 } else if (sender == user_card_view_ && SupportsMultiProfile()) {
810 if (!multiprofile_index_) {
811 ToggleAddUserMenuOption();
813 SwitchUser(multiprofile_index_);
814 // Since the user list is about to change the system menu should get
816 owner_->system_tray()->CloseSystemBubble();
818 } else if (add_menu_option_.get() &&
819 sender == add_menu_option_->GetContentsView()) {
820 // Let the user add another account to the session.
821 MultiProfileUMA::RecordSigninUser(MultiProfileUMA::SIGNIN_USER_BY_TRAY);
822 Shell::GetInstance()->system_tray_delegate()->ShowUserLogin();
823 owner_->system_tray()->CloseSystemBubble();
829 void UserView::AddLogoutButton(user::LoginStatus login) {
830 const base::string16 title = user::GetLocalizedSignOutStringForStatus(login,
832 TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title);
833 logout_button->SetAccessibleName(title);
834 logout_button_ = logout_button;
835 // In public account mode, the logout button border has a custom color.
836 if (login == user::LOGGED_IN_PUBLIC) {
837 scoped_ptr<TrayPopupLabelButtonBorder> border(
838 new TrayPopupLabelButtonBorder());
839 border->SetPainter(false, views::Button::STATE_NORMAL,
840 views::Painter::CreateImageGridPainter(
841 kPublicAccountLogoutButtonBorderImagesNormal));
842 border->SetPainter(false, views::Button::STATE_HOVERED,
843 views::Painter::CreateImageGridPainter(
844 kPublicAccountLogoutButtonBorderImagesHovered));
845 border->SetPainter(false, views::Button::STATE_PRESSED,
846 views::Painter::CreateImageGridPainter(
847 kPublicAccountLogoutButtonBorderImagesHovered));
848 logout_button_->SetBorder(border.PassAs<views::Border>());
850 AddChildView(logout_button_);
853 void UserView::AddUserCard(SystemTrayItem* owner, user::LoginStatus login) {
854 // Add padding around the panel.
855 SetBorder(views::Border::CreateEmptyBorder(kUserCardVerticalPadding,
856 kTrayPopupPaddingHorizontal,
857 kUserCardVerticalPadding,
858 kTrayPopupPaddingHorizontal));
860 if (SupportsMultiProfile() && login != user::LOGGED_IN_RETAIL_MODE) {
861 user_card_view_ = new UserCard(this, multiprofile_index_ == 0);
862 is_user_card_ = true;
864 user_card_view_ = new views::View();
865 is_user_card_ = false;
868 user_card_view_->SetLayoutManager(new views::BoxLayout(
869 views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems));
870 AddChildViewAt(user_card_view_, 0);
872 if (login == user::LOGGED_IN_RETAIL_MODE) {
873 AddLoggedInRetailModeUserCardContent();
877 // The entire user card should trigger hover (the inner items get disabled).
878 user_card_view_->SetEnabled(true);
879 user_card_view_->set_notify_enter_exit_on_child(true);
881 if (login == user::LOGGED_IN_PUBLIC) {
882 AddLoggedInPublicModeUserCardContent(owner);
886 views::View* icon = CreateIconForUserCard(login);
887 user_card_view_->AddChildView(icon);
889 // To allow the border to start before the icon, reduce the size before and
890 // add an inset to the icon to get the spacing.
891 if (multiprofile_index_ == 0 && SupportsMultiProfile()) {
892 icon->SetBorder(views::Border::CreateEmptyBorder(
893 0, kTrayUserTileHoverBorderInset, 0, 0));
894 SetBorder(views::Border::CreateEmptyBorder(
895 kUserCardVerticalPadding,
896 kTrayPopupPaddingHorizontal - kTrayUserTileHoverBorderInset,
897 kUserCardVerticalPadding,
898 kTrayPopupPaddingHorizontal));
900 SessionStateDelegate* delegate =
901 Shell::GetInstance()->session_state_delegate();
902 views::Label* username = NULL;
903 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
904 if (!multiprofile_index_) {
905 base::string16 user_name_string =
906 login == user::LOGGED_IN_GUEST ?
907 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL) :
908 delegate->GetUserDisplayName(multiprofile_index_);
909 if (!user_name_string.empty()) {
910 username = new views::Label(user_name_string);
911 username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
915 views::Label* additional = NULL;
916 if (login != user::LOGGED_IN_GUEST) {
917 base::string16 user_email_string =
918 login == user::LOGGED_IN_LOCALLY_MANAGED ?
919 bundle.GetLocalizedString(
920 IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) :
921 base::UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index_));
922 if (!user_email_string.empty()) {
923 additional = new views::Label(user_email_string);
924 additional->SetFontList(
925 bundle.GetFontList(ui::ResourceBundle::SmallFont));
926 additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
930 // Adjust text properties dependent on if it is an active or inactive user.
931 if (multiprofile_index_) {
932 // Fade the text of non active users to 50%.
933 SkColor text_color = additional->enabled_color();
934 text_color = SkColorSetA(text_color, SkColorGetA(text_color) / 2);
936 additional->SetDisabledColor(text_color);
938 username->SetDisabledColor(text_color);
941 if (additional && username) {
942 views::View* details = new views::View;
943 details->SetLayoutManager(new views::BoxLayout(
944 views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
945 details->AddChildView(username);
946 details->AddChildView(additional);
947 user_card_view_->AddChildView(details);
950 user_card_view_->AddChildView(username);
952 user_card_view_->AddChildView(additional);
956 views::View* UserView::CreateIconForUserCard(user::LoginStatus login) {
957 RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius,
958 multiprofile_index_ == 0);
959 icon->SetEnabled(false);
960 if (login == user::LOGGED_IN_GUEST) {
961 icon->SetImage(*ui::ResourceBundle::GetSharedInstance().
962 GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(),
963 gfx::Size(kUserIconSize, kUserIconSize));
966 Shell::GetInstance()->session_state_delegate()->
967 GetUserImage(multiprofile_index_),
968 gfx::Size(kUserIconSize, kUserIconSize));
973 void UserView::AddLoggedInRetailModeUserCardContent() {
974 views::Label* details = new views::Label;
975 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
977 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
978 details->SetBorder(views::Border::CreateEmptyBorder(0, 4, 0, 1));
979 details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
980 user_card_view_->AddChildView(details);
983 void UserView::AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner) {
984 user_card_view_->AddChildView(CreateIconForUserCard(user::LOGGED_IN_PUBLIC));
985 user_card_view_->AddChildView(new PublicAccountUserDetails(
986 owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems));
989 void UserView::ToggleAddUserMenuOption() {
990 if (add_menu_option_.get()) {
991 popup_message_.reset();
992 mouse_watcher_.reset();
993 add_menu_option_.reset();
997 // Note: We do not need to install a global event handler to delete this
998 // item since it will destroyed automatically before the menu / user menu item
1000 const SessionStateDelegate* session_state_delegate =
1001 Shell::GetInstance()->session_state_delegate();
1002 add_user_visible_but_disabled_ =
1003 session_state_delegate->NumberOfLoggedInUsers() >=
1004 session_state_delegate->GetMaximumNumberOfLoggedInUsers();
1005 add_menu_option_.reset(new views::Widget);
1006 views::Widget::InitParams params;
1007 params.type = views::Widget::InitParams::TYPE_TOOLTIP;
1008 params.keep_on_top = true;
1009 params.context = this->GetWidget()->GetNativeWindow();
1010 params.accept_events = true;
1011 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1012 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
1013 add_menu_option_->Init(params);
1014 add_menu_option_->SetOpacity(0xFF);
1015 add_menu_option_->GetNativeWindow()->set_owned_by_parent(false);
1016 SetShadowType(add_menu_option_->GetNativeView(),
1017 views::corewm::SHADOW_TYPE_NONE);
1019 // Position it below our user card.
1020 gfx::Rect bounds = user_card_view_->GetBoundsInScreen();
1021 bounds.set_y(bounds.y() + bounds.height());
1022 add_menu_option_->SetBounds(bounds);
1024 // Show the content.
1025 AddUserView* add_user_view = new AddUserView(
1026 static_cast<UserCard*>(user_card_view_), this);
1027 add_menu_option_->SetContentsView(add_user_view);
1028 add_menu_option_->SetAlwaysOnTop(true);
1029 add_menu_option_->Show();
1030 if (add_user_visible_but_disabled_) {
1031 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1032 popup_message_.reset(new PopupMessage(
1033 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER),
1034 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER),
1035 PopupMessage::ICON_WARNING,
1036 add_user_view->anchor(),
1037 views::BubbleBorder::TOP_LEFT,
1038 gfx::Size(parent()->bounds().width() - kPopupMessageOffset, 0),
1039 2 * kPopupMessageOffset));
1041 // Find the screen area which encloses both elements and sets then a mouse
1042 // watcher which will close the "menu".
1043 gfx::Rect area = user_card_view_->GetBoundsInScreen();
1044 area.set_height(2 * area.height());
1045 mouse_watcher_.reset(new views::MouseWatcher(
1046 new UserViewMouseWatcherHost(area),
1048 mouse_watcher_->Start();
1051 bool UserView::SupportsMultiProfile() {
1052 // We do not want to see any multi profile additions to a user view when the
1053 // log in screen is shown.
1054 return Shell::GetInstance()->delegate()->IsMultiProfilesEnabled() &&
1055 !Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked();
1058 AddUserView::AddUserView(UserCard* owner, views::ButtonListener* listener)
1059 : CustomButton(listener),
1061 listener_(listener),
1065 owner_->ForceBorderVisible(true);
1068 AddUserView::~AddUserView() {
1069 owner_->ForceBorderVisible(false);
1072 gfx::Size AddUserView::GetPreferredSize() {
1073 return owner_->bounds().size();
1076 int AddUserView::GetHeightForWidth(int width) {
1077 return owner_->bounds().size().height();
1080 void AddUserView::Layout() {
1081 gfx::Rect contents_area(GetContentsBounds());
1082 add_user_->SetBoundsRect(contents_area);
1085 void AddUserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
1086 if (add_user_ == sender)
1087 listener_->ButtonPressed(this, event);
1092 void AddUserView::AddContent() {
1093 set_notify_enter_exit_on_child(true);
1095 const SessionStateDelegate* delegate =
1096 Shell::GetInstance()->session_state_delegate();
1097 bool enable = delegate->NumberOfLoggedInUsers() <
1098 delegate->GetMaximumNumberOfLoggedInUsers();
1100 SetLayoutManager(new views::FillLayout());
1101 set_background(views::Background::CreateSolidBackground(kBackgroundColor));
1103 // Add padding around the panel.
1104 SetBorder(views::Border::CreateSolidBorder(1, kBorderColor));
1106 add_user_ = new UserCard(this, enable);
1107 add_user_->SetBorder(views::Border::CreateEmptyBorder(
1108 kUserCardVerticalPadding,
1109 kTrayPopupPaddingHorizontal - kTrayUserTileHoverBorderInset,
1110 kUserCardVerticalPadding,
1111 kTrayPopupPaddingHorizontal - kTrayUserTileHoverBorderInset));
1113 add_user_->SetLayoutManager(new views::BoxLayout(
1114 views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems));
1115 AddChildViewAt(add_user_, 0);
1117 // Add the [+] icon which is also the anchor for messages.
1118 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1119 RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius,
1122 icon->SetImage(*ui::ResourceBundle::GetSharedInstance().
1123 GetImageNamed(IDR_AURA_UBER_TRAY_ADD_MULTIPROFILE_USER).ToImageSkia(),
1124 gfx::Size(kUserIconSize, kUserIconSize));
1125 add_user_->AddChildView(icon);
1127 // Add the command text.
1128 views::Label* command_label = new views::Label(
1129 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT));
1130 command_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1131 add_user_->AddChildView(command_label);
1136 TrayUser::TrayUser(SystemTray* system_tray, MultiProfileIndex index)
1137 : SystemTrayItem(system_tray),
1138 multiprofile_index_(index),
1143 Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this);
1146 TrayUser::~TrayUser() {
1147 Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this);
1150 TrayUser::TestState TrayUser::GetStateForTest() const {
1153 return user_->GetStateForTest();
1156 bool TrayUser::CanDropWindowHereToTransferToUser(
1157 const gfx::Point& point_in_screen) {
1158 // Check that this item is shown in the system tray (which means it must have
1159 // a view there) and that the user it represents is not the current user (in
1160 // which case |GetTrayIndex()| would return NULL).
1161 if (!layout_view_ || !GetTrayIndex())
1163 return layout_view_->GetBoundsInScreen().Contains(point_in_screen);
1166 bool TrayUser::TransferWindowToUser(aura::Window* window) {
1167 SessionStateDelegate* session_state_delegate =
1168 ash::Shell::GetInstance()->session_state_delegate();
1169 return session_state_delegate->TransferWindowToDesktopOfUser(window,
1173 gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const {
1175 return user_->GetBoundsInScreenOfUserButtonForTest();
1178 views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
1179 CHECK(layout_view_ == NULL);
1181 layout_view_ = new views::View();
1182 layout_view_->SetLayoutManager(
1183 new views::BoxLayout(views::BoxLayout::kHorizontal,
1184 0, 0, kUserLabelToIconPadding));
1185 UpdateAfterLoginStatusChange(status);
1186 return layout_view_;
1189 views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
1190 if (status == user::LOGGED_IN_NONE)
1192 const SessionStateDelegate* session_state_delegate =
1193 Shell::GetInstance()->session_state_delegate();
1195 // If the screen is locked show only the currently active user.
1196 if (multiprofile_index_ && session_state_delegate->IsUserSessionBlocked())
1199 CHECK(user_ == NULL);
1201 int logged_in_users = session_state_delegate->NumberOfLoggedInUsers();
1203 // Do not show more UserView's then there are logged in users.
1204 if (multiprofile_index_ >= logged_in_users)
1207 user_ = new tray::UserView(this, status, multiprofile_index_);
1211 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
1215 void TrayUser::DestroyTrayView() {
1216 layout_view_ = NULL;
1221 void TrayUser::DestroyDefaultView() {
1225 void TrayUser::DestroyDetailedView() {
1228 void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) {
1229 // Only the active user is represented in the tray.
1232 if (GetTrayIndex() > 0 && !ash::switches::UseMultiUserTray())
1234 bool need_label = false;
1235 bool need_avatar = false;
1237 case user::LOGGED_IN_LOCKED:
1238 case user::LOGGED_IN_USER:
1239 case user::LOGGED_IN_OWNER:
1240 case user::LOGGED_IN_PUBLIC:
1243 case user::LOGGED_IN_LOCALLY_MANAGED:
1247 case user::LOGGED_IN_GUEST:
1250 case user::LOGGED_IN_RETAIL_MODE:
1251 case user::LOGGED_IN_KIOSK_APP:
1252 case user::LOGGED_IN_NONE:
1256 if ((need_avatar != (avatar_ != NULL)) ||
1257 (need_label != (label_ != NULL))) {
1258 layout_view_->RemoveAllChildViews(true);
1260 label_ = new views::Label;
1261 SetupLabelForTray(label_);
1262 layout_view_->AddChildView(label_);
1267 MultiProfileIndex tray_index = GetTrayIndex();
1269 // The active user (index #0) will always be the first.
1270 avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius, true);
1272 // All other users will be inactive users.
1273 avatar_ = new tray::UserSwitcherView(kProfileRoundedCornerRadius,
1276 layout_view_->AddChildView(avatar_);
1282 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1283 if (status == user::LOGGED_IN_LOCALLY_MANAGED) {
1285 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL));
1286 } else if (status == user::LOGGED_IN_GUEST) {
1287 label_->SetText(bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL));
1290 if (avatar_ && switches::UseAlternateShelfLayout()) {
1291 int corner_radius = GetTrayItemRadius();
1292 avatar_->SetCornerRadii(0, corner_radius, corner_radius, 0);
1293 avatar_->SetBorder(views::Border::NullBorder());
1295 UpdateAvatarImage(status);
1297 // Update layout after setting label_ and avatar_ with new login status.
1298 UpdateLayoutOfItem();
1301 void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
1302 // Inactive users won't have a layout.
1305 int corner_radius = GetTrayItemRadius();
1306 if (alignment == SHELF_ALIGNMENT_BOTTOM ||
1307 alignment == SHELF_ALIGNMENT_TOP) {
1309 if (switches::UseAlternateShelfLayout()) {
1310 if (multiprofile_index_) {
1312 views::Border::CreateEmptyBorder(0, kTrayLabelSpacing, 0, 0));
1314 avatar_->SetBorder(views::Border::NullBorder());
1316 avatar_->SetCornerRadii(0, corner_radius, corner_radius, 0);
1318 avatar_->SetBorder(views::Border::CreateEmptyBorder(
1320 kTrayImageItemHorizontalPaddingBottomAlignment + 2,
1322 kTrayImageItemHorizontalPaddingBottomAlignment));
1326 label_->SetBorder(views::Border::CreateEmptyBorder(
1328 kTrayLabelItemHorizontalPaddingBottomAlignment,
1330 kTrayLabelItemHorizontalPaddingBottomAlignment));
1332 layout_view_->SetLayoutManager(
1333 new views::BoxLayout(views::BoxLayout::kHorizontal,
1334 0, 0, kUserLabelToIconPadding));
1337 if (switches::UseAlternateShelfLayout()) {
1338 if (multiprofile_index_) {
1340 views::Border::CreateEmptyBorder(kTrayLabelSpacing, 0, 0, 0));
1342 avatar_->SetBorder(views::Border::NullBorder());
1344 avatar_->SetCornerRadii(0, 0, corner_radius, corner_radius);
1346 SetTrayImageItemBorder(avatar_, alignment);
1350 label_->SetBorder(views::Border::CreateEmptyBorder(
1351 kTrayLabelItemVerticalPaddingVerticalAlignment,
1352 kTrayLabelItemHorizontalPaddingBottomAlignment,
1353 kTrayLabelItemVerticalPaddingVerticalAlignment,
1354 kTrayLabelItemHorizontalPaddingBottomAlignment));
1356 layout_view_->SetLayoutManager(
1357 new views::BoxLayout(views::BoxLayout::kVertical,
1358 0, 0, kUserLabelToIconPadding));
1362 void TrayUser::OnUserUpdate() {
1363 UpdateAvatarImage(Shell::GetInstance()->system_tray_delegate()->
1364 GetUserLoginStatus());
1367 void TrayUser::OnUserAddedToSession() {
1368 SessionStateDelegate* session_state_delegate =
1369 Shell::GetInstance()->session_state_delegate();
1370 // Only create views for user items which are logged in.
1371 if (GetTrayIndex() >= session_state_delegate->NumberOfLoggedInUsers())
1374 // Enforce a layout change that newly added items become visible.
1375 UpdateLayoutOfItem();
1377 // Update the user item.
1379 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus());
1382 void TrayUser::UpdateAvatarImage(user::LoginStatus status) {
1383 SessionStateDelegate* session_state_delegate =
1384 Shell::GetInstance()->session_state_delegate();
1386 GetTrayIndex() >= session_state_delegate->NumberOfLoggedInUsers())
1389 int icon_size = switches::UseAlternateShelfLayout() ?
1390 kUserIconLargeSize : kUserIconSize;
1393 Shell::GetInstance()->session_state_delegate()->GetUserImage(
1395 gfx::Size(icon_size, icon_size));
1397 // Unit tests might come here with no images for some users.
1398 if (avatar_->size().IsEmpty())
1399 avatar_->SetSize(gfx::Size(icon_size, icon_size));
1402 MultiProfileIndex TrayUser::GetTrayIndex() {
1403 Shell* shell = Shell::GetInstance();
1404 // If multi profile is not enabled we can use the normal index.
1405 if (!shell->delegate()->IsMultiProfilesEnabled())
1406 return multiprofile_index_;
1407 // In case of multi profile we need to mirror the indices since the system
1408 // tray items are in the reverse order then the menu items.
1409 return shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers() -
1410 1 - multiprofile_index_;
1413 int TrayUser::GetTrayItemRadius() {
1414 SessionStateDelegate* delegate =
1415 Shell::GetInstance()->session_state_delegate();
1416 bool is_last_item = GetTrayIndex() == (delegate->NumberOfLoggedInUsers() - 1);
1417 return is_last_item ? kUserIconLargeCornerRadius : 0;
1420 void TrayUser::UpdateLayoutOfItem() {
1421 internal::RootWindowController* controller =
1422 internal::GetRootWindowController(
1423 system_tray()->GetWidget()->GetNativeWindow()->GetRootWindow());
1424 if (controller && controller->shelf()) {
1425 UpdateAfterShelfAlignmentChange(
1426 controller->GetShelfLayoutManager()->GetAlignment());
1430 } // namespace internal