Index: ash/system/user/tray_user.cc |
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc |
index 6b1ab2e349a59cb20e4264a4a9b7465347b77c09..dd297c1e30f5d1f75e187e9399655eb2e421b277 100644 |
--- a/ash/system/user/tray_user.cc |
+++ b/ash/system/user/tray_user.cc |
@@ -13,19 +13,27 @@ |
#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/utf_string_conversions.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/SkColor.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/native_theme/native_theme.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/size.h" |
#include "ui/gfx/skia_util.h" |
#include "ui/views/border.h" |
@@ -33,15 +41,27 @@ |
#include "ui/views/controls/button/text_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/view_text_utils.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; |
+ |
+string16 kDisplayName() { |
+ return ASCIIToUTF16("DISPLAY_NAME"); |
+} |
+ |
+string16 kDomain() { |
+ return ASCIIToUTF16("DOMAIN"); |
+} |
} // namespace |
@@ -106,6 +126,261 @@ class RoundedImageView : public views::View { |
DISALLOW_COPY_AND_ASSIGN(RoundedImageView); |
}; |
+// A chunk of text with associated color. |
+class TextChunk { |
+ public: |
+ TextChunk(const string16& text, const SkColor& color, bool is_url) |
+ : text_(text), color_(color), is_url_(is_url) {} |
+ |
+ const string16& Text() const { return text_; } |
+ const SkColor& Color() const { return color_; } |
+ bool IsUrl() const { return is_url_; } |
+ |
+ private: |
+ string16 text_; |
+ SkColor color_; |
+ bool is_url_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TextChunk); |
+}; |
+ |
+// 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() : learn_more_(NULL) { |
+ set_border(views::Border::CreateEmptyBorder( |
+ kUserDetailsVerticalPadding, 0, kUserDetailsVerticalPadding, 0)); |
+ const string16 text = |
+ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL); |
+ const size_t display_name_start = text.find(kDisplayName()); |
+ DCHECK(display_name_start != string16::npos); |
+ const size_t domain_start = text.find(kDomain()); |
+ DCHECK(domain_start != string16::npos); |
+ const size_t display_name_end = display_name_start + |
+ kDisplayName().size(); |
+ const size_t domain_end = domain_start + kDomain().size(); |
+ bool display_name_first = display_name_start < domain_start; |
+ |
+ SkColor default_color = GetNativeTheme()->GetSystemColor( |
+ ui::NativeTheme::kColorId_LabelEnabledColor); |
+ ash::SystemTrayDelegate* tray = ash::Shell::GetInstance()->tray_delegate(); |
+ TextChunk* display_name = new TextChunk(tray->GetUserDisplayName(), |
+ default_color, false); |
+ TextChunk *domain = new TextChunk(UTF8ToUTF16(tray->GetEnterpriseDomain()), |
+ kPublicAccountUserCardTextColor, true); |
+ const size_t chunk_1_end = std::min(display_name_start, domain_start); |
+ chunks_.push_back(new TextChunk(text.substr(0, chunk_1_end), |
+ kPublicAccountUserCardTextColor, false)); |
+ chunks_.push_back(display_name_first ? display_name : domain); |
+ const size_t chunk_2_start = std::min(display_name_end, domain_end); |
+ const size_t chunk_2_end = std::max(display_name_start, domain_start); |
+ chunks_.push_back(new TextChunk(text.substr(chunk_2_start, |
+ chunk_2_end - chunk_2_start), |
+ kPublicAccountUserCardTextColor, false)); |
+ chunks_.push_back(display_name_first ? domain : display_name); |
+ const size_t chunk_3_start = std::max(display_name_end, domain_end); |
+ chunks_.push_back(new TextChunk(text.substr(chunk_3_start), |
+ kPublicAccountUserCardTextColor, false)); |
stevenjb
2012/11/13 20:54:22
This seems like something that ought to be general
bartfab (slow)
2012/11/16 19:59:15
Done.
|
+ chunks_.push_back(new TextChunk(ASCIIToUTF16(" "), |
+ kPublicAccountUserCardTextColor, false)); |
stevenjb
2012/11/13 20:54:22
This bit seems strange to me. Is this the spacing
bartfab (slow)
2012/11/16 19:59:15
Done.
|
+ |
+ learn_more_ = new views::Link( |
+ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LEARN_MORE)); |
+ learn_more_->set_listener(this); |
+ AddChildView(learn_more_); |
+ } |
+ |
+ // Overridden from views::LinkListener. |
+ virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE { |
+ DCHECK_EQ(source, learn_more_); |
+ ash::Shell::GetInstance()->tray_delegate()->ShowPublicAccountInfo(); |
+ } |
+ |
+ // Overridden from views::View. |
+ void Layout() OVERRIDE { |
+ learn_more_->SetBoundsRect(PaintTextAndCalculateLinkRect( |
+ NULL, GetContentsBounds())); |
+ } |
+ |
+ virtual gfx::Size GetPreferredSize() OVERRIDE { |
+ if (!visible()) |
+ return gfx::Size(); |
+ const gfx::Insets insets = GetInsets(); |
+ const gfx::Rect link_rect = PaintTextAndCalculateLinkRect( |
+ NULL, gfx::Rect(INT_MAX, INT_MAX)); |
+ return gfx::Size(link_rect.right() + insets.width(), |
+ link_rect.bottom() + insets.height()); |
+ } |
+ |
+ virtual int GetHeightForWidth(int w) OVERRIDE { |
+ if (!visible()) |
+ return 0; |
+ const gfx::Insets insets = GetInsets(); |
+ const gfx::Rect link_rect = PaintTextAndCalculateLinkRect( |
+ NULL, gfx::Rect(w - insets.width(), INT_MAX)); |
+ return link_rect.bottom() + GetInsets().height(); |
+ } |
+ |
+ private: |
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
+ views::View::OnPaint(canvas); |
+ PaintTextAndCalculateLinkRect(canvas, GetContentsBounds()); |
+ } |
+ |
+ gfx::Rect PaintTextAndCalculateLinkRect(gfx::Canvas* canvas, |
+ const gfx::Rect& content_area) { |
+ gfx::Size position; |
+ const bool text_direction_is_rtl = base::i18n::IsRTL(); |
+ const gfx::Font& font = label_.font(); |
+ label_.SetBoundsRect(content_area); |
+ for (ScopedVector<TextChunk>::const_iterator it = chunks_.begin(); |
+ it != chunks_.end(); ++it) { |
+ label_.SetEnabledColor((*it)->Color()); |
+ string16 text; |
+ if ((*it)->IsUrl()) { |
+ text = ui::ElideText((*it)->Text(), font, content_area.width(), |
+ ui::ELIDE_IN_MIDDLE); |
+ base::i18n::WrapStringWithLTRFormatting(&text); |
+ } else { |
+ text = (*it)->Text(); |
+ } |
+ view_text_utils::DrawTextAndPositionUrl( |
+ canvas, &label_, text, NULL, NULL, &position, text_direction_is_rtl, |
+ content_area, font); |
+ } |
stevenjb
2012/11/13 20:54:22
So, I don't want to overcomplicate this CL too muc
bartfab (slow)
2012/11/16 19:59:15
I went for the bonus points: I replaced the views_
|
+ gfx::Rect link_rect; |
+ view_text_utils::DrawTextAndPositionUrl( |
+ canvas, &label_, string16(), learn_more_, &link_rect, &position, |
+ text_direction_is_rtl, content_area, font); |
+ // The link is separated from the text by a single space. If the link was |
+ // wrapped onto a new line, remove the preceding space as it is not needed |
+ // in this case. |
+ const int space_width = |
+ gfx::Canvas::GetStringWidth(ASCIIToUTF16(" "), font); |
+ if (link_rect.x() == content_area.x() + space_width) |
+ link_rect.Offset(-space_width, 0); |
+ return link_rect; |
+ } |
+ |
+ ScopedVector<TextChunk> chunks_; |
+ views::Label label_; |
+ views::Link* learn_more_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails); |
+}; |
+ |
+// The user card, consisting of an avatar picture (except in retail mode) and |
+// user details. |
+class UserCard : public views::View { |
+ public: |
+ explicit UserCard(ash::user::LoginStatus login) |
+ : avatar_(NULL), details_(NULL) { |
+ set_border(views::Border::CreateEmptyBorder(kUserCardVerticalPadding, 0, |
+ kUserCardVerticalPadding, 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); |
+ AddChildView(details); |
+ details_ = details; |
+ return; |
+ } |
+ |
+ avatar_ = new RoundedImageView(kProfileRoundedCornerRadius); |
+ avatar_->SetImage( |
+ ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), |
+ gfx::Size(kUserIconSize, kUserIconSize)); |
+ AddChildView(avatar_); |
+ |
+ if (login == ash::user::LOGGED_IN_PUBLIC) { |
+ details_ = new PublicAccountUserDetails(); |
+ AddChildView(details_); |
+ return; |
+ } |
+ |
+ ash::SystemTrayDelegate* tray = 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(tray->GetUserDisplayName()); |
+ username->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
+ details->AddChildView(username); |
+ |
+ views::Label* email = new views::Label(UTF8ToUTF16(tray->GetUserEmail())); |
+ email->SetFont(username->font().DeriveFont(-1)); |
+ email->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
+ email->SetEnabled(false); |
+ details->AddChildView(email); |
+ AddChildView(details); |
+ details_ = details; |
+ } |
+ |
+ // Overridden from views::View. |
+ virtual gfx::Size GetPreferredSize() OVERRIDE { |
+ gfx::Size size; |
+ if (!visible()) |
+ return size; |
+ if (avatar_) |
+ size = avatar_->GetPreferredSize(); |
+ if (details_) { |
+ gfx::Size details_size = details_->GetPreferredSize(); |
+ size.set_height(std::max(size.height(), details_size.height())); |
+ size.Enlarge(details_size.width(), 0); |
+ } |
+ if (avatar_ && details_) |
+ size.Enlarge(kTrayPopupPaddingBetweenItems, 0); |
+ gfx::Insets insets = GetInsets(); |
+ size.Enlarge(insets.width(), insets.height()); |
+ return size; |
+ } |
+ |
+ virtual int GetHeightForWidth(int w) OVERRIDE { |
+ if (!visible()) |
+ return 0; |
+ gfx::Insets insets = GetInsets(); |
+ int width = w - insets.width(); |
+ int height = 0; |
+ if (avatar_) { |
+ gfx::Size avatar_size = avatar_->GetPreferredSize(); |
+ width -= avatar_size.width(); |
+ height = avatar_size.height(); |
+ } |
+ if (avatar_ && details_) |
+ width -= kTrayPopupPaddingBetweenItems; |
+ if (details_) |
+ height = std::max(height, details_->GetHeightForWidth(width)); |
+ return height + insets.height(); |
+ } |
+ |
+ virtual void Layout() OVERRIDE { |
+ gfx::Rect contents_area(GetContentsBounds()); |
+ gfx::Point top_left = contents_area.origin(); |
+ if (avatar_) { |
+ const int avatar_width = avatar_->GetPreferredSize().width(); |
+ gfx::Rect avatar_area = contents_area; |
+ avatar_area.set_width(avatar_width); |
+ avatar_->SetBoundsRect(avatar_area); |
+ contents_area.Inset(avatar_width, 0, 0, 0); |
+ } |
+ if (avatar_ && details_) |
+ contents_area.Inset(kTrayPopupPaddingBetweenItems, 0, 0, 0); |
+ if (details_) |
+ details_->SetBoundsRect(contents_area); |
+ } |
+ |
+ private: |
+ RoundedImageView* avatar_; |
+ views::View* details_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(UserCard); |
+}; |
+ |
// A custom tray popup text button that can be queried for its preferred width |
// with a given upper limit, allowing the button's width to be reduced when |
// space is tight while guaranteeing that its content is wrapped and displayed |
@@ -181,7 +456,7 @@ class UserView : public views::View, |
set_background(views::Background::CreateSolidBackground( |
login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor : |
kBackgroundColor)); |
- AddUserInfo(login); |
+ AddUserCard(login); |
AddLogoutButton(login); |
} |
@@ -257,52 +532,16 @@ class UserView : public views::View, |
} |
private: |
- void AddUserInfo(ash::user::LoginStatus login) { |
+ void AddUserCard(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, |
- kUserInfoVerticalPadding, kTrayPopupPaddingBetweenItems)); |
+ user_card_ = new UserCard(login); |
AddChildView(user_card_); |
- |
- 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_card_->AddChildView(label); |
- return; |
- } |
- |
- RoundedImageView* image = new RoundedImageView(kProfileRoundedCornerRadius); |
- image->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), |
- gfx::Size(kUserIconSize, kUserIconSize)); |
- user_card_->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(); |
- views::Label* username = new views::Label(tray->GetUserDisplayName()); |
- username->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
- user->AddChildView(username); |
- |
- views::Label* 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_card_->AddChildView(user); |
+ return; |
} |
void AddLogoutButton(ash::user::LoginStatus login) { |