tray_user.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.
4
5#include "ash/system/user/tray_user.h"
6
7#include <algorithm>
8#include <climits>
9#include <vector>
10
11#include "ash/popup_message.h"
12#include "ash/session_state_delegate.h"
13#include "ash/shell.h"
14#include "ash/shell_delegate.h"
15#include "ash/system/tray/system_tray.h"
16#include "ash/system/tray/system_tray_delegate.h"
17#include "ash/system/tray/system_tray_notifier.h"
18#include "ash/system/tray/tray_constants.h"
19#include "ash/system/tray/tray_item_view.h"
20#include "ash/system/tray/tray_popup_label_button.h"
21#include "ash/system/tray/tray_popup_label_button_border.h"
22#include "ash/system/tray/tray_utils.h"
23#include "base/i18n/rtl.h"
24#include "base/logging.h"
25#include "base/memory/scoped_vector.h"
26#include "base/string16.h"
27#include "base/string_util.h"
28#include "base/utf_string_conversions.h"
29#include "grit/ash_resources.h"
30#include "grit/ash_strings.h"
31#include "skia/ext/image_operations.h"
32#include "third_party/skia/include/core/SkCanvas.h"
33#include "third_party/skia/include/core/SkPaint.h"
34#include "third_party/skia/include/core/SkPath.h"
35#include "ui/aura/window.h"
36#include "ui/base/l10n/l10n_util.h"
37#include "ui/base/range/range.h"
38#include "ui/base/resource/resource_bundle.h"
39#include "ui/base/text/text_elider.h"
40#include "ui/gfx/canvas.h"
41#include "ui/gfx/font.h"
42#include "ui/gfx/image/image.h"
43#include "ui/gfx/image/image_skia_operations.h"
44#include "ui/gfx/insets.h"
45#include "ui/gfx/rect.h"
46#include "ui/gfx/render_text.h"
47#include "ui/gfx/size.h"
48#include "ui/gfx/skia_util.h"
49#include "ui/views/border.h"
50#include "ui/views/bubble/tray_bubble_view.h"
51#include "ui/views/controls/button/button.h"
52#include "ui/views/controls/button/custom_button.h"
53#include "ui/views/controls/image_view.h"
54#include "ui/views/controls/label.h"
55#include "ui/views/controls/link.h"
56#include "ui/views/controls/link_listener.h"
57#include "ui/views/corewm/shadow_types.h"
58#include "ui/views/layout/box_layout.h"
59#include "ui/views/layout/fill_layout.h"
60#include "ui/views/mouse_watcher.h"
61#include "ui/views/painter.h"
62#include "ui/views/view.h"
63#include "ui/views/widget/widget.h"
64
65namespace {
66
67const int kUserDetailsVerticalPadding = 5;
68const int kUserCardVerticalPadding = 10;
69const int kInactiveUserCardVerticalPadding = 4;
70const int kProfileRoundedCornerRadius = 2;
71const int kUserIconSize = 27;
72const int kUserLabelToIconPadding = 5;
73
74// When a hover border is used, it is starting this many pixels before the icon
75// position.
76const int kTrayUserTileHoverBorderInset = 10;
77
78// The border color of the user button.
79const SkColor kBorderColor = 0xffdcdcdc;
80
81// The invisible word joiner character, used as a marker to indicate the start
82// and end of the user's display name in the public account user card's text.
83const char16 kDisplayNameMark[] = { 0x2060, 0 };
84
85const int kPublicAccountLogoutButtonBorderImagesNormal[] = {
86    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
87    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
88    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
89    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
90    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
91    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
92    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
93    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
94    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
95};
96
97const int kPublicAccountLogoutButtonBorderImagesHovered[] = {
98    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
99    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
100    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
101    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
102    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND,
103    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
104    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
105    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
106    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
107};
108
109// Offsetting the popup message relative to the tray menu.
110const int kPopupMessageOffset = 25;
111
112}  // namespace
113
114namespace ash {
115namespace internal {
116
117namespace tray {
118
119// A custom image view with rounded edges.
120class RoundedImageView : public views::View {
121 public:
122  // Constructs a new rounded image view with rounded corners of radius
123  // |corner_radius|. If |active_user| is set, the icon will be drawn in
124  // full colors - otherwise it will fade into the background.
125  RoundedImageView(int corner_radius, bool active_user);
126  virtual ~RoundedImageView();
127
128  // Set the image that should be displayed. The image contents is copied to the
129  // receiver's image.
130  void SetImage(const gfx::ImageSkia& img, const gfx::Size& size);
131
132 private:
133  // Overridden from views::View.
134  virtual gfx::Size GetPreferredSize() OVERRIDE;
135  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
136
137  gfx::ImageSkia image_;
138  gfx::ImageSkia resized_;
139  gfx::Size image_size_;
140  int corner_radius_;
141
142  // True if the given user is the active user and the icon should get
143  // painted as active.
144  bool active_user_;
145
146  DISALLOW_COPY_AND_ASSIGN(RoundedImageView);
147};
148
149// The user details shown in public account mode. This is essentially a label
150// but with custom painting code as the text is styled with multiple colors and
151// contains a link.
152class PublicAccountUserDetails : public views::View,
153                                 public views::LinkListener {
154 public:
155  PublicAccountUserDetails(SystemTrayItem* owner, int used_width);
156  virtual ~PublicAccountUserDetails();
157
158 private:
159  // Overridden from views::View.
160  virtual void Layout() OVERRIDE;
161  virtual gfx::Size GetPreferredSize() OVERRIDE;
162  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
163
164  // Overridden from views::LinkListener.
165  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
166
167  // Calculate a preferred size that ensures the label text and the following
168  // link do not wrap over more than three lines in total for aesthetic reasons
169  // if possible.
170  void CalculatePreferredSize(SystemTrayItem* owner, int used_width);
171
172  base::string16 text_;
173  views::Link* learn_more_;
174  gfx::Size preferred_size_;
175  ScopedVector<gfx::RenderText> lines_;
176
177  DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
178};
179
180// The button which holds the user card in case of multi profile.
181class UserCard : public views::CustomButton {
182 public:
183  UserCard(views::ButtonListener* listener, bool active_user);
184  virtual ~UserCard();
185
186  // Called when the border should remain even in the non highlighted state.
187  void ForceBorderVisible(bool show);
188
189  // Overridden from views::View
190  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
191  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
192
193  // Check if the item is hovered.
194  bool is_hovered_for_test() {return button_hovered_; }
195
196 private:
197  // Change the hover/active state of the "button" when the status changes.
198  void ShowActive();
199
200  // True if this is the active user.
201  bool is_active_user_;
202
203  // True if button is hovered.
204  bool button_hovered_;
205
206  // True if the border should be visible.
207  bool show_border_;
208
209  DISALLOW_COPY_AND_ASSIGN(UserCard);
210};
211
212class UserViewMouseWatcherHost : public views::MouseWatcherHost {
213public:
214 explicit UserViewMouseWatcherHost(const gfx::Rect& screen_area)
215     : screen_area_(screen_area) {}
216 virtual ~UserViewMouseWatcherHost() {}
217
218 // Implementation of MouseWatcherHost.
219 virtual bool Contains(const gfx::Point& screen_point,
220                       views::MouseWatcherHost::MouseEventType type) OVERRIDE {
221   return screen_area_.Contains(screen_point);
222 }
223
224private:
225 gfx::Rect screen_area_;
226
227 DISALLOW_COPY_AND_ASSIGN(UserViewMouseWatcherHost);
228};
229
230// The view of a user item.
231class UserView : public views::View,
232                 public views::ButtonListener,
233                 public views::MouseWatcherListener {
234 public:
235  UserView(SystemTrayItem* owner,
236           ash::user::LoginStatus login,
237           MultiProfileIndex index);
238  virtual ~UserView();
239
240  // Overridden from MouseWatcherListener:
241  virtual void MouseMovedOutOfHost() OVERRIDE;
242
243  TrayUser::TestState GetStateForTest() const;
244  gfx::Rect GetBoundsInScreenOfUserButtonForTest();
245
246 private:
247  // Overridden from views::View.
248  virtual gfx::Size GetPreferredSize() OVERRIDE;
249  virtual int GetHeightForWidth(int width) OVERRIDE;
250  virtual void Layout() OVERRIDE;
251
252  // Overridden from views::ButtonListener.
253  virtual void ButtonPressed(views::Button* sender,
254                             const ui::Event& event) OVERRIDE;
255
256  void AddLogoutButton(ash::user::LoginStatus login);
257  void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login);
258
259  // Create a user icon representation for the user card.
260  views::View* CreateIconForUserCard(ash::user::LoginStatus login);
261
262  // Create the additional user card content for the retail logged in mode.
263  void AddLoggedInRetailModeUserCardContent();
264
265  // Create the additional user card content for the public mode.
266  void AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner);
267
268  // Create the menu option to add another user. If |disabled| is set the user
269  // cannot actively click on the item.
270  void ToggleAddUserMenuOption();
271
272  MultiProfileIndex multiprofile_index_;
273  // The view of the user card.
274  views::View* user_card_view_;
275
276  // True if |user_card_view_| is a |UserView| - otherwise it is only a
277  // |views::View|.
278  bool is_user_card_;
279  views::View* logout_button_;
280  scoped_ptr<ash::PopupMessage> popup_message_;
281  scoped_ptr<views::Widget> add_menu_option_;
282
283  // True when the add user panel is visible but not activatable.
284  bool add_user_visible_but_disabled_;
285
286  // The mouse watcher which takes care of out of window hover events.
287  scoped_ptr<views::MouseWatcher> mouse_watcher_;
288
289  DISALLOW_COPY_AND_ASSIGN(UserView);
290};
291
292// The menu item view which gets shown when the user clicks in multi profile
293// mode onto the user item.
294class AddUserView : public views::CustomButton,
295                    public views::ButtonListener {
296 public:
297  // The |owner| is the view for which this view gets created. The |listener|
298  // will get notified when this item gets clicked.
299  AddUserView(UserCard* owner, views::ButtonListener* listener);
300  virtual ~AddUserView();
301
302  // Get the anchor view for a message.
303  views::View* anchor() { return anchor_; }
304
305  // Overridden from views::ButtonListener.
306  virtual void ButtonPressed(views::Button* sender,
307                             const ui::Event& event) OVERRIDE;
308
309 private:
310  // Overridden from views::View.
311  virtual gfx::Size GetPreferredSize() OVERRIDE;
312  virtual int GetHeightForWidth(int width) OVERRIDE;
313  virtual void Layout() OVERRIDE;
314
315  // Create the additional client content for this item.
316  void AddContent();
317
318  // This is the content we create and show.
319  views::View* add_user_;
320
321  // This listener will get informed when someone clicks on this button.
322  views::ButtonListener* listener_;
323
324  // This is the owner view of this item.
325  UserCard* owner_;
326
327  // The anchor view for targetted bubble messages.
328  views::View* anchor_;
329
330  DISALLOW_COPY_AND_ASSIGN(AddUserView);
331};
332
333RoundedImageView::RoundedImageView(int corner_radius, bool active_user)
334    : corner_radius_(corner_radius),
335      active_user_(active_user) {}
336
337RoundedImageView::~RoundedImageView() {}
338
339void RoundedImageView::SetImage(const gfx::ImageSkia& img,
340                                const gfx::Size& size) {
341  image_ = img;
342  image_size_ = size;
343
344  // Try to get the best image quality for the avatar.
345  resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_,
346      skia::ImageOperations::RESIZE_BEST, size);
347  if (GetWidget() && visible()) {
348    PreferredSizeChanged();
349    SchedulePaint();
350  }
351}
352
353gfx::Size RoundedImageView::GetPreferredSize() {
354  return gfx::Size(image_size_.width() + GetInsets().width(),
355                   image_size_.height() + GetInsets().height());
356}
357
358void RoundedImageView::OnPaint(gfx::Canvas* canvas) {
359  View::OnPaint(canvas);
360  gfx::Rect image_bounds(size());
361  image_bounds.ClampToCenteredSize(GetPreferredSize());
362  image_bounds.Inset(GetInsets());
363  const SkScalar kRadius = SkIntToScalar(corner_radius_);
364  SkPath path;
365  path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius);
366  SkPaint paint;
367  paint.setAntiAlias(true);
368  paint.setXfermodeMode(active_user_ ? SkXfermode::kSrcOver_Mode :
369                                       SkXfermode::kLuminosity_Mode);
370  canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(),
371                          path, paint);
372}
373
374PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner,
375                                                   int used_width)
376    : learn_more_(NULL) {
377  const int inner_padding =
378      kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
379  const bool rtl = base::i18n::IsRTL();
380  set_border(views::Border::CreateEmptyBorder(
381      kUserDetailsVerticalPadding, rtl ? 0 : inner_padding,
382      kUserDetailsVerticalPadding, rtl ? inner_padding : 0));
383
384  // Retrieve the user's display name and wrap it with markers.
385  // Note that since this is a public account it always has to be the primary
386  // user.
387  base::string16 display_name =
388      ash::Shell::GetInstance()->session_state_delegate()->
389          GetUserDisplayName(0);
390  RemoveChars(display_name, kDisplayNameMark, &display_name);
391  display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
392  // Retrieve the domain managing the device and wrap it with markers.
393  base::string16 domain = UTF8ToUTF16(
394      ash::Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
395  RemoveChars(domain, kDisplayNameMark, &domain);
396  base::i18n::WrapStringWithLTRFormatting(&domain);
397  // Retrieve the label text, inserting the display name and domain.
398  text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
399                                     display_name, domain);
400
401  learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
402  learn_more_->SetUnderline(false);
403  learn_more_->set_listener(this);
404  AddChildView(learn_more_);
405
406  CalculatePreferredSize(owner, used_width);
407}
408
409PublicAccountUserDetails::~PublicAccountUserDetails() {}
410
411void PublicAccountUserDetails::Layout() {
412  lines_.clear();
413  const gfx::Rect contents_area = GetContentsBounds();
414  if (contents_area.IsEmpty())
415    return;
416
417  // Word-wrap the label text.
418  const gfx::Font font;
419  std::vector<base::string16> lines;
420  ui::ElideRectangleText(text_, font, contents_area.width(),
421                         contents_area.height(), ui::ELIDE_LONG_WORDS, &lines);
422  // Loop through the lines, creating a renderer for each.
423  gfx::Point position = contents_area.origin();
424  ui::Range display_name(ui::Range::InvalidRange());
425  for (std::vector<base::string16>::const_iterator it = lines.begin();
426       it != lines.end(); ++it) {
427    gfx::RenderText* line = gfx::RenderText::CreateInstance();
428    line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
429    line->SetText(*it);
430    const gfx::Size size(contents_area.width(), line->GetStringSize().height());
431    line->SetDisplayRect(gfx::Rect(position, size));
432    position.set_y(position.y() + size.height());
433
434    // Set the default text color for the line.
435    line->SetColor(kPublicAccountUserCardTextColor);
436
437    // If a range of the line contains the user's display name, apply a custom
438    // text color to it.
439    if (display_name.is_empty())
440      display_name.set_start(it->find(kDisplayNameMark));
441    if (!display_name.is_empty()) {
442      display_name.set_end(
443          it->find(kDisplayNameMark, display_name.start() + 1));
444      ui::Range line_range(0, it->size());
445      line->ApplyColor(kPublicAccountUserCardNameColor,
446                       display_name.Intersect(line_range));
447      // Update the range for the next line.
448      if (display_name.end() >= line_range.end())
449        display_name.set_start(0);
450      else
451        display_name = ui::Range::InvalidRange();
452    }
453
454    lines_.push_back(line);
455  }
456
457  // Position the link after the label text, separated by a space. If it does
458  // not fit onto the last line of the text, wrap the link onto its own line.
459  const gfx::Size last_line_size = lines_.back()->GetStringSize();
460  const int space_width = font.GetStringWidth(ASCIIToUTF16(" "));
461  const gfx::Size link_size = learn_more_->GetPreferredSize();
462  if (contents_area.width() - last_line_size.width() >=
463      space_width + link_size.width()) {
464    position.set_x(position.x() + last_line_size.width() + space_width);
465    position.set_y(position.y() - last_line_size.height());
466  }
467  position.set_y(position.y() - learn_more_->GetInsets().top());
468  gfx::Rect learn_more_bounds(position, link_size);
469  learn_more_bounds.Intersect(contents_area);
470  if (base::i18n::IsRTL()) {
471    const gfx::Insets insets = GetInsets();
472    learn_more_bounds.Offset(insets.right() - insets.left(), 0);
473  }
474  learn_more_->SetBoundsRect(learn_more_bounds);
475}
476
477gfx::Size PublicAccountUserDetails::GetPreferredSize() {
478  return preferred_size_;
479}
480
481void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) {
482  for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin();
483       it != lines_.end(); ++it) {
484    (*it)->Draw(canvas);
485  }
486  views::View::OnPaint(canvas);
487}
488
489void PublicAccountUserDetails::LinkClicked(views::Link* source,
490                                           int event_flags) {
491  DCHECK_EQ(source, learn_more_);
492  ash::Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo();
493}
494
495void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner,
496                                                      int used_width) {
497  const gfx::Font font;
498  const gfx::Size link_size = learn_more_->GetPreferredSize();
499  const int space_width = font.GetStringWidth(ASCIIToUTF16(" "));
500  const gfx::Insets insets = GetInsets();
501  views::TrayBubbleView* bubble_view =
502      owner->system_tray()->GetSystemBubble()->bubble_view();
503  int min_width = std::max(
504      link_size.width(),
505      bubble_view->GetPreferredSize().width() - (used_width + insets.width()));
506  int max_width = std::min(
507      font.GetStringWidth(text_) + space_width + link_size.width(),
508      bubble_view->GetMaximumSize().width() - (used_width + insets.width()));
509  // Do a binary search for the minimum width that ensures no more than three
510  // lines are needed. The lower bound is the minimum of the current bubble
511  // width and the width of the link (as no wrapping is permitted inside the
512  // link). The upper bound is the maximum of the largest allowed bubble width
513  // and the sum of the label text and link widths when put on a single line.
514  std::vector<base::string16> lines;
515  while (min_width < max_width) {
516    lines.clear();
517    const int width = (min_width + max_width) / 2;
518    const bool too_narrow = ui::ElideRectangleText(
519        text_, font, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines) != 0;
520    int line_count = lines.size();
521    if (!too_narrow && line_count == 3 &&
522            width - font.GetStringWidth(lines.back()) <=
523            space_width + link_size.width()) {
524      ++line_count;
525    }
526    if (too_narrow || line_count > 3)
527      min_width = width + 1;
528    else
529      max_width = width;
530  }
531
532  // Calculate the corresponding height and set the preferred size.
533  lines.clear();
534  ui::ElideRectangleText(
535      text_, font, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines);
536  int line_count = lines.size();
537  if (min_width - font.GetStringWidth(lines.back()) <=
538      space_width + link_size.width()) {
539    ++line_count;
540  }
541  const int line_height = font.GetHeight();
542  const int link_extra_height = std::max(
543      link_size.height() - learn_more_->GetInsets().top() - line_height, 0);
544  preferred_size_ = gfx::Size(
545      min_width + insets.width(),
546      line_count * line_height + link_extra_height + insets.height());
547
548  bubble_view->SetWidth(preferred_size_.width() + used_width);
549}
550
551UserCard::UserCard(views::ButtonListener* listener, bool active_user)
552    : CustomButton(listener),
553      is_active_user_(active_user),
554      button_hovered_(false),
555      show_border_(false) {
556  if (is_active_user_) {
557    set_background(
558        views::Background::CreateSolidBackground(kBackgroundColor));
559    ShowActive();
560  }
561}
562
563UserCard::~UserCard() {}
564
565void UserCard::ForceBorderVisible(bool show) {
566  show_border_ = show;
567  ShowActive();
568}
569
570void UserCard::OnMouseEntered(const ui::MouseEvent& event) {
571  if (is_active_user_) {
572    button_hovered_ = true;
573    background()->SetNativeControlColor(kHoverBackgroundColor);
574    ShowActive();
575  }
576}
577
578void UserCard::OnMouseExited(const ui::MouseEvent& event) {
579  if (is_active_user_) {
580    button_hovered_ = false;
581    background()->SetNativeControlColor(kBackgroundColor);
582    ShowActive();
583  }
584}
585
586void UserCard::ShowActive() {
587  int width = button_hovered_ || show_border_ ? 1 : 0;
588  set_border(views::Border::CreateSolidSidedBorder(width, width, width, 1,
589                                                   kBorderColor));
590  SchedulePaint();
591}
592
593UserView::UserView(SystemTrayItem* owner,
594                   ash::user::LoginStatus login,
595                   MultiProfileIndex index)
596    : multiprofile_index_(index),
597      user_card_view_(NULL),
598      is_user_card_(false),
599      logout_button_(NULL),
600      add_user_visible_but_disabled_(false) {
601  CHECK_NE(ash::user::LOGGED_IN_NONE, login);
602  if (!index) {
603    // Only the logged in user will have a background. All other users will have
604    // to allow the TrayPopupContainer highlighting the menu line.
605    set_background(views::Background::CreateSolidBackground(
606        login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor :
607                                               kBackgroundColor));
608  }
609  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
610                                        kTrayPopupPaddingBetweenItems));
611  // The logout button must be added before the user card so that the user card
612  // can correctly calculate the remaining available width.
613  // Note that only the current multiprofile user gets a button.
614  AddLogoutButton(!multiprofile_index_ ? login : ash::user::LOGGED_IN_LOCKED);
615  AddUserCard(owner, login);
616}
617
618UserView::~UserView() {}
619
620void UserView::MouseMovedOutOfHost() {
621  popup_message_.reset();
622  mouse_watcher_.reset();
623  add_menu_option_.reset();
624}
625
626TrayUser::TestState UserView::GetStateForTest() const {
627  if (add_menu_option_.get()) {
628    return add_user_visible_but_disabled_ ? TrayUser::ACTIVE_BUT_DISABLED :
629                                            TrayUser::ACTIVE;
630  }
631
632  if (!is_user_card_)
633    return TrayUser::SHOWN;
634
635  return static_cast<UserCard*>(user_card_view_)->is_hovered_for_test() ?
636      TrayUser::HOVERED : TrayUser::SHOWN;
637}
638
639gfx::Rect UserView::GetBoundsInScreenOfUserButtonForTest() {
640  DCHECK(user_card_view_);
641  return user_card_view_->GetBoundsInScreen();
642}
643
644gfx::Size UserView::GetPreferredSize() {
645  gfx::Size size = views::View::GetPreferredSize();
646  // Only the active user panel will be forced to a certain height.
647  if (!multiprofile_index_) {
648    size.set_height(std::max(size.height(),
649                             kTrayPopupItemHeight + GetInsets().height()));
650  }
651  return size;
652}
653
654int UserView::GetHeightForWidth(int width) {
655  return GetPreferredSize().height();
656}
657
658void UserView::Layout() {
659  gfx::Rect contents_area(GetContentsBounds());
660  if (user_card_view_ && logout_button_) {
661    // Give the logout button the space it requests.
662    gfx::Rect logout_area = contents_area;
663    logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize());
664    logout_area.set_x(contents_area.right() - logout_area.width());
665
666    // Give the remaining space to the user card.
667    gfx::Rect user_card_area = contents_area;
668    int remaining_width = contents_area.width() - logout_area.width();
669    if (ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) {
670      // In multiprofile case |user_card_view_| and |logout_button_| have to
671      // have the same height.
672      int y = std::min(user_card_area.y(), logout_area.y());
673      int height = std::max(user_card_area.height(), logout_area.height());
674      logout_area.set_y(y);
675      logout_area.set_height(height);
676      user_card_area.set_y(y);
677      user_card_area.set_height(height);
678
679      // In multiprofile mode we have also to increase the size of the card by
680      // the size of the border to make it overlap with the logout button.
681      user_card_area.set_width(std::max(0, remaining_width + 1));
682
683      // To make the logout button symmetrical with the user card we also make
684      // the button longer by the same size the hover area in front of the icon
685      // got inset.
686      logout_area.set_width(logout_area.width() +
687                            kTrayUserTileHoverBorderInset);
688    } else {
689      // In all other modes we have to make sure that there is enough spacing
690      // between the two.
691      remaining_width -= kTrayPopupPaddingBetweenItems;
692    }
693    user_card_view_->SetBoundsRect(user_card_area);
694    logout_button_->SetBoundsRect(logout_area);
695  } else if (user_card_view_) {
696    user_card_view_->SetBoundsRect(contents_area);
697  } else if (logout_button_) {
698    logout_button_->SetBoundsRect(contents_area);
699  }
700}
701
702void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
703  if (sender == logout_button_) {
704    ash::Shell::GetInstance()->system_tray_delegate()->SignOut();
705  } else if (sender == user_card_view_ &&
706             ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) {
707    if (!multiprofile_index_) {
708      ToggleAddUserMenuOption();
709    } else {
710      ash::SessionStateDelegate* delegate =
711          ash::Shell::GetInstance()->session_state_delegate();
712      delegate->SwitchActiveUser(delegate->GetUserEmail(multiprofile_index_));
713    }
714  } else if (add_menu_option_.get() &&
715             sender == add_menu_option_->GetContentsView()) {
716    // Let the user add another account to the session.
717    ash::Shell::GetInstance()->system_tray_delegate()->ShowUserLogin();
718  } else {
719    NOTREACHED();
720  }
721}
722
723void UserView::AddLogoutButton(ash::user::LoginStatus login) {
724  // A user should not be able to modify logged-in state when screen is
725  // locked.
726  if (login == ash::user::LOGGED_IN_LOCKED)
727    return;
728
729  const base::string16 title = ash::user::GetLocalizedSignOutStringForStatus(
730      login, true);
731  TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title);
732  logout_button->SetAccessibleName(title);
733  logout_button_ = logout_button;
734  // In public account mode, the logout button border has a custom color.
735  if (login == ash::user::LOGGED_IN_PUBLIC) {
736    TrayPopupLabelButtonBorder* border =
737        static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border());
738    border->SetPainter(false, views::Button::STATE_NORMAL,
739                       views::Painter::CreateImageGridPainter(
740                           kPublicAccountLogoutButtonBorderImagesNormal));
741    border->SetPainter(false, views::Button::STATE_HOVERED,
742                       views::Painter::CreateImageGridPainter(
743                           kPublicAccountLogoutButtonBorderImagesHovered));
744    border->SetPainter(false, views::Button::STATE_PRESSED,
745                       views::Painter::CreateImageGridPainter(
746                           kPublicAccountLogoutButtonBorderImagesHovered));
747  }
748  AddChildView(logout_button_);
749}
750
751void UserView::AddUserCard(SystemTrayItem* owner,
752                           ash::user::LoginStatus login) {
753  // Add padding around the panel.
754  set_border(views::Border::CreateEmptyBorder(
755      kUserCardVerticalPadding, kTrayPopupPaddingHorizontal,
756      kUserCardVerticalPadding, kTrayPopupPaddingHorizontal));
757
758  if (ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled() &&
759      login != ash::user::LOGGED_IN_RETAIL_MODE) {
760    user_card_view_ = new UserCard(this, multiprofile_index_ == 0);
761    is_user_card_ = true;
762  } else {
763    user_card_view_ = new views::View();
764    is_user_card_ = false;
765  }
766
767  user_card_view_->SetLayoutManager(new views::BoxLayout(
768      views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems));
769  AddChildViewAt(user_card_view_, 0);
770
771  if (login == ash::user::LOGGED_IN_RETAIL_MODE) {
772    AddLoggedInRetailModeUserCardContent();
773    return;
774  }
775
776  // The entire user card should trigger hover (the inner items get disabled).
777  user_card_view_->SetEnabled(true);
778  user_card_view_->set_notify_enter_exit_on_child(true);
779
780  if (login == ash::user::LOGGED_IN_PUBLIC) {
781    AddLoggedInPublicModeUserCardContent(owner);
782    return;
783  }
784
785  views::View* icon = CreateIconForUserCard(login);
786  user_card_view_->AddChildView(icon);
787
788  // To allow the border to start before the icon, reduce the size before and
789  // add an inset to the icon to get the spacing.
790  if (multiprofile_index_ == 0 &&
791      ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) {
792    icon->set_border(views::Border::CreateEmptyBorder(
793        0, kTrayUserTileHoverBorderInset, 0, 0));
794    set_border(views::Border::CreateEmptyBorder(
795        kUserCardVerticalPadding,
796        kTrayPopupPaddingHorizontal - kTrayUserTileHoverBorderInset,
797        kUserCardVerticalPadding,
798        kTrayPopupPaddingHorizontal));
799  }
800  ash::SessionStateDelegate* delegate =
801      ash::Shell::GetInstance()->session_state_delegate();
802  views::Label* username = NULL;
803  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
804  if (!multiprofile_index_) {
805    username = new views::Label(
806        login == ash::user::LOGGED_IN_GUEST ?
807            bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL) :
808            delegate->GetUserDisplayName(multiprofile_index_));
809    username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
810  }
811
812  views::Label* additional = NULL;
813  if (login != ash::user::LOGGED_IN_GUEST) {
814    additional = new views::Label();
815    additional->SetText(login == ash::user::LOGGED_IN_LOCALLY_MANAGED ?
816        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) :
817        UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index_)));
818
819    additional->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont));
820    additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
821  }
822
823  // Adjust text properties dependent on if it is an active or inactive user.
824  if (multiprofile_index_) {
825    // Fade the text of non active users to 50%.
826    SkColor text_color = additional->enabled_color();
827    text_color = SkColorSetA(text_color, SkColorGetA(text_color) / 2);
828    if (additional)
829      additional->SetDisabledColor(text_color);
830    if (username)
831      username->SetDisabledColor(text_color);
832  }
833
834  if (additional && username) {
835    views::View* details = new views::View;
836    details->SetLayoutManager(new views::BoxLayout(
837        views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
838    details->AddChildView(username);
839    details->AddChildView(additional);
840    user_card_view_->AddChildView(details);
841  } else {
842    if (username)
843      user_card_view_->AddChildView(username);
844    if (additional)
845      user_card_view_->AddChildView(additional);
846  }
847}
848
849views::View* UserView::CreateIconForUserCard(ash::user::LoginStatus login) {
850  RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius,
851                                                multiprofile_index_ == 0);
852  icon->SetEnabled(false);
853  if (login == ash::user::LOGGED_IN_GUEST) {
854    icon->SetImage(*ui::ResourceBundle::GetSharedInstance().
855        GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(),
856        gfx::Size(kUserIconSize, kUserIconSize));
857  } else {
858    icon->SetImage(
859        ash::Shell::GetInstance()->session_state_delegate()->
860            GetUserImage(multiprofile_index_),
861        gfx::Size(kUserIconSize, kUserIconSize));
862  }
863  return icon;
864}
865
866void UserView::AddLoggedInRetailModeUserCardContent() {
867  views::Label* details = new views::Label;
868  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
869  details->SetText(
870      bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
871  details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1));
872  details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
873  user_card_view_->AddChildView(details);
874}
875
876void UserView::AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner) {
877  user_card_view_->AddChildView(
878      CreateIconForUserCard(ash::user::LOGGED_IN_PUBLIC));
879  user_card_view_->AddChildView(new PublicAccountUserDetails(
880      owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems));
881}
882
883void UserView::ToggleAddUserMenuOption() {
884  if (add_menu_option_.get()) {
885    popup_message_.reset();
886    mouse_watcher_.reset();
887    add_menu_option_.reset();
888    return;
889  }
890
891  // Note: We do not need to install a global event handler to delete this
892  // item since it will destroyed automatically before the menu / user menu item
893  // gets destroyed..
894  const SessionStateDelegate* session_state_delegate =
895      ash::Shell::GetInstance()->session_state_delegate();
896  add_user_visible_but_disabled_ =
897      session_state_delegate->NumberOfLoggedInUsers() >=
898          session_state_delegate->GetMaximumNumberOfLoggedInUsers();
899  add_menu_option_.reset(new views::Widget);
900  views::Widget::InitParams params;
901  params.type = views::Widget::InitParams::TYPE_TOOLTIP;
902  params.keep_on_top = true;
903  params.context = this->GetWidget()->GetNativeWindow();
904  params.accept_events = true;
905  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
906  params.transparent = true;
907  add_menu_option_->Init(params);
908  add_menu_option_->SetOpacity(0xFF);
909  add_menu_option_->GetNativeWindow()->set_owned_by_parent(false);
910  SetShadowType(add_menu_option_->GetNativeView(),
911                views::corewm::SHADOW_TYPE_NONE);
912
913  // Position it below our user card.
914  gfx::Rect bounds = user_card_view_->GetBoundsInScreen();
915  bounds.set_y(bounds.y() + bounds.height());
916  add_menu_option_->SetBounds(bounds);
917
918  // Show the content.
919  AddUserView* add_user_view = new AddUserView(
920      static_cast<UserCard*>(user_card_view_), this);
921  add_menu_option_->SetContentsView(add_user_view);
922  add_menu_option_->SetAlwaysOnTop(true);
923  add_menu_option_->Show();
924  if (add_user_visible_but_disabled_) {
925    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
926    popup_message_.reset(new PopupMessage(
927        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER),
928        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER),
929        PopupMessage::ICON_WARNING,
930        add_user_view->anchor(),
931        views::BubbleBorder::TOP_LEFT,
932        gfx::Size(parent()->bounds().width() - kPopupMessageOffset, 0),
933        2 * kPopupMessageOffset));
934  }
935  // Find the screen area which encloses both elements and sets then a mouse
936  // watcher which will close the "menu".
937  gfx::Rect area = user_card_view_->GetBoundsInScreen();
938  area.set_height(2 * area.height());
939  mouse_watcher_.reset(new views::MouseWatcher(
940      new UserViewMouseWatcherHost(area),
941      this));
942  mouse_watcher_->Start();
943}
944
945AddUserView::AddUserView(UserCard* owner, views::ButtonListener* listener)
946    : CustomButton(listener_),
947      add_user_(NULL),
948      listener_(listener),
949      owner_(owner),
950      anchor_(NULL) {
951  AddContent();
952  owner_->ForceBorderVisible(true);
953}
954
955AddUserView::~AddUserView() {
956  owner_->ForceBorderVisible(false);
957}
958
959gfx::Size AddUserView::GetPreferredSize() {
960  return owner_->bounds().size();
961}
962
963int AddUserView::GetHeightForWidth(int width) {
964  return owner_->bounds().size().height();
965}
966
967void AddUserView::Layout() {
968  gfx::Rect contents_area(GetContentsBounds());
969  add_user_->SetBoundsRect(contents_area);
970}
971
972void AddUserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
973  if (add_user_ == sender)
974    listener_->ButtonPressed(this, event);
975  else
976    NOTREACHED();
977}
978
979void AddUserView::AddContent() {
980  set_notify_enter_exit_on_child(true);
981
982  const SessionStateDelegate* delegate =
983      ash::Shell::GetInstance()->session_state_delegate();
984  bool enable = delegate->NumberOfLoggedInUsers() <
985                    delegate->GetMaximumNumberOfLoggedInUsers();
986
987  SetLayoutManager(new views::FillLayout());
988  set_background(views::Background::CreateSolidBackground(kBackgroundColor));
989
990  // Add padding around the panel.
991  set_border(views::Border::CreateSolidBorder(1, kBorderColor));
992
993  add_user_ = new UserCard(this, enable);
994  add_user_->set_border(views::Border::CreateEmptyBorder(
995      kUserCardVerticalPadding,
996      kTrayPopupPaddingHorizontal- kTrayUserTileHoverBorderInset,
997      kUserCardVerticalPadding,
998      kTrayPopupPaddingHorizontal- kTrayUserTileHoverBorderInset));
999
1000  add_user_->SetLayoutManager(new views::BoxLayout(
1001      views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems));
1002  AddChildViewAt(add_user_, 0);
1003
1004  // Add the [+] icon which is also the anchor for messages.
1005  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1006  RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius,
1007                                                true);
1008  anchor_ = icon;
1009  icon->SetImage(*ui::ResourceBundle::GetSharedInstance().
1010      GetImageNamed(IDR_AURA_UBER_TRAY_ADD_MULTIPROFILE_USER).ToImageSkia(),
1011      gfx::Size(kUserIconSize, kUserIconSize));
1012  add_user_->AddChildView(icon);
1013
1014  // Add the command text.
1015  views::Label* command_label = new views::Label(
1016      bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT));
1017  command_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1018  add_user_->AddChildView(command_label);
1019}
1020
1021}  // namespace tray
1022
1023TrayUser::TrayUser(SystemTray* system_tray, MultiProfileIndex index)
1024    : SystemTrayItem(system_tray),
1025      multiprofile_index_(index),
1026      user_(NULL),
1027      layout_view_(NULL),
1028      avatar_(NULL),
1029      label_(NULL),
1030      separator_shown_(false) {
1031  Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this);
1032}
1033
1034TrayUser::~TrayUser() {
1035  Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this);
1036}
1037
1038TrayUser::TestState TrayUser::GetStateForTest() const {
1039  if (separator_shown_)
1040    return SEPARATOR;
1041  if (!user_)
1042    return HIDDEN;
1043  return user_->GetStateForTest();
1044}
1045
1046gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const {
1047  DCHECK(user_);
1048  return user_->GetBoundsInScreenOfUserButtonForTest();
1049}
1050
1051views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
1052  CHECK(layout_view_ == NULL);
1053  // Only the current user gets an icon. All other users will only be
1054  // represented in the tray menu.
1055  if (multiprofile_index_)
1056    return NULL;
1057
1058  layout_view_ = new views::View();
1059  layout_view_->SetLayoutManager(
1060      new views::BoxLayout(views::BoxLayout::kHorizontal,
1061                           0, 0, kUserLabelToIconPadding));
1062  UpdateAfterLoginStatusChange(status);
1063  return layout_view_;
1064}
1065
1066views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
1067  if (status == user::LOGGED_IN_NONE)
1068    return NULL;
1069
1070  CHECK(user_ == NULL);
1071
1072  const SessionStateDelegate* session_state_delegate =
1073      ash::Shell::GetInstance()->session_state_delegate();
1074  int logged_in_users = session_state_delegate->NumberOfLoggedInUsers();
1075
1076  // If there are multiple users logged in, the users will be separated from the
1077  // rest of the menu by a separator.
1078  if (multiprofile_index_ ==
1079          session_state_delegate->GetMaximumNumberOfLoggedInUsers() &&
1080      logged_in_users > 1) {
1081    separator_shown_ = true;
1082    return new views::View();
1083  }
1084
1085  // Do not show more UserView's then there are logged in users.
1086  if (multiprofile_index_ >= logged_in_users)
1087    return NULL;
1088
1089  user_ = new tray::UserView(this, status, multiprofile_index_);
1090  return user_;
1091}
1092
1093views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
1094  return NULL;
1095}
1096
1097void TrayUser::DestroyTrayView() {
1098  layout_view_ = NULL;
1099  avatar_ = NULL;
1100  label_ = NULL;
1101  separator_shown_ = false;
1102}
1103
1104void TrayUser::DestroyDefaultView() {
1105  user_ = NULL;
1106}
1107
1108void TrayUser::DestroyDetailedView() {
1109}
1110
1111void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) {
1112  // Only the active user is represented in the tray.
1113  if (!layout_view_)
1114    return;
1115  bool need_label = false;
1116  bool need_avatar = false;
1117  switch (status) {
1118    case user::LOGGED_IN_LOCKED:
1119    case user::LOGGED_IN_USER:
1120    case user::LOGGED_IN_OWNER:
1121    case user::LOGGED_IN_PUBLIC:
1122      need_avatar = true;
1123      break;
1124    case user::LOGGED_IN_LOCALLY_MANAGED:
1125      need_avatar = true;
1126      need_label = true;
1127      break;
1128    case user::LOGGED_IN_GUEST:
1129      need_avatar = true;
1130      break;
1131    case user::LOGGED_IN_RETAIL_MODE:
1132    case user::LOGGED_IN_KIOSK_APP:
1133    case user::LOGGED_IN_NONE:
1134      break;
1135  }
1136
1137  if ((need_avatar != (avatar_ != NULL)) ||
1138      (need_label != (label_ != NULL))) {
1139    layout_view_->RemoveAllChildViews(true);
1140    if (need_label) {
1141      label_ = new views::Label;
1142      SetupLabelForTray(label_);
1143      layout_view_->AddChildView(label_);
1144    } else {
1145      label_ = NULL;
1146    }
1147    if (need_avatar) {
1148      avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius, true);
1149      layout_view_->AddChildView(avatar_);
1150    } else {
1151      avatar_ = NULL;
1152    }
1153  }
1154
1155  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1156  if (status == user::LOGGED_IN_LOCALLY_MANAGED) {
1157    label_->SetText(
1158        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL));
1159  }
1160
1161  if (avatar_) {
1162    if (status == user::LOGGED_IN_GUEST) {
1163        avatar_->SetImage(*ui::ResourceBundle::GetSharedInstance().
1164            GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(),
1165            gfx::Size(kUserIconSize, kUserIconSize));
1166    } else {
1167      avatar_->SetImage(
1168          ash::Shell::GetInstance()->session_state_delegate()->GetUserImage(
1169              multiprofile_index_),
1170          gfx::Size(kUserIconSize, kUserIconSize));
1171    }
1172  }
1173}
1174
1175void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
1176  // Inactive users won't have a layout.
1177  if (!layout_view_)
1178    return;
1179  if (alignment == SHELF_ALIGNMENT_BOTTOM ||
1180      alignment == SHELF_ALIGNMENT_TOP) {
1181    if (avatar_) {
1182      avatar_->set_border(views::Border::CreateEmptyBorder(
1183          0, kTrayImageItemHorizontalPaddingBottomAlignment + 2,
1184          0, kTrayImageItemHorizontalPaddingBottomAlignment));
1185
1186    }
1187    if (label_) {
1188      label_->set_border(views::Border::CreateEmptyBorder(
1189          0, kTrayLabelItemHorizontalPaddingBottomAlignment,
1190          0, kTrayLabelItemHorizontalPaddingBottomAlignment));
1191    }
1192    layout_view_->SetLayoutManager(
1193        new views::BoxLayout(views::BoxLayout::kHorizontal,
1194                             0, 0, kUserLabelToIconPadding));
1195  } else {
1196    if (avatar_)
1197      SetTrayImageItemBorder(avatar_, alignment);
1198    if (label_) {
1199      label_->set_border(views::Border::CreateEmptyBorder(
1200          kTrayLabelItemVerticalPaddingVeriticalAlignment,
1201          kTrayLabelItemHorizontalPaddingBottomAlignment,
1202          kTrayLabelItemVerticalPaddingVeriticalAlignment,
1203          kTrayLabelItemHorizontalPaddingBottomAlignment));
1204    }
1205    layout_view_->SetLayoutManager(
1206        new views::BoxLayout(views::BoxLayout::kVertical,
1207                             0, 0, kUserLabelToIconPadding));
1208  }
1209}
1210
1211void TrayUser::OnUserUpdate() {
1212  // Check for null to avoid crbug.com/150944.
1213  if (avatar_) {
1214    avatar_->SetImage(
1215        ash::Shell::GetInstance()->session_state_delegate()->GetUserImage(
1216            multiprofile_index_),
1217        gfx::Size(kUserIconSize, kUserIconSize));
1218  }
1219}
1220
1221}  // namespace internal
1222}  // namespace ash
1223