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