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