Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: ash/system/user/user_card_view.cc

Issue 210903003: Implemented system tray UI for new account management. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge conflicts resolved. Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ash/system/user/user_card_view.h ('k') | ash/system/user/user_view.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 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/user_card_view.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "ash/session_state_delegate.h"
11 #include "ash/shell.h"
12 #include "ash/system/tray/system_tray_delegate.h"
13 #include "ash/system/tray/tray_constants.h"
14 #include "ash/system/user/config.h"
15 #include "ash/system/user/rounded_image_view.h"
16 #include "base/i18n/rtl.h"
17 #include "base/memory/scoped_vector.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "grit/ash_resources.h"
22 #include "grit/ash_strings.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/insets.h"
26 #include "ui/gfx/range/range.h"
27 #include "ui/gfx/rect.h"
28 #include "ui/gfx/render_text.h"
29 #include "ui/gfx/size.h"
30 #include "ui/gfx/text_elider.h"
31 #include "ui/gfx/text_utils.h"
32 #include "ui/views/border.h"
33 #include "ui/views/controls/link.h"
34 #include "ui/views/controls/link_listener.h"
35 #include "ui/views/layout/box_layout.h"
36
37 namespace ash {
38 namespace tray {
39
40 namespace {
41
42 const int kUserDetailsVerticalPadding = 5;
43
44 // The invisible word joiner character, used as a marker to indicate the start
45 // and end of the user's display name in the public account user card's text.
46 const base::char16 kDisplayNameMark[] = {0x2060, 0};
47
48 // The user details shown in public account mode. This is essentially a label
49 // but with custom painting code as the text is styled with multiple colors and
50 // contains a link.
51 class PublicAccountUserDetails : public views::View,
52 public views::LinkListener {
53 public:
54 PublicAccountUserDetails(int max_width);
55 virtual ~PublicAccountUserDetails();
56
57 private:
58 // Overridden from views::View.
59 virtual void Layout() OVERRIDE;
60 virtual gfx::Size GetPreferredSize() OVERRIDE;
61 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
62
63 // Overridden from views::LinkListener.
64 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
65
66 // Calculate a preferred size that ensures the label text and the following
67 // link do not wrap over more than three lines in total for aesthetic reasons
68 // if possible.
69 void CalculatePreferredSize(int max_allowed_width);
70
71 base::string16 text_;
72 views::Link* learn_more_;
73 gfx::Size preferred_size_;
74 ScopedVector<gfx::RenderText> lines_;
75
76 DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
77 };
78
79 PublicAccountUserDetails::PublicAccountUserDetails(int max_width)
80 : learn_more_(NULL) {
81 const int inner_padding =
82 kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
83 const bool rtl = base::i18n::IsRTL();
84 SetBorder(views::Border::CreateEmptyBorder(kUserDetailsVerticalPadding,
85 rtl ? 0 : inner_padding,
86 kUserDetailsVerticalPadding,
87 rtl ? inner_padding : 0));
88
89 // Retrieve the user's display name and wrap it with markers.
90 // Note that since this is a public account it always has to be the primary
91 // user.
92 base::string16 display_name =
93 Shell::GetInstance()->session_state_delegate()->GetUserDisplayName(0);
94 base::RemoveChars(display_name, kDisplayNameMark, &display_name);
95 display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
96 // Retrieve the domain managing the device and wrap it with markers.
97 base::string16 domain = base::UTF8ToUTF16(
98 Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
99 base::RemoveChars(domain, kDisplayNameMark, &domain);
100 base::i18n::WrapStringWithLTRFormatting(&domain);
101 // Retrieve the label text, inserting the display name and domain.
102 text_ = l10n_util::GetStringFUTF16(
103 IDS_ASH_STATUS_TRAY_PUBLIC_LABEL, display_name, domain);
104
105 learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
106 learn_more_->SetUnderline(false);
107 learn_more_->set_listener(this);
108 AddChildView(learn_more_);
109
110 CalculatePreferredSize(max_width);
111 }
112
113 PublicAccountUserDetails::~PublicAccountUserDetails() {}
114
115 void PublicAccountUserDetails::Layout() {
116 lines_.clear();
117 const gfx::Rect contents_area = GetContentsBounds();
118 if (contents_area.IsEmpty())
119 return;
120
121 // Word-wrap the label text.
122 const gfx::FontList font_list;
123 std::vector<base::string16> lines;
124 gfx::ElideRectangleText(text_,
125 font_list,
126 contents_area.width(),
127 contents_area.height(),
128 gfx::ELIDE_LONG_WORDS,
129 &lines);
130 // Loop through the lines, creating a renderer for each.
131 gfx::Point position = contents_area.origin();
132 gfx::Range display_name(gfx::Range::InvalidRange());
133 for (std::vector<base::string16>::const_iterator it = lines.begin();
134 it != lines.end();
135 ++it) {
136 gfx::RenderText* line = gfx::RenderText::CreateInstance();
137 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
138 line->SetText(*it);
139 const gfx::Size size(contents_area.width(), line->GetStringSize().height());
140 line->SetDisplayRect(gfx::Rect(position, size));
141 position.set_y(position.y() + size.height());
142
143 // Set the default text color for the line.
144 line->SetColor(kPublicAccountUserCardTextColor);
145
146 // If a range of the line contains the user's display name, apply a custom
147 // text color to it.
148 if (display_name.is_empty())
149 display_name.set_start(it->find(kDisplayNameMark));
150 if (!display_name.is_empty()) {
151 display_name.set_end(
152 it->find(kDisplayNameMark, display_name.start() + 1));
153 gfx::Range line_range(0, it->size());
154 line->ApplyColor(kPublicAccountUserCardNameColor,
155 display_name.Intersect(line_range));
156 // Update the range for the next line.
157 if (display_name.end() >= line_range.end())
158 display_name.set_start(0);
159 else
160 display_name = gfx::Range::InvalidRange();
161 }
162
163 lines_.push_back(line);
164 }
165
166 // Position the link after the label text, separated by a space. If it does
167 // not fit onto the last line of the text, wrap the link onto its own line.
168 const gfx::Size last_line_size = lines_.back()->GetStringSize();
169 const int space_width =
170 gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list);
171 const gfx::Size link_size = learn_more_->GetPreferredSize();
172 if (contents_area.width() - last_line_size.width() >=
173 space_width + link_size.width()) {
174 position.set_x(position.x() + last_line_size.width() + space_width);
175 position.set_y(position.y() - last_line_size.height());
176 }
177 position.set_y(position.y() - learn_more_->GetInsets().top());
178 gfx::Rect learn_more_bounds(position, link_size);
179 learn_more_bounds.Intersect(contents_area);
180 if (base::i18n::IsRTL()) {
181 const gfx::Insets insets = GetInsets();
182 learn_more_bounds.Offset(insets.right() - insets.left(), 0);
183 }
184 learn_more_->SetBoundsRect(learn_more_bounds);
185 }
186
187 gfx::Size PublicAccountUserDetails::GetPreferredSize() {
188 return preferred_size_;
189 }
190
191 void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) {
192 for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin();
193 it != lines_.end();
194 ++it) {
195 (*it)->Draw(canvas);
196 }
197 views::View::OnPaint(canvas);
198 }
199
200 void PublicAccountUserDetails::LinkClicked(views::Link* source,
201 int event_flags) {
202 DCHECK_EQ(source, learn_more_);
203 Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo();
204 }
205
206 void PublicAccountUserDetails::CalculatePreferredSize(int max_allowed_width) {
207 const gfx::FontList font_list;
208 const gfx::Size link_size = learn_more_->GetPreferredSize();
209 const int space_width =
210 gfx::GetStringWidth(base::ASCIIToUTF16(" "), font_list);
211 const gfx::Insets insets = GetInsets();
212 int min_width = link_size.width();
213 int max_width = std::min(
214 gfx::GetStringWidth(text_, font_list) + space_width + link_size.width(),
215 max_allowed_width - insets.width());
216 // Do a binary search for the minimum width that ensures no more than three
217 // lines are needed. The lower bound is the minimum of the current bubble
218 // width and the width of the link (as no wrapping is permitted inside the
219 // link). The upper bound is the maximum of the largest allowed bubble width
220 // and the sum of the label text and link widths when put on a single line.
221 std::vector<base::string16> lines;
222 while (min_width < max_width) {
223 lines.clear();
224 const int width = (min_width + max_width) / 2;
225 const bool too_narrow = gfx::ElideRectangleText(text_,
226 font_list,
227 width,
228 INT_MAX,
229 gfx::TRUNCATE_LONG_WORDS,
230 &lines) != 0;
231 int line_count = lines.size();
232 if (!too_narrow && line_count == 3 &&
233 width - gfx::GetStringWidth(lines.back(), font_list) <=
234 space_width + link_size.width())
235 ++line_count;
236 if (too_narrow || line_count > 3)
237 min_width = width + 1;
238 else
239 max_width = width;
240 }
241
242 // Calculate the corresponding height and set the preferred size.
243 lines.clear();
244 gfx::ElideRectangleText(
245 text_, font_list, min_width, INT_MAX, gfx::TRUNCATE_LONG_WORDS, &lines);
246 int line_count = lines.size();
247 if (min_width - gfx::GetStringWidth(lines.back(), font_list) <=
248 space_width + link_size.width()) {
249 ++line_count;
250 }
251 const int line_height = font_list.GetHeight();
252 const int link_extra_height = std::max(
253 link_size.height() - learn_more_->GetInsets().top() - line_height, 0);
254 preferred_size_ =
255 gfx::Size(min_width + insets.width(),
256 line_count * line_height + link_extra_height + insets.height());
257 }
258
259 } // namespace
260
261 UserCardView::UserCardView(user::LoginStatus login_status,
262 int max_width,
263 int multiprofile_index) {
264 SetLayoutManager(new views::BoxLayout(
265 views::BoxLayout::kHorizontal, 0, 0, kTrayPopupPaddingBetweenItems));
266 switch (login_status) {
267 case user::LOGGED_IN_RETAIL_MODE:
268 AddRetailModeUserContent();
269 break;
270 case user::LOGGED_IN_PUBLIC:
271 AddPublicModeUserContent(max_width);
272 break;
273 default:
274 AddUserContent(login_status, multiprofile_index);
275 break;
276 }
277 }
278
279 UserCardView::~UserCardView() {}
280
281 void UserCardView::AddRetailModeUserContent() {
282 views::Label* details = new views::Label;
283 details->SetText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
284 details->SetBorder(views::Border::CreateEmptyBorder(0, 4, 0, 1));
285 details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
286 AddChildView(details);
287 }
288
289 void UserCardView::AddPublicModeUserContent(int max_width) {
290 views::View* icon = CreateIcon(user::LOGGED_IN_PUBLIC, 0);
291 AddChildView(icon);
292 int details_max_width = max_width - icon->GetPreferredSize().width() -
293 kTrayPopupPaddingBetweenItems;
294 AddChildView(new PublicAccountUserDetails(details_max_width));
295 }
296
297 void UserCardView::AddUserContent(user::LoginStatus login_status,
298 int multiprofile_index) {
299 views::View* icon = CreateIcon(login_status, multiprofile_index);
300 AddChildView(icon);
301 views::Label* username = NULL;
302 SessionStateDelegate* delegate =
303 Shell::GetInstance()->session_state_delegate();
304 if (!multiprofile_index) {
305 base::string16 user_name_string =
306 login_status == user::LOGGED_IN_GUEST
307 ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_GUEST_LABEL)
308 : delegate->GetUserDisplayName(multiprofile_index);
309 if (user_name_string.empty() && IsMultiAccountSupportedAndUserActive())
310 user_name_string =
311 base::ASCIIToUTF16(delegate->GetUserEmail(multiprofile_index));
312 if (!user_name_string.empty()) {
313 username = new views::Label(user_name_string);
314 username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
315 }
316 }
317
318 views::Label* additional = NULL;
319 if (login_status != user::LOGGED_IN_GUEST &&
320 (multiprofile_index || !IsMultiAccountSupportedAndUserActive())) {
321 base::string16 user_email_string =
322 login_status == user::LOGGED_IN_LOCALLY_MANAGED
323 ? l10n_util::GetStringUTF16(
324 IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL)
325 : base::UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index));
326 if (!user_email_string.empty()) {
327 additional = new views::Label(user_email_string);
328 additional->SetFontList(
329 ui::ResourceBundle::GetSharedInstance().GetFontList(
330 ui::ResourceBundle::SmallFont));
331 additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
332 }
333 }
334
335 // Adjust text properties dependent on if it is an active or inactive user.
336 if (multiprofile_index) {
337 // Fade the text of non active users to 50%.
338 SkColor text_color = additional->enabled_color();
339 text_color = SkColorSetA(text_color, SkColorGetA(text_color) / 2);
340 if (additional)
341 additional->SetDisabledColor(text_color);
342 if (username)
343 username->SetDisabledColor(text_color);
344 }
345
346 if (additional && username) {
347 views::View* details = new views::View;
348 details->SetLayoutManager(new views::BoxLayout(
349 views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
350 details->AddChildView(username);
351 details->AddChildView(additional);
352 AddChildView(details);
353 } else {
354 if (username)
355 AddChildView(username);
356 if (additional)
357 AddChildView(additional);
358 }
359 }
360
361 views::View* UserCardView::CreateIcon(user::LoginStatus login_status,
362 int multiprofile_index) {
363 RoundedImageView* icon =
364 new RoundedImageView(kTrayAvatarCornerRadius, multiprofile_index == 0);
365 if (login_status == user::LOGGED_IN_GUEST) {
366 icon->SetImage(*ui::ResourceBundle::GetSharedInstance()
367 .GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON)
368 .ToImageSkia(),
369 gfx::Size(kTrayAvatarSize, kTrayAvatarSize));
370 } else {
371 SessionStateDelegate* delegate =
372 Shell::GetInstance()->session_state_delegate();
373 content::BrowserContext* context =
374 delegate->GetBrowserContextByIndex(multiprofile_index);
375 icon->SetImage(delegate->GetUserImage(context),
376 gfx::Size(kTrayAvatarSize, kTrayAvatarSize));
377 }
378 return icon;
379 }
380
381 } // namespace tray
382 } // namespace ash
OLDNEW
« no previous file with comments | « ash/system/user/user_card_view.h ('k') | ash/system/user/user_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698