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 19 using WebKit::WebAutofillClient; 20 21 namespace { 22 23 const SkColor kBorderColor = SkColorSetARGB(0xFF, 0xC7, 0xCA, 0xCE); 24 const SkColor kHoveredBackgroundColor = SkColorSetARGB(0xFF, 0xCD, 0xCD, 0xCD); 25 const SkColor kItemTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F); 26 const SkColor kPopupBackground = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF); 27 const SkColor kValueTextColor = SkColorSetARGB(0xFF, 0x00, 0x00, 0x00); 28 const SkColor kWarningTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F); 29 30 } // namespace 31 32 namespace autofill { 33 34 AutofillPopupViewViews::AutofillPopupViewViews( 35 AutofillPopupController* controller, views::Widget* observing_widget) 36 : controller_(controller), 37 observing_widget_(observing_widget) {} 38 39 AutofillPopupViewViews::~AutofillPopupViewViews() { 40 if (controller_) { 41 controller_->ViewDestroyed(); 42 43 HideInternal(); 44 } 45 } 46 47 void 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 64 void 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 83 void AutofillPopupViewViews::OnMouseCaptureLost() { 84 if (controller_) 85 controller_->MouseExitedPopup(); 86 } 87 88 bool 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 105 void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent& event) { 106 if (controller_) 107 controller_->MouseExitedPopup(); 108 } 109 110 void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) { 111 if (controller_) 112 controller_->MouseHovered(event.x(), event.y()); 113 } 114 115 bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent& event) { 116 // We must return true in order to get the OnMouseReleased event later. 117 return true; 118 } 119 120 void 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 130 void AutofillPopupViewViews::OnWidgetBoundsChanged( 131 views::Widget* widget, 132 const gfx::Rect& new_bounds) { 133 controller_->Hide(); 134 } 135 136 void 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 157 void AutofillPopupViewViews::InvalidateRow(size_t row) { 158 SchedulePaintInRect(controller_->GetRowBounds(row)); 159 } 160 161 void AutofillPopupViewViews::UpdateBoundsAndRedrawPopup() { 162 GetWidget()->SetBounds(controller_->popup_bounds()); 163 SchedulePaint(); 164 } 165 166 void AutofillPopupViewViews::HideInternal() { 167 observing_widget_->RemoveObserver(this); 168 } 169 170 void 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 229 AutofillPopupView* 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