autofill_popup_base_view.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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/autofill/autofill_popup_base_view.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/message_loop/message_loop.h"
10#include "chrome/browser/ui/autofill/popup_constants.h"
11#include "ui/views/border.h"
12#include "ui/views/widget/widget.h"
13#include "ui/wm/core/window_animations.h"
14
15namespace autofill {
16
17const SkColor AutofillPopupBaseView::kBorderColor =
18    SkColorSetARGB(0xFF, 0xC7, 0xCA, 0xCE);
19const SkColor AutofillPopupBaseView::kHoveredBackgroundColor =
20    SkColorSetARGB(0xFF, 0xCD, 0xCD, 0xCD);
21const SkColor AutofillPopupBaseView::kItemTextColor =
22    SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
23const SkColor AutofillPopupBaseView::kPopupBackground =
24    SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
25const SkColor AutofillPopupBaseView::kValueTextColor =
26    SkColorSetARGB(0xFF, 0x00, 0x00, 0x00);
27const SkColor AutofillPopupBaseView::kWarningTextColor =
28    SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
29
30AutofillPopupBaseView::AutofillPopupBaseView(
31    AutofillPopupViewDelegate* delegate,
32    views::Widget* observing_widget)
33    : delegate_(delegate),
34      observing_widget_(observing_widget),
35      weak_ptr_factory_(this) {}
36
37AutofillPopupBaseView::~AutofillPopupBaseView() {
38  if (delegate_) {
39    delegate_->ViewDestroyed();
40
41    RemoveObserver();
42  }
43}
44
45void AutofillPopupBaseView::DoShow() {
46  const bool initialize_widget = !GetWidget();
47  if (initialize_widget) {
48    observing_widget_->AddObserver(this);
49
50    views::FocusManager* focus_manager = observing_widget_->GetFocusManager();
51    focus_manager->RegisterAccelerator(
52        ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE),
53        ui::AcceleratorManager::kNormalPriority,
54        this);
55    focus_manager->RegisterAccelerator(
56        ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE),
57        ui::AcceleratorManager::kNormalPriority,
58        this);
59
60    // The widget is destroyed by the corresponding NativeWidget, so we use
61    // a weak pointer to hold the reference and don't have to worry about
62    // deletion.
63    views::Widget* widget = new views::Widget;
64    views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
65    params.delegate = this;
66    params.parent = container_view();
67    widget->Init(params);
68    widget->SetContentsView(this);
69
70    // No animation for popup appearance (too distracting).
71    wm::SetWindowVisibilityAnimationTransition(
72        widget->GetNativeView(), wm::ANIMATE_HIDE);
73  }
74
75  SetBorder(views::Border::CreateSolidBorder(kPopupBorderThickness,
76                                             kBorderColor));
77
78  DoUpdateBoundsAndRedrawPopup();
79  GetWidget()->Show();
80
81  // Showing the widget can change native focus (which would result in an
82  // immediate hiding of the popup). Only start observing after shown.
83  if (initialize_widget)
84    views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
85}
86
87void AutofillPopupBaseView::DoHide() {
88  // The controller is no longer valid after it hides us.
89  delegate_ = NULL;
90
91  RemoveObserver();
92
93  if (GetWidget()) {
94    // Don't call CloseNow() because some of the functions higher up the stack
95    // assume the the widget is still valid after this point.
96    // http://crbug.com/229224
97    // NOTE: This deletes |this|.
98    GetWidget()->Close();
99  } else {
100    delete this;
101  }
102}
103
104void AutofillPopupBaseView::RemoveObserver() {
105  observing_widget_->GetFocusManager()->UnregisterAccelerators(this);
106  observing_widget_->RemoveObserver(this);
107  views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
108}
109
110void AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() {
111  GetWidget()->SetBounds(delegate_->popup_bounds());
112  SchedulePaint();
113}
114
115void AutofillPopupBaseView::OnNativeFocusChange(
116    gfx::NativeView focused_before,
117    gfx::NativeView focused_now) {
118  if (GetWidget() && GetWidget()->GetNativeView() != focused_now)
119    HideController();
120}
121
122void AutofillPopupBaseView::OnWidgetBoundsChanged(views::Widget* widget,
123                                                  const gfx::Rect& new_bounds) {
124  DCHECK_EQ(widget, observing_widget_);
125  HideController();
126}
127
128void AutofillPopupBaseView::OnMouseCaptureLost() {
129  ClearSelection();
130}
131
132bool AutofillPopupBaseView::OnMouseDragged(const ui::MouseEvent& event) {
133  if (HitTestPoint(event.location())) {
134    SetSelection(event.location());
135
136    // We must return true in order to get future OnMouseDragged and
137    // OnMouseReleased events.
138    return true;
139  }
140
141  // If we move off of the popup, we lose the selection.
142  ClearSelection();
143  return false;
144}
145
146void AutofillPopupBaseView::OnMouseExited(const ui::MouseEvent& event) {
147  // Pressing return causes the cursor to hide, which will generate an
148  // OnMouseExited event. Pressing return should activate the current selection
149  // via AcceleratorPressed, so we need to let that run first.
150  base::MessageLoop::current()->PostTask(
151      FROM_HERE,
152      base::Bind(&AutofillPopupBaseView::ClearSelection,
153                 weak_ptr_factory_.GetWeakPtr()));
154}
155
156void AutofillPopupBaseView::OnMouseMoved(const ui::MouseEvent& event) {
157  if (HitTestPoint(event.location()))
158    SetSelection(event.location());
159  else
160    ClearSelection();
161}
162
163bool AutofillPopupBaseView::OnMousePressed(const ui::MouseEvent& event) {
164  return event.GetClickCount() == 1;
165}
166
167void AutofillPopupBaseView::OnMouseReleased(const ui::MouseEvent& event) {
168  // We only care about the left click.
169  if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
170    AcceptSelection(event.location());
171}
172
173void AutofillPopupBaseView::OnGestureEvent(ui::GestureEvent* event) {
174  switch (event->type()) {
175    case ui::ET_GESTURE_TAP_DOWN:
176    case ui::ET_GESTURE_SCROLL_BEGIN:
177    case ui::ET_GESTURE_SCROLL_UPDATE:
178      if (HitTestPoint(event->location()))
179        SetSelection(event->location());
180      else
181        ClearSelection();
182      break;
183    case ui::ET_GESTURE_TAP:
184    case ui::ET_GESTURE_SCROLL_END:
185      if (HitTestPoint(event->location()))
186        AcceptSelection(event->location());
187      else
188        ClearSelection();
189      break;
190    case ui::ET_GESTURE_TAP_CANCEL:
191    case ui::ET_SCROLL_FLING_START:
192      ClearSelection();
193      break;
194    default:
195      return;
196  }
197  event->SetHandled();
198}
199
200bool AutofillPopupBaseView::AcceleratorPressed(
201    const ui::Accelerator& accelerator) {
202  DCHECK_EQ(accelerator.modifiers(), ui::EF_NONE);
203
204  if (accelerator.key_code() == ui::VKEY_ESCAPE) {
205    HideController();
206    return true;
207  }
208
209  if (accelerator.key_code() == ui::VKEY_RETURN)
210    return delegate_->AcceptSelectedLine();
211
212  NOTREACHED();
213  return false;
214}
215
216void AutofillPopupBaseView::SetSelection(const gfx::Point& point) {
217  if (delegate_)
218    delegate_->SetSelectionAtPoint(point);
219}
220
221void AutofillPopupBaseView::AcceptSelection(const gfx::Point& point) {
222  if (!delegate_)
223    return;
224
225  delegate_->SetSelectionAtPoint(point);
226  delegate_->AcceptSelectedLine();
227}
228
229void AutofillPopupBaseView::ClearSelection() {
230  if (delegate_)
231    delegate_->SelectionCleared();
232}
233
234void AutofillPopupBaseView::HideController() {
235  if (delegate_)
236    delegate_->Hide();
237}
238
239gfx::NativeView AutofillPopupBaseView::container_view() {
240  return delegate_->container_view();
241}
242
243}  // namespace autofill
244