OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h" | 5 #include "chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h" |
6 | 6 |
7 #include <gdk/gdkkeysyms.h> | 7 #include <gdk/gdkkeysyms.h> |
8 | 8 |
9 #include "base/i18n/rtl.h" | 9 #include "base/i18n/rtl.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/utf_string_conversions.h" | 11 #include "base/utf_string_conversions.h" |
12 #include "chrome/browser/ui/autofill/autofill_popup_controller.h" | 12 #include "chrome/browser/ui/autofill/autofill_popup_controller.h" |
13 #include "chrome/browser/ui/gtk/gtk_util.h" | 13 #include "chrome/browser/ui/gtk/gtk_util.h" |
14 #include "grit/ui_resources.h" | 14 #include "grit/ui_resources.h" |
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" | 15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" |
16 #include "ui/base/gtk/gtk_compat.h" | 16 #include "ui/base/gtk/gtk_compat.h" |
17 #include "ui/base/gtk/gtk_hig_constants.h" | 17 #include "ui/base/gtk/gtk_hig_constants.h" |
18 #include "ui/base/gtk/gtk_windowing.h" | 18 #include "ui/base/gtk/gtk_windowing.h" |
19 #include "ui/base/resource/resource_bundle.h" | 19 #include "ui/base/resource/resource_bundle.h" |
20 #include "ui/gfx/image/image.h" | 20 #include "ui/gfx/image/image.h" |
21 #include "ui/gfx/native_widget_types.h" | 21 #include "ui/gfx/native_widget_types.h" |
22 #include "ui/gfx/pango_util.h" | 22 #include "ui/gfx/pango_util.h" |
23 #include "ui/gfx/rect.h" | 23 #include "ui/gfx/rect.h" |
24 | 24 |
25 using WebKit::WebAutofillClient; | 25 using WebKit::WebAutofillClient; |
26 | 26 |
27 namespace { | 27 namespace { |
| 28 |
28 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); | 29 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); |
29 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0xcd, 0xcd, 0xcd); | 30 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0xcd, 0xcd, 0xcd); |
30 const GdkColor kValueTextColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); | 31 const GdkColor kNameColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); |
31 const GdkColor kLabelTextColor = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f); | 32 const GdkColor kSubtextColor = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f); |
32 | |
33 gfx::Rect GetWindowRect(GdkWindow* window) { | |
34 return gfx::Rect(gdk_window_get_width(window), | |
35 gdk_window_get_height(window)); | |
36 } | |
37 | 33 |
38 } // namespace | 34 } // namespace |
39 | 35 |
40 AutofillPopupViewGtk::AutofillPopupViewGtk( | 36 AutofillPopupViewGtk::AutofillPopupViewGtk( |
41 AutofillPopupController* controller) | 37 AutofillPopupController* controller) |
42 : controller_(controller), | 38 : controller_(controller), |
43 window_(gtk_window_new(GTK_WINDOW_POPUP)) { | 39 window_(gtk_window_new(GTK_WINDOW_POPUP)) { |
44 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 40 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
45 gtk_widget_set_app_paintable(window_, TRUE); | 41 gtk_widget_set_app_paintable(window_, TRUE); |
46 gtk_widget_set_double_buffered(window_, TRUE); | 42 gtk_widget_set_double_buffered(window_, TRUE); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 UpdateBoundsAndRedrawPopup(); | 74 UpdateBoundsAndRedrawPopup(); |
79 | 75 |
80 gtk_widget_show(window_); | 76 gtk_widget_show(window_); |
81 | 77 |
82 GtkWidget* parent_window = | 78 GtkWidget* parent_window = |
83 gtk_widget_get_toplevel(controller_->container_view()); | 79 gtk_widget_get_toplevel(controller_->container_view()); |
84 ui::StackPopupWindow(window_, parent_window); | 80 ui::StackPopupWindow(window_, parent_window); |
85 } | 81 } |
86 | 82 |
87 void AutofillPopupViewGtk::InvalidateRow(size_t row) { | 83 void AutofillPopupViewGtk::InvalidateRow(size_t row) { |
88 GdkRectangle row_rect = controller_->GetRectForRow( | 84 GdkRectangle row_rect = controller_->GetRowBounds(row).ToGdkRectangle(); |
89 row, | |
90 controller_->popup_bounds().width()).ToGdkRectangle(); | |
91 GdkWindow* gdk_window = gtk_widget_get_window(window_); | 85 GdkWindow* gdk_window = gtk_widget_get_window(window_); |
92 gdk_window_invalidate_rect(gdk_window, &row_rect, FALSE); | 86 gdk_window_invalidate_rect(gdk_window, &row_rect, FALSE); |
93 } | 87 } |
94 | 88 |
95 void AutofillPopupViewGtk::UpdateBoundsAndRedrawPopup() { | 89 void AutofillPopupViewGtk::UpdateBoundsAndRedrawPopup() { |
96 gtk_widget_set_size_request(window_, | 90 gtk_widget_set_size_request(window_, |
97 controller_->popup_bounds().width(), | 91 controller_->popup_bounds().width(), |
98 controller_->popup_bounds().height()); | 92 controller_->popup_bounds().height()); |
99 gtk_window_move(GTK_WINDOW(window_), | 93 gtk_window_move(GTK_WINDOW(window_), |
100 controller_->popup_bounds().x(), | 94 controller_->popup_bounds().x(), |
101 controller_->popup_bounds().y()); | 95 controller_->popup_bounds().y()); |
102 | 96 |
103 GdkWindow* gdk_window = gtk_widget_get_window(window_); | 97 GdkWindow* gdk_window = gtk_widget_get_window(window_); |
104 GdkRectangle popup_rect = controller_->popup_bounds().ToGdkRectangle(); | 98 GdkRectangle popup_rect = controller_->popup_bounds().ToGdkRectangle(); |
105 if (gdk_window != NULL) | 99 if (gdk_window != NULL) |
106 gdk_window_invalidate_rect(gdk_window, &popup_rect, FALSE); | 100 gdk_window_invalidate_rect(gdk_window, &popup_rect, FALSE); |
107 } | 101 } |
108 | 102 |
109 gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget, | 103 gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget, |
110 GdkEventButton* event) { | 104 GdkEventButton* event) { |
111 // We only care about the left click. | 105 // We only care about the left click. |
112 if (event->button != 1) | 106 if (event->button != 1) |
113 return FALSE; | 107 return FALSE; |
114 | 108 |
115 controller_->AcceptSelectedPosition(event->x, event->y); | 109 controller_->MouseClicked(event->x, event->y); |
116 return TRUE; | 110 return TRUE; |
117 } | 111 } |
118 | 112 |
119 gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, | 113 gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, |
120 GdkEventExpose* event) { | 114 GdkEventExpose* event) { |
121 gfx::Rect window_rect = GetWindowRect(event->window); | |
122 gfx::Rect damage_rect = gfx::Rect(event->area); | |
123 | |
124 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget))); | 115 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget))); |
125 gdk_cairo_rectangle(cr, &event->area); | 116 gdk_cairo_rectangle(cr, &event->area); |
126 cairo_clip(cr); | 117 cairo_clip(cr); |
127 | 118 |
128 // This assert is kinda ugly, but it would be more currently unneeded work | 119 // This assert is kinda ugly, but it would be more currently unneeded work |
129 // to support painting a border that isn't 1 pixel thick. There is no point | 120 // to support painting a border that isn't 1 pixel thick. There is no point |
130 // in writing that code now, and explode if that day ever comes. | 121 // in writing that code now, and explode if that day ever comes. |
131 DCHECK_EQ(1, kBorderThickness); | 122 DCHECK_EQ(1, kBorderThickness); |
132 // Draw the 1px border around the entire window. | 123 // Draw the 1px border around the entire window. |
133 gdk_cairo_set_source_color(cr, &kBorderColor); | 124 gdk_cairo_set_source_color(cr, &kBorderColor); |
134 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); | 125 gdk_cairo_rectangle(cr, &widget->allocation); |
135 cairo_stroke(cr); | 126 cairo_stroke(cr); |
| 127 SetUpLayout(); |
136 | 128 |
137 SetupLayout(window_rect); | 129 gfx::Rect damage_rect(event->area); |
138 | 130 |
139 for (size_t i = 0; i < controller_->autofill_values().size(); ++i) { | 131 for (size_t i = 0; i < controller_->names().size(); ++i) { |
140 gfx::Rect line_rect = controller_->GetRectForRow(i, window_rect.width()); | 132 gfx::Rect line_rect = controller_->GetRowBounds(i); |
141 // Only repaint and layout damaged lines. | 133 // Only repaint and layout damaged lines. |
142 if (!line_rect.Intersects(damage_rect)) | 134 if (!line_rect.Intersects(damage_rect)) |
143 continue; | 135 continue; |
144 | 136 |
145 if (controller_->autofill_unique_ids()[i] == | 137 if (controller_->identifiers()[i] == |
146 WebAutofillClient::MenuItemIDSeparator) { | 138 WebAutofillClient::MenuItemIDSeparator) { |
147 DrawSeparator(cr, line_rect); | 139 DrawSeparator(cr, line_rect); |
148 } else { | 140 } else { |
149 DrawAutofillEntry(cr, i, line_rect); | 141 DrawAutofillEntry(cr, i, line_rect); |
150 } | 142 } |
151 } | 143 } |
152 | 144 |
153 cairo_destroy(cr); | 145 cairo_destroy(cr); |
154 | 146 |
155 return TRUE; | 147 return TRUE; |
156 } | 148 } |
157 | 149 |
158 gboolean AutofillPopupViewGtk::HandleLeave(GtkWidget* widget, | 150 gboolean AutofillPopupViewGtk::HandleLeave(GtkWidget* widget, |
159 GdkEventCrossing* event) { | 151 GdkEventCrossing* event) { |
160 controller_->ClearSelectedLine(); | 152 controller_->MouseExitedPopup(); |
161 | 153 |
162 return FALSE; | 154 return FALSE; |
163 } | 155 } |
164 | 156 |
165 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, | 157 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, |
166 GdkEventMotion* event) { | 158 GdkEventMotion* event) { |
167 controller_->SetSelectedPosition(event->x, event->y); | 159 controller_->MouseHovered(event->x, event->y); |
168 | 160 |
169 return TRUE; | 161 return TRUE; |
170 } | 162 } |
171 | 163 |
172 void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect) { | 164 void AutofillPopupViewGtk::SetUpLayout() { |
173 pango_layout_set_width(layout_, window_rect.width() * PANGO_SCALE); | 165 pango_layout_set_width(layout_, window_->allocation.width * PANGO_SCALE); |
174 pango_layout_set_height(layout_, window_rect.height() * PANGO_SCALE); | 166 pango_layout_set_height(layout_, window_->allocation.height * PANGO_SCALE); |
175 } | 167 } |
176 | 168 |
177 void AutofillPopupViewGtk::SetLayoutText(const string16& text, | 169 void AutofillPopupViewGtk::SetLayoutText(const string16& text, |
178 const gfx::Font& font, | 170 const gfx::Font& font, |
179 const GdkColor text_color) { | 171 const GdkColor text_color) { |
180 PangoAttrList* attrs = pango_attr_list_new(); | 172 PangoAttrList* attrs = pango_attr_list_new(); |
181 | 173 |
182 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, | 174 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, |
183 text_color.green, | 175 text_color.green, |
184 text_color.blue); | 176 text_color.blue); |
(...skipping 29 matching lines...) Expand all Loading... |
214 size_t index, | 206 size_t index, |
215 const gfx::Rect& entry_rect) { | 207 const gfx::Rect& entry_rect) { |
216 if (controller_->selected_line() == static_cast<int>(index)) { | 208 if (controller_->selected_line() == static_cast<int>(index)) { |
217 gdk_cairo_set_source_color(cairo_context, &kHoveredBackgroundColor); | 209 gdk_cairo_set_source_color(cairo_context, &kHoveredBackgroundColor); |
218 cairo_rectangle(cairo_context, entry_rect.x(), entry_rect.y(), | 210 cairo_rectangle(cairo_context, entry_rect.x(), entry_rect.y(), |
219 entry_rect.width(), entry_rect.height()); | 211 entry_rect.width(), entry_rect.height()); |
220 cairo_fill(cairo_context); | 212 cairo_fill(cairo_context); |
221 } | 213 } |
222 | 214 |
223 // Draw the value. | 215 // Draw the value. |
224 SetLayoutText(controller_->autofill_values()[index], | 216 SetLayoutText(controller_->names()[index], |
225 controller_->value_font(), | 217 controller_->name_font(), |
226 kValueTextColor); | 218 kNameColor); |
227 int value_text_width = controller_->value_font().GetStringWidth( | 219 int value_text_width = |
228 controller_->autofill_values()[index]); | 220 controller_->name_font().GetStringWidth(controller_->names()[index]); |
229 | 221 |
230 // Center the text within the line. | 222 // Center the text within the line. |
231 int row_height = controller_->GetRowHeightFromId( | 223 int row_height = controller_->GetRowBounds(index).height(); |
232 controller_->autofill_unique_ids()[index]); | |
233 int value_content_y = std::max( | 224 int value_content_y = std::max( |
234 entry_rect.y(), | 225 entry_rect.y(), |
235 entry_rect.y() + | 226 entry_rect.y() + |
236 (row_height - controller_->value_font().GetHeight()) / 2); | 227 (row_height - controller_->name_font().GetHeight()) / 2); |
237 | 228 |
238 bool is_rtl = base::i18n::IsRTL(); | 229 bool is_rtl = base::i18n::IsRTL(); |
239 int value_content_x = is_rtl ? | 230 int value_content_x = is_rtl ? |
240 entry_rect.width() - value_text_width - kEndPadding : kEndPadding; | 231 entry_rect.width() - value_text_width - kEndPadding : kEndPadding; |
241 | 232 |
242 cairo_save(cairo_context); | 233 cairo_save(cairo_context); |
243 cairo_move_to(cairo_context, value_content_x, value_content_y); | 234 cairo_move_to(cairo_context, value_content_x, value_content_y); |
244 pango_cairo_show_layout(cairo_context, layout_); | 235 pango_cairo_show_layout(cairo_context, layout_); |
245 cairo_restore(cairo_context); | 236 cairo_restore(cairo_context); |
246 | 237 |
247 // Use this to figure out where all the other Autofill items should be placed. | 238 // Use this to figure out where all the other Autofill items should be placed. |
248 int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding; | 239 int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding; |
249 | 240 |
250 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 241 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
251 | 242 |
252 // Draw the delete icon, if one is needed. | 243 // Draw the delete icon, if one is needed. |
253 if (controller_->CanDelete(controller_->autofill_unique_ids()[index])) { | 244 if (controller_->CanDelete(index)) { |
254 x_align_left += is_rtl ? 0 : -kDeleteIconWidth; | 245 x_align_left += is_rtl ? 0 : -kDeleteIconWidth; |
255 | 246 |
256 gfx::Image delete_icon; | 247 gfx::Image delete_icon; |
257 if (static_cast<int>(index) == controller_->selected_line() && | 248 if (static_cast<int>(index) == controller_->selected_line() && |
258 controller_->delete_icon_hovered()) { | 249 controller_->delete_icon_hovered()) { |
259 delete_icon = rb.GetImageNamed(IDR_CLOSE_BAR_H); | 250 delete_icon = rb.GetImageNamed(IDR_CLOSE_BAR_H); |
260 } else { | 251 } else { |
261 delete_icon = rb.GetImageNamed(IDR_CLOSE_BAR); | 252 delete_icon = rb.GetImageNamed(IDR_CLOSE_BAR); |
262 } | 253 } |
263 | 254 |
264 // TODO(csharp): Create a custom resource for the delete icon. | 255 // TODO(csharp): Create a custom resource for the delete icon. |
265 // http://crbug.com/131801 | 256 // http://crbug.com/131801 |
266 cairo_save(cairo_context); | 257 cairo_save(cairo_context); |
267 gtk_util::DrawFullImage( | 258 gtk_util::DrawFullImage( |
268 cairo_context, | 259 cairo_context, |
269 window_, | 260 window_, |
270 delete_icon, | 261 delete_icon, |
271 x_align_left, | 262 x_align_left, |
272 entry_rect.y() + | 263 entry_rect.y() + |
273 ((row_height - kDeleteIconHeight) / 2)); | 264 ((row_height - kDeleteIconHeight) / 2)); |
274 cairo_restore(cairo_context); | 265 cairo_restore(cairo_context); |
275 cairo_save(cairo_context); | 266 cairo_save(cairo_context); |
276 | 267 |
277 x_align_left += is_rtl ? kDeleteIconWidth + kIconPadding : -kIconPadding; | 268 x_align_left += is_rtl ? kDeleteIconWidth + kIconPadding : -kIconPadding; |
278 } | 269 } |
279 | 270 |
280 // Draw the Autofill icon, if one exists | 271 // Draw the Autofill icon, if one exists |
281 if (!controller_->autofill_icons()[index].empty()) { | 272 if (!controller_->icons()[index].empty()) { |
282 int icon = | 273 int icon = controller_->GetIconResourceID(controller_->icons()[index]); |
283 controller_->GetIconResourceID(controller_->autofill_icons()[index]); | |
284 DCHECK_NE(-1, icon); | 274 DCHECK_NE(-1, icon); |
285 int icon_y = entry_rect.y() + (row_height - kAutofillIconHeight) / 2; | 275 int icon_y = entry_rect.y() + (row_height - kAutofillIconHeight) / 2; |
286 | 276 |
287 x_align_left += is_rtl ? 0 : -kAutofillIconWidth; | 277 x_align_left += is_rtl ? 0 : -kAutofillIconWidth; |
288 | 278 |
289 cairo_save(cairo_context); | 279 cairo_save(cairo_context); |
290 gtk_util::DrawFullImage(cairo_context, | 280 gtk_util::DrawFullImage(cairo_context, |
291 window_, | 281 window_, |
292 rb.GetImageNamed(icon), | 282 rb.GetImageNamed(icon), |
293 x_align_left, | 283 x_align_left, |
294 icon_y); | 284 icon_y); |
295 cairo_restore(cairo_context); | 285 cairo_restore(cairo_context); |
296 | 286 |
297 x_align_left += is_rtl ? kAutofillIconWidth + kIconPadding : -kIconPadding; | 287 x_align_left += is_rtl ? kAutofillIconWidth + kIconPadding : -kIconPadding; |
298 } | 288 } |
299 | 289 |
300 // Draw the label text. | 290 // Draw the subtext. |
301 SetLayoutText(controller_->autofill_labels()[index], | 291 SetLayoutText(controller_->subtexts()[index], |
302 controller_->label_font(), | 292 controller_->subtext_font(), |
303 kLabelTextColor); | 293 kSubtextColor); |
304 if (!is_rtl) { | 294 if (!is_rtl) { |
305 x_align_left -= controller_->label_font().GetStringWidth( | 295 x_align_left -= controller_->subtext_font().GetStringWidth( |
306 controller_->autofill_labels()[index]); | 296 controller_->subtexts()[index]); |
307 } | 297 } |
308 | 298 |
309 // Center the text within the line. | 299 // Center the text within the line. |
310 int label_content_y = std::max( | 300 int subtext_content_y = std::max( |
311 entry_rect.y(), | 301 entry_rect.y(), |
312 entry_rect.y() + | 302 entry_rect.y() + |
313 (row_height - controller_->label_font().GetHeight()) / 2); | 303 (row_height - controller_->subtext_font().GetHeight()) / 2); |
314 | 304 |
315 cairo_save(cairo_context); | 305 cairo_save(cairo_context); |
316 cairo_move_to(cairo_context, x_align_left, label_content_y); | 306 cairo_move_to(cairo_context, x_align_left, subtext_content_y); |
317 pango_cairo_show_layout(cairo_context, layout_); | 307 pango_cairo_show_layout(cairo_context, layout_); |
318 cairo_restore(cairo_context); | 308 cairo_restore(cairo_context); |
319 } | 309 } |
320 | 310 |
321 void AutofillPopupViewGtk::SetInitialBounds() { | 311 void AutofillPopupViewGtk::SetInitialBounds() { |
322 GdkScreen* screen = | 312 GdkScreen* screen = |
323 gtk_widget_get_screen(GTK_WIDGET(controller_->container_view())); | 313 gtk_widget_get_screen(GTK_WIDGET(controller_->container_view())); |
324 gint screen_height = gdk_screen_get_height(screen); | 314 gint screen_height = gdk_screen_get_height(screen); |
325 | 315 |
326 int x = 0; | 316 int x = 0; |
(...skipping 19 matching lines...) Expand all Loading... |
346 x + controller_->element_bounds().x(), | 336 x + controller_->element_bounds().x(), |
347 top_of_popup, | 337 top_of_popup, |
348 controller_->GetPopupRequiredWidth(), | 338 controller_->GetPopupRequiredWidth(), |
349 popup_height)); | 339 popup_height)); |
350 } | 340 } |
351 | 341 |
352 AutofillPopupView* AutofillPopupView::Create( | 342 AutofillPopupView* AutofillPopupView::Create( |
353 AutofillPopupController* controller) { | 343 AutofillPopupController* controller) { |
354 return new AutofillPopupViewGtk(controller); | 344 return new AutofillPopupViewGtk(controller); |
355 } | 345 } |
OLD | NEW |