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