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