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