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