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

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

Issue 11377133: Customize user details in ash system bubble for public account mode (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Per UI feedback, set custom border color on public account logout button. Created 8 years, 1 month 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ash/system/user/tray_user.h" 5 #include "ash/system/user/tray_user.h"
6 6
7 #include <algorithm>
8 #include <climits>
9 #include <vector>
10
7 #include "ash/shell.h" 11 #include "ash/shell.h"
12 #include "ash/system/tray/system_tray.h"
8 #include "ash/system/tray/system_tray_delegate.h" 13 #include "ash/system/tray/system_tray_delegate.h"
9 #include "ash/system/tray/tray_constants.h" 14 #include "ash/system/tray/tray_constants.h"
10 #include "ash/system/tray/tray_item_view.h" 15 #include "ash/system/tray/tray_item_view.h"
11 #include "ash/system/tray/tray_views.h" 16 #include "ash/system/tray/tray_views.h"
17 #include "base/i18n/rtl.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_vector.h"
20 #include "base/string16.h"
21 #include "base/string_util.h"
12 #include "base/utf_string_conversions.h" 22 #include "base/utf_string_conversions.h"
23 #include "grit/ash_resources.h"
13 #include "grit/ash_strings.h" 24 #include "grit/ash_strings.h"
14 #include "skia/ext/image_operations.h" 25 #include "skia/ext/image_operations.h"
15 #include "third_party/skia/include/core/SkCanvas.h" 26 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "third_party/skia/include/core/SkPaint.h" 27 #include "third_party/skia/include/core/SkPaint.h"
17 #include "third_party/skia/include/core/SkPath.h" 28 #include "third_party/skia/include/core/SkPath.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/range/range.h"
18 #include "ui/base/resource/resource_bundle.h" 31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/base/text/text_elider.h"
19 #include "ui/gfx/canvas.h" 33 #include "ui/gfx/canvas.h"
34 #include "ui/gfx/font.h"
20 #include "ui/gfx/image/image.h" 35 #include "ui/gfx/image/image.h"
21 #include "ui/gfx/image/image_skia_operations.h" 36 #include "ui/gfx/image/image_skia_operations.h"
37 #include "ui/gfx/insets.h"
38 #include "ui/gfx/rect.h"
39 #include "ui/gfx/render_text.h"
22 #include "ui/gfx/size.h" 40 #include "ui/gfx/size.h"
23 #include "ui/gfx/skia_util.h" 41 #include "ui/gfx/skia_util.h"
42 #include "ui/views/border.h"
43 #include "ui/views/bubble/tray_bubble_view.h"
44 #include "ui/views/controls/button/border_images.h"
24 #include "ui/views/controls/button/button.h" 45 #include "ui/views/controls/button/button.h"
25 #include "ui/views/controls/button/text_button.h" 46 #include "ui/views/controls/button/custom_button.h"
26 #include "ui/views/controls/image_view.h" 47 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h" 48 #include "ui/views/controls/label.h"
49 #include "ui/views/controls/link.h"
50 #include "ui/views/controls/link_listener.h"
28 #include "ui/views/layout/box_layout.h" 51 #include "ui/views/layout/box_layout.h"
29 #include "ui/views/view.h" 52 #include "ui/views/view.h"
30 #include "ui/views/widget/widget.h" 53 #include "ui/views/widget/widget.h"
31 54
32 namespace { 55 namespace {
33 56
34 const int kUserInfoVerticalPadding = 10; 57 const int kUserDetailsVerticalPadding = 5;
58 const int kUserCardVerticalPadding = 10;
59 const int kProfileRoundedCornerRadius = 2;
35 const int kUserIconSize = 27; 60 const int kUserIconSize = 27;
36 const int kProfileRoundedCornerRadius = 2; 61
62 // The invisible word joiner character, used as a marker to indicate the start
63 // and end of the user's display name in the public account user card's text.
64 const char16 kDisplayNameMark[] = { 0x2060, 0 };
65
66 const int kPublicAccountLogoutButtonBorderImagesNormal[] = {
67 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
68 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
69 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
70 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
71 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
72 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
73 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
74 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
75 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
76 };
77 const int kPublicAccountLogoutButtonBorderImagesHovered[] = {
msw 2012/11/29 01:14:10 nit: add a blank line above.
bartfab (slow) 2012/11/29 16:28:59 Done.
78 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
79 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
80 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
81 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
82 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND,
83 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
84 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
85 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
86 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
87 };
37 88
38 } // namespace 89 } // namespace
39 90
40 namespace ash { 91 namespace ash {
41 namespace internal { 92 namespace internal {
42 93
43 namespace tray { 94 namespace tray {
44 95
45 // A custom image view with rounded edges. 96 // A custom image view with rounded edges.
46 class RoundedImageView : public views::View { 97 class RoundedImageView : public views::View {
47 public: 98 public:
48 // Constructs a new rounded image view with rounded corners of radius 99 // Constructs a new rounded image view with rounded corners of radius
49 // |corner_radius|. 100 // |corner_radius|.
50 explicit RoundedImageView(int corner_radius) : corner_radius_(corner_radius) { 101 explicit RoundedImageView(int corner_radius);
51 } 102 virtual ~RoundedImageView();
52
53 virtual ~RoundedImageView() {
54 }
55 103
56 // Set the image that should be displayed from a pointer. The pointer 104 // Set the image that should be displayed from a pointer. The pointer
msw 2012/11/29 01:14:10 nit: update comment to remove inaccurate mention o
bartfab (slow) 2012/11/29 16:28:59 Done.
57 // contents is copied in the receiver's image. 105 // contents is copied in the receiver's image.
58 void SetImage(const gfx::ImageSkia& img, const gfx::Size& size) { 106 void SetImage(const gfx::ImageSkia& img, const gfx::Size& size);
59 image_ = img;
60 image_size_ = size;
61
62 // Try to get the best image quality for the avatar.
63 resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_,
64 skia::ImageOperations::RESIZE_BEST, size);
65 if (GetWidget() && visible()) {
66 PreferredSizeChanged();
67 SchedulePaint();
68 }
69 }
70
71 // Overridden from views::View.
72 virtual gfx::Size GetPreferredSize() OVERRIDE {
73 return gfx::Size(image_size_.width() + GetInsets().width(),
74 image_size_.height() + GetInsets().height());
75 }
76
77 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
78 View::OnPaint(canvas);
79 gfx::Rect image_bounds(size());
80 image_bounds.ClampToCenteredSize(GetPreferredSize());
81 image_bounds.Inset(GetInsets());
82 const SkScalar kRadius = SkIntToScalar(corner_radius_);
83 SkPath path;
84 path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius);
85 SkPaint paint;
86 paint.setAntiAlias(true);
87 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
88 canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(),
89 path, paint);
90 }
91 107
92 private: 108 private:
109 // Overridden from views::View.
110 virtual gfx::Size GetPreferredSize() OVERRIDE;
111 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
112
93 gfx::ImageSkia image_; 113 gfx::ImageSkia image_;
94 gfx::ImageSkia resized_; 114 gfx::ImageSkia resized_;
95 gfx::Size image_size_; 115 gfx::Size image_size_;
96 int corner_radius_; 116 int corner_radius_;
97 117
98 DISALLOW_COPY_AND_ASSIGN(RoundedImageView); 118 DISALLOW_COPY_AND_ASSIGN(RoundedImageView);
99 }; 119 };
100 120
121 // The user details shown in public account mode. This is essentially a label
122 // but with custom painting code as the text is styled with multiple colors and
123 // contains a link.
124 class PublicAccountUserDetails : public views::View,
125 public views::LinkListener {
126 public:
127 PublicAccountUserDetails(SystemTrayItem* owner, int used_width);
128 virtual ~PublicAccountUserDetails();
129
130 private:
131 // Overridden from views::LinkListener.
msw 2012/11/29 01:14:10 nit: Match LinkListener/View OVERRIDE and base cla
bartfab (slow) 2012/11/29 16:28:59 Done.
132 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
133
134 // Overridden from views::View.
135 virtual void Layout() OVERRIDE;
136 virtual gfx::Size GetPreferredSize() OVERRIDE { return preferred_size_; };
msw 2012/11/29 01:14:10 The style guide strongly discourages inline virtua
bartfab (slow) 2012/11/29 16:28:59 Done.
137 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
138
139 // Calculate a preferred size that ensures the label text and the following
140 // link do not wrap over more than three lines in total for aesthetic reasons
141 // if possible.
142 void CalculatePreferredSize(SystemTrayItem* owner, int used_width);
143
144 string16 text_;
145 views::Link* learn_more_;
146 gfx::Font font_;
147 gfx::Size preferred_size_;
148 ScopedVector<gfx::RenderText> lines_;
149
150 DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
151 };
152
101 class UserView : public views::View, 153 class UserView : public views::View,
102 public views::ButtonListener { 154 public views::ButtonListener {
103 public: 155 public:
104 explicit UserView(ash::user::LoginStatus login) 156 explicit UserView(SystemTrayItem* owner, ash::user::LoginStatus login);
105 : login_(login), 157 virtual ~UserView();
106 user_info_(NULL),
107 username_(NULL),
108 email_(NULL),
109 signout_(NULL) {
110 CHECK(login_ != ash::user::LOGGED_IN_NONE);
111
112 bool public_account = login_ == ash::user::LOGGED_IN_PUBLIC;
113 bool guest = login_ == ash::user::LOGGED_IN_GUEST;
114 bool locked = login_ == ash::user::LOGGED_IN_LOCKED;
115
116 set_background(views::Background::CreateSolidBackground(
117 public_account ? kPublicAccountBackgroundColor : kBackgroundColor));
118
119 if (!guest)
120 AddUserInfo();
121
122 // A user should not be able to modify logged in state when screen is
123 // locked.
124 if (!locked)
125 AddButtonContainer();
126 }
127
128 virtual ~UserView() {}
129
130 // Create container for buttons.
131 void AddButtonContainer() {
132 TrayPopupLabelButton* button = new TrayPopupLabelButton(this,
133 ash::user::GetLocalizedSignOutStringForStatus(login_, true));
134 AddChildView(button);
135 signout_ = button;
136 }
137 158
138 private: 159 private:
139 void AddUserInfo() { 160 // Overridden from views::View.
140 user_info_ = new views::View; 161 virtual gfx::Size GetPreferredSize() OVERRIDE;
141 user_info_->SetLayoutManager(new views::BoxLayout( 162 virtual void Layout() OVERRIDE;
142 views::BoxLayout::kHorizontal, kTrayPopupPaddingHorizontal,
143 kUserInfoVerticalPadding, kTrayPopupPaddingBetweenItems));
144 AddChildView(user_info_);
145
146 if (login_ == ash::user::LOGGED_IN_KIOSK) {
147 views::Label* label = new views::Label;
148 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
149 label->SetText(
150 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
151 label->set_border(views::Border::CreateEmptyBorder(
152 0, 4, 0, 1));
153 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
154 user_info_->AddChildView(label);
155 return;
156 }
157
158 RoundedImageView* image = new RoundedImageView(kProfileRoundedCornerRadius);
159 image->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(),
160 gfx::Size(kUserIconSize, kUserIconSize));
161 user_info_->AddChildView(image);
162
163 views::View* user = new views::View;
164 user->SetLayoutManager(new views::BoxLayout(
165 views::BoxLayout::kVertical, 0, 5, 0));
166 ash::SystemTrayDelegate* tray =
167 ash::Shell::GetInstance()->tray_delegate();
168 username_ = new views::Label(tray->GetUserDisplayName());
169 username_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
170 user->AddChildView(username_);
171
172 email_ = new views::Label(UTF8ToUTF16(tray->GetUserEmail()));
173 email_->SetFont(username_->font().DeriveFont(-1));
174 email_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
175 email_->SetEnabled(false);
176 user->AddChildView(email_);
177
178 user_info_->AddChildView(user);
179 }
180 163
181 // Overridden from views::ButtonListener. 164 // Overridden from views::ButtonListener.
182 virtual void ButtonPressed(views::Button* sender, 165 virtual void ButtonPressed(views::Button* sender,
183 const ui::Event& event) OVERRIDE { 166 const ui::Event& event) OVERRIDE;
184 CHECK(sender == signout_); 167
185 ash::SystemTrayDelegate* tray = ash::Shell::GetInstance()->tray_delegate(); 168 void AddLogoutButton(ash::user::LoginStatus login);
186 tray->SignOut(); 169 void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login);
187 } 170
188 171 views::View* user_card_;
189 // Overridden from views::View. 172 views::View* logout_button_;
190 virtual gfx::Size GetPreferredSize() OVERRIDE {
191 gfx::Size size;
192 if (user_info_)
193 size = user_info_->GetPreferredSize();
194 if (signout_) {
195 gfx::Size signout_size = signout_->GetPreferredSize();
196 // Make sure the user default view item at least as tall as the other
197 // tray popup items.
198 if (size.height() == 0)
199 size.set_height(kTrayPopupItemHeight);
200 size.set_height(std::max(size.height(), signout_size.height()));
201 size.set_width(size.width() + signout_size.width() +
202 kTrayPopupPaddingHorizontal * 2 + kTrayPopupPaddingBetweenItems);
203 }
204 return size;
205 }
206
207 virtual void Layout() OVERRIDE {
208 views::View::Layout();
209 if (bounds().IsEmpty())
210 return;
211
212 if (signout_ && user_info_) {
213 gfx::Rect signout_bounds(bounds());
214 signout_bounds.ClampToCenteredSize(signout_->GetPreferredSize());
215 signout_bounds.set_x(width() - signout_bounds.width() -
216 kTrayPopupPaddingHorizontal);
217 signout_->SetBoundsRect(signout_bounds);
218
219 gfx::Rect usercard_bounds(user_info_->GetPreferredSize());
220 usercard_bounds.set_width(signout_bounds.x());
221 user_info_->SetBoundsRect(usercard_bounds);
222 } else if (signout_) {
223 signout_->SetBoundsRect(gfx::Rect(size()));
224 } else if (user_info_) {
225 user_info_->SetBoundsRect(gfx::Rect(size()));
226 }
227 }
228
229 user::LoginStatus login_;
230
231 views::View* user_info_;
232 views::Label* username_;
233 views::Label* email_;
234
235 views::Button* signout_;
236 173
237 DISALLOW_COPY_AND_ASSIGN(UserView); 174 DISALLOW_COPY_AND_ASSIGN(UserView);
238 }; 175 };
239 176
177 RoundedImageView::RoundedImageView(int corner_radius)
178 : corner_radius_(corner_radius) {}
179
180 RoundedImageView::~RoundedImageView() {}
181
182 void RoundedImageView::SetImage(const gfx::ImageSkia& img,
183 const gfx::Size& size) {
184 image_ = img;
185 image_size_ = size;
186
187 // Try to get the best image quality for the avatar.
188 resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_,
189 skia::ImageOperations::RESIZE_BEST, size);
190 if (GetWidget() && visible()) {
191 PreferredSizeChanged();
192 SchedulePaint();
193 }
194 }
195
196 gfx::Size RoundedImageView::GetPreferredSize() {
197 return gfx::Size(image_size_.width() + GetInsets().width(),
198 image_size_.height() + GetInsets().height());
199 }
200
201 void RoundedImageView::OnPaint(gfx::Canvas* canvas) {
202 View::OnPaint(canvas);
203 gfx::Rect image_bounds(size());
204 image_bounds.ClampToCenteredSize(GetPreferredSize());
205 image_bounds.Inset(GetInsets());
206 const SkScalar kRadius = SkIntToScalar(corner_radius_);
207 SkPath path;
208 path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius, kRadius);
209 SkPaint paint;
210 paint.setAntiAlias(true);
211 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
212 canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(),
213 path, paint);
214 }
215
216 PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner,
217 int used_width)
218 : learn_more_(NULL),
219 font_(ResourceBundle::GetSharedInstance().GetFont(
msw 2012/11/29 01:14:10 nit: You should be able to omit this, as the defau
bartfab (slow) 2012/11/29 16:28:59 Done.
220 ResourceBundle::BaseFont)) {
221 const int inner_padding =
222 kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
223 const bool rtl = base::i18n::IsRTL();
224 set_border(views::Border::CreateEmptyBorder(
225 kUserDetailsVerticalPadding, rtl ? 0 : inner_padding,
226 kUserDetailsVerticalPadding, rtl ? inner_padding : 0));
227
228 ash::SystemTrayDelegate* delegate =
229 ash::Shell::GetInstance()->tray_delegate();
230 // Retrieve the user's display name and wrap it with markers.
231 string16 display_name = delegate->GetUserDisplayName();
232 RemoveChars(display_name, kDisplayNameMark, &display_name);
233 display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
234 // Retrieve the domain managing the device and wrap it with markers.
235 string16 domain = UTF8ToUTF16(delegate->GetEnterpriseDomain());
236 RemoveChars(domain, kDisplayNameMark, &domain);
237 base::i18n::WrapStringWithLTRFormatting(&domain);
238 // Retrieve the label text, inserting the display name and domain.
239 text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
240 display_name, domain);
241
242 learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
243 learn_more_->SetUnderline(false);
244 learn_more_->set_listener(this);
245 AddChildView(learn_more_);
246
247 CalculatePreferredSize(owner, used_width);
248 }
249
250 PublicAccountUserDetails::~PublicAccountUserDetails() {}
251
252 void PublicAccountUserDetails::LinkClicked(views::Link* source,
253 int event_flags) {
254 DCHECK_EQ(source, learn_more_);
255 ash::Shell::GetInstance()->tray_delegate()->ShowPublicAccountInfo();
256 }
257
258 void PublicAccountUserDetails::Layout() {
259 lines_.clear();
260 const gfx::Rect contents_area = GetContentsBounds();
261 if (contents_area.IsEmpty())
262 return;
263
264 // Word-wrap the label text.
265 std::vector<string16> lines;
266 ui::ElideRectangleText(text_, font_, contents_area.width(),
267 contents_area.height(), ui::ELIDE_LONG_WORDS, &lines);
268 // Loop through the lines, creating a renderer for each.
269 gfx::Point position = contents_area.origin();
270 ui::Range display_name(ui::Range::InvalidRange());
271 for (std::vector<string16>::const_iterator it = lines.begin();
272 it != lines.end(); ++it) {
273 gfx::RenderText* line = gfx::RenderText::CreateInstance();
274 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
275 line->SetText(*it);
276 const gfx::Size size(contents_area.width(), line->GetStringSize().height());
277 line->SetDisplayRect(gfx::Rect(position, size));
278 position.set_y(position.y() + size.height());
279
280 // Set the default text color for the line.
281 gfx::StyleRange default_style(line->default_style());
282 default_style.foreground = kPublicAccountUserCardTextColor;
283 line->set_default_style(default_style);
284 line->ApplyDefaultStyle();
285
286 // If a range of the line contains the user's display name, apply a custom
287 // text color to it.
288 if (display_name.is_empty())
289 display_name.set_start(it->find(kDisplayNameMark));
290 if (!display_name.is_empty()) {
291 display_name.set_end(
292 it->find(kDisplayNameMark, display_name.start() + 1));
293 gfx::StyleRange display_name_style(line->default_style());
294 display_name_style.foreground = kPublicAccountUserCardNameColor;
295 ui::Range line_range(0, it->size());
296 display_name_style.range = display_name.Intersect(line_range);
297 line->ApplyStyleRange(display_name_style);
298 // Update the range for the next line.
299 if (display_name.end() >= line_range.end())
300 display_name.set_start(0);
301 else
302 display_name = ui::Range::InvalidRange();
303 }
304
305 lines_.push_back(line);
306 }
307
308 // Position link after the label text, separated by a space. If it does not
msw 2012/11/29 01:14:10 nit: "the link"
bartfab (slow) 2012/11/29 16:28:59 Done.
309 // fit onto the last line of the text, wrap the link onto its own line.
310 const gfx::Size last_line_size = lines_.back()->GetStringSize();
311 const int space_width = font_.GetStringWidth(ASCIIToUTF16(" "));
312 const gfx::Size link_size = learn_more_->GetPreferredSize();
313 if (contents_area.width() - last_line_size.width() >=
314 space_width + link_size.width()) {
315 position.set_x(position.x() + last_line_size.width() + space_width);
316 position.set_y(position.y() - last_line_size.height());
317 }
318 position.set_y(position.y() - learn_more_->GetInsets().top());
319 gfx::Rect learn_more_bounds(position, link_size);
320 learn_more_bounds.Intersect(contents_area);
321 if (base::i18n::IsRTL()) {
322 const gfx::Insets insets = GetInsets();
323 learn_more_bounds.Offset(insets.right() - insets.left(), 0);
324 }
325 learn_more_->SetBoundsRect(learn_more_bounds);
326 }
327
328 void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) {
329 for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin();
330 it != lines_.end(); ++it) {
331 (*it)->Draw(canvas);
332 }
333 views::View::OnPaint(canvas);
msw 2012/11/29 01:14:10 nit: call this first, to avoid anything painting o
bartfab (slow) 2012/11/29 16:28:59 This is actually on purpose. In line 318, the link
334 }
335
336 void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner,
337 int used_width) {
338
msw 2012/11/29 01:14:10 nit: remove blank line
bartfab (slow) 2012/11/29 16:28:59 Done.
339 const gfx::Size link_size = learn_more_->GetPreferredSize();
340 const int space_width = font_.GetStringWidth(ASCIIToUTF16(" "));
341 const gfx::Insets insets = GetInsets();
342 views::TrayBubbleView* bubble_view =
343 owner->system_tray()->GetSystemBubble()->bubble_view();
344 int min_width = std::max(
345 link_size.width(),
346 bubble_view->GetPreferredSize().width() - (used_width + insets.width()));
347 int max_width = std::min(
348 font_.GetStringWidth(text_) + space_width + link_size.width(),
349 bubble_view->GetMaximumSize().width() - (used_width + insets.width()));
350 // Do a binary search for the minimum width that ensures no more than three
351 // lines are needed. The lower bound is the minimum of the current bubble
352 // width and the width of the link (as no wrapping is permitted inside the
353 // link). The upper bound is the maximum of the largest allowed bubble width
354 // and the sum of the label text and link widths when put on a single line.
355 std::vector<string16> lines;
356 while (min_width < max_width) {
357 lines.clear();
358 const int width = (min_width + max_width) / 2;
359 const bool too_narrow = ui::ElideRectangleText(
360 text_, font_, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines);
361 int line_count = lines.size();
362 if (!too_narrow && line_count == 3 &&
363 width - font_.GetStringWidth(lines.back()) <=
364 space_width + link_size.width()) {
365 ++line_count;
366 }
367 if (too_narrow || line_count > 3)
368 min_width = width + 1;
369 else
370 max_width = width;
371 }
372
373 // Calculate the corresponding height and set the preferred size.
374 lines.clear();
375 ui::ElideRectangleText(
376 text_, font_, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines);
377 int line_count = lines.size();
378 if (min_width - font_.GetStringWidth(lines.back()) <=
379 space_width + link_size.width()) {
380 ++line_count;
381 }
382 const int line_height = font_.GetHeight();
383 const int link_extra_height = std::max(
384 link_size.height() - learn_more_->GetInsets().top() - line_height, 0);
385 preferred_size_ = gfx::Size(
386 min_width + insets.width(),
387 line_count * line_height + link_extra_height + insets.height());
388
389 bubble_view->SetWidth(preferred_size_.width() + used_width);
390 }
391
392 UserView::UserView(SystemTrayItem* owner, ash::user::LoginStatus login)
393 : user_card_(NULL),
394 logout_button_(NULL) {
395 CHECK(login != ash::user::LOGGED_IN_NONE);
msw 2012/11/29 01:14:10 nit: CHECK_NE
bartfab (slow) 2012/11/29 16:28:59 Done.
396 set_background(views::Background::CreateSolidBackground(
397 login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor :
398 kBackgroundColor));
399 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
400 kTrayPopupPaddingBetweenItems));
401 AddLogoutButton(login);
402 AddUserCard(owner, login);
403 }
404
405 UserView::~UserView() {}
406
407 gfx::Size UserView::GetPreferredSize() {
408 gfx::Size size = views::View::GetPreferredSize();
409 if (!user_card_) {
410 // Make sure the default user default view item is at least as tall as the
msw 2012/11/29 01:14:10 nit: Is "default user default view item" correct?
bartfab (slow) 2012/11/29 16:28:59 Typo. Thanks for catching it.
411 // other items.
412 size.set_height(std::max(size.height(),
413 kTrayPopupItemHeight + GetInsets().height()));
414 }
415 return size;
416 }
417
418 void UserView::Layout() {
419 gfx::Rect contents_area(GetContentsBounds());
420 if (user_card_ && logout_button_) {
421 // Give the logout button the space it requests.
422 gfx::Rect logout_area = contents_area;
423 logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize());
424 logout_area.set_x(contents_area.right() - logout_area.width());
425 logout_button_->SetBoundsRect(logout_area);
426
427 // Give the remaining space to the user card.
428 gfx::Rect user_card_area = contents_area;
429 user_card_area.set_width(contents_area.width() -
430 (logout_area.width() + kTrayPopupPaddingBetweenItems));
431 user_card_->SetBoundsRect(user_card_area);
432 } else if (user_card_) {
433 user_card_->SetBoundsRect(contents_area);
434 } else if (logout_button_) {
435 logout_button_->SetBoundsRect(contents_area);
436 }
437 }
438
439 void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
440 DCHECK(sender == logout_button_);
msw 2012/11/29 01:14:10 nit: DCHECK_EQ
bartfab (slow) 2012/11/29 16:28:59 Done.
441 ash::Shell::GetInstance()->tray_delegate()->SignOut();
442 }
443
444 void UserView::AddLogoutButton(ash::user::LoginStatus login) {
445 // The logout button must be added before the user card.
msw 2012/11/29 01:14:10 Why? Also, this is trivially enforced by the order
bartfab (slow) 2012/11/29 16:28:59 The reason is that for the user card to calculate
446 DCHECK(!user_card_);
447
448 // A user should not be able to modify logged-in state when screen is
449 // locked.
450 if (login == ash::user::LOGGED_IN_LOCKED)
451 return;
452
453 TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(
msw 2012/11/29 01:14:10 nit: just assign to |logout_button_| directly.
bartfab (slow) 2012/11/29 16:28:59 Done.
454 this, ash::user::GetLocalizedSignOutStringForStatus(login, true));
455 // In public account mode, the logout button border has a custom color.
456 if (login == ash::user::LOGGED_IN_PUBLIC) {
457 TrayPopupLabelButtonBorder* border =
458 static_cast<TrayPopupLabelButtonBorder*>(logout_button->border());
459 border->SetImages(views::CustomButton::STATE_NORMAL, views::BorderImages(
460 kPublicAccountLogoutButtonBorderImagesNormal));
461 border->SetImages(views::CustomButton::STATE_HOVERED, views::BorderImages(
462 kPublicAccountLogoutButtonBorderImagesHovered));
463 border->SetImages(views::CustomButton::STATE_PRESSED, views::BorderImages(
464 kPublicAccountLogoutButtonBorderImagesHovered));
465 }
466 logout_button_ = logout_button;
467 AddChildView(logout_button);
468 }
469
470 void UserView::AddUserCard(SystemTrayItem* owner,
471 ash::user::LoginStatus login) {
472 if (login == ash::user::LOGGED_IN_GUEST)
473 return;
474
475 set_border(views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal,
476 0, kTrayPopupPaddingHorizontal));
477
478 user_card_ = new views::View();
479 user_card_->SetLayoutManager(new views::BoxLayout(
480 views::BoxLayout::kHorizontal, 0, kUserCardVerticalPadding,
481 kTrayPopupPaddingBetweenItems));
482 AddChildViewAt(user_card_, 0);
483
484 if (login == ash::user::LOGGED_IN_KIOSK) {
485 views::Label* details = new views::Label;
486 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
487 details->SetText(
488 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
489 details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1));
490 details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
491 user_card_->AddChildView(details);
492 return;
493 }
494
495 RoundedImageView* avatar = new RoundedImageView(kProfileRoundedCornerRadius);
496 avatar->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(),
497 gfx::Size(kUserIconSize, kUserIconSize));
498 user_card_->AddChildView(avatar);
499
500 if (login == ash::user::LOGGED_IN_PUBLIC) {
501 user_card_->AddChildView(new PublicAccountUserDetails(
502 owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems));
503 return;
504 }
505
506 ash::SystemTrayDelegate* delegate =
507 ash::Shell::GetInstance()->tray_delegate();
508 views::View* details = new views::View;
509 details->SetLayoutManager(new views::BoxLayout(
510 views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
511 views::Label* username = new views::Label(delegate->GetUserDisplayName());
512 username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
513 details->AddChildView(username);
514
515 views::Label* email = new views::Label(UTF8ToUTF16(delegate->GetUserEmail()));
516 email->SetFont(username->font().DeriveFont(-1));
517 email->SetHorizontalAlignment(gfx::ALIGN_LEFT);
518 email->SetEnabled(false);
519 details->AddChildView(email);
520 user_card_->AddChildView(details);
521 }
522
240 } // namespace tray 523 } // namespace tray
241 524
242 TrayUser::TrayUser(SystemTray* system_tray) 525 TrayUser::TrayUser(SystemTray* system_tray)
243 : SystemTrayItem(system_tray), 526 : SystemTrayItem(system_tray),
244 user_(NULL), 527 user_(NULL),
245 avatar_(NULL), 528 avatar_(NULL),
246 label_(NULL) { 529 label_(NULL) {
247 } 530 }
248 531
249 TrayUser::~TrayUser() { 532 TrayUser::~TrayUser() {
(...skipping 13 matching lines...) Expand all
263 UpdateAfterLoginStatusChange(status); 546 UpdateAfterLoginStatusChange(status);
264 return avatar_ ? static_cast<views::View*>(avatar_) 547 return avatar_ ? static_cast<views::View*>(avatar_)
265 : static_cast<views::View*>(label_); 548 : static_cast<views::View*>(label_);
266 } 549 }
267 550
268 views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { 551 views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
269 if (status == user::LOGGED_IN_NONE) 552 if (status == user::LOGGED_IN_NONE)
270 return NULL; 553 return NULL;
271 554
272 CHECK(user_ == NULL); 555 CHECK(user_ == NULL);
273 user_ = new tray::UserView(status); 556 user_ = new tray::UserView(this, status);
274 return user_; 557 return user_;
275 } 558 }
276 559
277 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) { 560 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
278 return NULL; 561 return NULL;
279 } 562 }
280 563
281 void TrayUser::DestroyTrayView() { 564 void TrayUser::DestroyTrayView() {
282 avatar_ = NULL; 565 avatar_ = NULL;
283 label_ = NULL; 566 label_ = NULL;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 // Check for null to avoid crbug.com/150944. 624 // Check for null to avoid crbug.com/150944.
342 if (avatar_) { 625 if (avatar_) {
343 avatar_->SetImage( 626 avatar_->SetImage(
344 ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), 627 ash::Shell::GetInstance()->tray_delegate()->GetUserImage(),
345 gfx::Size(kUserIconSize, kUserIconSize)); 628 gfx::Size(kUserIconSize, kUserIconSize));
346 } 629 }
347 } 630 }
348 631
349 } // namespace internal 632 } // namespace internal
350 } // namespace ash 633 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698