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
27namespace {
28
29// Background color and corner radius of the login status label and
30// signout button.
31const SkColor kSignoutBackgroundColor = 0xFF007700;
32const int kSignoutBackgroundCornerRadius = 4;
33
34// Horiz/Vert insets for Signout view.
35const int kSignoutViewHorizontalInsets = 10;
36const int kSignoutViewVerticalInsets = 5;
37const int kMinControlHeight = 16;
38
39// Padding between remove button and top right image corner.
40const int kRemoveButtonPadding = 3;
41
42// Draws green-ish background for signout view with
43// rounded corners at the bottom.
44class 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
60namespace chromeos {
61
62using login::kBackgroundColor;
63using login::kTextColor;
64using login::kUserImageSize;
65
66// The view that shows the Sign out button below the user's image.
67class 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
129class 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
216class 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
252UserView::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
269void 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
295void 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
304void UserView::SetTooltipText(const std::wstring& text) {
305  DCHECK(image_view_);
306  image_view_->SetTooltipText(text);
307}
308
309gfx::Size UserView::GetPreferredSize() {
310  return gfx::Size(
311      kUserImageSize,
312      kUserImageSize +
313      (signout_view_ ? signout_view_->GetPreferredSize().height() : 0));
314}
315
316void 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
324void 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
331void UserView::SetRemoveButtonVisible(bool flag) {
332  remove_button_->SetVisible(flag);
333}
334
335void UserView::ButtonPressed(views::Button* sender, const views::Event& event) {
336  DCHECK(delegate_);
337  if (remove_button_ == sender)
338    delegate_->OnRemoveUser();
339}
340
341void UserView::OnLocaleChanged() {
342  remove_button_->SetText(
343      UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_REMOVE)));
344  delegate_->OnLocaleChanged();
345}
346
347}  // namespace chromeos
348