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