1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/login/user_view.h" 6 7 #include "base/utf_string_conversions.h" 8 #include "chrome/browser/chromeos/login/helper.h" 9 #include "chrome/browser/chromeos/login/rounded_rect_painter.h" 10 #include "chrome/browser/chromeos/login/rounded_view.h" 11 #include "chrome/browser/chromeos/view_ids.h" 12 #include "grit/generated_resources.h" 13 #include "grit/theme_resources.h" 14 #include "ui/base/l10n/l10n_util.h" 15 #include "ui/base/resource/resource_bundle.h" 16 #include "ui/gfx/canvas.h" 17 #include "ui/gfx/canvas_skia.h" 18 #include "ui/gfx/gtk_util.h" 19 #include "ui/gfx/rect.h" 20 #include "views/background.h" 21 #include "views/controls/button/text_button.h" 22 #include "views/controls/image_view.h" 23 #include "views/controls/label.h" 24 #include "views/controls/link.h" 25 #include "views/painter.h" 26 27 namespace { 28 29 // Background color and corner radius of the login status label and 30 // signout button. 31 const SkColor kSignoutBackgroundColor = 0xFF007700; 32 const int kSignoutBackgroundCornerRadius = 4; 33 34 // Horiz/Vert insets for Signout view. 35 const int kSignoutViewHorizontalInsets = 10; 36 const int kSignoutViewVerticalInsets = 5; 37 const int kMinControlHeight = 16; 38 39 // Padding between remove button and top right image corner. 40 const int kRemoveButtonPadding = 3; 41 42 // Draws green-ish background for signout view with 43 // rounded corners at the bottom. 44 class SignoutBackgroundPainter : public views::Painter { 45 virtual void Paint(int w, int h, gfx::Canvas* canvas) { 46 SkRect rect = {0, 0, w, h}; 47 SkPath path; 48 path.addRoundRect(rect, 49 kSignoutBackgroundCornerRadius, kSignoutBackgroundCornerRadius); 50 SkPaint paint; 51 paint.setStyle(SkPaint::kFill_Style); 52 paint.setFlags(SkPaint::kAntiAlias_Flag); 53 paint.setColor(kSignoutBackgroundColor); 54 canvas->AsCanvasSkia()->drawPath(path, paint); 55 } 56 }; 57 58 } // namespace 59 60 namespace chromeos { 61 62 using login::kBackgroundColor; 63 using login::kTextColor; 64 using login::kUserImageSize; 65 66 // The view that shows the Sign out button below the user's image. 67 class SignoutView : public views::View { 68 public: 69 explicit SignoutView(views::LinkController* link_controller) { 70 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 71 const gfx::Font& font = rb.GetFont(ResourceBundle::SmallFont); 72 73 active_user_label_ = new views::Label( 74 UTF16ToWide(l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_ACTIVE_USER))); 75 active_user_label_->SetFont(font); 76 active_user_label_->SetColor(kTextColor); 77 78 signout_link_ = new views::Link( 79 UTF16ToWide(l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT))); 80 signout_link_->SetController(link_controller); 81 signout_link_->SetFont(font); 82 signout_link_->SetColor(kTextColor); 83 signout_link_->SetFocusable(true); 84 signout_link_->SetHighlightedColor(kTextColor); 85 signout_link_->SetDisabledColor(kTextColor); 86 signout_link_->SetNormalColor(kTextColor); 87 signout_link_->SetID(VIEW_ID_SCREEN_LOCKER_SIGNOUT_LINK); 88 89 AddChildView(active_user_label_); 90 AddChildView(signout_link_); 91 92 set_background(views::Background::CreateBackgroundPainter( 93 true, new SignoutBackgroundPainter())); 94 } 95 96 // views::View overrides. 97 virtual void Layout() { 98 gfx::Size label = active_user_label_->GetPreferredSize(); 99 gfx::Size button = signout_link_->GetPreferredSize(); 100 active_user_label_->SetBounds( 101 kSignoutViewHorizontalInsets, (height() - label.height()) / 2, 102 label.width(), label.height()); 103 signout_link_->SetBounds( 104 width() - button.width() - kSignoutViewHorizontalInsets, 105 (height() - button.height()) / 2, 106 button.width(), button.height()); 107 } 108 109 virtual gfx::Size GetPreferredSize() { 110 gfx::Size label = active_user_label_->GetPreferredSize(); 111 gfx::Size button = signout_link_->GetPreferredSize(); 112 int width = 113 label.width() + button.width() + 2 * kSignoutViewHorizontalInsets; 114 int height = 115 std::max(kMinControlHeight, std::max(label.height(), button.height())) + 116 kSignoutViewVerticalInsets * 2; 117 return gfx::Size(width, height); 118 } 119 120 views::Link* signout_link() { return signout_link_; } 121 122 private: 123 views::Label* active_user_label_; 124 views::Link* signout_link_; 125 126 DISALLOW_COPY_AND_ASSIGN(SignoutView); 127 }; 128 129 class RemoveButton : public views::TextButton { 130 public: 131 RemoveButton(views::ButtonListener* listener, 132 const SkBitmap& icon, 133 const std::wstring& text, 134 const gfx::Point& top_right) 135 : views::TextButton(listener, std::wstring()), 136 icon_(icon), 137 text_(text), 138 top_right_(top_right), 139 was_first_click_(false) { 140 SetEnabledColor(SK_ColorWHITE); 141 SetDisabledColor(SK_ColorWHITE); 142 SetHighlightColor(SK_ColorWHITE); 143 SetHoverColor(SK_ColorWHITE); 144 SetIcon(icon_); 145 UpdatePosition(); 146 } 147 148 protected: 149 // Overridden from View: 150 virtual void OnMouseExited(const views::MouseEvent& event) { 151 SetIcon(icon_); 152 views::TextButton::SetText(std::wstring()); 153 ClearMaxTextSize(); 154 set_background(NULL); 155 set_border(new views::TextButtonBorder); 156 UpdatePosition(); 157 views::TextButton::OnMouseExited(event); 158 was_first_click_ = false; 159 } 160 161 void NotifyClick(const views::Event& event) { 162 if (!was_first_click_) { 163 // On first click transform image to "remove" label. 164 SetIcon(SkBitmap()); 165 views::TextButton::SetText(text_); 166 167 const SkColor kStrokeColor = SK_ColorWHITE; 168 const SkColor kButtonColor = 0xFFE94949; 169 const int kStrokeWidth = 1; 170 const int kVerticalPadding = 4; 171 const int kHorizontalPadding = 8; 172 const int kCornerRadius = 4; 173 174 set_background( 175 CreateRoundedBackground( 176 kCornerRadius, kStrokeWidth, kButtonColor, kStrokeColor)); 177 178 set_border( 179 views::Border::CreateEmptyBorder(kVerticalPadding, 180 kHorizontalPadding, 181 kVerticalPadding, 182 kHorizontalPadding)); 183 184 UpdatePosition(); 185 was_first_click_ = true; 186 } else { 187 // On second click propagate to base class to fire ButtonPressed. 188 views::TextButton::NotifyClick(event); 189 } 190 } 191 192 void SetText(const std::wstring& text) { 193 text_ = text; 194 } 195 196 private: 197 // Update button position and schedule paint event for the view and parent. 198 void UpdatePosition() { 199 gfx::Size size = GetPreferredSize(); 200 gfx::Point origin = top_right_; 201 origin.Offset(-size.width(), 0); 202 SetBoundsRect(gfx::Rect(origin, size)); 203 204 if (parent()) 205 parent()->SchedulePaint(); 206 } 207 208 SkBitmap icon_; 209 std::wstring text_; 210 gfx::Point top_right_; 211 bool was_first_click_; 212 213 DISALLOW_COPY_AND_ASSIGN(RemoveButton); 214 }; 215 216 class PodImageView : public views::ImageView { 217 public: 218 explicit PodImageView(const UserView::Delegate* delegate) 219 : delegate_(delegate) { } 220 221 void SetImage(const SkBitmap& image, const SkBitmap& image_hot) { 222 image_ = image; 223 image_hot_ = image_hot; 224 views::ImageView::SetImage(image_); 225 } 226 227 protected: 228 // Overridden from View: 229 virtual void OnMouseEntered(const views::MouseEvent& event) { 230 views::ImageView::SetImage(image_hot_); 231 } 232 233 virtual void OnMouseExited(const views::MouseEvent& event) { 234 views::ImageView::SetImage(image_); 235 } 236 237 gfx::NativeCursor GetCursorForPoint( 238 ui::EventType event_type, 239 const gfx::Point& p) { 240 return (delegate_->IsUserSelected()) ? NULL : gfx::GetCursor(GDK_HAND2); 241 } 242 243 private: 244 const UserView::Delegate* delegate_; 245 246 SkBitmap image_; 247 SkBitmap image_hot_; 248 249 DISALLOW_COPY_AND_ASSIGN(PodImageView); 250 }; 251 252 UserView::UserView(Delegate* delegate, bool is_login, bool need_background) 253 : delegate_(delegate), 254 signout_view_(NULL), 255 image_view_(NULL), 256 remove_button_(NULL) { 257 DCHECK(delegate); 258 if (!is_login) 259 signout_view_ = new SignoutView(this); 260 261 if (need_background) 262 image_view_ = new RoundedView<PodImageView>(delegate); 263 else 264 image_view_ = new PodImageView(delegate); 265 266 Init(need_background); 267 } 268 269 void UserView::Init(bool need_background) { 270 if (need_background) { 271 image_view_->set_background( 272 views::Background::CreateSolidBackground(kBackgroundColor)); 273 } 274 275 // UserView's layout never changes, so let's layout once here. 276 image_view_->SetBounds(0, 0, kUserImageSize, kUserImageSize); 277 AddChildView(image_view_); 278 279 if (signout_view_) { 280 signout_view_->SetBounds(0, kUserImageSize, kUserImageSize, 281 signout_view_->GetPreferredSize().height()); 282 AddChildView(signout_view_); 283 } 284 285 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 286 remove_button_ = new RemoveButton( 287 this, 288 *rb.GetBitmapNamed(IDR_CLOSE_BAR_H), 289 UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_REMOVE)), 290 gfx::Point(kUserImageSize - kRemoveButtonPadding, kRemoveButtonPadding)); 291 remove_button_->SetVisible(false); 292 AddChildView(remove_button_); 293 } 294 295 void UserView::SetImage(const SkBitmap& image, const SkBitmap& image_hot) { 296 int desired_size = std::min(image.width(), image.height()); 297 // Desired size is not preserved if it's greater than 75% of kUserImageSize. 298 if (desired_size * 4 > 3 * kUserImageSize) 299 desired_size = kUserImageSize; 300 image_view_->SetImageSize(gfx::Size(desired_size, desired_size)); 301 image_view_->SetImage(image, image_hot); 302 } 303 304 void UserView::SetTooltipText(const std::wstring& text) { 305 DCHECK(image_view_); 306 image_view_->SetTooltipText(text); 307 } 308 309 gfx::Size UserView::GetPreferredSize() { 310 return gfx::Size( 311 kUserImageSize, 312 kUserImageSize + 313 (signout_view_ ? signout_view_->GetPreferredSize().height() : 0)); 314 } 315 316 void UserView::SetSignoutEnabled(bool enabled) { 317 DCHECK(signout_view_); 318 signout_view_->signout_link()->SetEnabled(enabled); 319 320 // Relayout because active and inactive link has different preferred size. 321 Layout(); 322 } 323 324 void UserView::LinkActivated(views::Link* source, int event_flags) { 325 DCHECK(delegate_); 326 DCHECK(signout_view_); 327 if (signout_view_->signout_link() == source) 328 delegate_->OnSignout(); 329 } 330 331 void UserView::SetRemoveButtonVisible(bool flag) { 332 remove_button_->SetVisible(flag); 333 } 334 335 void UserView::ButtonPressed(views::Button* sender, const views::Event& event) { 336 DCHECK(delegate_); 337 if (remove_button_ == sender) 338 delegate_->OnRemoveUser(); 339 } 340 341 void UserView::OnLocaleChanged() { 342 remove_button_->SetText( 343 UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_REMOVE))); 344 delegate_->OnLocaleChanged(); 345 } 346 347 } // namespace chromeos 348