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 |
| 78 const int kPublicAccountLogoutButtonBorderImagesHovered[] = { |
| 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_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
| 83 IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND, |
| 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 IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, |
| 88 }; |
37 | 89 |
38 } // namespace | 90 } // namespace |
39 | 91 |
40 namespace ash { | 92 namespace ash { |
41 namespace internal { | 93 namespace internal { |
42 | 94 |
43 namespace tray { | 95 namespace tray { |
44 | 96 |
45 // A custom image view with rounded edges. | 97 // A custom image view with rounded edges. |
46 class RoundedImageView : public views::View { | 98 class RoundedImageView : public views::View { |
47 public: | 99 public: |
48 // Constructs a new rounded image view with rounded corners of radius | 100 // Constructs a new rounded image view with rounded corners of radius |
49 // |corner_radius|. | 101 // |corner_radius|. |
50 explicit RoundedImageView(int corner_radius) : corner_radius_(corner_radius) { | 102 explicit RoundedImageView(int corner_radius); |
51 } | 103 virtual ~RoundedImageView(); |
52 | 104 |
53 virtual ~RoundedImageView() { | 105 // Set the image that should be displayed. The image contents is copied to the |
54 } | 106 // receiver's image. |
55 | 107 void SetImage(const gfx::ImageSkia& img, const gfx::Size& size); |
56 // Set the image that should be displayed from a pointer. The pointer | |
57 // contents is copied in the receiver's image. | |
58 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 | 108 |
92 private: | 109 private: |
| 110 // Overridden from views::View. |
| 111 virtual gfx::Size GetPreferredSize() OVERRIDE; |
| 112 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| 113 |
93 gfx::ImageSkia image_; | 114 gfx::ImageSkia image_; |
94 gfx::ImageSkia resized_; | 115 gfx::ImageSkia resized_; |
95 gfx::Size image_size_; | 116 gfx::Size image_size_; |
96 int corner_radius_; | 117 int corner_radius_; |
97 | 118 |
98 DISALLOW_COPY_AND_ASSIGN(RoundedImageView); | 119 DISALLOW_COPY_AND_ASSIGN(RoundedImageView); |
99 }; | 120 }; |
100 | 121 |
| 122 // The user details shown in public account mode. This is essentially a label |
| 123 // but with custom painting code as the text is styled with multiple colors and |
| 124 // contains a link. |
| 125 class PublicAccountUserDetails : public views::View, |
| 126 public views::LinkListener { |
| 127 public: |
| 128 PublicAccountUserDetails(SystemTrayItem* owner, int used_width); |
| 129 virtual ~PublicAccountUserDetails(); |
| 130 |
| 131 private: |
| 132 // Overridden from views::View. |
| 133 virtual void Layout() OVERRIDE; |
| 134 virtual gfx::Size GetPreferredSize() OVERRIDE; |
| 135 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| 136 |
| 137 // Overridden from views::LinkListener. |
| 138 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; |
| 139 |
| 140 // Calculate a preferred size that ensures the label text and the following |
| 141 // link do not wrap over more than three lines in total for aesthetic reasons |
| 142 // if possible. |
| 143 void CalculatePreferredSize(SystemTrayItem* owner, int used_width); |
| 144 |
| 145 string16 text_; |
| 146 views::Link* learn_more_; |
| 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 const string16 title = ash::user::GetLocalizedSignOutStringForStatus(login_, | |
133 true); | |
134 TrayPopupLabelButton* button = new TrayPopupLabelButton(this, title); | |
135 button->SetAccessibleName(title); | |
136 AddChildView(button); | |
137 signout_ = button; | |
138 } | |
139 | 158 |
140 private: | 159 private: |
141 void AddUserInfo() { | 160 // Overridden from views::View. |
142 user_info_ = new views::View; | 161 virtual gfx::Size GetPreferredSize() OVERRIDE; |
143 user_info_->SetLayoutManager(new views::BoxLayout( | 162 virtual void Layout() OVERRIDE; |
144 views::BoxLayout::kHorizontal, kTrayPopupPaddingHorizontal, | |
145 kUserInfoVerticalPadding, kTrayPopupPaddingBetweenItems)); | |
146 AddChildView(user_info_); | |
147 | |
148 if (login_ == ash::user::LOGGED_IN_KIOSK) { | |
149 views::Label* label = new views::Label; | |
150 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
151 label->SetText( | |
152 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL)); | |
153 label->set_border(views::Border::CreateEmptyBorder( | |
154 0, 4, 0, 1)); | |
155 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
156 user_info_->AddChildView(label); | |
157 return; | |
158 } | |
159 | |
160 RoundedImageView* image = new RoundedImageView(kProfileRoundedCornerRadius); | |
161 image->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), | |
162 gfx::Size(kUserIconSize, kUserIconSize)); | |
163 user_info_->AddChildView(image); | |
164 | |
165 views::View* user = new views::View; | |
166 user->SetLayoutManager(new views::BoxLayout( | |
167 views::BoxLayout::kVertical, 0, 5, 0)); | |
168 ash::SystemTrayDelegate* tray = | |
169 ash::Shell::GetInstance()->tray_delegate(); | |
170 username_ = new views::Label(tray->GetUserDisplayName()); | |
171 username_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
172 user->AddChildView(username_); | |
173 | |
174 email_ = new views::Label(UTF8ToUTF16(tray->GetUserEmail())); | |
175 email_->SetFont(username_->font().DeriveFont(-1)); | |
176 email_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
177 email_->SetEnabled(false); | |
178 user->AddChildView(email_); | |
179 | |
180 user_info_->AddChildView(user); | |
181 } | |
182 | 163 |
183 // Overridden from views::ButtonListener. | 164 // Overridden from views::ButtonListener. |
184 virtual void ButtonPressed(views::Button* sender, | 165 virtual void ButtonPressed(views::Button* sender, |
185 const ui::Event& event) OVERRIDE { | 166 const ui::Event& event) OVERRIDE; |
186 CHECK(sender == signout_); | 167 |
187 ash::SystemTrayDelegate* tray = ash::Shell::GetInstance()->tray_delegate(); | 168 void AddLogoutButton(ash::user::LoginStatus login); |
188 tray->SignOut(); | 169 void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login); |
189 } | 170 |
190 | 171 views::View* user_card_; |
191 // Overridden from views::View. | 172 views::View* logout_button_; |
192 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
193 gfx::Size size; | |
194 if (user_info_) | |
195 size = user_info_->GetPreferredSize(); | |
196 if (signout_) { | |
197 gfx::Size signout_size = signout_->GetPreferredSize(); | |
198 // Make sure the user default view item at least as tall as the other | |
199 // tray popup items. | |
200 if (size.height() == 0) | |
201 size.set_height(kTrayPopupItemHeight); | |
202 size.set_height(std::max(size.height(), signout_size.height())); | |
203 size.set_width(size.width() + signout_size.width() + | |
204 kTrayPopupPaddingHorizontal * 2 + kTrayPopupPaddingBetweenItems); | |
205 } | |
206 return size; | |
207 } | |
208 | |
209 virtual void Layout() OVERRIDE { | |
210 views::View::Layout(); | |
211 if (bounds().IsEmpty()) | |
212 return; | |
213 | |
214 if (signout_ && user_info_) { | |
215 gfx::Rect signout_bounds(bounds()); | |
216 signout_bounds.ClampToCenteredSize(signout_->GetPreferredSize()); | |
217 signout_bounds.set_x(width() - signout_bounds.width() - | |
218 kTrayPopupPaddingHorizontal); | |
219 signout_->SetBoundsRect(signout_bounds); | |
220 | |
221 gfx::Rect usercard_bounds(user_info_->GetPreferredSize()); | |
222 usercard_bounds.set_width(signout_bounds.x()); | |
223 user_info_->SetBoundsRect(usercard_bounds); | |
224 } else if (signout_) { | |
225 signout_->SetBoundsRect(gfx::Rect(size())); | |
226 } else if (user_info_) { | |
227 user_info_->SetBoundsRect(gfx::Rect(size())); | |
228 } | |
229 } | |
230 | |
231 user::LoginStatus login_; | |
232 | |
233 views::View* user_info_; | |
234 views::Label* username_; | |
235 views::Label* email_; | |
236 | |
237 views::Button* signout_; | |
238 | 173 |
239 DISALLOW_COPY_AND_ASSIGN(UserView); | 174 DISALLOW_COPY_AND_ASSIGN(UserView); |
240 }; | 175 }; |
241 | 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 const int inner_padding = |
| 220 kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems; |
| 221 const bool rtl = base::i18n::IsRTL(); |
| 222 set_border(views::Border::CreateEmptyBorder( |
| 223 kUserDetailsVerticalPadding, rtl ? 0 : inner_padding, |
| 224 kUserDetailsVerticalPadding, rtl ? inner_padding : 0)); |
| 225 |
| 226 ash::SystemTrayDelegate* delegate = |
| 227 ash::Shell::GetInstance()->tray_delegate(); |
| 228 // Retrieve the user's display name and wrap it with markers. |
| 229 string16 display_name = delegate->GetUserDisplayName(); |
| 230 RemoveChars(display_name, kDisplayNameMark, &display_name); |
| 231 display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0]; |
| 232 // Retrieve the domain managing the device and wrap it with markers. |
| 233 string16 domain = UTF8ToUTF16(delegate->GetEnterpriseDomain()); |
| 234 RemoveChars(domain, kDisplayNameMark, &domain); |
| 235 base::i18n::WrapStringWithLTRFormatting(&domain); |
| 236 // Retrieve the label text, inserting the display name and domain. |
| 237 text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL, |
| 238 display_name, domain); |
| 239 |
| 240 learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE)); |
| 241 learn_more_->SetUnderline(false); |
| 242 learn_more_->set_listener(this); |
| 243 AddChildView(learn_more_); |
| 244 |
| 245 CalculatePreferredSize(owner, used_width); |
| 246 } |
| 247 |
| 248 PublicAccountUserDetails::~PublicAccountUserDetails() {} |
| 249 |
| 250 void PublicAccountUserDetails::Layout() { |
| 251 lines_.clear(); |
| 252 const gfx::Rect contents_area = GetContentsBounds(); |
| 253 if (contents_area.IsEmpty()) |
| 254 return; |
| 255 |
| 256 // Word-wrap the label text. |
| 257 const gfx::Font font; |
| 258 std::vector<string16> lines; |
| 259 ui::ElideRectangleText(text_, font, contents_area.width(), |
| 260 contents_area.height(), ui::ELIDE_LONG_WORDS, &lines); |
| 261 // Loop through the lines, creating a renderer for each. |
| 262 gfx::Point position = contents_area.origin(); |
| 263 ui::Range display_name(ui::Range::InvalidRange()); |
| 264 for (std::vector<string16>::const_iterator it = lines.begin(); |
| 265 it != lines.end(); ++it) { |
| 266 gfx::RenderText* line = gfx::RenderText::CreateInstance(); |
| 267 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI); |
| 268 line->SetText(*it); |
| 269 const gfx::Size size(contents_area.width(), line->GetStringSize().height()); |
| 270 line->SetDisplayRect(gfx::Rect(position, size)); |
| 271 position.set_y(position.y() + size.height()); |
| 272 |
| 273 // Set the default text color for the line. |
| 274 gfx::StyleRange default_style(line->default_style()); |
| 275 default_style.foreground = kPublicAccountUserCardTextColor; |
| 276 line->set_default_style(default_style); |
| 277 line->ApplyDefaultStyle(); |
| 278 |
| 279 // If a range of the line contains the user's display name, apply a custom |
| 280 // text color to it. |
| 281 if (display_name.is_empty()) |
| 282 display_name.set_start(it->find(kDisplayNameMark)); |
| 283 if (!display_name.is_empty()) { |
| 284 display_name.set_end( |
| 285 it->find(kDisplayNameMark, display_name.start() + 1)); |
| 286 gfx::StyleRange display_name_style(line->default_style()); |
| 287 display_name_style.foreground = kPublicAccountUserCardNameColor; |
| 288 ui::Range line_range(0, it->size()); |
| 289 display_name_style.range = display_name.Intersect(line_range); |
| 290 line->ApplyStyleRange(display_name_style); |
| 291 // Update the range for the next line. |
| 292 if (display_name.end() >= line_range.end()) |
| 293 display_name.set_start(0); |
| 294 else |
| 295 display_name = ui::Range::InvalidRange(); |
| 296 } |
| 297 |
| 298 lines_.push_back(line); |
| 299 } |
| 300 |
| 301 // Position the link after the label text, separated by a space. If it does |
| 302 // not fit onto the last line of the text, wrap the link onto its own line. |
| 303 const gfx::Size last_line_size = lines_.back()->GetStringSize(); |
| 304 const int space_width = font.GetStringWidth(ASCIIToUTF16(" ")); |
| 305 const gfx::Size link_size = learn_more_->GetPreferredSize(); |
| 306 if (contents_area.width() - last_line_size.width() >= |
| 307 space_width + link_size.width()) { |
| 308 position.set_x(position.x() + last_line_size.width() + space_width); |
| 309 position.set_y(position.y() - last_line_size.height()); |
| 310 } |
| 311 position.set_y(position.y() - learn_more_->GetInsets().top()); |
| 312 gfx::Rect learn_more_bounds(position, link_size); |
| 313 learn_more_bounds.Intersect(contents_area); |
| 314 if (base::i18n::IsRTL()) { |
| 315 const gfx::Insets insets = GetInsets(); |
| 316 learn_more_bounds.Offset(insets.right() - insets.left(), 0); |
| 317 } |
| 318 learn_more_->SetBoundsRect(learn_more_bounds); |
| 319 } |
| 320 |
| 321 gfx::Size PublicAccountUserDetails::GetPreferredSize() { |
| 322 return preferred_size_; |
| 323 } |
| 324 |
| 325 void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) { |
| 326 for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin(); |
| 327 it != lines_.end(); ++it) { |
| 328 (*it)->Draw(canvas); |
| 329 } |
| 330 views::View::OnPaint(canvas); |
| 331 } |
| 332 |
| 333 void PublicAccountUserDetails::LinkClicked(views::Link* source, |
| 334 int event_flags) { |
| 335 DCHECK_EQ(source, learn_more_); |
| 336 ash::Shell::GetInstance()->tray_delegate()->ShowPublicAccountInfo(); |
| 337 } |
| 338 |
| 339 void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner, |
| 340 int used_width) { |
| 341 const gfx::Font font; |
| 342 const gfx::Size link_size = learn_more_->GetPreferredSize(); |
| 343 const int space_width = font.GetStringWidth(ASCIIToUTF16(" ")); |
| 344 const gfx::Insets insets = GetInsets(); |
| 345 views::TrayBubbleView* bubble_view = |
| 346 owner->system_tray()->GetSystemBubble()->bubble_view(); |
| 347 int min_width = std::max( |
| 348 link_size.width(), |
| 349 bubble_view->GetPreferredSize().width() - (used_width + insets.width())); |
| 350 int max_width = std::min( |
| 351 font.GetStringWidth(text_) + space_width + link_size.width(), |
| 352 bubble_view->GetMaximumSize().width() - (used_width + insets.width())); |
| 353 // Do a binary search for the minimum width that ensures no more than three |
| 354 // lines are needed. The lower bound is the minimum of the current bubble |
| 355 // width and the width of the link (as no wrapping is permitted inside the |
| 356 // link). The upper bound is the maximum of the largest allowed bubble width |
| 357 // and the sum of the label text and link widths when put on a single line. |
| 358 std::vector<string16> lines; |
| 359 while (min_width < max_width) { |
| 360 lines.clear(); |
| 361 const int width = (min_width + max_width) / 2; |
| 362 const bool too_narrow = ui::ElideRectangleText( |
| 363 text_, font, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines) != 0; |
| 364 int line_count = lines.size(); |
| 365 if (!too_narrow && line_count == 3 && |
| 366 width - font.GetStringWidth(lines.back()) <= |
| 367 space_width + link_size.width()) { |
| 368 ++line_count; |
| 369 } |
| 370 if (too_narrow || line_count > 3) |
| 371 min_width = width + 1; |
| 372 else |
| 373 max_width = width; |
| 374 } |
| 375 |
| 376 // Calculate the corresponding height and set the preferred size. |
| 377 lines.clear(); |
| 378 ui::ElideRectangleText( |
| 379 text_, font, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines); |
| 380 int line_count = lines.size(); |
| 381 if (min_width - font.GetStringWidth(lines.back()) <= |
| 382 space_width + link_size.width()) { |
| 383 ++line_count; |
| 384 } |
| 385 const int line_height = font.GetHeight(); |
| 386 const int link_extra_height = std::max( |
| 387 link_size.height() - learn_more_->GetInsets().top() - line_height, 0); |
| 388 preferred_size_ = gfx::Size( |
| 389 min_width + insets.width(), |
| 390 line_count * line_height + link_extra_height + insets.height()); |
| 391 |
| 392 bubble_view->SetWidth(preferred_size_.width() + used_width); |
| 393 } |
| 394 |
| 395 UserView::UserView(SystemTrayItem* owner, ash::user::LoginStatus login) |
| 396 : user_card_(NULL), |
| 397 logout_button_(NULL) { |
| 398 CHECK_NE(ash::user::LOGGED_IN_NONE, login); |
| 399 set_background(views::Background::CreateSolidBackground( |
| 400 login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor : |
| 401 kBackgroundColor)); |
| 402 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, |
| 403 kTrayPopupPaddingBetweenItems)); |
| 404 // The logout button must be added before the user card so that the user card |
| 405 // can correctly calculate the remaining available width. |
| 406 AddLogoutButton(login); |
| 407 AddUserCard(owner, login); |
| 408 } |
| 409 |
| 410 UserView::~UserView() {} |
| 411 |
| 412 gfx::Size UserView::GetPreferredSize() { |
| 413 gfx::Size size = views::View::GetPreferredSize(); |
| 414 if (!user_card_) { |
| 415 // Make sure the default user view item is at least as tall as the other |
| 416 // items. |
| 417 size.set_height(std::max(size.height(), |
| 418 kTrayPopupItemHeight + GetInsets().height())); |
| 419 } |
| 420 return size; |
| 421 } |
| 422 |
| 423 void UserView::Layout() { |
| 424 gfx::Rect contents_area(GetContentsBounds()); |
| 425 if (user_card_ && logout_button_) { |
| 426 // Give the logout button the space it requests. |
| 427 gfx::Rect logout_area = contents_area; |
| 428 logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize()); |
| 429 logout_area.set_x(contents_area.right() - logout_area.width()); |
| 430 logout_button_->SetBoundsRect(logout_area); |
| 431 |
| 432 // Give the remaining space to the user card. |
| 433 gfx::Rect user_card_area = contents_area; |
| 434 user_card_area.set_width(contents_area.width() - |
| 435 (logout_area.width() + kTrayPopupPaddingBetweenItems)); |
| 436 user_card_->SetBoundsRect(user_card_area); |
| 437 } else if (user_card_) { |
| 438 user_card_->SetBoundsRect(contents_area); |
| 439 } else if (logout_button_) { |
| 440 logout_button_->SetBoundsRect(contents_area); |
| 441 } |
| 442 } |
| 443 |
| 444 void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) { |
| 445 DCHECK_EQ(logout_button_, sender); |
| 446 ash::Shell::GetInstance()->tray_delegate()->SignOut(); |
| 447 } |
| 448 |
| 449 void UserView::AddLogoutButton(ash::user::LoginStatus login) { |
| 450 // A user should not be able to modify logged-in state when screen is |
| 451 // locked. |
| 452 if (login == ash::user::LOGGED_IN_LOCKED) |
| 453 return; |
| 454 |
| 455 const string16 title = ash::user::GetLocalizedSignOutStringForStatus(login, |
| 456 true); |
| 457 TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title); |
| 458 logout_button->SetAccessibleName(title); |
| 459 logout_button_ = logout_button; |
| 460 // In public account mode, the logout button border has a custom color. |
| 461 if (login == ash::user::LOGGED_IN_PUBLIC) { |
| 462 TrayPopupLabelButtonBorder* border = |
| 463 static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border()); |
| 464 border->SetImages(views::CustomButton::STATE_NORMAL, views::BorderImages( |
| 465 kPublicAccountLogoutButtonBorderImagesNormal)); |
| 466 border->SetImages(views::CustomButton::STATE_HOVERED, views::BorderImages( |
| 467 kPublicAccountLogoutButtonBorderImagesHovered)); |
| 468 border->SetImages(views::CustomButton::STATE_PRESSED, views::BorderImages( |
| 469 kPublicAccountLogoutButtonBorderImagesHovered)); |
| 470 } |
| 471 AddChildView(logout_button_); |
| 472 } |
| 473 |
| 474 void UserView::AddUserCard(SystemTrayItem* owner, |
| 475 ash::user::LoginStatus login) { |
| 476 if (login == ash::user::LOGGED_IN_GUEST) |
| 477 return; |
| 478 |
| 479 set_border(views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal, |
| 480 0, kTrayPopupPaddingHorizontal)); |
| 481 |
| 482 user_card_ = new views::View(); |
| 483 user_card_->SetLayoutManager(new views::BoxLayout( |
| 484 views::BoxLayout::kHorizontal, 0, kUserCardVerticalPadding, |
| 485 kTrayPopupPaddingBetweenItems)); |
| 486 AddChildViewAt(user_card_, 0); |
| 487 |
| 488 if (login == ash::user::LOGGED_IN_KIOSK) { |
| 489 views::Label* details = new views::Label; |
| 490 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| 491 details->SetText( |
| 492 bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL)); |
| 493 details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1)); |
| 494 details->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| 495 user_card_->AddChildView(details); |
| 496 return; |
| 497 } |
| 498 |
| 499 RoundedImageView* avatar = new RoundedImageView(kProfileRoundedCornerRadius); |
| 500 avatar->SetImage(ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), |
| 501 gfx::Size(kUserIconSize, kUserIconSize)); |
| 502 user_card_->AddChildView(avatar); |
| 503 |
| 504 if (login == ash::user::LOGGED_IN_PUBLIC) { |
| 505 user_card_->AddChildView(new PublicAccountUserDetails( |
| 506 owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems)); |
| 507 return; |
| 508 } |
| 509 |
| 510 ash::SystemTrayDelegate* delegate = |
| 511 ash::Shell::GetInstance()->tray_delegate(); |
| 512 views::View* details = new views::View; |
| 513 details->SetLayoutManager(new views::BoxLayout( |
| 514 views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0)); |
| 515 views::Label* username = new views::Label(delegate->GetUserDisplayName()); |
| 516 username->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| 517 details->AddChildView(username); |
| 518 |
| 519 views::Label* email = new views::Label(UTF8ToUTF16(delegate->GetUserEmail())); |
| 520 email->SetFont(username->font().DeriveFont(-1)); |
| 521 email->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| 522 email->SetEnabled(false); |
| 523 details->AddChildView(email); |
| 524 user_card_->AddChildView(details); |
| 525 } |
| 526 |
242 } // namespace tray | 527 } // namespace tray |
243 | 528 |
244 TrayUser::TrayUser(SystemTray* system_tray) | 529 TrayUser::TrayUser(SystemTray* system_tray) |
245 : SystemTrayItem(system_tray), | 530 : SystemTrayItem(system_tray), |
246 user_(NULL), | 531 user_(NULL), |
247 avatar_(NULL), | 532 avatar_(NULL), |
248 label_(NULL) { | 533 label_(NULL) { |
249 } | 534 } |
250 | 535 |
251 TrayUser::~TrayUser() { | 536 TrayUser::~TrayUser() { |
(...skipping 13 matching lines...) Expand all Loading... |
265 UpdateAfterLoginStatusChange(status); | 550 UpdateAfterLoginStatusChange(status); |
266 return avatar_ ? static_cast<views::View*>(avatar_) | 551 return avatar_ ? static_cast<views::View*>(avatar_) |
267 : static_cast<views::View*>(label_); | 552 : static_cast<views::View*>(label_); |
268 } | 553 } |
269 | 554 |
270 views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { | 555 views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { |
271 if (status == user::LOGGED_IN_NONE) | 556 if (status == user::LOGGED_IN_NONE) |
272 return NULL; | 557 return NULL; |
273 | 558 |
274 CHECK(user_ == NULL); | 559 CHECK(user_ == NULL); |
275 user_ = new tray::UserView(status); | 560 user_ = new tray::UserView(this, status); |
276 return user_; | 561 return user_; |
277 } | 562 } |
278 | 563 |
279 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) { | 564 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) { |
280 return NULL; | 565 return NULL; |
281 } | 566 } |
282 | 567 |
283 void TrayUser::DestroyTrayView() { | 568 void TrayUser::DestroyTrayView() { |
284 avatar_ = NULL; | 569 avatar_ = NULL; |
285 label_ = NULL; | 570 label_ = NULL; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 // Check for null to avoid crbug.com/150944. | 628 // Check for null to avoid crbug.com/150944. |
344 if (avatar_) { | 629 if (avatar_) { |
345 avatar_->SetImage( | 630 avatar_->SetImage( |
346 ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), | 631 ash::Shell::GetInstance()->tray_delegate()->GetUserImage(), |
347 gfx::Size(kUserIconSize, kUserIconSize)); | 632 gfx::Size(kUserIconSize, kUserIconSize)); |
348 } | 633 } |
349 } | 634 } |
350 | 635 |
351 } // namespace internal | 636 } // namespace internal |
352 } // namespace ash | 637 } // namespace ash |
OLD | NEW |