new_avatar_button.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 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/ui/views/profiles/new_avatar_button.h"
6
7#include "base/win/windows_version.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/profiles/profile_manager.h"
10#include "chrome/browser/profiles/profiles_state.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
13#include "grit/theme_resources.h"
14#include "ui/base/resource/resource_bundle.h"
15#include "ui/gfx/canvas.h"
16#include "ui/views/border.h"
17#include "ui/views/controls/button/label_button_border.h"
18#include "ui/views/painter.h"
19
20namespace {
21
22scoped_ptr<views::Border> CreateBorder(const int normal_image_set[],
23                                       const int hot_image_set[],
24                                       const int pushed_image_set[]) {
25  scoped_ptr<views::LabelButtonBorder> border(
26      new views::LabelButtonBorder(views::Button::STYLE_TEXTBUTTON));
27  border->SetPainter(false, views::Button::STATE_NORMAL,
28      views::Painter::CreateImageGridPainter(normal_image_set));
29  border->SetPainter(false, views::Button::STATE_HOVERED,
30      views::Painter::CreateImageGridPainter(hot_image_set));
31  border->SetPainter(false, views::Button::STATE_PRESSED,
32      views::Painter::CreateImageGridPainter(pushed_image_set));
33
34  const int kLeftRightInset = 8;
35  const int kTopInset = 2;
36  const int kBottomInset = 4;
37  border->set_insets(gfx::Insets(kTopInset, kLeftRightInset,
38                                 kBottomInset, kLeftRightInset));
39
40  return border.PassAs<views::Border>();
41}
42
43}  // namespace
44
45NewAvatarButton::NewAvatarButton(views::ButtonListener* listener,
46                                 AvatarButtonStyle button_style,
47                                 Browser* browser)
48    : LabelButton(listener, base::string16()),
49      browser_(browser),
50      has_auth_error_(false),
51      suppress_mouse_released_action_(false) {
52  set_animate_on_state_change(false);
53  SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE);
54  SetTextColor(views::Button::STATE_HOVERED, SK_ColorWHITE);
55  SetTextColor(views::Button::STATE_PRESSED, SK_ColorWHITE);
56  SetTextShadows(gfx::ShadowValues(10,
57      gfx::ShadowValue(gfx::Point(), 1.0f, SK_ColorDKGRAY)));
58  SetTextSubpixelRenderingEnabled(false);
59  SetHorizontalAlignment(gfx::ALIGN_CENTER);
60
61  // The largest text height that fits in the button. If the font list height
62  // is larger than this, it will be shrunk to match it.
63  // TODO(noms): Calculate this constant algorithmically.
64  const int kDisplayFontHeight = 15;
65  SetFontList(GetFontList().DeriveWithHeightUpperBound(kDisplayFontHeight));
66
67  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
68  if (button_style == THEMED_BUTTON) {
69    const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_NORMAL);
70    const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_HOVER);
71    const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_PRESSED);
72
73    SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
74    generic_avatar_ =
75        *rb->GetImageNamed(IDR_AVATAR_THEMED_BUTTON_AVATAR).ToImageSkia();
76#if defined(OS_WIN)
77  } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
78    const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_NORMAL);
79    const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_HOVER);
80    const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_PRESSED);
81
82    SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
83    generic_avatar_ =
84        *rb->GetImageNamed(IDR_AVATAR_METRO_BUTTON_AVATAR).ToImageSkia();
85#endif
86  } else {
87    const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_NORMAL);
88    const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_HOVER);
89    const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_PRESSED);
90
91    SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
92    generic_avatar_ =
93        *rb->GetImageNamed(IDR_AVATAR_GLASS_BUTTON_AVATAR).ToImageSkia();
94  }
95
96  g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this);
97
98  // Subscribe to authentication error changes so that the avatar button can
99  // update itself.  Note that guest mode profiles won't have a token service.
100  SigninErrorController* error =
101      profiles::GetSigninErrorController(browser_->profile());
102  if (error) {
103    error->AddObserver(this);
104    // This calls UpdateAvatarButtonAndRelayoutParent().
105    OnErrorChanged();
106  } else {
107    UpdateAvatarButtonAndRelayoutParent();
108  }
109  SchedulePaint();
110}
111
112NewAvatarButton::~NewAvatarButton() {
113  g_browser_process->profile_manager()->
114      GetProfileInfoCache().RemoveObserver(this);
115  SigninErrorController* error =
116      profiles::GetSigninErrorController(browser_->profile());
117  if (error)
118    error->RemoveObserver(this);
119}
120
121bool NewAvatarButton::OnMousePressed(const ui::MouseEvent& event) {
122  // Prevent the bubble from being re-shown if it's already showing.
123  suppress_mouse_released_action_ = ProfileChooserView::IsShowing();
124  return LabelButton::OnMousePressed(event);
125}
126
127void NewAvatarButton::OnMouseReleased(const ui::MouseEvent& event) {
128  if (suppress_mouse_released_action_)
129    suppress_mouse_released_action_ = false;
130  else
131    LabelButton::OnMouseReleased(event);
132}
133
134void NewAvatarButton::OnProfileAdded(const base::FilePath& profile_path) {
135  UpdateAvatarButtonAndRelayoutParent();
136}
137
138void NewAvatarButton::OnProfileWasRemoved(
139      const base::FilePath& profile_path,
140      const base::string16& profile_name) {
141  UpdateAvatarButtonAndRelayoutParent();
142}
143
144void NewAvatarButton::OnProfileNameChanged(
145      const base::FilePath& profile_path,
146      const base::string16& old_profile_name) {
147  UpdateAvatarButtonAndRelayoutParent();
148}
149
150void NewAvatarButton::OnProfileAvatarChanged(
151      const base::FilePath& profile_path) {
152  UpdateAvatarButtonAndRelayoutParent();
153}
154
155void NewAvatarButton::OnProfileSupervisedUserIdChanged(
156      const base::FilePath& profile_path) {
157  UpdateAvatarButtonAndRelayoutParent();
158}
159
160void NewAvatarButton::OnErrorChanged() {
161  // If there is an error, show an warning icon.
162  const SigninErrorController* error =
163      profiles::GetSigninErrorController(browser_->profile());
164  has_auth_error_ = error && error->HasError();
165
166  UpdateAvatarButtonAndRelayoutParent();
167}
168
169void NewAvatarButton::UpdateAvatarButtonAndRelayoutParent() {
170  const ProfileInfoCache& cache =
171      g_browser_process->profile_manager()->GetProfileInfoCache();
172
173  // If we have a single local profile, then use the generic avatar
174  // button instead of the profile name. Never use the generic button if
175  // the active profile is Guest.
176  bool use_generic_button = (!browser_->profile()->IsGuestSession() &&
177                             cache.GetNumberOfProfiles() == 1 &&
178                             cache.GetUserNameOfProfileAtIndex(0).empty());
179
180  SetText(use_generic_button ? base::string16() :
181      profiles::GetAvatarButtonTextForProfile(browser_->profile()));
182  // We want the button to resize if the new text is shorter.
183  SetMinSize(gfx::Size());
184
185  if (use_generic_button) {
186    SetImage(views::Button::STATE_NORMAL, generic_avatar_);
187  } else if (has_auth_error_) {
188    SetImage(views::Button::STATE_NORMAL,
189             *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
190                  IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR).ToImageSkia());
191  } else {
192    SetImage(views::Button::STATE_NORMAL, gfx::ImageSkia());
193  }
194
195  // If we are not using the generic button, then reset the spacing between
196  // the text and the possible authentication error icon.
197  const int kDefaultImageTextSpacing = 5;
198  SetImageLabelSpacing(use_generic_button ? 0 : kDefaultImageTextSpacing);
199
200  InvalidateLayout();
201
202  // Because the width of the button might have changed, the parent browser
203  // frame needs to recalculate the button bounds and redraw it.
204  if (parent())
205    parent()->Layout();
206}
207