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"
24 #include "ui/views/corewm/window_animations.h"
27 using blink::WebAutofillClient
;
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);
42 AutofillPopupViewViews::AutofillPopupViewViews(
43 AutofillPopupController
* controller
, views::Widget
* observing_widget
)
44 : controller_(controller
),
45 observing_widget_(observing_widget
) {}
47 AutofillPopupViewViews::~AutofillPopupViewViews() {
49 controller_
->ViewDestroyed();
55 void AutofillPopupViewViews::Hide() {
56 // The controller is no longer valid after it hides us.
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|.
72 void AutofillPopupViewViews::OnPaint(gfx::Canvas
* canvas
) {
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
);
86 DrawAutofillEntry(canvas
, i
, line_rect
);
91 void AutofillPopupViewViews::OnMouseCaptureLost() {
93 controller_
->SelectionCleared();
96 bool AutofillPopupViewViews::OnMouseDragged(const ui::MouseEvent
& event
) {
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.
108 // If we move off of the popup, we lose the selection.
109 controller_
->SelectionCleared();
113 void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent
& event
) {
115 controller_
->SelectionCleared();
118 void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent
& event
) {
122 if (HitTestPoint(event
.location()))
123 controller_
->LineSelectedAtPoint(event
.x(), event
.y());
125 controller_
->SelectionCleared();
128 bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent
& event
) {
129 if (HitTestPoint(event
.location()))
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
);
149 // |this| is now deleted.
155 void AutofillPopupViewViews::OnMouseReleased(const ui::MouseEvent
& event
) {
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
) {
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());
181 controller_
->SelectionCleared();
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());
188 controller_
->SelectionCleared();
190 case ui::ET_GESTURE_TAP_CANCEL
:
191 case ui::ET_SCROLL_FLING_START
:
192 controller_
->SelectionCleared();
200 void AutofillPopupViewViews::OnWidgetBoundsChanged(
201 views::Widget
* widget
,
202 const gfx::Rect
& new_bounds
) {
203 DCHECK_EQ(widget
, observing_widget_
);
207 void AutofillPopupViewViews::Show() {
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
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
);
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());
245 void AutofillPopupViewViews::HideInternal() {
246 observing_widget_
->RemoveObserver(this);
249 void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas
* canvas
,
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
,
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
]);
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());
296 x_align_left
-= subtext_width
;
298 canvas
->DrawStringRectWithFlags(
299 controller_
->subtexts()[index
],
300 controller_
->subtext_font_list(),
302 gfx::Rect(x_align_left
,
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
317 if (!observing_widget
)
320 return new AutofillPopupViewViews(controller
, observing_widget
);
323 } // namespace autofill