autofill_popup_view_views.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/autofill/autofill_popup_view_views.h" 6 7#include "chrome/browser/ui/autofill/autofill_popup_controller.h" 8#include "grit/ui_resources.h" 9#include "third_party/WebKit/public/web/WebAutofillClient.h" 10#include "ui/base/resource/resource_bundle.h" 11#include "ui/events/keycodes/keyboard_codes.h" 12#include "ui/gfx/canvas.h" 13#include "ui/gfx/image/image.h" 14#include "ui/gfx/native_widget_types.h" 15#include "ui/gfx/point.h" 16#include "ui/gfx/rect.h" 17#include "ui/gfx/screen.h" 18#include "ui/views/border.h" 19#include "ui/views/event_utils.h" 20#include "ui/views/widget/widget.h" 21 22#if defined(USE_AURA) 23#include "ui/views/corewm/window_animations.h" 24#endif 25 26using blink::WebAutofillClient; 27 28namespace { 29 30const SkColor kBorderColor = SkColorSetARGB(0xFF, 0xC7, 0xCA, 0xCE); 31const SkColor kHoveredBackgroundColor = SkColorSetARGB(0xFF, 0xCD, 0xCD, 0xCD); 32const SkColor kItemTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F); 33const SkColor kPopupBackground = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF); 34const SkColor kValueTextColor = SkColorSetARGB(0xFF, 0x00, 0x00, 0x00); 35const SkColor kWarningTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F); 36 37} // namespace 38 39namespace autofill { 40 41AutofillPopupViewViews::AutofillPopupViewViews( 42 AutofillPopupController* controller, views::Widget* observing_widget) 43 : controller_(controller), 44 observing_widget_(observing_widget) {} 45 46AutofillPopupViewViews::~AutofillPopupViewViews() { 47 if (controller_) { 48 controller_->ViewDestroyed(); 49 50 HideInternal(); 51 } 52} 53 54void AutofillPopupViewViews::Hide() { 55 // The controller is no longer valid after it hides us. 56 controller_ = NULL; 57 58 HideInternal(); 59 60 if (GetWidget()) { 61 // Don't call CloseNow() because some of the functions higher up the stack 62 // assume the the widget is still valid after this point. 63 // http://crbug.com/229224 64 // NOTE: This deletes |this|. 65 GetWidget()->Close(); 66 } else { 67 delete this; 68 } 69} 70 71void AutofillPopupViewViews::OnPaint(gfx::Canvas* canvas) { 72 if (!controller_) 73 return; 74 75 canvas->DrawColor(kPopupBackground); 76 OnPaintBorder(canvas); 77 78 for (size_t i = 0; i < controller_->names().size(); ++i) { 79 gfx::Rect line_rect = controller_->GetRowBounds(i); 80 81 if (controller_->identifiers()[i] == 82 WebAutofillClient::MenuItemIDSeparator) { 83 canvas->DrawRect(line_rect, kItemTextColor); 84 } else { 85 DrawAutofillEntry(canvas, i, line_rect); 86 } 87 } 88} 89 90void AutofillPopupViewViews::OnMouseCaptureLost() { 91 if (controller_) 92 controller_->SelectionCleared(); 93} 94 95bool AutofillPopupViewViews::OnMouseDragged(const ui::MouseEvent& event) { 96 if (!controller_) 97 return false; 98 99 if (HitTestPoint(event.location())) { 100 controller_->LineSelectedAtPoint(event.x(), event.y()); 101 102 // We must return true in order to get future OnMouseDragged and 103 // OnMouseReleased events. 104 return true; 105 } 106 107 // If we move off of the popup, we lose the selection. 108 controller_->SelectionCleared(); 109 return false; 110} 111 112void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent& event) { 113 if (controller_) 114 controller_->SelectionCleared(); 115} 116 117void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) { 118 if (!controller_) 119 return; 120 121 if (HitTestPoint(event.location())) 122 controller_->LineSelectedAtPoint(event.x(), event.y()); 123 else 124 controller_->SelectionCleared(); 125} 126 127bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent& event) { 128 if (HitTestPoint(event.location())) 129 return true; 130 131 if (controller_->hide_on_outside_click()) { 132 GetWidget()->ReleaseCapture(); 133 134 gfx::Point screen_loc = event.location(); 135 views::View::ConvertPointToScreen(this, &screen_loc); 136 137 ui::MouseEvent mouse_event = event; 138 mouse_event.set_location(screen_loc); 139 140 if (controller_->ShouldRepostEvent(mouse_event)) { 141 gfx::NativeView native_view = GetWidget()->GetNativeView(); 142 gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view); 143 gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc); 144 views::RepostLocatedEvent(window, mouse_event); 145 } 146 147 controller_->Hide(); 148 // |this| is now deleted. 149 } 150 151 return false; 152} 153 154void AutofillPopupViewViews::OnMouseReleased(const ui::MouseEvent& event) { 155 if (!controller_) 156 return; 157 158 // Because this view can can be shown in response to a mouse press, it can 159 // receive an OnMouseReleased event just after showing. This breaks the mouse 160 // capture, so restart capturing here. 161 if (controller_->hide_on_outside_click() && GetWidget()) 162 GetWidget()->SetCapture(this); 163 164 // We only care about the left click. 165 if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location())) 166 controller_->LineAcceptedAtPoint(event.x(), event.y()); 167} 168 169void AutofillPopupViewViews::OnGestureEvent(ui::GestureEvent* event) { 170 if (!controller_) 171 return; 172 173 switch (event->type()) { 174 case ui::ET_GESTURE_TAP_DOWN: 175 case ui::ET_GESTURE_SCROLL_BEGIN: 176 case ui::ET_GESTURE_SCROLL_UPDATE: 177 if (HitTestPoint(event->location())) 178 controller_->LineSelectedAtPoint(event->x(), event->y()); 179 else 180 controller_->SelectionCleared(); 181 break; 182 case ui::ET_GESTURE_TAP: 183 case ui::ET_GESTURE_SCROLL_END: 184 if (HitTestPoint(event->location())) 185 controller_->LineAcceptedAtPoint(event->x(), event->y()); 186 else 187 controller_->SelectionCleared(); 188 break; 189 case ui::ET_GESTURE_TAP_CANCEL: 190 case ui::ET_SCROLL_FLING_START: 191 controller_->SelectionCleared(); 192 break; 193 default: 194 return; 195 } 196 event->SetHandled(); 197} 198 199void AutofillPopupViewViews::OnWidgetBoundsChanged( 200 views::Widget* widget, 201 const gfx::Rect& new_bounds) { 202 DCHECK_EQ(widget, observing_widget_); 203 controller_->Hide(); 204} 205 206void AutofillPopupViewViews::Show() { 207 if (!GetWidget()) { 208 observing_widget_->AddObserver(this); 209 210 // The widget is destroyed by the corresponding NativeWidget, so we use 211 // a weak pointer to hold the reference and don't have to worry about 212 // deletion. 213 views::Widget* widget = new views::Widget; 214 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 215 params.delegate = this; 216 params.parent = controller_->container_view(); 217 widget->Init(params); 218 widget->SetContentsView(this); 219#if defined(USE_AURA) 220 // No animation for popup appearance (too distracting). 221 views::corewm::SetWindowVisibilityAnimationTransition( 222 widget->GetNativeView(), views::corewm::ANIMATE_HIDE); 223#endif 224 } 225 226 set_border(views::Border::CreateSolidBorder(kBorderThickness, kBorderColor)); 227 228 UpdateBoundsAndRedrawPopup(); 229 GetWidget()->Show(); 230 231 if (controller_->hide_on_outside_click()) 232 GetWidget()->SetCapture(this); 233} 234 235void AutofillPopupViewViews::InvalidateRow(size_t row) { 236 SchedulePaintInRect(controller_->GetRowBounds(row)); 237} 238 239void AutofillPopupViewViews::UpdateBoundsAndRedrawPopup() { 240 GetWidget()->SetBounds(controller_->popup_bounds()); 241 SchedulePaint(); 242} 243 244void AutofillPopupViewViews::HideInternal() { 245 observing_widget_->RemoveObserver(this); 246} 247 248void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas, 249 int index, 250 const gfx::Rect& entry_rect) { 251 if (controller_->selected_line() == index) 252 canvas->FillRect(entry_rect, kHoveredBackgroundColor); 253 254 bool is_rtl = controller_->IsRTL(); 255 int value_text_width = controller_->GetNameFontForRow(index).GetStringWidth( 256 controller_->names()[index]); 257 int value_content_x = is_rtl ? 258 entry_rect.width() - value_text_width - kEndPadding : kEndPadding; 259 260 canvas->DrawStringInt( 261 controller_->names()[index], 262 controller_->GetNameFontForRow(index), 263 controller_->IsWarning(index) ? kWarningTextColor : kValueTextColor, 264 value_content_x, 265 entry_rect.y(), 266 canvas->GetStringWidth(controller_->names()[index], 267 controller_->GetNameFontForRow(index)), 268 entry_rect.height(), 269 gfx::Canvas::TEXT_ALIGN_CENTER); 270 271 // Use this to figure out where all the other Autofill items should be placed. 272 int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding; 273 274 // Draw the Autofill icon, if one exists 275 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 276 int row_height = controller_->GetRowBounds(index).height(); 277 if (!controller_->icons()[index].empty()) { 278 int icon = controller_->GetIconResourceID(controller_->icons()[index]); 279 DCHECK_NE(-1, icon); 280 const gfx::ImageSkia* image = rb.GetImageSkiaNamed(icon); 281 int icon_y = entry_rect.y() + (row_height - image->height()) / 2; 282 283 x_align_left += is_rtl ? 0 : -image->width(); 284 285 canvas->DrawImageInt(*image, x_align_left, icon_y); 286 287 x_align_left += is_rtl ? image->width() + kIconPadding : -kIconPadding; 288 } 289 290 // Draw the name text. 291 if (!is_rtl) { 292 x_align_left -= canvas->GetStringWidth(controller_->subtexts()[index], 293 controller_->subtext_font()); 294 } 295 296 canvas->DrawStringInt( 297 controller_->subtexts()[index], 298 controller_->subtext_font(), 299 kItemTextColor, 300 x_align_left, 301 entry_rect.y(), 302 canvas->GetStringWidth(controller_->subtexts()[index], 303 controller_->subtext_font()), 304 entry_rect.height(), 305 gfx::Canvas::TEXT_ALIGN_CENTER); 306} 307 308AutofillPopupView* AutofillPopupView::Create( 309 AutofillPopupController* controller) { 310 views::Widget* observing_widget = 311 views::Widget::GetTopLevelWidgetForNativeView( 312 controller->container_view()); 313 314 // If the top level widget can't be found, cancel the popup since we can't 315 // fully set it up. 316 if (!observing_widget) 317 return NULL; 318 319 return new AutofillPopupViewViews(controller, observing_widget); 320} 321 322} // namespace autofill 323