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/views/border.h"
19 #include "ui/views/event_utils.h"
20 #include "ui/views/widget/widget.h"
23 #include "ui/views/corewm/window_animations.h"
26 using WebKit::WebAutofillClient;
30 const SkColor kBorderColor = SkColorSetARGB(0xFF, 0xC7, 0xCA, 0xCE);
31 const SkColor kHoveredBackgroundColor = SkColorSetARGB(0xFF, 0xCD, 0xCD, 0xCD);
32 const SkColor kItemTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
33 const SkColor kPopupBackground = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
34 const SkColor kValueTextColor = SkColorSetARGB(0xFF, 0x00, 0x00, 0x00);
35 const SkColor kWarningTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
41 AutofillPopupViewViews::AutofillPopupViewViews(
42 AutofillPopupController* controller, views::Widget* observing_widget)
43 : controller_(controller),
44 observing_widget_(observing_widget) {}
46 AutofillPopupViewViews::~AutofillPopupViewViews() {
48 controller_->ViewDestroyed();
54 void AutofillPopupViewViews::Hide() {
55 // The controller is no longer valid after it hides us.
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|.
71 void AutofillPopupViewViews::OnPaint(gfx::Canvas* canvas) {
75 canvas->DrawColor(kPopupBackground);
76 OnPaintBorder(canvas);
78 for (size_t i = 0; i < controller_->names().size(); ++i) {
79 gfx::Rect line_rect = controller_->GetRowBounds(i);
81 if (controller_->identifiers()[i] ==
82 WebAutofillClient::MenuItemIDSeparator) {
83 canvas->DrawRect(line_rect, kItemTextColor);
85 DrawAutofillEntry(canvas, i, line_rect);
90 void AutofillPopupViewViews::OnMouseCaptureLost() {
92 controller_->MouseExitedPopup();
95 bool AutofillPopupViewViews::OnMouseDragged(const ui::MouseEvent& event) {
99 if (HitTestPoint(event.location())) {
100 controller_->MouseHovered(event.x(), event.y());
102 // We must return true in order to get future OnMouseDragged and
103 // OnMouseReleased events.
107 // If we move off of the popup, we lose the selection.
108 controller_->MouseExitedPopup();
112 void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent& event) {
114 controller_->MouseExitedPopup();
117 void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) {
121 if (HitTestPoint(event.location()))
122 controller_->MouseHovered(event.x(), event.y());
124 controller_->MouseExitedPopup();
127 bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent& event) {
128 if (HitTestPoint(event.location()))
131 if (controller_->hide_on_outside_click()) {
132 GetWidget()->ReleaseCapture();
134 gfx::Point screen_loc = event.location();
135 views::View::ConvertPointToScreen(this, &screen_loc);
137 ui::MouseEvent mouse_event = event;
138 mouse_event.set_location(screen_loc);
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);
148 // |this| is now deleted.
154 void AutofillPopupViewViews::OnMouseReleased(const ui::MouseEvent& event) {
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);
164 // We only care about the left click.
165 if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
166 controller_->MouseClicked(event.x(), event.y());
169 void AutofillPopupViewViews::OnWidgetBoundsChanged(
170 views::Widget* widget,
171 const gfx::Rect& new_bounds) {
172 DCHECK_EQ(widget, observing_widget_);
176 void AutofillPopupViewViews::Show() {
178 observing_widget_->AddObserver(this);
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
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);
196 set_border(views::Border::CreateSolidBorder(kBorderThickness, kBorderColor));
198 UpdateBoundsAndRedrawPopup();
201 if (controller_->hide_on_outside_click())
202 GetWidget()->SetCapture(this);
205 void AutofillPopupViewViews::InvalidateRow(size_t row) {
206 SchedulePaintInRect(controller_->GetRowBounds(row));
209 void AutofillPopupViewViews::UpdateBoundsAndRedrawPopup() {
210 GetWidget()->SetBounds(controller_->popup_bounds());
214 void AutofillPopupViewViews::HideInternal() {
215 observing_widget_->RemoveObserver(this);
218 void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas,
220 const gfx::Rect& entry_rect) {
221 if (controller_->selected_line() == index)
222 canvas->FillRect(entry_rect, kHoveredBackgroundColor);
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;
230 canvas->DrawStringInt(
231 controller_->names()[index],
232 controller_->GetNameFontForRow(index),
233 controller_->IsWarning(index) ? kWarningTextColor : kValueTextColor,
236 canvas->GetStringWidth(controller_->names()[index],
237 controller_->GetNameFontForRow(index)),
239 gfx::Canvas::TEXT_ALIGN_CENTER);
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;
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]);
250 const gfx::ImageSkia* image = rb.GetImageSkiaNamed(icon);
251 int icon_y = entry_rect.y() + (row_height - image->height()) / 2;
253 x_align_left += is_rtl ? 0 : -image->width();
255 canvas->DrawImageInt(*image, x_align_left, icon_y);
257 x_align_left += is_rtl ? image->width() + kIconPadding : -kIconPadding;
260 // Draw the name text.
262 x_align_left -= canvas->GetStringWidth(controller_->subtexts()[index],
263 controller_->subtext_font());
266 canvas->DrawStringInt(
267 controller_->subtexts()[index],
268 controller_->subtext_font(),
272 canvas->GetStringWidth(controller_->subtexts()[index],
273 controller_->subtext_font()),
275 gfx::Canvas::TEXT_ALIGN_CENTER);
278 AutofillPopupView* AutofillPopupView::Create(
279 AutofillPopupController* controller) {
280 views::Widget* observing_widget =
281 views::Widget::GetTopLevelWidgetForNativeView(
282 controller->container_view());
284 // If the top level widget can't be found, cancel the popup since we can't
286 if (!observing_widget)
289 return new AutofillPopupViewViews(controller, observing_widget);
292 } // namespace autofill