Index: ash/system/user/tray_user.cc |
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc |
index d55ac7da0720eee2bd1c8d9748ae44d15430dd4f..1c0196692ada347b31bf451cb48b463c5d43f858 100644 |
--- a/ash/system/user/tray_user.cc |
+++ b/ash/system/user/tray_user.cc |
@@ -4,36 +4,88 @@ |
#include "ash/system/user/tray_user.h" |
+#include <algorithm> |
+#include <climits> |
+#include <vector> |
+ |
#include "ash/shell.h" |
+#include "ash/system/tray/system_tray.h" |
#include "ash/system/tray/system_tray_delegate.h" |
#include "ash/system/tray/tray_constants.h" |
#include "ash/system/tray/tray_item_view.h" |
#include "ash/system/tray/tray_views.h" |
+#include "base/i18n/rtl.h" |
+#include "base/logging.h" |
+#include "base/memory/scoped_vector.h" |
+#include "base/string16.h" |
+#include "base/string_util.h" |
#include "base/utf_string_conversions.h" |
+#include "grit/ash_resources.h" |
#include "grit/ash_strings.h" |
#include "skia/ext/image_operations.h" |
#include "third_party/skia/include/core/SkCanvas.h" |
#include "third_party/skia/include/core/SkPaint.h" |
#include "third_party/skia/include/core/SkPath.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/range/range.h" |
#include "ui/base/resource/resource_bundle.h" |
+#include "ui/base/text/text_elider.h" |
#include "ui/gfx/canvas.h" |
+#include "ui/gfx/font.h" |
#include "ui/gfx/image/image.h" |
#include "ui/gfx/image/image_skia_operations.h" |
+#include "ui/gfx/insets.h" |
+#include "ui/gfx/rect.h" |
+#include "ui/gfx/render_text.h" |
#include "ui/gfx/size.h" |
#include "ui/gfx/skia_util.h" |
+#include "ui/views/border.h" |
+#include "ui/views/bubble/tray_bubble_view.h" |
+#include "ui/views/controls/button/border_images.h" |
#include "ui/views/controls/button/button.h" |
-#include "ui/views/controls/button/text_button.h" |
+#include "ui/views/controls/button/custom_button.h" |
#include "ui/views/controls/image_view.h" |
#include "ui/views/controls/label.h" |
+#include "ui/views/controls/link.h" |
+#include "ui/views/controls/link_listener.h" |
#include "ui/views/layout/box_layout.h" |
#include "ui/views/view.h" |
#include "ui/views/widget/widget.h" |
namespace { |
-const int kUserInfoVerticalPadding = 10; |
-const int kUserIconSize = 27; |
+const int kUserDetailsVerticalPadding = 5; |
+const int kUserCardVerticalPadding = 10; |
const int kProfileRoundedCornerRadius = 2; |
+const int kUserIconSize = 27; |
+ |
+// The invisible word joiner character, used as a marker to indicate the start |
+// and end of the user's display name in the public account user card's text. |
+const char16 kDisplayNameMark[] = { 0x2060, 0 }; |
+ |
+const int kPublicAccountLogoutButtonBorderImagesNormal[] = { |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND, |
+}; |
+ |
+const int kPublicAccountLogoutButtonBorderImagesHovered[] = { |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+ IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
+}; |
} // namespace |
@@ -47,49 +99,18 @@ class RoundedImageView : public views::View { |
public: |
// Constructs a new rounded image view with rounded corners of radius |
// |corner_radius|. |
- explicit RoundedImageView(int corner_radius) : corner_radius_(corner_radius) { |
- } |
+ explicit RoundedImageView(int corner_radius); |
+ virtual ~RoundedImageView(); |
- virtual ~RoundedImageView() { |
- } |
- |
- // Set the image that should be displayed from a pointer. The pointer |
- // contents is copied in the receiver's image. |
- void SetImage(const gfx::ImageSkia& img, const gfx::Size& size) { |
- image_ = img; |
- image_size_ = size; |
- |
- // Try to get the best image quality for the avatar. |
- resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_, |
- skia::ImageOperations::RESIZE_BEST, size); |
- if (GetWidget() && visible()) { |
- PreferredSizeChanged(); |
- SchedulePaint(); |
- } |
- } |
+ // Set the image that should be displayed. The image contents is copied to the |
+ // receiver's image. |
+ void SetImage(const gfx::ImageSkia& img, const gfx::Size& size); |
+ private: |
// Overridden from views::View. |
- virtual gfx::Size GetPreferredSize() OVERRIDE { |
- return gfx::Size(image_size_.width() + GetInsets().width(), |
- image_size_.height() + GetInsets().height()); |
- } |
+ virtual gfx::Size GetPreferredSize() OVERRIDE; |
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
- View::OnPaint(canvas); |
- gfx::Rect image_bounds(size()); |
- image_bounds.ClampToCenteredSize(GetPreferredSize()); |
- image_bounds.Inset(GetInsets()); |
- const SkScalar kRadius = SkIntToScalar(corner_radius_); |
- SkPath path; |
- path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius); |
- SkPaint paint; |
- paint.setAntiAlias(true); |
- paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); |
- canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(), |
- path, paint); |
- } |
- |
- private: |
gfx::ImageSkia image_; |
gfx::ImageSkia resized_; |
gfx::Size image_size_; |
@@ -98,146 +119,410 @@ class RoundedImageView : public views::View { |
DISALLOW_COPY_AND_ASSIGN(RoundedImageView); |
}; |
+// The user details shown in public account mode. This is essentially a label |
+// but with custom painting code as the text is styled with multiple colors and |
+// contains a link. |
+class PublicAccountUserDetails : public views::View, |
+ public views::LinkListener { |
+ public: |
+ PublicAccountUserDetails(SystemTrayItem* owner, int used_width); |
+ virtual ~PublicAccountUserDetails(); |
+ |
+ private: |
+ // Overridden from views::View. |
+ virtual void Layout() OVERRIDE; |
+ virtual gfx::Size GetPreferredSize() OVERRIDE; |
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
+ |
+ // Overridden from views::LinkListener. |
+ virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; |
+ |
+ // Calculate a preferred size that ensures the label text and the following |
+ // link do not wrap over more than three lines in total for aesthetic reasons |
+ // if possible. |
+ void CalculatePreferredSize(SystemTrayItem* owner, int used_width); |
+ |
+ string16 text_; |
+ views::Link* learn_more_; |
+ gfx::Size preferred_size_; |
+ ScopedVector<gfx::RenderText> lines_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails); |
+}; |
+ |
class UserView : public views::View, |
public views::ButtonListener { |
public: |
- explicit UserView(ash::user::LoginStatus login) |
- : login_(login), |
- user_info_(NULL), |
- username_(NULL), |
- email_(NULL), |
- signout_(NULL) { |
- CHECK(login_ != ash::user::LOGGED_IN_NONE); |
- |
- bool public_account = login_ == ash::user::LOGGED_IN_PUBLIC; |
- bool guest = login_ == ash::user::LOGGED_IN_GUEST; |
- bool locked = login_ == ash::user::LOGGED_IN_LOCKED; |
- |
- set_background(views::Background::CreateSolidBackground( |
- public_account ? kPublicAccountBackgroundColor : kBackgroundColor)); |
- |
- if (!guest) |
- AddUserInfo(); |
- |
- // A user should not be able to modify logged in state when screen is |
- // locked. |
- if (!locked) |
- AddButtonContainer(); |
- } |
+ explicit UserView(SystemTrayItem* owner, ash::user::LoginStatus login); |
+ virtual ~UserView(); |
+ |
+ private: |
+ // Overridden from views::View. |
+ virtual gfx::Size GetPreferredSize() OVERRIDE; |
+ virtual void Layout() OVERRIDE; |
+ |
+ // Overridden from views::ButtonListener. |
+ virtual void ButtonPressed(views::Button* sender, |
+ const ui::Event& event) OVERRIDE; |
+ |
+ void AddLogoutButton(ash::user::LoginStatus login); |
+ void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login); |
+ |
+ views::View* user_card_; |
+ views::View* logout_button_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(UserView); |
+}; |
- virtual ~UserView() {} |
+RoundedImageView::RoundedImageView(int corner_radius) |
+ : corner_radius_(corner_radius) {} |
- // Create container for buttons. |
- void AddButtonContainer() { |
- const string16 title = ash::user::GetLocalizedSignOutStringForStatus(login_, |
- true); |
- TrayPopupLabelButton* button = new TrayPopupLabelButton(this, title); |
- button->SetAccessibleName(title); |
- AddChildView(button); |
- signout_ = button; |
+RoundedImageView::~RoundedImageView() {} |
+ |
+void RoundedImageView::SetImage(const gfx::ImageSkia& img, |
+ const gfx::Size& size) { |
+ image_ = img; |
+ image_size_ = size; |
+ |
+ // Try to get the best image quality for the avatar. |
+ resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_, |
+ skia::ImageOperations::RESIZE_BEST, size); |
+ if (GetWidget() && visible()) { |
+ PreferredSizeChanged(); |
+ SchedulePaint(); |
} |
+} |
- private: |
- void AddUserInfo() { |
- user_info_ = new views::View; |
- user_info_->SetLayoutManager(new views::BoxLayout( |
- views::BoxLayout::kHorizontal, kTrayPopupPaddingHorizontal, |
- kUserInfoVerticalPadding, kTrayPopupPaddingBetweenItems)); |
- AddChildView(user_info_); |
- |
- if (login_ == ash::user::LOGGED_IN_KIOSK) { |
- views::Label* label = new views::Label; |
- ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
- label->SetText( |
- bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL)); |
- label->set_border(views::Border::CreateEmptyBorder( |
- 0, 4, 0, 1)); |
- label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
- user_info_->AddChildView(label); |
- return; |
+gfx::Size RoundedImageView::GetPreferredSize() { |
+ return gfx::Size(image_size_.width() + GetInsets().width(), |
+ image_size_.height() + GetInsets().height()); |
+} |
+ |
+void RoundedImageView::OnPaint(gfx::Canvas* canvas) { |
+ View::OnPaint(canvas); |
+ gfx::Rect image_bounds(size()); |
+ image_bounds.ClampToCenteredSize(GetPreferredSize()); |
+ image_bounds.Inset(GetInsets()); |
+ const SkScalar kRadius = SkIntToScalar(corner_radius_); |
+ SkPath path; |
+ path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius); |
+ SkPaint paint; |
+ paint.setAntiAlias(true); |
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); |
+ canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(), |
+ path, paint); |
+} |
+ |
+PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner, |
+ int used_width) |
+ : learn_more_(NULL) { |
+ const int inner_padding = |
+ kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems; |
+ const bool rtl = base::i18n::IsRTL(); |
+ set_border(views::Border::CreateEmptyBorder( |
+ kUserDetailsVerticalPadding, rtl ? 0 : inner_padding, |
+ kUserDetailsVerticalPadding, rtl ? inner_padding : 0)); |
+ |
+ ash::SystemTrayDelegate* delegate = |
+ ash::Shell::GetInstance()->tray_delegate(); |
+ // Retrieve the user's display name and wrap it with markers. |
+ string16 display_name = delegate->GetUserDisplayName(); |
+ RemoveChars(display_name, kDisplayNameMark, &display_name); |
+ display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0]; |
+ // Retrieve the domain managing the device and wrap it with markers. |
+ string16 domain = UTF8ToUTF16(delegate->GetEnterpriseDomain()); |
+ RemoveChars(domain, kDisplayNameMark, &domain); |
+ base::i18n::WrapStringWithLTRFormatting(&domain); |
+ // Retrieve the label text, inserting the display name and domain. |
+ text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL, |
+ display_name, domain); |
+ |
+ learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE)); |
+ learn_more_->SetUnderline(false); |
+ learn_more_->set_listener(this); |
+ AddChildView(learn_more_); |
+ |
+ CalculatePreferredSize(owner, used_width); |
+} |
+ |
+PublicAccountUserDetails::~PublicAccountUserDetails() {} |
+ |
+void PublicAccountUserDetails::Layout() { |
+ lines_.clear(); |
+ const gfx::Rect contents_area = GetContentsBounds(); |
+ if (contents_area.IsEmpty()) |
+ return; |
+ |
+ // Word-wrap the label text. |
+ const gfx::Font font; |
+ std::vector<string16> lines; |
+ ui::ElideRectangleText(text_, font, contents_area.width(), |
+ contents_area.height(), ui::ELIDE_LONG_WORDS, &lines); |
+ // Loop through the lines, creating a renderer for each. |
+ gfx::Point position = contents_area.origin(); |
+ ui::Range display_name(ui::Range::InvalidRange()); |
+ for (std::vector<string16>::const_iterator it = lines.begin(); |
+ it != lines.end(); ++it) { |
+ gfx::RenderText* line = gfx::RenderText::CreateInstance(); |
+ line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI); |
+ line->SetText(*it); |
+ const gfx::Size size(contents_area.width(), line->GetStringSize().height()); |
+ line->SetDisplayRect(gfx::Rect(position, size)); |
+ position.set_y(position.y() + size.height()); |
+ |
+ // Set the default text color for the line. |
+ gfx::StyleRange default_style(line->default_style()); |
+ default_style.foreground = kPublicAccountUserCardTextColor; |
+ line->set_default_style(default_style); |
+ line->ApplyDefaultStyle(); |
+ |
+ // If a range of the line contains the user's display name, apply a custom |
+ // text color to it. |
+ if (display_name.is_empty()) |
+ display_name.set_start(it->find(kDisplayNameMark)); |
+ if (!display_name.is_empty()) { |
+ display_name.set_end( |
+ it->find(kDisplayNameMark, display_name.start() + 1)); |
+ gfx::StyleRange display_name_style(line->default_style()); |
+ display_name_style.foreground = kPublicAccountUserCardNameColor; |
+ ui::Range line_range(0, it->size()); |
+ display_name_style.range = display_name.Intersect(line_range); |
+ line->ApplyStyleRange(display_name_style); |
+ // Update the range for the next line. |
+ if (display_name.end() >= line_range.end()) |
+ display_name.set_start(0); |
+ else |
+ display_name = ui::Range::InvalidRange(); |
} |
- RoundedImageView* image = new RoundedImageView(kProfileRoundedCornerRadius); |
- image->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), |
- gfx::Size(kUserIconSize, kUserIconSize)); |
- user_info_->AddChildView(image); |
- |
- views::View* user = new views::View; |
- user->SetLayoutManager(new views::BoxLayout( |
- views::BoxLayout::kVertical, 0, 5, 0)); |
- ash::SystemTrayDelegate* tray = |
- ash::Shell::GetInstance()->tray_delegate(); |
- username_ = new views::Label(tray->GetUserDisplayName()); |
- username_->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
- user->AddChildView(username_); |
- |
- email_ = new views::Label(UTF8ToUTF16(tray->GetUserEmail())); |
- email_->SetFont(username_->font().DeriveFont(-1)); |
- email_->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
- email_->SetEnabled(false); |
- user->AddChildView(email_); |
- |
- user_info_->AddChildView(user); |
+ lines_.push_back(line); |
} |
- // Overridden from views::ButtonListener. |
- virtual void ButtonPressed(views::Button* sender, |
- const ui::Event& event) OVERRIDE { |
- CHECK(sender == signout_); |
- ash::SystemTrayDelegate* tray = ash::Shell::GetInstance()->tray_delegate(); |
- tray->SignOut(); |
+ // Position the link after the label text, separated by a space. If it does |
+ // not fit onto the last line of the text, wrap the link onto its own line. |
+ const gfx::Size last_line_size = lines_.back()->GetStringSize(); |
+ const int space_width = font.GetStringWidth(ASCIIToUTF16(" ")); |
+ const gfx::Size link_size = learn_more_->GetPreferredSize(); |
+ if (contents_area.width() - last_line_size.width() >= |
+ space_width + link_size.width()) { |
+ position.set_x(position.x() + last_line_size.width() + space_width); |
+ position.set_y(position.y() - last_line_size.height()); |
+ } |
+ position.set_y(position.y() - learn_more_->GetInsets().top()); |
+ gfx::Rect learn_more_bounds(position, link_size); |
+ learn_more_bounds.Intersect(contents_area); |
+ if (base::i18n::IsRTL()) { |
+ const gfx::Insets insets = GetInsets(); |
+ learn_more_bounds.Offset(insets.right() - insets.left(), 0); |
} |
+ learn_more_->SetBoundsRect(learn_more_bounds); |
+} |
- // Overridden from views::View. |
- virtual gfx::Size GetPreferredSize() OVERRIDE { |
- gfx::Size size; |
- if (user_info_) |
- size = user_info_->GetPreferredSize(); |
- if (signout_) { |
- gfx::Size signout_size = signout_->GetPreferredSize(); |
- // Make sure the user default view item at least as tall as the other |
- // tray popup items. |
- if (size.height() == 0) |
- size.set_height(kTrayPopupItemHeight); |
- size.set_height(std::max(size.height(), signout_size.height())); |
- size.set_width(size.width() + signout_size.width() + |
- kTrayPopupPaddingHorizontal * 2 + kTrayPopupPaddingBetweenItems); |
- } |
- return size; |
+gfx::Size PublicAccountUserDetails::GetPreferredSize() { |
+ return preferred_size_; |
+} |
+ |
+void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) { |
+ for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin(); |
+ it != lines_.end(); ++it) { |
+ (*it)->Draw(canvas); |
} |
+ views::View::OnPaint(canvas); |
+} |
+ |
+void PublicAccountUserDetails::LinkClicked(views::Link* source, |
+ int event_flags) { |
+ DCHECK_EQ(source, learn_more_); |
+ ash::Shell::GetInstance()->tray_delegate()->ShowPublicAccountInfo(); |
+} |
- virtual void Layout() OVERRIDE { |
- views::View::Layout(); |
- if (bounds().IsEmpty()) |
- return; |
- |
- if (signout_ && user_info_) { |
- gfx::Rect signout_bounds(bounds()); |
- signout_bounds.ClampToCenteredSize(signout_->GetPreferredSize()); |
- signout_bounds.set_x(width() - signout_bounds.width() - |
- kTrayPopupPaddingHorizontal); |
- signout_->SetBoundsRect(signout_bounds); |
- |
- gfx::Rect usercard_bounds(user_info_->GetPreferredSize()); |
- usercard_bounds.set_width(signout_bounds.x()); |
- user_info_->SetBoundsRect(usercard_bounds); |
- } else if (signout_) { |
- signout_->SetBoundsRect(gfx::Rect(size())); |
- } else if (user_info_) { |
- user_info_->SetBoundsRect(gfx::Rect(size())); |
+void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner, |
+ int used_width) { |
+ const gfx::Font font; |
+ const gfx::Size link_size = learn_more_->GetPreferredSize(); |
+ const int space_width = font.GetStringWidth(ASCIIToUTF16(" ")); |
+ const gfx::Insets insets = GetInsets(); |
+ views::TrayBubbleView* bubble_view = |
+ owner->system_tray()->GetSystemBubble()->bubble_view(); |
+ int min_width = std::max( |
+ link_size.width(), |
+ bubble_view->GetPreferredSize().width() - (used_width + insets.width())); |
+ int max_width = std::min( |
+ font.GetStringWidth(text_) + space_width + link_size.width(), |
+ bubble_view->GetMaximumSize().width() - (used_width + insets.width())); |
+ // Do a binary search for the minimum width that ensures no more than three |
+ // lines are needed. The lower bound is the minimum of the current bubble |
+ // width and the width of the link (as no wrapping is permitted inside the |
+ // link). The upper bound is the maximum of the largest allowed bubble width |
+ // and the sum of the label text and link widths when put on a single line. |
+ std::vector<string16> lines; |
+ while (min_width < max_width) { |
+ lines.clear(); |
+ const int width = (min_width + max_width) / 2; |
+ const bool too_narrow = ui::ElideRectangleText( |
+ text_, font, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines) != 0; |
+ int line_count = lines.size(); |
+ if (!too_narrow && line_count == 3 && |
+ width - font.GetStringWidth(lines.back()) <= |
+ space_width + link_size.width()) { |
+ ++line_count; |
} |
+ if (too_narrow || line_count > 3) |
+ min_width = width + 1; |
+ else |
+ max_width = width; |
} |
- user::LoginStatus login_; |
+ // Calculate the corresponding height and set the preferred size. |
+ lines.clear(); |
+ ui::ElideRectangleText( |
+ text_, font, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines); |
+ int line_count = lines.size(); |
+ if (min_width - font.GetStringWidth(lines.back()) <= |
+ space_width + link_size.width()) { |
+ ++line_count; |
+ } |
+ const int line_height = font.GetHeight(); |
+ const int link_extra_height = std::max( |
+ link_size.height() - learn_more_->GetInsets().top() - line_height, 0); |
+ preferred_size_ = gfx::Size( |
+ min_width + insets.width(), |
+ line_count * line_height + link_extra_height + insets.height()); |
+ |
+ bubble_view->SetWidth(preferred_size_.width() + used_width); |
+} |
- views::View* user_info_; |
- views::Label* username_; |
- views::Label* email_; |
+UserView::UserView(SystemTrayItem* owner, ash::user::LoginStatus login) |
+ : user_card_(NULL), |
+ logout_button_(NULL) { |
+ CHECK_NE(ash::user::LOGGED_IN_NONE, login); |
+ set_background(views::Background::CreateSolidBackground( |
+ login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor : |
+ kBackgroundColor)); |
+ SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, |
+ kTrayPopupPaddingBetweenItems)); |
+ // The logout button must be added before the user card so that the user card |
+ // can correctly calculate the remaining available width. |
+ AddLogoutButton(login); |
+ AddUserCard(owner, login); |
+} |
- views::Button* signout_; |
+UserView::~UserView() {} |
- DISALLOW_COPY_AND_ASSIGN(UserView); |
-}; |
+gfx::Size UserView::GetPreferredSize() { |
+ gfx::Size size = views::View::GetPreferredSize(); |
+ if (!user_card_) { |
+ // Make sure the default user view item is at least as tall as the other |
+ // items. |
+ size.set_height(std::max(size.height(), |
+ kTrayPopupItemHeight + GetInsets().height())); |
+ } |
+ return size; |
+} |
+ |
+void UserView::Layout() { |
+ gfx::Rect contents_area(GetContentsBounds()); |
+ if (user_card_ && logout_button_) { |
+ // Give the logout button the space it requests. |
+ gfx::Rect logout_area = contents_area; |
+ logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize()); |
+ logout_area.set_x(contents_area.right() - logout_area.width()); |
+ logout_button_->SetBoundsRect(logout_area); |
+ |
+ // Give the remaining space to the user card. |
+ gfx::Rect user_card_area = contents_area; |
+ user_card_area.set_width(contents_area.width() - |
+ (logout_area.width() + kTrayPopupPaddingBetweenItems)); |
+ user_card_->SetBoundsRect(user_card_area); |
+ } else if (user_card_) { |
+ user_card_->SetBoundsRect(contents_area); |
+ } else if (logout_button_) { |
+ logout_button_->SetBoundsRect(contents_area); |
+ } |
+} |
+ |
+void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) { |
+ DCHECK_EQ(logout_button_, sender); |
+ ash::Shell::GetInstance()->tray_delegate()->SignOut(); |
+} |
+ |
+void UserView::AddLogoutButton(ash::user::LoginStatus login) { |
+ // A user should not be able to modify logged-in state when screen is |
+ // locked. |
+ if (login == ash::user::LOGGED_IN_LOCKED) |
+ return; |
+ |
+ const string16 title = ash::user::GetLocalizedSignOutStringForStatus(login, |
+ true); |
+ TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title); |
+ logout_button->SetAccessibleName(title); |
+ logout_button_ = logout_button; |
+ // In public account mode, the logout button border has a custom color. |
+ if (login == ash::user::LOGGED_IN_PUBLIC) { |
+ TrayPopupLabelButtonBorder* border = |
+ static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border()); |
+ border->SetImages(views::CustomButton::STATE_NORMAL, views::BorderImages( |
+ kPublicAccountLogoutButtonBorderImagesNormal)); |
+ border->SetImages(views::CustomButton::STATE_HOVERED, views::BorderImages( |
+ kPublicAccountLogoutButtonBorderImagesHovered)); |
+ border->SetImages(views::CustomButton::STATE_PRESSED, views::BorderImages( |
+ kPublicAccountLogoutButtonBorderImagesHovered)); |
+ } |
+ AddChildView(logout_button_); |
+} |
+ |
+void UserView::AddUserCard(SystemTrayItem* owner, |
+ ash::user::LoginStatus login) { |
+ if (login == ash::user::LOGGED_IN_GUEST) |
+ return; |
+ |
+ set_border(views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal, |
+ 0, kTrayPopupPaddingHorizontal)); |
+ |
+ user_card_ = new views::View(); |
+ user_card_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kHorizontal, 0, kUserCardVerticalPadding, |
+ kTrayPopupPaddingBetweenItems)); |
+ AddChildViewAt(user_card_, 0); |
+ |
+ if (login == ash::user::LOGGED_IN_KIOSK) { |
+ views::Label* details = new views::Label; |
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
+ details->SetText( |
+ bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL)); |
+ details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1)); |
+ details->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
+ user_card_->AddChildView(details); |
+ return; |
+ } |
+ |
+ RoundedImageView* avatar = new RoundedImageView(kProfileRoundedCornerRadius); |
+ avatar->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), |
+ gfx::Size(kUserIconSize, kUserIconSize)); |
+ user_card_->AddChildView(avatar); |
+ |
+ if (login == ash::user::LOGGED_IN_PUBLIC) { |
+ user_card_->AddChildView(new PublicAccountUserDetails( |
+ owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems)); |
+ return; |
+ } |
+ |
+ ash::SystemTrayDelegate* delegate = |
+ ash::Shell::GetInstance()->tray_delegate(); |
+ views::View* details = new views::View; |
+ details->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0)); |
+ views::Label* username = new views::Label(delegate->GetUserDisplayName()); |
+ username->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
+ details->AddChildView(username); |
+ |
+ views::Label* email = new views::Label(UTF8ToUTF16(delegate->GetUserEmail())); |
+ email->SetFont(username->font().DeriveFont(-1)); |
+ email->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
+ email->SetEnabled(false); |
+ details->AddChildView(email); |
+ user_card_->AddChildView(details); |
+} |
} // namespace tray |
@@ -272,7 +557,7 @@ views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { |
return NULL; |
CHECK(user_ == NULL); |
- user_ = new tray::UserView(status); |
+ user_ = new tray::UserView(this, status); |
return user_; |
} |