autofill_popup_view_views.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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 WebKit::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_->MouseExitedPopup(); 93} 94 95bool AutofillPopupViewViews::OnMouseDragged(const ui::MouseEvent& event) { 96 if (!controller_) 97 return false; 98 99 if (HitTestPoint(event.location())) { 100 controller_->MouseHovered(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_->MouseExitedPopup(); 109 return false; 110} 111 112void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent& event) { 113 if (controller_) 114 controller_->MouseExitedPopup(); 115} 116 117void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) { 118 if (!controller_) 119 return; 120 121 if (HitTestPoint(event.location())) 122 controller_->MouseHovered(event.x(), event.y()); 123 else 124 controller_->MouseExitedPopup(); 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_->MouseClicked(event.x(), event.y()); 167} 168 169void AutofillPopupViewViews::OnWidgetBoundsChanged( 170 views::Widget* widget, 171 const gfx::Rect& new_bounds) { 172 DCHECK_EQ(widget, observing_widget_); 173 controller_->Hide(); 174} 175 176void AutofillPopupViewViews::Show() { 177 if (!GetWidget()) { 178 observing_widget_->AddObserver(this); 179 180 // The widget is destroyed by the corresponding NativeWidget, so we use 181 // a weak pointer to hold the reference and don't have to worry about 182 // deletion. 183 views::Widget* widget = new views::Widget; 184 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 185 params.delegate = this; 186 params.parent = controller_->container_view(); 187 widget->Init(params); 188 widget->SetContentsView(this); 189#if defined(USE_AURA) 190 // No animation for popup appearance (too distracting). 191 views::corewm::SetWindowVisibilityAnimationTransition( 192 widget->GetNativeView(), views::corewm::ANIMATE_HIDE); 193#endif 194 } 195 196 set_border(views::Border::CreateSolidBorder(kBorderThickness, kBorderColor)); 197 198 UpdateBoundsAndRedrawPopup(); 199 GetWidget()->Show(); 200 201 if (controller_->hide_on_outside_click()) 202 GetWidget()->SetCapture(this); 203} 204 205void AutofillPopupViewViews::InvalidateRow(size_t row) { 206 SchedulePaintInRect(controller_->GetRowBounds(row)); 207} 208 209void AutofillPopupViewViews::UpdateBoundsAndRedrawPopup() { 210 GetWidget()->SetBounds(controller_->popup_bounds()); 211 SchedulePaint(); 212} 213 214void AutofillPopupViewViews::HideInternal() { 215 observing_widget_->RemoveObserver(this); 216} 217 218void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas, 219 int index, 220 const gfx::Rect& entry_rect) { 221 if (controller_->selected_line() == index) 222 canvas->FillRect(entry_rect, kHoveredBackgroundColor); 223 224 bool is_rtl = controller_->IsRTL(); 225 int value_text_width = controller_->GetNameFontForRow(index).GetStringWidth( 226 controller_->names()[index]); 227 int value_content_x = is_rtl ? 228 entry_rect.width() - value_text_width - kEndPadding : kEndPadding; 229 230 canvas->DrawStringInt( 231 controller_->names()[index], 232 controller_->GetNameFontForRow(index), 233 controller_->IsWarning(index) ? kWarningTextColor : kValueTextColor, 234 value_content_x, 235 entry_rect.y(), 236 canvas->GetStringWidth(controller_->names()[index], 237 controller_->GetNameFontForRow(index)), 238 entry_rect.height(), 239 gfx::Canvas::TEXT_ALIGN_CENTER); 240 241 // Use this to figure out where all the other Autofill items should be placed. 242 int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding; 243 244 // Draw the Autofill icon, if one exists 245 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 246 int row_height = controller_->GetRowBounds(index).height(); 247 if (!controller_->icons()[index].empty()) { 248 int icon = controller_->GetIconResourceID(controller_->icons()[index]); 249 DCHECK_NE(-1, icon); 250 const gfx::ImageSkia* image = rb.GetImageSkiaNamed(icon); 251 int icon_y = entry_rect.y() + (row_height - image->height()) / 2; 252 253 x_align_left += is_rtl ? 0 : -image->width(); 254 255 canvas->DrawImageInt(*image, x_align_left, icon_y); 256 257 x_align_left += is_rtl ? image->width() + kIconPadding : -kIconPadding; 258 } 259 260 // Draw the name text. 261 if (!is_rtl) { 262 x_align_left -= canvas->GetStringWidth(controller_->subtexts()[index], 263 controller_->subtext_font()); 264 } 265 266 canvas->DrawStringInt( 267 controller_->subtexts()[index], 268 controller_->subtext_font(), 269 kItemTextColor, 270 x_align_left, 271 entry_rect.y(), 272 canvas->GetStringWidth(controller_->subtexts()[index], 273 controller_->subtext_font()), 274 entry_rect.height(), 275 gfx::Canvas::TEXT_ALIGN_CENTER); 276} 277 278AutofillPopupView* AutofillPopupView::Create( 279 AutofillPopupController* controller) { 280 views::Widget* observing_widget = 281 views::Widget::GetTopLevelWidgetForNativeView( 282 controller->container_view()); 283 284 // If the top level widget can't be found, cancel the popup since we can't 285 // fully set it up. 286 if (!observing_widget) 287 return NULL; 288 289 return new AutofillPopupViewViews(controller, observing_widget); 290} 291 292} // namespace autofill 293