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