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