Home | History | Annotate | Download | only in login
      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