Chromium Code Reviews| 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) { |