new_avatar_button.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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/strings/utf_string_conversions.h"
8#include "base/win/windows_version.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/profiles/profile_manager.h"
11#include "chrome/browser/profiles/profiles_state.h"
12#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
13#include "chrome/browser/ui/browser.h"
14#include "components/signin/core/browser/profile_oauth2_token_service.h"
15#include "grit/generated_resources.h"
16#include "grit/theme_resources.h"
17#include "ui/base/l10n/l10n_util.h"
18#include "ui/base/resource/resource_bundle.h"
19#include "ui/gfx/canvas.h"
20#include "ui/gfx/color_utils.h"
21#include "ui/gfx/font_list.h"
22#include "ui/gfx/text_constants.h"
23#include "ui/gfx/text_elider.h"
24#include "ui/views/border.h"
25#include "ui/views/controls/button/label_button_border.h"
26#include "ui/views/painter.h"
27
28namespace {
29
30scoped_ptr<views::Border> CreateBorder(const int normal_image_set[],
31                                       const int hot_image_set[],
32                                       const int pushed_image_set[]) {
33  scoped_ptr<views::LabelButtonBorder> border(
34      new views::LabelButtonBorder(views::Button::STYLE_TEXTBUTTON));
35  border->SetPainter(false, views::Button::STATE_NORMAL,
36      views::Painter::CreateImageGridPainter(normal_image_set));
37  border->SetPainter(false, views::Button::STATE_HOVERED,
38      views::Painter::CreateImageGridPainter(hot_image_set));
39  border->SetPainter(false, views::Button::STATE_PRESSED,
40      views::Painter::CreateImageGridPainter(pushed_image_set));
41
42  const int kLeftRightInset = 10;
43  const int kTopInset = 0;
44  const int kBottomInset = 4;
45  border->set_insets(gfx::Insets(kTopInset, kLeftRightInset,
46                                 kBottomInset, kLeftRightInset));
47
48  return border.PassAs<views::Border>();
49}
50
51base::string16 GetElidedText(const base::string16& original_text) {
52  // Maximum characters the button can be before the text will get elided.
53  const int kMaxCharactersToDisplay = 15;
54  const gfx::FontList font_list;
55  return gfx::ElideText(original_text, font_list,
56                        font_list.GetExpectedTextWidth(kMaxCharactersToDisplay),
57                        gfx::ELIDE_TAIL);
58}
59
60base::string16 GetButtonText(Profile* profile) {
61  base::string16 name = GetElidedText(
62      profiles::GetAvatarNameForProfile(profile));
63  if (profile->IsSupervised())
64    name = l10n_util::GetStringFUTF16(IDS_MANAGED_USER_NEW_AVATAR_LABEL, name);
65  return name;
66}
67
68}  // namespace
69
70NewAvatarButton::NewAvatarButton(
71    views::ButtonListener* listener,
72    const base::string16& profile_name,
73    AvatarButtonStyle button_style,
74    Browser* browser)
75    : MenuButton(listener, GetButtonText(browser->profile()), NULL, true),
76      browser_(browser) {
77  set_animate_on_state_change(false);
78  SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE);
79  SetTextColor(views::Button::STATE_HOVERED, SK_ColorWHITE);
80  SetTextColor(views::Button::STATE_PRESSED, SK_ColorWHITE);
81  SetTextShadows(gfx::ShadowValues(10,
82      gfx::ShadowValue(gfx::Point(), 1.0f, SK_ColorDKGRAY)));
83  SetTextSubpixelRenderingEnabled(false);
84
85  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
86  if (button_style == THEMED_BUTTON) {
87    const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_NORMAL);
88    const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_HOVER);
89    const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_PRESSED);
90
91    SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
92    set_menu_marker(
93        rb->GetImageNamed(IDR_AVATAR_THEMED_BUTTON_DROPARROW).ToImageSkia());
94#if defined(OS_WIN)
95  } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
96    const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_NORMAL);
97    const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_HOVER);
98    const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_PRESSED);
99
100    SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
101    set_menu_marker(
102        rb->GetImageNamed(IDR_AVATAR_METRO_BUTTON_DROPARROW).ToImageSkia());
103#endif
104  } else {
105    const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_NORMAL);
106    const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_HOVER);
107    const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_PRESSED);
108
109    SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
110    set_menu_marker(
111        rb->GetImageNamed(IDR_AVATAR_GLASS_BUTTON_DROPARROW).ToImageSkia());
112  }
113
114  g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this);
115
116  // Subscribe to authentication error changes so that the avatar button can
117  // update itself.  Note that guest mode profiles won't have a token service.
118  SigninErrorController* error =
119      profiles::GetSigninErrorController(browser_->profile());
120  if (error) {
121    error->AddObserver(this);
122    OnErrorChanged();
123  }
124
125  SchedulePaint();
126}
127
128NewAvatarButton::~NewAvatarButton() {
129  g_browser_process->profile_manager()->
130      GetProfileInfoCache().RemoveObserver(this);
131  SigninErrorController* error =
132      profiles::GetSigninErrorController(browser_->profile());
133  if (error)
134    error->RemoveObserver(this);
135}
136
137void NewAvatarButton::OnProfileAdded(const base::FilePath& profile_path) {
138  UpdateAvatarButtonAndRelayoutParent();
139}
140
141void NewAvatarButton::OnProfileWasRemoved(
142      const base::FilePath& profile_path,
143      const base::string16& profile_name) {
144  UpdateAvatarButtonAndRelayoutParent();
145}
146
147void NewAvatarButton::OnProfileNameChanged(
148      const base::FilePath& profile_path,
149      const base::string16& old_profile_name) {
150  UpdateAvatarButtonAndRelayoutParent();
151}
152
153void NewAvatarButton::OnProfileSupervisedUserIdChanged(
154      const base::FilePath& profile_path) {
155  UpdateAvatarButtonAndRelayoutParent();
156}
157
158void NewAvatarButton::OnErrorChanged() {
159  gfx::ImageSkia icon;
160
161  // If there is an error, show an warning icon.
162  const SigninErrorController* error =
163      profiles::GetSigninErrorController(browser_->profile());
164  if (error && error->HasError()) {
165    icon = *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
166        IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR).ToImageSkia();
167  }
168
169  SetImage(views::Button::STATE_NORMAL, icon);
170  UpdateAvatarButtonAndRelayoutParent();
171}
172
173void NewAvatarButton::UpdateAvatarButtonAndRelayoutParent() {
174  // We want the button to resize if the new text is shorter.
175  SetText(GetButtonText(browser_->profile()));
176  set_min_size(gfx::Size());
177  InvalidateLayout();
178
179  // Because the width of the button might have changed, the parent browser
180  // frame needs to recalculate the button bounds and redraw it.
181  if (parent())
182    parent()->Layout();
183}
184