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