summaryrefslogtreecommitdiffstats
path: root/ash/system/user/user_card_view.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ash/system/user/user_card_view.cc')
-rw-r--r--ash/system/user/user_card_view.cc382
1 files changed, 382 insertions, 0 deletions
diff --git a/ash/system/user/user_card_view.cc b/ash/system/user/user_card_view.cc
new file mode 100644
index 0000000..6d5413e
--- /dev/null
+++ b/ash/system/user/user_card_view.cc
@@ -0,0 +1,382 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/user/user_card_view.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "ash/session_state_delegate.h"
+#include "ash/shell.h"
+#include "ash/system/tray/system_tray_delegate.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/user/config.h"
+#include "ash/system/user/rounded_image_view.h"
+#include "base/i18n/rtl.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/range/range.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/render_text.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/text_elider.h"
+#include "ui/gfx/text_utils.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/controls/link_listener.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+namespace tray {
+
+namespace {
+
+const int kUserDetailsVerticalPadding = 5;
+
+// 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 base::char16 kDisplayNameMark[] = {0x2060, 0};
+
+// 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(int max_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(int max_allowed_width);
+
+ base::string16 text_;
+ views::Link* learn_more_;
+ gfx::Size preferred_size_;
+ ScopedVector<gfx::RenderText> lines_;
+
+ DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
+};
+
+PublicAccountUserDetails::PublicAccountUserDetails(int max_width)
+ : learn_more_(NULL) {
+ const int inner_padding =
+ kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
+ const bool rtl = base::i18n::IsRTL();
+ SetBorder(views::Border::CreateEmptyBorder(kUserDetailsVerticalPadding,
+ rtl ? 0 : inner_padding,
+ kUserDetailsVerticalPadding,
+ rtl ? inner_padding : 0));
+
+ // Retrieve the user's display name and wrap it with markers.
+ // Note that since this is a public account it always has to be the primary
+ // user.
+ base::string16 display_name =
+ Shell::GetInstance()->session_state_delegate()->GetUserDisplayName(0);
+ base::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.
+ base::string16 domain = base::UTF8ToUTF16(
+ Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
+ base::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(max_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::FontList font_list;
+ std::vector<base::string16> lines;
+ gfx::ElideRectangleText(text_,
+ font_list,
+ contents_area.width(),
+ contents_area.height(),
+ gfx::ELIDE_LONG_WORDS,
+ &lines);
+ // Loop through the lines, creating a renderer for each.
+ gfx::Point position = contents_area.origin();
+ gfx::Range display_name(gfx::Range::InvalidRange());
+ for (std::vector<base::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.
+ line->SetColor(kPublicAccountUserCardTextColor);
+
+ // 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::Range line_range(0, it->size());
+ line->ApplyColor(kPublicAccountUserCardNameColor,
+ display_name.Intersect(line_range));
+ // Update the range for the next line.
+ if (display_name.end() >= line_range.end())
+ display_name.set_start(0);
+ else
+ display_name = gfx::Range::InvalidRange();
+ }
+
+ lines_.push_back(line);
+ }
+
+ // 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 =
+ gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list);
+ 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);
+}
+
+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_);
+ Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo();
+}
+
+void PublicAccountUserDetails::CalculatePreferredSize(int max_allowed_width) {
+ const gfx::FontList font_list;
+ const gfx::Size link_size = learn_more_->GetPreferredSize();
+ const int space_width =
+ gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list);
+ const gfx::Insets insets = GetInsets();
+ int min_width = link_size.width();
+ int max_width = std::min(
+ gfx::GetStringWidth(text_, font_list) + space_width + link_size.width(),
+ max_allowed_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<base::string16> lines;
+ while (min_width < max_width) {
+ lines.clear();
+ const int width = (min_width + max_width) / 2;
+ const bool too_narrow = gfx::ElideRectangleText(text_,
+ font_list,
+ width,
+ INT_MAX,
+ gfx::TRUNCATE_LONG_WORDS,
+ &lines) != 0;
+ int line_count = lines.size();
+ if (!too_narrow && line_count == 3 &&
+ width - gfx::GetStringWidth(lines.back(), font_list) <=
+ space_width + link_size.width())
+ ++line_count;
+ if (too_narrow || line_count > 3)
+ min_width = width + 1;
+ else
+ max_width = width;
+ }
+
+ // Calculate the corresponding height and set the preferred size.
+ lines.clear();
+ gfx::ElideRectangleText(
+ text_, font_list, min_width, INT_MAX, gfx::TRUNCATE_LONG_WORDS, &lines);
+ int line_count = lines.size();
+ if (min_width - gfx::GetStringWidth(lines.back(), font_list) <=
+ space_width + link_size.width()) {
+ ++line_count;
+ }
+ const int line_height = font_list.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());
+}
+
+} // namespace
+
+UserCardView::UserCardView(user::LoginStatus login_status,
+ int max_width,
+ int multiprofile_index) {
+ SetLayoutManager(new views::BoxLayout(
+ views::BoxLayout::kHorizontal, 0, 0, kTrayPopupPaddingBetweenItems));
+ switch (login_status) {
+ case user::LOGGED_IN_RETAIL_MODE:
+ AddRetailModeUserContent();
+ break;
+ case user::LOGGED_IN_PUBLIC:
+ AddPublicModeUserContent(max_width);
+ break;
+ default:
+ AddUserContent(login_status, multiprofile_index);
+ break;
+ }
+}
+
+UserCardView::~UserCardView() {}
+
+void UserCardView::AddRetailModeUserContent() {
+ views::Label* details = new views::Label;
+ details->SetText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
+ details->SetBorder(views::Border::CreateEmptyBorder(0, 4, 0, 1));
+ details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ AddChildView(details);
+}
+
+void UserCardView::AddPublicModeUserContent(int max_width) {
+ views::View* icon = CreateIcon(user::LOGGED_IN_PUBLIC, 0);
+ AddChildView(icon);
+ int details_max_width = max_width - icon->GetPreferredSize().width() -
+ kTrayPopupPaddingBetweenItems;
+ AddChildView(new PublicAccountUserDetails(details_max_width));
+}
+
+void UserCardView::AddUserContent(user::LoginStatus login_status,
+ int multiprofile_index) {
+ views::View* icon = CreateIcon(login_status, multiprofile_index);
+ AddChildView(icon);
+ views::Label* username = NULL;
+ SessionStateDelegate* delegate =
+ Shell::GetInstance()->session_state_delegate();
+ if (!multiprofile_index) {
+ base::string16 user_name_string =
+ login_status == user::LOGGED_IN_GUEST
+ ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_GUEST_LABEL)
+ : delegate->GetUserDisplayName(multiprofile_index);
+ if (user_name_string.empty() && IsMultiAccountSupportedAndUserActive())
+ user_name_string =
+ base::ASCIIToUTF16(delegate->GetUserEmail(multiprofile_index));
+ if (!user_name_string.empty()) {
+ username = new views::Label(user_name_string);
+ username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ }
+ }
+
+ views::Label* additional = NULL;
+ if (login_status != user::LOGGED_IN_GUEST &&
+ (multiprofile_index || !IsMultiAccountSupportedAndUserActive())) {
+ base::string16 user_email_string =
+ login_status == user::LOGGED_IN_LOCALLY_MANAGED
+ ? l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL)
+ : base::UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index));
+ if (!user_email_string.empty()) {
+ additional = new views::Label(user_email_string);
+ additional->SetFontList(
+ ui::ResourceBundle::GetSharedInstance().GetFontList(
+ ui::ResourceBundle::SmallFont));
+ additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ }
+ }
+
+ // Adjust text properties dependent on if it is an active or inactive user.
+ if (multiprofile_index) {
+ // Fade the text of non active users to 50%.
+ SkColor text_color = additional->enabled_color();
+ text_color = SkColorSetA(text_color, SkColorGetA(text_color) / 2);
+ if (additional)
+ additional->SetDisabledColor(text_color);
+ if (username)
+ username->SetDisabledColor(text_color);
+ }
+
+ if (additional && username) {
+ views::View* details = new views::View;
+ details->SetLayoutManager(new views::BoxLayout(
+ views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
+ details->AddChildView(username);
+ details->AddChildView(additional);
+ AddChildView(details);
+ } else {
+ if (username)
+ AddChildView(username);
+ if (additional)
+ AddChildView(additional);
+ }
+}
+
+views::View* UserCardView::CreateIcon(user::LoginStatus login_status,
+ int multiprofile_index) {
+ RoundedImageView* icon =
+ new RoundedImageView(kTrayAvatarCornerRadius, multiprofile_index == 0);
+ if (login_status == user::LOGGED_IN_GUEST) {
+ icon->SetImage(*ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON)
+ .ToImageSkia(),
+ gfx::Size(kTrayAvatarSize, kTrayAvatarSize));
+ } else {
+ SessionStateDelegate* delegate =
+ Shell::GetInstance()->session_state_delegate();
+ content::BrowserContext* context =
+ delegate->GetBrowserContextByIndex(multiprofile_index);
+ icon->SetImage(delegate->GetUserImage(context),
+ gfx::Size(kTrayAvatarSize, kTrayAvatarSize));
+ }
+ return icon;
+}
+
+} // namespace tray
+} // namespace ash