tray_user.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/shell.h" 12#include "ash/shell_delegate.h" 13#include "ash/system/tray/system_tray.h" 14#include "ash/system/tray/system_tray_delegate.h" 15#include "ash/system/tray/system_tray_notifier.h" 16#include "ash/system/tray/tray_constants.h" 17#include "ash/system/tray/tray_item_view.h" 18#include "ash/system/tray/tray_popup_label_button.h" 19#include "ash/system/tray/tray_popup_label_button_border.h" 20#include "ash/system/tray/tray_utils.h" 21#include "base/i18n/rtl.h" 22#include "base/logging.h" 23#include "base/memory/scoped_vector.h" 24#include "base/string16.h" 25#include "base/string_util.h" 26#include "base/utf_string_conversions.h" 27#include "grit/ash_resources.h" 28#include "grit/ash_strings.h" 29#include "skia/ext/image_operations.h" 30#include "third_party/skia/include/core/SkCanvas.h" 31#include "third_party/skia/include/core/SkPaint.h" 32#include "third_party/skia/include/core/SkPath.h" 33#include "ui/base/l10n/l10n_util.h" 34#include "ui/base/range/range.h" 35#include "ui/base/resource/resource_bundle.h" 36#include "ui/base/text/text_elider.h" 37#include "ui/gfx/canvas.h" 38#include "ui/gfx/font.h" 39#include "ui/gfx/image/image.h" 40#include "ui/gfx/image/image_skia_operations.h" 41#include "ui/gfx/insets.h" 42#include "ui/gfx/rect.h" 43#include "ui/gfx/render_text.h" 44#include "ui/gfx/size.h" 45#include "ui/gfx/skia_util.h" 46#include "ui/views/border.h" 47#include "ui/views/bubble/tray_bubble_view.h" 48#include "ui/views/controls/button/button.h" 49#include "ui/views/controls/button/custom_button.h" 50#include "ui/views/controls/image_view.h" 51#include "ui/views/controls/label.h" 52#include "ui/views/controls/link.h" 53#include "ui/views/controls/link_listener.h" 54#include "ui/views/layout/box_layout.h" 55#include "ui/views/layout/fill_layout.h" 56#include "ui/views/painter.h" 57#include "ui/views/view.h" 58#include "ui/views/widget/widget.h" 59 60namespace { 61 62const int kUserDetailsVerticalPadding = 5; 63const int kUserCardVerticalPadding = 10; 64const int kProfileRoundedCornerRadius = 2; 65const int kUserIconSize = 27; 66const int kUserLabelToIconPadding = 5; 67 68// The invisible word joiner character, used as a marker to indicate the start 69// and end of the user's display name in the public account user card's text. 70const char16 kDisplayNameMark[] = { 0x2060, 0 }; 71 72const int kPublicAccountLogoutButtonBorderImagesNormal[] = { 73 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 74 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, 75 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, 76 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 77 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, 78 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, 79 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 80 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, 81 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, 82}; 83 84const int kPublicAccountLogoutButtonBorderImagesHovered[] = { 85 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 86 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 87 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 88 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 89 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND, 90 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 91 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 92 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 93 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, 94}; 95 96} // namespace 97 98namespace ash { 99namespace internal { 100 101namespace tray { 102 103// A custom image view with rounded edges. 104class RoundedImageView : public views::View { 105 public: 106 // Constructs a new rounded image view with rounded corners of radius 107 // |corner_radius|. 108 explicit RoundedImageView(int corner_radius); 109 virtual ~RoundedImageView(); 110 111 // Set the image that should be displayed. The image contents is copied to the 112 // receiver's image. 113 void SetImage(const gfx::ImageSkia& img, const gfx::Size& size); 114 115 private: 116 // Overridden from views::View. 117 virtual gfx::Size GetPreferredSize() OVERRIDE; 118 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 119 120 gfx::ImageSkia image_; 121 gfx::ImageSkia resized_; 122 gfx::Size image_size_; 123 int corner_radius_; 124 125 DISALLOW_COPY_AND_ASSIGN(RoundedImageView); 126}; 127 128class ClickableAvatar : public views::CustomButton { 129 public: 130 explicit ClickableAvatar(views::ButtonListener* listener); 131 virtual ~ClickableAvatar(); 132 133 private: 134 DISALLOW_COPY_AND_ASSIGN(ClickableAvatar); 135}; 136 137// The user details shown in public account mode. This is essentially a label 138// but with custom painting code as the text is styled with multiple colors and 139// contains a link. 140class PublicAccountUserDetails : public views::View, 141 public views::LinkListener { 142 public: 143 PublicAccountUserDetails(SystemTrayItem* owner, int used_width); 144 virtual ~PublicAccountUserDetails(); 145 146 private: 147 // Overridden from views::View. 148 virtual void Layout() OVERRIDE; 149 virtual gfx::Size GetPreferredSize() OVERRIDE; 150 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 151 152 // Overridden from views::LinkListener. 153 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; 154 155 // Calculate a preferred size that ensures the label text and the following 156 // link do not wrap over more than three lines in total for aesthetic reasons 157 // if possible. 158 void CalculatePreferredSize(SystemTrayItem* owner, int used_width); 159 160 base::string16 text_; 161 views::Link* learn_more_; 162 gfx::Size preferred_size_; 163 ScopedVector<gfx::RenderText> lines_; 164 165 DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails); 166}; 167 168class UserView : public views::View, 169 public views::ButtonListener { 170 public: 171 explicit UserView(SystemTrayItem* owner, ash::user::LoginStatus login); 172 virtual ~UserView(); 173 174 private: 175 // Overridden from views::View. 176 virtual gfx::Size GetPreferredSize() OVERRIDE; 177 virtual int GetHeightForWidth(int width) OVERRIDE; 178 virtual void Layout() OVERRIDE; 179 180 // Overridden from views::ButtonListener. 181 virtual void ButtonPressed(views::Button* sender, 182 const ui::Event& event) OVERRIDE; 183 184 void AddLogoutButton(ash::user::LoginStatus login); 185 void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login); 186 187 views::View* user_card_; 188 views::View* logout_button_; 189 ClickableAvatar* profile_picture_; 190 191 DISALLOW_COPY_AND_ASSIGN(UserView); 192}; 193 194RoundedImageView::RoundedImageView(int corner_radius) 195 : corner_radius_(corner_radius) {} 196 197RoundedImageView::~RoundedImageView() {} 198 199void RoundedImageView::SetImage(const gfx::ImageSkia& img, 200 const gfx::Size& size) { 201 image_ = img; 202 image_size_ = size; 203 204 // Try to get the best image quality for the avatar. 205 resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_, 206 skia::ImageOperations::RESIZE_BEST, size); 207 if (GetWidget() && visible()) { 208 PreferredSizeChanged(); 209 SchedulePaint(); 210 } 211} 212 213gfx::Size RoundedImageView::GetPreferredSize() { 214 return gfx::Size(image_size_.width() + GetInsets().width(), 215 image_size_.height() + GetInsets().height()); 216} 217 218void RoundedImageView::OnPaint(gfx::Canvas* canvas) { 219 View::OnPaint(canvas); 220 gfx::Rect image_bounds(size()); 221 image_bounds.ClampToCenteredSize(GetPreferredSize()); 222 image_bounds.Inset(GetInsets()); 223 const SkScalar kRadius = SkIntToScalar(corner_radius_); 224 SkPath path; 225 path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius); 226 SkPaint paint; 227 paint.setAntiAlias(true); 228 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); 229 canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(), 230 path, paint); 231} 232 233ClickableAvatar::ClickableAvatar(views::ButtonListener* listener) 234 : views::CustomButton(listener) { 235 SetLayoutManager(new views::FillLayout()); 236 RoundedImageView* user_picture = 237 new RoundedImageView(kProfileRoundedCornerRadius); 238 user_picture->SetImage( 239 ash::Shell::GetInstance()->system_tray_delegate()->GetUserImage(), 240 gfx::Size(kUserIconSize, kUserIconSize)); 241 AddChildView(user_picture); 242} 243 244ClickableAvatar::~ClickableAvatar() {} 245 246PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner, 247 int used_width) 248 : learn_more_(NULL) { 249 const int inner_padding = 250 kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems; 251 const bool rtl = base::i18n::IsRTL(); 252 set_border(views::Border::CreateEmptyBorder( 253 kUserDetailsVerticalPadding, rtl ? 0 : inner_padding, 254 kUserDetailsVerticalPadding, rtl ? inner_padding : 0)); 255 256 ash::SystemTrayDelegate* delegate = 257 ash::Shell::GetInstance()->system_tray_delegate(); 258 // Retrieve the user's display name and wrap it with markers. 259 base::string16 display_name = delegate->GetUserDisplayName(); 260 RemoveChars(display_name, kDisplayNameMark, &display_name); 261 display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0]; 262 // Retrieve the domain managing the device and wrap it with markers. 263 base::string16 domain = UTF8ToUTF16(delegate->GetEnterpriseDomain()); 264 RemoveChars(domain, kDisplayNameMark, &domain); 265 base::i18n::WrapStringWithLTRFormatting(&domain); 266 // Retrieve the label text, inserting the display name and domain. 267 text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL, 268 display_name, domain); 269 270 learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE)); 271 learn_more_->SetUnderline(false); 272 learn_more_->set_listener(this); 273 AddChildView(learn_more_); 274 275 CalculatePreferredSize(owner, used_width); 276} 277 278PublicAccountUserDetails::~PublicAccountUserDetails() {} 279 280void PublicAccountUserDetails::Layout() { 281 lines_.clear(); 282 const gfx::Rect contents_area = GetContentsBounds(); 283 if (contents_area.IsEmpty()) 284 return; 285 286 // Word-wrap the label text. 287 const gfx::Font font; 288 std::vector<base::string16> lines; 289 ui::ElideRectangleText(text_, font, contents_area.width(), 290 contents_area.height(), ui::ELIDE_LONG_WORDS, &lines); 291 // Loop through the lines, creating a renderer for each. 292 gfx::Point position = contents_area.origin(); 293 ui::Range display_name(ui::Range::InvalidRange()); 294 for (std::vector<base::string16>::const_iterator it = lines.begin(); 295 it != lines.end(); ++it) { 296 gfx::RenderText* line = gfx::RenderText::CreateInstance(); 297 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI); 298 line->SetText(*it); 299 const gfx::Size size(contents_area.width(), line->GetStringSize().height()); 300 line->SetDisplayRect(gfx::Rect(position, size)); 301 position.set_y(position.y() + size.height()); 302 303 // Set the default text color for the line. 304 line->SetColor(kPublicAccountUserCardTextColor); 305 306 // If a range of the line contains the user's display name, apply a custom 307 // text color to it. 308 if (display_name.is_empty()) 309 display_name.set_start(it->find(kDisplayNameMark)); 310 if (!display_name.is_empty()) { 311 display_name.set_end( 312 it->find(kDisplayNameMark, display_name.start() + 1)); 313 ui::Range line_range(0, it->size()); 314 line->ApplyColor(kPublicAccountUserCardNameColor, 315 display_name.Intersect(line_range)); 316 // Update the range for the next line. 317 if (display_name.end() >= line_range.end()) 318 display_name.set_start(0); 319 else 320 display_name = ui::Range::InvalidRange(); 321 } 322 323 lines_.push_back(line); 324 } 325 326 // Position the link after the label text, separated by a space. If it does 327 // not fit onto the last line of the text, wrap the link onto its own line. 328 const gfx::Size last_line_size = lines_.back()->GetStringSize(); 329 const int space_width = font.GetStringWidth(ASCIIToUTF16(" ")); 330 const gfx::Size link_size = learn_more_->GetPreferredSize(); 331 if (contents_area.width() - last_line_size.width() >= 332 space_width + link_size.width()) { 333 position.set_x(position.x() + last_line_size.width() + space_width); 334 position.set_y(position.y() - last_line_size.height()); 335 } 336 position.set_y(position.y() - learn_more_->GetInsets().top()); 337 gfx::Rect learn_more_bounds(position, link_size); 338 learn_more_bounds.Intersect(contents_area); 339 if (base::i18n::IsRTL()) { 340 const gfx::Insets insets = GetInsets(); 341 learn_more_bounds.Offset(insets.right() - insets.left(), 0); 342 } 343 learn_more_->SetBoundsRect(learn_more_bounds); 344} 345 346gfx::Size PublicAccountUserDetails::GetPreferredSize() { 347 return preferred_size_; 348} 349 350void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) { 351 for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin(); 352 it != lines_.end(); ++it) { 353 (*it)->Draw(canvas); 354 } 355 views::View::OnPaint(canvas); 356} 357 358void PublicAccountUserDetails::LinkClicked(views::Link* source, 359 int event_flags) { 360 DCHECK_EQ(source, learn_more_); 361 ash::Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo(); 362} 363 364void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner, 365 int used_width) { 366 const gfx::Font font; 367 const gfx::Size link_size = learn_more_->GetPreferredSize(); 368 const int space_width = font.GetStringWidth(ASCIIToUTF16(" ")); 369 const gfx::Insets insets = GetInsets(); 370 views::TrayBubbleView* bubble_view = 371 owner->system_tray()->GetSystemBubble()->bubble_view(); 372 int min_width = std::max( 373 link_size.width(), 374 bubble_view->GetPreferredSize().width() - (used_width + insets.width())); 375 int max_width = std::min( 376 font.GetStringWidth(text_) + space_width + link_size.width(), 377 bubble_view->GetMaximumSize().width() - (used_width + insets.width())); 378 // Do a binary search for the minimum width that ensures no more than three 379 // lines are needed. The lower bound is the minimum of the current bubble 380 // width and the width of the link (as no wrapping is permitted inside the 381 // link). The upper bound is the maximum of the largest allowed bubble width 382 // and the sum of the label text and link widths when put on a single line. 383 std::vector<base::string16> lines; 384 while (min_width < max_width) { 385 lines.clear(); 386 const int width = (min_width + max_width) / 2; 387 const bool too_narrow = ui::ElideRectangleText( 388 text_, font, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines) != 0; 389 int line_count = lines.size(); 390 if (!too_narrow && line_count == 3 && 391 width - font.GetStringWidth(lines.back()) <= 392 space_width + link_size.width()) { 393 ++line_count; 394 } 395 if (too_narrow || line_count > 3) 396 min_width = width + 1; 397 else 398 max_width = width; 399 } 400 401 // Calculate the corresponding height and set the preferred size. 402 lines.clear(); 403 ui::ElideRectangleText( 404 text_, font, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines); 405 int line_count = lines.size(); 406 if (min_width - font.GetStringWidth(lines.back()) <= 407 space_width + link_size.width()) { 408 ++line_count; 409 } 410 const int line_height = font.GetHeight(); 411 const int link_extra_height = std::max( 412 link_size.height() - learn_more_->GetInsets().top() - line_height, 0); 413 preferred_size_ = gfx::Size( 414 min_width + insets.width(), 415 line_count * line_height + link_extra_height + insets.height()); 416 417 bubble_view->SetWidth(preferred_size_.width() + used_width); 418} 419 420UserView::UserView(SystemTrayItem* owner, ash::user::LoginStatus login) 421 : user_card_(NULL), 422 logout_button_(NULL), 423 profile_picture_(NULL) { 424 CHECK_NE(ash::user::LOGGED_IN_NONE, login); 425 set_background(views::Background::CreateSolidBackground( 426 login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor : 427 kBackgroundColor)); 428 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 429 kTrayPopupPaddingBetweenItems)); 430 // The logout button must be added before the user card so that the user card 431 // can correctly calculate the remaining available width. 432 AddLogoutButton(login); 433 AddUserCard(owner, login); 434} 435 436UserView::~UserView() {} 437 438gfx::Size UserView::GetPreferredSize() { 439 gfx::Size size = views::View::GetPreferredSize(); 440 if (!user_card_) { 441 // Make sure the default user view item is at least as tall as the other 442 // items. 443 size.set_height(std::max(size.height(), 444 kTrayPopupItemHeight + GetInsets().height())); 445 } 446 return size; 447} 448 449int UserView::GetHeightForWidth(int width) { 450 return GetPreferredSize().height(); 451} 452 453void UserView::Layout() { 454 gfx::Rect contents_area(GetContentsBounds()); 455 if (user_card_ && logout_button_) { 456 // Give the logout button the space it requests. 457 gfx::Rect logout_area = contents_area; 458 logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize()); 459 logout_area.set_x(contents_area.right() - logout_area.width()); 460 logout_button_->SetBoundsRect(logout_area); 461 462 // Give the remaining space to the user card. 463 gfx::Rect user_card_area = contents_area; 464 int remaining_width = contents_area.width() - 465 (logout_area.width() + kTrayPopupPaddingBetweenItems); 466 user_card_area.set_width(std::max(0, remaining_width)); 467 user_card_->SetBoundsRect(user_card_area); 468 } else if (user_card_) { 469 user_card_->SetBoundsRect(contents_area); 470 } else if (logout_button_) { 471 logout_button_->SetBoundsRect(contents_area); 472 } 473} 474 475void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) { 476 if (sender == logout_button_) { 477 ash::Shell::GetInstance()->system_tray_delegate()->SignOut(); 478 } else if (sender == profile_picture_) { 479 if (ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) 480 ash::Shell::GetInstance()->system_tray_delegate()->ShowUserLogin(); 481 else 482 ash::Shell::GetInstance()->system_tray_delegate()->ChangeProfilePicture(); 483 } else { 484 NOTREACHED(); 485 } 486} 487 488void UserView::AddLogoutButton(ash::user::LoginStatus login) { 489 // A user should not be able to modify logged-in state when screen is 490 // locked. 491 if (login == ash::user::LOGGED_IN_LOCKED) 492 return; 493 494 const base::string16 title = ash::user::GetLocalizedSignOutStringForStatus( 495 login, true); 496 TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title); 497 logout_button->SetAccessibleName(title); 498 logout_button_ = logout_button; 499 // In public account mode, the logout button border has a custom color. 500 if (login == ash::user::LOGGED_IN_PUBLIC) { 501 TrayPopupLabelButtonBorder* border = 502 static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border()); 503 border->SetPainter(false, views::Button::STATE_NORMAL, 504 views::Painter::CreateImageGridPainter( 505 kPublicAccountLogoutButtonBorderImagesNormal)); 506 border->SetPainter(false, views::Button::STATE_HOVERED, 507 views::Painter::CreateImageGridPainter( 508 kPublicAccountLogoutButtonBorderImagesHovered)); 509 border->SetPainter(false, views::Button::STATE_PRESSED, 510 views::Painter::CreateImageGridPainter( 511 kPublicAccountLogoutButtonBorderImagesHovered)); 512 } 513 AddChildView(logout_button_); 514} 515 516void UserView::AddUserCard(SystemTrayItem* owner, 517 ash::user::LoginStatus login) { 518 if (login == ash::user::LOGGED_IN_GUEST) 519 return; 520 521 set_border(views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal, 522 0, kTrayPopupPaddingHorizontal)); 523 524 user_card_ = new views::View(); 525 user_card_->SetLayoutManager(new views::BoxLayout( 526 views::BoxLayout::kHorizontal, 0, kUserCardVerticalPadding, 527 kTrayPopupPaddingBetweenItems)); 528 AddChildViewAt(user_card_, 0); 529 530 if (login == ash::user::LOGGED_IN_RETAIL_MODE) { 531 views::Label* details = new views::Label; 532 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 533 details->SetText( 534 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL)); 535 details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1)); 536 details->SetHorizontalAlignment(gfx::ALIGN_LEFT); 537 user_card_->AddChildView(details); 538 return; 539 } 540 profile_picture_ = new ClickableAvatar(this); 541 user_card_->AddChildView(profile_picture_); 542 543 if (login == ash::user::LOGGED_IN_PUBLIC) { 544 user_card_->AddChildView(new PublicAccountUserDetails( 545 owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems)); 546 return; 547 } 548 549 ash::SystemTrayDelegate* delegate = 550 ash::Shell::GetInstance()->system_tray_delegate(); 551 views::View* details = new views::View; 552 details->SetLayoutManager(new views::BoxLayout( 553 views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0)); 554 views::Label* username = new views::Label(delegate->GetUserDisplayName()); 555 username->SetHorizontalAlignment(gfx::ALIGN_LEFT); 556 details->AddChildView(username); 557 558 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 559 560 views::Label* additional = new views::Label(); 561 562 additional->SetText(login == ash::user::LOGGED_IN_LOCALLY_MANAGED ? 563 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) : 564 UTF8ToUTF16(delegate->GetUserEmail())); 565 566 additional->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont)); 567 additional->SetHorizontalAlignment(gfx::ALIGN_LEFT); 568 additional->SetEnabled(false); 569 details->AddChildView(additional); 570 user_card_->AddChildView(details); 571} 572 573} // namespace tray 574 575TrayUser::TrayUser(SystemTray* system_tray) 576 : SystemTrayItem(system_tray), 577 user_(NULL), 578 layout_view_(NULL), 579 avatar_(NULL), 580 label_(NULL) { 581 Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this); 582} 583 584TrayUser::~TrayUser() { 585 Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this); 586} 587 588views::View* TrayUser::CreateTrayView(user::LoginStatus status) { 589 CHECK(layout_view_ == NULL); 590 layout_view_ = new views::View(); 591 layout_view_->SetLayoutManager( 592 new views::BoxLayout(views::BoxLayout::kHorizontal, 593 0, 0, kUserLabelToIconPadding)); 594 UpdateAfterLoginStatusChange(status); 595 return layout_view_; 596} 597 598views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { 599 if (status == user::LOGGED_IN_NONE) 600 return NULL; 601 602 CHECK(user_ == NULL); 603 user_ = new tray::UserView(this, status); 604 return user_; 605} 606 607views::View* TrayUser::CreateDetailedView(user::LoginStatus status) { 608 return NULL; 609} 610 611void TrayUser::DestroyTrayView() { 612 layout_view_ = NULL; 613 avatar_ = NULL; 614 label_ = NULL; 615} 616 617void TrayUser::DestroyDefaultView() { 618 user_ = NULL; 619} 620 621void TrayUser::DestroyDetailedView() { 622} 623 624void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) { 625 CHECK(layout_view_); 626 bool need_label = false; 627 bool need_avatar = false; 628 switch (status) { 629 case user::LOGGED_IN_LOCKED: 630 case user::LOGGED_IN_USER: 631 case user::LOGGED_IN_OWNER: 632 case user::LOGGED_IN_PUBLIC: 633 need_avatar = true; 634 break; 635 case user::LOGGED_IN_LOCALLY_MANAGED: 636 need_avatar = true; 637 need_label = true; 638 break; 639 case user::LOGGED_IN_GUEST: 640 need_label = true; 641 break; 642 case user::LOGGED_IN_RETAIL_MODE: 643 case user::LOGGED_IN_KIOSK_APP: 644 case user::LOGGED_IN_NONE: 645 break; 646 } 647 648 if ((need_avatar != (avatar_ != NULL)) || 649 (need_label != (label_ != NULL))) { 650 layout_view_->RemoveAllChildViews(true); 651 if (need_label) { 652 label_ = new views::Label; 653 SetupLabelForTray(label_); 654 layout_view_->AddChildView(label_); 655 } else { 656 label_ = NULL; 657 } 658 if (need_avatar) { 659 avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius); 660 layout_view_->AddChildView(avatar_); 661 } else { 662 avatar_ = NULL; 663 } 664 } 665 666 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 667 if (status == user::LOGGED_IN_LOCALLY_MANAGED) { 668 label_->SetText( 669 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL)); 670 } else if (status == user::LOGGED_IN_GUEST) { 671 label_->SetText(bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL)); 672 } 673 674 if (avatar_) { 675 avatar_->SetImage( 676 ash::Shell::GetInstance()->system_tray_delegate()->GetUserImage(), 677 gfx::Size(kUserIconSize, kUserIconSize)); 678 } 679} 680 681void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 682 CHECK(layout_view_); 683 if (alignment == SHELF_ALIGNMENT_BOTTOM || 684 alignment == SHELF_ALIGNMENT_TOP) { 685 if (avatar_) { 686 avatar_->set_border(views::Border::CreateEmptyBorder( 687 0, kTrayImageItemHorizontalPaddingBottomAlignment + 2, 688 0, kTrayImageItemHorizontalPaddingBottomAlignment)); 689 690 } 691 if (label_) { 692 label_->set_border(views::Border::CreateEmptyBorder( 693 0, kTrayLabelItemHorizontalPaddingBottomAlignment, 694 0, kTrayLabelItemHorizontalPaddingBottomAlignment)); 695 } 696 layout_view_->SetLayoutManager( 697 new views::BoxLayout(views::BoxLayout::kHorizontal, 698 0, 0, kUserLabelToIconPadding)); 699 } else { 700 if (avatar_) 701 SetTrayImageItemBorder(avatar_, alignment); 702 if (label_) { 703 label_->set_border(views::Border::CreateEmptyBorder( 704 kTrayLabelItemVerticalPaddingVeriticalAlignment, 705 kTrayLabelItemHorizontalPaddingBottomAlignment, 706 kTrayLabelItemVerticalPaddingVeriticalAlignment, 707 kTrayLabelItemHorizontalPaddingBottomAlignment)); 708 } 709 layout_view_->SetLayoutManager( 710 new views::BoxLayout(views::BoxLayout::kVertical, 711 0, 0, kUserLabelToIconPadding)); 712 } 713} 714 715void TrayUser::OnUserUpdate() { 716 // Check for null to avoid crbug.com/150944. 717 if (avatar_) { 718 avatar_->SetImage( 719 ash::Shell::GetInstance()->system_tray_delegate()->GetUserImage(), 720 gfx::Size(kUserIconSize, kUserIconSize)); 721 } 722} 723 724} // namespace internal 725} // namespace ash 726