tray_user.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_view_->SetBoundsRect(user_card_area); 698 logout_button_->SetBoundsRect(logout_area); 699 } else if (user_card_view_) { 700 user_card_view_->SetBoundsRect(contents_area); 701 } else if (logout_button_) { 702 logout_button_->SetBoundsRect(contents_area); 703 } 704} 705 706void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) { 707 if (sender == logout_button_) { 708 ash::Shell::GetInstance()->system_tray_delegate()->SignOut(); 709 } else if (sender == user_card_view_ && 710 ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) { 711 if (!multiprofile_index_) { 712 ToggleAddUserMenuOption(); 713 } else { 714 ash::SessionStateDelegate* delegate = 715 ash::Shell::GetInstance()->session_state_delegate(); 716 delegate->SwitchActiveUser(delegate->GetUserEmail(multiprofile_index_)); 717 // Since the user list is about to change the system menu should get 718 // closed. 719 owner_->system_tray()->CloseSystemBubble(); 720 } 721 } else if (add_menu_option_.get() && 722 sender == add_menu_option_->GetContentsView()) { 723 // Let the user add another account to the session. 724 ash::Shell::GetInstance()->system_tray_delegate()->ShowUserLogin(); 725 } else { 726 NOTREACHED(); 727 } 728} 729 730void UserView::AddLogoutButton(ash::user::LoginStatus login) { 731 // A user should not be able to modify logged-in state when screen is 732 // locked. 733 if (login == ash::user::LOGGED_IN_LOCKED) 734 return; 735 736 const base::string16 title = ash::user::GetLocalizedSignOutStringForStatus( 737 login, true); 738 TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title); 739 logout_button->SetAccessibleName(title); 740 logout_button_ = logout_button; 741 // In public account mode, the logout button border has a custom color. 742 if (login == ash::user::LOGGED_IN_PUBLIC) { 743 TrayPopupLabelButtonBorder* border = 744 static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border()); 745 border->SetPainter(false, views::Button::STATE_NORMAL, 746 views::Painter::CreateImageGridPainter( 747 kPublicAccountLogoutButtonBorderImagesNormal)); 748 border->SetPainter(false, views::Button::STATE_HOVERED, 749 views::Painter::CreateImageGridPainter( 750 kPublicAccountLogoutButtonBorderImagesHovered)); 751 border->SetPainter(false, views::Button::STATE_PRESSED, 752 views::Painter::CreateImageGridPainter( 753 kPublicAccountLogoutButtonBorderImagesHovered)); 754 } 755 AddChildView(logout_button_); 756} 757 758void UserView::AddUserCard(SystemTrayItem* owner, 759 ash::user::LoginStatus login) { 760 // Add padding around the panel. 761 set_border(views::Border::CreateEmptyBorder( 762 kUserCardVerticalPadding, kTrayPopupPaddingHorizontal, 763 kUserCardVerticalPadding, kTrayPopupPaddingHorizontal)); 764 765 if (ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled() && 766 login != ash::user::LOGGED_IN_RETAIL_MODE) { 767 user_card_view_ = new UserCard(this, multiprofile_index_ == 0); 768 is_user_card_ = true; 769 } else { 770 user_card_view_ = new views::View(); 771 is_user_card_ = false; 772 } 773 774 user_card_view_->SetLayoutManager(new views::BoxLayout( 775 views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems)); 776 AddChildViewAt(user_card_view_, 0); 777 778 if (login == ash::user::LOGGED_IN_RETAIL_MODE) { 779 AddLoggedInRetailModeUserCardContent(); 780 return; 781 } 782 783 // The entire user card should trigger hover (the inner items get disabled). 784 user_card_view_->SetEnabled(true); 785 user_card_view_->set_notify_enter_exit_on_child(true); 786 787 if (login == ash::user::LOGGED_IN_PUBLIC) { 788 AddLoggedInPublicModeUserCardContent(owner); 789 return; 790 } 791 792 views::View* icon = CreateIconForUserCard(login); 793 user_card_view_->AddChildView(icon); 794 795 // To allow the border to start before the icon, reduce the size before and 796 // add an inset to the icon to get the spacing. 797 if (multiprofile_index_ == 0 && 798 ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) { 799 icon->set_border(views::Border::CreateEmptyBorder( 800 0, kTrayUserTileHoverBorderInset, 0, 0)); 801 set_border(views::Border::CreateEmptyBorder( 802 kUserCardVerticalPadding, 803 kTrayPopupPaddingHorizontal - kTrayUserTileHoverBorderInset, 804 kUserCardVerticalPadding, 805 kTrayPopupPaddingHorizontal)); 806 } 807 ash::SessionStateDelegate* delegate = 808 ash::Shell::GetInstance()->session_state_delegate(); 809 views::Label* username = NULL; 810 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 811 if (!multiprofile_index_) { 812 username = new views::Label( 813 login == ash::user::LOGGED_IN_GUEST ? 814 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL) : 815 delegate->GetUserDisplayName(multiprofile_index_)); 816 username->SetHorizontalAlignment(gfx::ALIGN_LEFT); 817 } 818 819 views::Label* additional = NULL; 820 if (login != ash::user::LOGGED_IN_GUEST) { 821 additional = new views::Label(); 822 additional->SetText(login == ash::user::LOGGED_IN_LOCALLY_MANAGED ? 823 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) : 824 UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index_))); 825 826 additional->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont)); 827 additional->SetHorizontalAlignment(gfx::ALIGN_LEFT); 828 } 829 830 // Adjust text properties dependent on if it is an active or inactive user. 831 if (multiprofile_index_) { 832 // Fade the text of non active users to 50%. 833 SkColor text_color = additional->enabled_color(); 834 text_color = SkColorSetA(text_color, SkColorGetA(text_color) / 2); 835 if (additional) 836 additional->SetDisabledColor(text_color); 837 if (username) 838 username->SetDisabledColor(text_color); 839 } 840 841 if (additional && username) { 842 views::View* details = new views::View; 843 details->SetLayoutManager(new views::BoxLayout( 844 views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0)); 845 details->AddChildView(username); 846 details->AddChildView(additional); 847 user_card_view_->AddChildView(details); 848 } else { 849 if (username) 850 user_card_view_->AddChildView(username); 851 if (additional) 852 user_card_view_->AddChildView(additional); 853 } 854} 855 856views::View* UserView::CreateIconForUserCard(ash::user::LoginStatus login) { 857 RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius, 858 multiprofile_index_ == 0); 859 icon->SetEnabled(false); 860 if (login == ash::user::LOGGED_IN_GUEST) { 861 icon->SetImage(*ui::ResourceBundle::GetSharedInstance(). 862 GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(), 863 gfx::Size(kUserIconSize, kUserIconSize)); 864 } else { 865 icon->SetImage( 866 ash::Shell::GetInstance()->session_state_delegate()-> 867 GetUserImage(multiprofile_index_), 868 gfx::Size(kUserIconSize, kUserIconSize)); 869 } 870 return icon; 871} 872 873void UserView::AddLoggedInRetailModeUserCardContent() { 874 views::Label* details = new views::Label; 875 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 876 details->SetText( 877 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL)); 878 details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1)); 879 details->SetHorizontalAlignment(gfx::ALIGN_LEFT); 880 user_card_view_->AddChildView(details); 881} 882 883void UserView::AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner) { 884 user_card_view_->AddChildView( 885 CreateIconForUserCard(ash::user::LOGGED_IN_PUBLIC)); 886 user_card_view_->AddChildView(new PublicAccountUserDetails( 887 owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems)); 888} 889 890void UserView::ToggleAddUserMenuOption() { 891 if (add_menu_option_.get()) { 892 popup_message_.reset(); 893 mouse_watcher_.reset(); 894 add_menu_option_.reset(); 895 return; 896 } 897 898 // Note: We do not need to install a global event handler to delete this 899 // item since it will destroyed automatically before the menu / user menu item 900 // gets destroyed.. 901 const SessionStateDelegate* session_state_delegate = 902 ash::Shell::GetInstance()->session_state_delegate(); 903 add_user_visible_but_disabled_ = 904 session_state_delegate->NumberOfLoggedInUsers() >= 905 session_state_delegate->GetMaximumNumberOfLoggedInUsers(); 906 add_menu_option_.reset(new views::Widget); 907 views::Widget::InitParams params; 908 params.type = views::Widget::InitParams::TYPE_TOOLTIP; 909 params.keep_on_top = true; 910 params.context = this->GetWidget()->GetNativeWindow(); 911 params.accept_events = true; 912 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 913 params.transparent = true; 914 add_menu_option_->Init(params); 915 add_menu_option_->SetOpacity(0xFF); 916 add_menu_option_->GetNativeWindow()->set_owned_by_parent(false); 917 SetShadowType(add_menu_option_->GetNativeView(), 918 views::corewm::SHADOW_TYPE_NONE); 919 920 // Position it below our user card. 921 gfx::Rect bounds = user_card_view_->GetBoundsInScreen(); 922 bounds.set_y(bounds.y() + bounds.height()); 923 add_menu_option_->SetBounds(bounds); 924 925 // Show the content. 926 AddUserView* add_user_view = new AddUserView( 927 static_cast<UserCard*>(user_card_view_), this); 928 add_menu_option_->SetContentsView(add_user_view); 929 add_menu_option_->SetAlwaysOnTop(true); 930 add_menu_option_->Show(); 931 if (add_user_visible_but_disabled_) { 932 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 933 popup_message_.reset(new PopupMessage( 934 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER), 935 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER), 936 PopupMessage::ICON_WARNING, 937 add_user_view->anchor(), 938 views::BubbleBorder::TOP_LEFT, 939 gfx::Size(parent()->bounds().width() - kPopupMessageOffset, 0), 940 2 * kPopupMessageOffset)); 941 } 942 // Find the screen area which encloses both elements and sets then a mouse 943 // watcher which will close the "menu". 944 gfx::Rect area = user_card_view_->GetBoundsInScreen(); 945 area.set_height(2 * area.height()); 946 mouse_watcher_.reset(new views::MouseWatcher( 947 new UserViewMouseWatcherHost(area), 948 this)); 949 mouse_watcher_->Start(); 950} 951 952AddUserView::AddUserView(UserCard* owner, views::ButtonListener* listener) 953 : CustomButton(listener_), 954 add_user_(NULL), 955 listener_(listener), 956 owner_(owner), 957 anchor_(NULL) { 958 AddContent(); 959 owner_->ForceBorderVisible(true); 960} 961 962AddUserView::~AddUserView() { 963 owner_->ForceBorderVisible(false); 964} 965 966gfx::Size AddUserView::GetPreferredSize() { 967 return owner_->bounds().size(); 968} 969 970int AddUserView::GetHeightForWidth(int width) { 971 return owner_->bounds().size().height(); 972} 973 974void AddUserView::Layout() { 975 gfx::Rect contents_area(GetContentsBounds()); 976 add_user_->SetBoundsRect(contents_area); 977} 978 979void AddUserView::ButtonPressed(views::Button* sender, const ui::Event& event) { 980 if (add_user_ == sender) 981 listener_->ButtonPressed(this, event); 982 else 983 NOTREACHED(); 984} 985 986void AddUserView::AddContent() { 987 set_notify_enter_exit_on_child(true); 988 989 const SessionStateDelegate* delegate = 990 ash::Shell::GetInstance()->session_state_delegate(); 991 bool enable = delegate->NumberOfLoggedInUsers() < 992 delegate->GetMaximumNumberOfLoggedInUsers(); 993 994 SetLayoutManager(new views::FillLayout()); 995 set_background(views::Background::CreateSolidBackground(kBackgroundColor)); 996 997 // Add padding around the panel. 998 set_border(views::Border::CreateSolidBorder(1, kBorderColor)); 999 1000 add_user_ = new UserCard(this, enable); 1001 add_user_->set_border(views::Border::CreateEmptyBorder( 1002 kUserCardVerticalPadding, 1003 kTrayPopupPaddingHorizontal- kTrayUserTileHoverBorderInset, 1004 kUserCardVerticalPadding, 1005 kTrayPopupPaddingHorizontal- kTrayUserTileHoverBorderInset)); 1006 1007 add_user_->SetLayoutManager(new views::BoxLayout( 1008 views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems)); 1009 AddChildViewAt(add_user_, 0); 1010 1011 // Add the [+] icon which is also the anchor for messages. 1012 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 1013 RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius, 1014 true); 1015 anchor_ = icon; 1016 icon->SetImage(*ui::ResourceBundle::GetSharedInstance(). 1017 GetImageNamed(IDR_AURA_UBER_TRAY_ADD_MULTIPROFILE_USER).ToImageSkia(), 1018 gfx::Size(kUserIconSize, kUserIconSize)); 1019 add_user_->AddChildView(icon); 1020 1021 // Add the command text. 1022 views::Label* command_label = new views::Label( 1023 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT)); 1024 command_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1025 add_user_->AddChildView(command_label); 1026} 1027 1028} // namespace tray 1029 1030TrayUser::TrayUser(SystemTray* system_tray, MultiProfileIndex index) 1031 : SystemTrayItem(system_tray), 1032 multiprofile_index_(index), 1033 user_(NULL), 1034 layout_view_(NULL), 1035 avatar_(NULL), 1036 label_(NULL), 1037 separator_shown_(false) { 1038 Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this); 1039} 1040 1041TrayUser::~TrayUser() { 1042 Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this); 1043} 1044 1045TrayUser::TestState TrayUser::GetStateForTest() const { 1046 if (separator_shown_) 1047 return SEPARATOR; 1048 if (!user_) 1049 return HIDDEN; 1050 return user_->GetStateForTest(); 1051} 1052 1053gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const { 1054 DCHECK(user_); 1055 return user_->GetBoundsInScreenOfUserButtonForTest(); 1056} 1057 1058views::View* TrayUser::CreateTrayView(user::LoginStatus status) { 1059 CHECK(layout_view_ == NULL); 1060 // Only the current user gets an icon. All other users will only be 1061 // represented in the tray menu. 1062 if (multiprofile_index_) 1063 return NULL; 1064 1065 layout_view_ = new views::View(); 1066 layout_view_->SetLayoutManager( 1067 new views::BoxLayout(views::BoxLayout::kHorizontal, 1068 0, 0, kUserLabelToIconPadding)); 1069 UpdateAfterLoginStatusChange(status); 1070 return layout_view_; 1071} 1072 1073views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { 1074 if (status == user::LOGGED_IN_NONE) 1075 return NULL; 1076 1077 CHECK(user_ == NULL); 1078 1079 const SessionStateDelegate* session_state_delegate = 1080 ash::Shell::GetInstance()->session_state_delegate(); 1081 int logged_in_users = session_state_delegate->NumberOfLoggedInUsers(); 1082 1083 // If there are multiple users logged in, the users will be separated from the 1084 // rest of the menu by a separator. 1085 if (multiprofile_index_ == 1086 session_state_delegate->GetMaximumNumberOfLoggedInUsers() && 1087 logged_in_users > 1) { 1088 separator_shown_ = true; 1089 return new views::View(); 1090 } 1091 1092 // Do not show more UserView's then there are logged in users. 1093 if (multiprofile_index_ >= logged_in_users) 1094 return NULL; 1095 1096 user_ = new tray::UserView(this, status, multiprofile_index_); 1097 return user_; 1098} 1099 1100views::View* TrayUser::CreateDetailedView(user::LoginStatus status) { 1101 return NULL; 1102} 1103 1104void TrayUser::DestroyTrayView() { 1105 layout_view_ = NULL; 1106 avatar_ = NULL; 1107 label_ = NULL; 1108 separator_shown_ = false; 1109} 1110 1111void TrayUser::DestroyDefaultView() { 1112 user_ = NULL; 1113} 1114 1115void TrayUser::DestroyDetailedView() { 1116} 1117 1118void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) { 1119 // Only the active user is represented in the tray. 1120 if (!layout_view_) 1121 return; 1122 bool need_label = false; 1123 bool need_avatar = false; 1124 switch (status) { 1125 case user::LOGGED_IN_LOCKED: 1126 case user::LOGGED_IN_USER: 1127 case user::LOGGED_IN_OWNER: 1128 case user::LOGGED_IN_PUBLIC: 1129 need_avatar = true; 1130 break; 1131 case user::LOGGED_IN_LOCALLY_MANAGED: 1132 need_avatar = true; 1133 need_label = true; 1134 break; 1135 case user::LOGGED_IN_GUEST: 1136 need_avatar = true; 1137 break; 1138 case user::LOGGED_IN_RETAIL_MODE: 1139 case user::LOGGED_IN_KIOSK_APP: 1140 case user::LOGGED_IN_NONE: 1141 break; 1142 } 1143 1144 if ((need_avatar != (avatar_ != NULL)) || 1145 (need_label != (label_ != NULL))) { 1146 layout_view_->RemoveAllChildViews(true); 1147 if (need_label) { 1148 label_ = new views::Label; 1149 SetupLabelForTray(label_); 1150 layout_view_->AddChildView(label_); 1151 } else { 1152 label_ = NULL; 1153 } 1154 if (need_avatar) { 1155 avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius, true); 1156 layout_view_->AddChildView(avatar_); 1157 } else { 1158 avatar_ = NULL; 1159 } 1160 } 1161 1162 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 1163 if (status == user::LOGGED_IN_LOCALLY_MANAGED) { 1164 label_->SetText( 1165 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL)); 1166 } 1167 1168 if (avatar_) { 1169 if (status == user::LOGGED_IN_GUEST) { 1170 avatar_->SetImage(*ui::ResourceBundle::GetSharedInstance(). 1171 GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(), 1172 gfx::Size(kUserIconSize, kUserIconSize)); 1173 } else { 1174 avatar_->SetImage( 1175 ash::Shell::GetInstance()->session_state_delegate()->GetUserImage( 1176 multiprofile_index_), 1177 gfx::Size(kUserIconSize, kUserIconSize)); 1178 } 1179 } 1180} 1181 1182void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 1183 // Inactive users won't have a layout. 1184 if (!layout_view_) 1185 return; 1186 if (alignment == SHELF_ALIGNMENT_BOTTOM || 1187 alignment == SHELF_ALIGNMENT_TOP) { 1188 if (avatar_) { 1189 avatar_->set_border(views::Border::CreateEmptyBorder( 1190 0, kTrayImageItemHorizontalPaddingBottomAlignment + 2, 1191 0, kTrayImageItemHorizontalPaddingBottomAlignment)); 1192 1193 } 1194 if (label_) { 1195 label_->set_border(views::Border::CreateEmptyBorder( 1196 0, kTrayLabelItemHorizontalPaddingBottomAlignment, 1197 0, kTrayLabelItemHorizontalPaddingBottomAlignment)); 1198 } 1199 layout_view_->SetLayoutManager( 1200 new views::BoxLayout(views::BoxLayout::kHorizontal, 1201 0, 0, kUserLabelToIconPadding)); 1202 } else { 1203 if (avatar_) 1204 SetTrayImageItemBorder(avatar_, alignment); 1205 if (label_) { 1206 label_->set_border(views::Border::CreateEmptyBorder( 1207 kTrayLabelItemVerticalPaddingVeriticalAlignment, 1208 kTrayLabelItemHorizontalPaddingBottomAlignment, 1209 kTrayLabelItemVerticalPaddingVeriticalAlignment, 1210 kTrayLabelItemHorizontalPaddingBottomAlignment)); 1211 } 1212 layout_view_->SetLayoutManager( 1213 new views::BoxLayout(views::BoxLayout::kVertical, 1214 0, 0, kUserLabelToIconPadding)); 1215 } 1216} 1217 1218void TrayUser::OnUserUpdate() { 1219 // Check for null to avoid crbug.com/150944. 1220 if (avatar_) { 1221 avatar_->SetImage( 1222 ash::Shell::GetInstance()->session_state_delegate()->GetUserImage( 1223 multiprofile_index_), 1224 gfx::Size(kUserIconSize, kUserIconSize)); 1225 } 1226} 1227 1228} // namespace internal 1229} // namespace ash 1230