tray_user.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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
3// found in the LICENSE file.
4
5#include "ash/system/user/tray_user.h"
6
7#include "ash/shell.h"
8#include "ash/system/tray/system_tray_delegate.h"
9#include "ash/system/tray/tray_constants.h"
10#include "ash/system/tray/tray_item_view.h"
11#include "ash/system/tray/tray_views.h"
12#include "base/utf_string_conversions.h"
13#include "grit/ash_strings.h"
14#include "skia/ext/image_operations.h"
15#include "third_party/skia/include/core/SkCanvas.h"
16#include "third_party/skia/include/core/SkPaint.h"
17#include "third_party/skia/include/core/SkPath.h"
18#include "ui/base/resource/resource_bundle.h"
19#include "ui/gfx/canvas.h"
20#include "ui/gfx/image/image.h"
21#include "ui/gfx/image/image_skia_operations.h"
22#include "ui/gfx/size.h"
23#include "ui/gfx/skia_util.h"
24#include "ui/views/controls/button/button.h"
25#include "ui/views/controls/button/text_button.h"
26#include "ui/views/controls/image_view.h"
27#include "ui/views/controls/label.h"
28#include "ui/views/layout/box_layout.h"
29#include "ui/views/view.h"
30#include "ui/views/widget/widget.h"
31
32namespace {
33
34const int kUserInfoVerticalPadding = 10;
35const int kUserIconSize = 27;
36const int kProfileRoundedCornerRadius = 2;
37
38}  // namespace
39
40namespace ash {
41namespace internal {
42
43namespace tray {
44
45// A custom image view with rounded edges.
46class RoundedImageView : public views::View {
47 public:
48  // Constructs a new rounded image view with rounded corners of radius
49  // |corner_radius|.
50  explicit RoundedImageView(int corner_radius) : corner_radius_(corner_radius) {
51  }
52
53  virtual ~RoundedImageView() {
54  }
55
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
92 private:
93  gfx::ImageSkia image_;
94  gfx::ImageSkia resized_;
95  gfx::Size image_size_;
96  int corner_radius_;
97
98  DISALLOW_COPY_AND_ASSIGN(RoundedImageView);
99};
100
101class UserView : public views::View,
102                 public views::ButtonListener {
103 public:
104  explicit UserView(ash::user::LoginStatus login)
105      : login_(login),
106        container_(NULL),
107        user_info_(NULL),
108        username_(NULL),
109        email_(NULL),
110        signout_(NULL) {
111    CHECK(login_ != ash::user::LOGGED_IN_NONE);
112    set_background(views::Background::CreateSolidBackground(kBackgroundColor));
113
114    bool guest = login_ == ash::user::LOGGED_IN_GUEST;
115    bool locked = login_ == ash::user::LOGGED_IN_LOCKED;
116
117    container_ = new TrayPopupTextButtonContainer;
118    container_->layout()->set_spread_blank_space(false);
119    AddChildView(container_);
120
121    if (!guest)
122      AddUserInfo();
123
124    // A user should not be able to modify logged in state when screen is
125    // locked.
126    if (!locked)
127      AddButtonContainer();
128  }
129
130  virtual ~UserView() {}
131
132  // Create container for buttons.
133  void AddButtonContainer() {
134    TrayPopupTextButton* button = new TrayPopupTextButton(this,
135        ash::user::GetLocalizedSignOutStringForStatus(login_));
136    container_->AddTextButton(button);
137    signout_ = button;
138  }
139
140 private:
141  void AddUserInfo() {
142    user_info_ = new views::View;
143    user_info_->SetLayoutManager(new views::BoxLayout(
144        views::BoxLayout::kHorizontal, kTrayPopupPaddingHorizontal,
145        kUserInfoVerticalPadding, kTrayPopupPaddingBetweenItems));
146    container_->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
183  // Overridden from views::ButtonListener.
184  virtual void ButtonPressed(views::Button* sender,
185                             const ui::Event& event) OVERRIDE {
186    CHECK(sender == signout_);
187    ash::SystemTrayDelegate* tray = ash::Shell::GetInstance()->tray_delegate();
188    tray->SignOut();
189  }
190
191  // Overridden from views::View.
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    container_->SetBoundsRect(gfx::Rect(size()));
215    if (signout_ && user_info_) {
216      gfx::Rect signout_bounds(bounds());
217      signout_bounds.ClampToCenteredSize(signout_->GetPreferredSize());
218      signout_bounds.set_x(width() - signout_bounds.width() -
219          kTrayPopupPaddingHorizontal);
220      signout_->SetBoundsRect(signout_bounds);
221
222      gfx::Rect usercard_bounds(user_info_->GetPreferredSize());
223      usercard_bounds.set_width(signout_bounds.x());
224      user_info_->SetBoundsRect(usercard_bounds);
225    } else if (signout_) {
226      signout_->SetBoundsRect(gfx::Rect(size()));
227    } else if (user_info_) {
228      user_info_->SetBoundsRect(gfx::Rect(size()));
229    }
230  }
231
232  user::LoginStatus login_;
233
234  TrayPopupTextButtonContainer* container_;
235  views::View* user_info_;
236  views::Label* username_;
237  views::Label* email_;
238
239  views::Button* signout_;
240
241  DISALLOW_COPY_AND_ASSIGN(UserView);
242};
243
244}  // namespace tray
245
246TrayUser::TrayUser()
247    : user_(NULL),
248      avatar_(NULL),
249      label_(NULL) {
250}
251
252TrayUser::~TrayUser() {
253}
254
255views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
256  CHECK(avatar_ == NULL);
257  CHECK(label_ == NULL);
258  if (status == user::LOGGED_IN_GUEST) {
259    label_ = new views::Label;
260    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
261    label_->SetText(bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL));
262    SetupLabelForTray(label_);
263  } else {
264    avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius);
265  }
266  UpdateAfterLoginStatusChange(status);
267  return avatar_ ? static_cast<views::View*>(avatar_)
268                 : static_cast<views::View*>(label_);
269}
270
271views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
272  if (status == user::LOGGED_IN_NONE)
273    return NULL;
274
275  CHECK(user_ == NULL);
276  user_ = new tray::UserView(status);
277  return user_;
278}
279
280views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
281  return NULL;
282}
283
284void TrayUser::DestroyTrayView() {
285  avatar_ = NULL;
286  label_ = NULL;
287}
288
289void TrayUser::DestroyDefaultView() {
290  user_ = NULL;
291}
292
293void TrayUser::DestroyDetailedView() {
294}
295
296void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) {
297  switch (status) {
298    case user::LOGGED_IN_LOCKED:
299    case user::LOGGED_IN_USER:
300    case user::LOGGED_IN_OWNER:
301    case user::LOGGED_IN_PUBLIC:
302      avatar_->SetImage(
303          ash::Shell::GetInstance()->tray_delegate()->GetUserImage(),
304          gfx::Size(kUserIconSize, kUserIconSize));
305      avatar_->SetVisible(true);
306      break;
307
308    case user::LOGGED_IN_GUEST:
309      label_->SetVisible(true);
310      break;
311
312    case user::LOGGED_IN_KIOSK:
313    case user::LOGGED_IN_NONE:
314      avatar_->SetVisible(false);
315      break;
316  }
317}
318
319void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
320  if (avatar_) {
321    if (alignment == SHELF_ALIGNMENT_BOTTOM) {
322      avatar_->set_border(views::Border::CreateEmptyBorder(
323          0, kTrayImageItemHorizontalPaddingBottomAlignment + 2,
324          0, kTrayImageItemHorizontalPaddingBottomAlignment));
325    } else {
326        SetTrayImageItemBorder(avatar_, alignment);
327    }
328  } else {
329    if (alignment == SHELF_ALIGNMENT_BOTTOM) {
330      label_->set_border(views::Border::CreateEmptyBorder(
331          0, kTrayLabelItemHorizontalPaddingBottomAlignment,
332          0, kTrayLabelItemHorizontalPaddingBottomAlignment));
333    } else {
334      label_->set_border(views::Border::CreateEmptyBorder(
335          kTrayLabelItemVerticalPaddingVeriticalAlignment,
336          kTrayLabelItemHorizontalPaddingBottomAlignment,
337          kTrayLabelItemVerticalPaddingVeriticalAlignment,
338          kTrayLabelItemHorizontalPaddingBottomAlignment));
339    }
340  }
341}
342
343void TrayUser::OnUserUpdate() {
344  // Check for null to avoid crbug.com/150944.
345  if (avatar_) {
346    avatar_->SetImage(
347        ash::Shell::GetInstance()->tray_delegate()->GetUserImage(),
348        gfx::Size(kUserIconSize, kUserIconSize));
349  }
350}
351
352}  // namespace internal
353}  // namespace ash
354