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