one_click_signin_bubble_view.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
6
7#include "base/callback_helpers.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "chrome/browser/google/google_util.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/common/url_constants.h"
13#include "content/public/browser/web_contents.h"
14#include "grit/chromium_strings.h"
15#include "grit/generated_resources.h"
16#include "grit/theme_resources.h"
17#include "grit/ui_resources.h"
18#include "ui/base/keycodes/keyboard_codes.h"
19#include "ui/base/l10n/l10n_util.h"
20#include "ui/base/resource/resource_bundle.h"
21#include "ui/views/controls/button/image_button.h"
22#include "ui/views/controls/button/text_button.h"
23#include "ui/views/controls/image_view.h"
24#include "ui/views/controls/label.h"
25#include "ui/views/controls/link.h"
26#include "ui/views/layout/grid_layout.h"
27#include "ui/views/layout/layout_constants.h"
28#include "ui/views/widget/widget.h"
29
30// Minimum width for the mutli-line label.
31const int kMinimumDialogLabelWidth = 400;
32const int kMinimumLabelWidth = 240;
33const int kDialogMargin = 16;
34
35namespace {
36
37// The column set constants that can be used in the InitContent() function
38// to layout views.
39enum OneClickSigninBubbleColumnTypes {
40  COLUMN_SET_FILL_ALIGN,
41  COLUMN_SET_CONTROLS,
42  COLUMN_SET_TITLE_BAR
43};
44
45class OneClickSigninDialogView : public OneClickSigninBubbleView {
46 public:
47  OneClickSigninDialogView(
48      content::WebContents* web_content,
49      views::View* anchor_view,
50      const BrowserWindow::StartSyncCallback& start_sync_callback);
51
52 private:
53  // Overridden from views::WidgetDelegate:
54  virtual ui::ModalType GetModalType() const OVERRIDE;
55
56  // Overridden from OneClickSigninBubbleView:
57  virtual void InitContent(views::GridLayout* layout) OVERRIDE;
58  virtual void GetButtons(views::TextButton** ok_button,
59                          views::TextButton** undo_button) OVERRIDE;
60  virtual views::Link* GetAdvancedLink() OVERRIDE;
61
62  // Overridden from views::LinkListener:
63  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
64
65  content::WebContents* web_content_;
66  views::Link* learn_more_link_;
67  views::ImageButton* close_button_;
68
69  DISALLOW_COPY_AND_ASSIGN(OneClickSigninDialogView);
70};
71
72OneClickSigninDialogView::OneClickSigninDialogView(
73    content::WebContents* web_content,
74    views::View* anchor_view,
75    const BrowserWindow::StartSyncCallback& start_sync_callback)
76    : OneClickSigninBubbleView(anchor_view, start_sync_callback),
77      web_content_(web_content),
78      learn_more_link_(NULL),
79      close_button_(NULL) {
80  set_arrow_location(views::BubbleBorder::NONE);
81  set_anchor_insets(gfx::Insets(0, 0, anchor_view->height() / 2, 0));
82  set_close_on_deactivate(false);
83  set_margins(gfx::Insets(kDialogMargin, kDialogMargin, kDialogMargin,
84                          kDialogMargin));
85}
86
87ui::ModalType OneClickSigninDialogView::GetModalType() const {
88  return ui::MODAL_TYPE_CHILD;
89}
90
91void OneClickSigninDialogView::InitContent(views::GridLayout* layout) {
92  // Column set for title bar.
93  views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_TITLE_BAR);
94  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
95                views::GridLayout::USE_PREF, 0, 0);
96  cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
97  cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
98                views::GridLayout::USE_PREF, 0, 0);
99
100  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
101
102  {
103    layout->StartRow(0, COLUMN_SET_TITLE_BAR);
104
105    views::Label* label = new views::Label(
106        l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE));
107    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
108    label->SetFont(label->font().DeriveFont(3, gfx::Font::BOLD));
109    layout->AddView(label);
110
111    close_button_ = new views::ImageButton(this);
112    close_button_->SetImage(views::ImageButton::STATE_NORMAL,
113                            rb.GetImageNamed(IDR_CLOSE_BAR).ToImageSkia());
114    close_button_->SetImage(views::ImageButton::STATE_HOVERED,
115                            rb.GetImageNamed(IDR_CLOSE_BAR_H).ToImageSkia());
116    close_button_->SetImage(views::ImageButton::STATE_PRESSED,
117                            rb.GetImageNamed(IDR_CLOSE_BAR_P).ToImageSkia());
118    layout->AddView(close_button_);
119  }
120
121  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
122
123  {
124    layout->StartRow(0, COLUMN_SET_FILL_ALIGN);
125
126    views::Label* label = new views::Label(
127        l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE));
128    label->SetMultiLine(true);
129    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
130    label->SizeToFit(kMinimumDialogLabelWidth);
131    layout->AddView(label);
132
133    layout->StartRow(0, COLUMN_SET_FILL_ALIGN);
134
135    learn_more_link_ = new views::Link(
136        l10n_util::GetStringUTF16(IDS_LEARN_MORE));
137    learn_more_link_->set_listener(this);
138    learn_more_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
139    layout->AddView(learn_more_link_, 1, 1, views::GridLayout::TRAILING,
140                    views::GridLayout::CENTER);
141  }
142
143  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
144}
145
146void OneClickSigninDialogView::GetButtons(views::TextButton** ok_button,
147                                          views::TextButton** undo_button) {
148  *ok_button = new views::NativeTextButton(this);
149  *undo_button = new views::NativeTextButton(this);
150
151  // The default size of the buttons is too large.  To allow them to be smaller
152  // ignore the minimum default size.  Furthermore, to make sure they are the
153  // same size, SetText() is called with both strings on both buttons.
154  (*ok_button)->set_ignore_minimum_size(true);
155  (*undo_button)->set_ignore_minimum_size(true);
156  string16 ok_label =
157      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON);
158  string16 undo_label =
159      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON);
160  (*ok_button)->SetText(undo_label);
161  (*ok_button)->SetText(ok_label);
162  (*undo_button)->SetText(ok_label);
163  (*undo_button)->SetText(undo_label);
164}
165
166views::Link* OneClickSigninDialogView::GetAdvancedLink() {
167  views::Link* advanced_link= new views::Link(
168      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_ADVANCED));
169  advanced_link->set_listener(this);
170  advanced_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
171  return advanced_link;
172}
173
174void OneClickSigninDialogView::LinkClicked(views::Link* source,
175                                           int event_flags) {
176  if (source == learn_more_link_) {
177    content::OpenURLParams params(
178        GURL(chrome::kChromeSyncLearnMoreURL), content::Referrer(),
179        NEW_WINDOW, content::PAGE_TRANSITION_LINK, false);
180    web_content_->OpenURL(params);
181    return;
182  }
183
184  OneClickSigninBubbleView::LinkClicked(source, event_flags);
185}
186
187}  // namespace
188
189// OneClickSigninBubbleView ----------------------------------------------------
190
191// static
192OneClickSigninBubbleView* OneClickSigninBubbleView::bubble_view_ = NULL;
193
194// static
195void OneClickSigninBubbleView::ShowBubble(
196    BrowserWindow::OneClickSigninBubbleType type,
197    ToolbarView* toolbar_view,
198    const BrowserWindow::StartSyncCallback& start_sync) {
199  if (IsShowing())
200    return;
201
202  bubble_view_ = type == BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE ?
203      new OneClickSigninBubbleView(toolbar_view->app_menu(), start_sync) :
204      new OneClickSigninDialogView(toolbar_view->GetWebContents(),
205                                   toolbar_view->location_bar(), start_sync);
206
207  views::BubbleDelegateView::CreateBubble(bubble_view_)->Show();
208}
209
210// static
211bool OneClickSigninBubbleView::IsShowing() {
212  return bubble_view_ != NULL;
213}
214
215// static
216void OneClickSigninBubbleView::Hide() {
217  if (IsShowing())
218    bubble_view_->GetWidget()->Close();
219}
220
221OneClickSigninBubbleView::OneClickSigninBubbleView(
222    views::View* anchor_view,
223    const BrowserWindow::StartSyncCallback& start_sync_callback)
224    : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
225      advanced_link_(NULL),
226      ok_button_(NULL),
227      undo_button_(NULL),
228      start_sync_callback_(start_sync_callback),
229      message_loop_for_testing_(NULL) {
230  DCHECK(!start_sync_callback_.is_null());
231}
232
233OneClickSigninBubbleView::~OneClickSigninBubbleView() {
234}
235
236void OneClickSigninBubbleView::AnimationEnded(const ui::Animation* animation) {
237  views::BubbleDelegateView::AnimationEnded(animation);
238  if (message_loop_for_testing_)
239    message_loop_for_testing_->Quit();
240}
241
242void OneClickSigninBubbleView::Init() {
243  views::GridLayout* layout = new views::GridLayout(this);
244  SetLayoutManager(layout);
245  set_border(views::Border::CreateEmptyBorder(8, 8, 8, 8));
246
247  // Column set for descriptive text and link.
248  views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_FILL_ALIGN);
249  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1,
250                views::GridLayout::USE_PREF, 0, 0);
251
252  // Column set for buttons at bottom of bubble.
253  cs = layout->AddColumnSet(COLUMN_SET_CONTROLS);
254  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
255                views::GridLayout::USE_PREF, 0, 0);
256  cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
257  cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
258                views::GridLayout::USE_PREF, 0, 0);
259  cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
260  cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
261                views::GridLayout::USE_PREF, 0, 0);
262
263  InitContent(layout);
264
265  // Add controls at the bottom.
266  advanced_link_= GetAdvancedLink();
267  GetButtons(&ok_button_, &undo_button_);
268  ok_button_->SetIsDefault(true);
269
270  layout->StartRow(0, COLUMN_SET_CONTROLS);
271  layout->AddView(advanced_link_);
272  layout->AddView(ok_button_);
273  layout->AddView(undo_button_);
274
275  AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0));
276}
277
278void OneClickSigninBubbleView::InitContent(views::GridLayout* layout) {
279  // Add main text description.
280  views::Label* label = new views::Label(
281      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_BUBBLE_MESSAGE));
282  label->SetMultiLine(true);
283  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
284  label->SizeToFit(kMinimumLabelWidth);
285
286  layout->StartRow(0, COLUMN_SET_FILL_ALIGN);
287  layout->AddView(label);
288
289  layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
290}
291
292void OneClickSigninBubbleView::GetButtons(views::TextButton** ok_button,
293                                          views::TextButton** undo_button) {
294  *ok_button = new views::NativeTextButton(this);
295  *undo_button = new views::NativeTextButton(this);
296
297  // The default size of the buttons is too large.  To allow them to be smaller
298  // ignore the minimum default size.  Furthermore, to make sure they are the
299  // same size, SetText() is called with both strings on both buttons.
300  (*ok_button)->set_ignore_minimum_size(true);
301  (*undo_button)->set_ignore_minimum_size(true);
302  string16 ok_label = l10n_util::GetStringUTF16(IDS_OK);
303  string16 undo_label = l10n_util::GetStringUTF16(IDS_ONE_CLICK_BUBBLE_UNDO);
304  (*ok_button)->SetText(undo_label);
305  (*ok_button)->SetText(ok_label);
306  (*undo_button)->SetText(ok_label);
307  (*undo_button)->SetText(undo_label);
308}
309
310views::Link* OneClickSigninBubbleView::GetAdvancedLink() {
311  views::Link* advanced_link= new views::Link(
312      l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_ADVANCED));
313  advanced_link->set_listener(this);
314  advanced_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
315  return advanced_link;
316}
317
318void OneClickSigninBubbleView::WindowClosing() {
319  // We have to reset |bubble_view_| here, not in our destructor, because
320  // we'll be destroyed asynchronously and the shown state will be checked
321  // before then.
322  DCHECK_EQ(bubble_view_, this);
323  bubble_view_ = NULL;
324
325  if (!start_sync_callback_.is_null()) {
326    base::ResetAndReturn(&start_sync_callback_).Run(
327        OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
328  }
329}
330
331bool OneClickSigninBubbleView::AcceleratorPressed(
332    const ui::Accelerator& accelerator) {
333  if (accelerator.key_code() == ui::VKEY_RETURN ||
334      accelerator.key_code() == ui::VKEY_ESCAPE) {
335    StartFade(false);
336    if (accelerator.key_code() == ui::VKEY_RETURN) {
337      base::ResetAndReturn(&start_sync_callback_).Run(
338          OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
339    } else {
340      start_sync_callback_.Reset();
341    }
342
343    return true;
344  }
345
346  return BubbleDelegateView::AcceleratorPressed(accelerator);
347}
348
349void OneClickSigninBubbleView::LinkClicked(views::Link* source,
350                                           int event_flags) {
351  StartFade(false);
352  base::ResetAndReturn(&start_sync_callback_).Run(
353      OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
354}
355
356void OneClickSigninBubbleView::ButtonPressed(views::Button* sender,
357                                             const ui::Event& event) {
358  StartFade(false);
359  base::ResetAndReturn(&start_sync_callback_).Run((sender == ok_button_) ?
360      OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS :
361      OneClickSigninSyncStarter::UNDO_SYNC);
362}
363