OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |