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 "autofill_popup_view_gtk.h" | 5 #include "autofill_popup_view_gtk.h" |
6 | 6 |
7 #include <gdk/gdkkeysyms.h> | 7 #include <gdk/gdkkeysyms.h> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
11 #include "chrome/browser/autofill/autofill_external_delegate.h" | 11 #include "chrome/browser/autofill/autofill_external_delegate.h" |
12 #include "chrome/browser/ui/gtk/gtk_util.h" | 12 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 13 #include "chrome/browser/ui/gtk/gtk_theme_service.h" |
13 #include "content/public/browser/render_view_host.h" | 14 #include "content/public/browser/render_view_host.h" |
14 #include "content/public/browser/web_contents.h" | 15 #include "content/public/browser/web_contents.h" |
15 #include "ui/base/gtk/gtk_compat.h" | 16 #include "ui/base/gtk/gtk_compat.h" |
16 #include "ui/base/gtk/gtk_hig_constants.h" | 17 #include "ui/base/gtk/gtk_hig_constants.h" |
17 #include "ui/base/gtk/gtk_windowing.h" | 18 #include "ui/base/gtk/gtk_windowing.h" |
18 #include "ui/gfx/native_widget_types.h" | 19 #include "ui/gfx/native_widget_types.h" |
19 #include "ui/gfx/rect.h" | 20 #include "ui/gfx/rect.h" |
20 | 21 |
21 namespace { | 22 namespace { |
22 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); | 23 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); |
23 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0x0CD, 0xCD, 0xCD); | 24 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0x0CD, 0xCD, 0xCD); |
24 const GdkColor kTextColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); | 25 const GdkColor kTextColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); |
25 | 26 |
| 27 // The vertical height of each row in pixels. |
| 28 const int kRowHeight = 24; |
| 29 |
26 // The amount of minimum padding between the Autofill value and label in pixels. | 30 // The amount of minimum padding between the Autofill value and label in pixels. |
27 const int kMiddlePadding = 10; | 31 const int kMiddlePadding = 10; |
28 | 32 |
| 33 // The amount of padding between the label and icon or edge and icon in pixels. |
| 34 const int kIconPadding = 5; |
| 35 |
29 // We have a 1 pixel border around the entire results popup. | 36 // We have a 1 pixel border around the entire results popup. |
30 const int kBorderThickness = 1; | 37 const int kBorderThickness = 1; |
31 | 38 |
| 39 // Width of the icons in pixels. |
| 40 const int kIconWidth = 25; |
| 41 |
| 42 // Height of the icons in pixels. |
| 43 const int kIconHeight = 16; |
| 44 |
32 gfx::Rect GetWindowRect(GdkWindow* window) { | 45 gfx::Rect GetWindowRect(GdkWindow* window) { |
33 return gfx::Rect(gdk_window_get_width(window), | 46 return gfx::Rect(gdk_window_get_width(window), |
34 gdk_window_get_height(window)); | 47 gdk_window_get_height(window)); |
35 } | 48 } |
36 | 49 |
37 // Returns the rectangle containing the item at position |index| in the popup. | 50 // Returns the rectangle containing the item at position |index| in the popup. |
38 gfx::Rect GetRectForRow(size_t index, int width, int height) { | 51 gfx::Rect GetRectForRow(size_t index, int width) { |
39 return gfx::Rect(0, (index * height), width, height); | 52 return gfx::Rect(0, (index * kRowHeight), width, kRowHeight); |
40 } | 53 } |
41 | 54 |
42 } // namespace | 55 } // namespace |
43 | 56 |
44 AutofillPopupViewGtk::AutofillPopupViewGtk( | 57 AutofillPopupViewGtk::AutofillPopupViewGtk( |
45 content::WebContents* web_contents, | 58 content::WebContents* web_contents, |
| 59 GtkThemeService* theme_service, |
46 AutofillExternalDelegate* external_delegate, | 60 AutofillExternalDelegate* external_delegate, |
47 GtkWidget* parent) | 61 GtkWidget* parent) |
48 : AutofillPopupView(web_contents, external_delegate), | 62 : AutofillPopupView(web_contents, external_delegate), |
49 parent_(parent), | 63 parent_(parent), |
50 window_(gtk_window_new(GTK_WINDOW_POPUP)), | 64 window_(gtk_window_new(GTK_WINDOW_POPUP)), |
| 65 theme_service_(theme_service), |
51 render_view_host_(web_contents->GetRenderViewHost()) { | 66 render_view_host_(web_contents->GetRenderViewHost()) { |
52 CHECK(parent != NULL); | 67 CHECK(parent != NULL); |
53 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 68 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
54 gtk_widget_set_app_paintable(window_, TRUE); | 69 gtk_widget_set_app_paintable(window_, TRUE); |
55 gtk_widget_set_double_buffered(window_, TRUE); | 70 gtk_widget_set_double_buffered(window_, TRUE); |
56 | 71 |
57 // Setup the window to ensure it receives the expose event. | 72 // Setup the window to ensure it receives the expose event. |
58 gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK | | 73 gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK | |
59 GDK_BUTTON_RELEASE_MASK | | 74 GDK_BUTTON_RELEASE_MASK | |
60 GDK_EXPOSURE_MASK | | 75 GDK_EXPOSURE_MASK | |
61 GDK_POINTER_MOTION_MASK); | 76 GDK_POINTER_MOTION_MASK); |
62 g_signal_connect(window_, "expose-event", | 77 g_signal_connect(window_, "expose-event", |
63 G_CALLBACK(HandleExposeThunk), this); | 78 G_CALLBACK(HandleExposeThunk), this); |
64 | 79 |
65 g_signal_connect(window_, "motion-notify-event", | 80 g_signal_connect(window_, "motion-notify-event", |
66 G_CALLBACK(HandleMotionThunk), this); | 81 G_CALLBACK(HandleMotionThunk), this); |
67 g_signal_connect(window_, "button-release-event", | 82 g_signal_connect(window_, "button-release-event", |
68 G_CALLBACK(HandleButtonReleaseThunk), this); | 83 G_CALLBACK(HandleButtonReleaseThunk), this); |
69 | 84 |
70 // Cache the layout so we don't have to create it for every expose. | 85 // Cache the layout so we don't have to create it for every expose. |
71 layout_ = gtk_widget_create_pango_layout(window_, NULL); | 86 layout_ = gtk_widget_create_pango_layout(window_, NULL); |
72 | |
73 row_height_ = font_.GetHeight(); | |
74 } | 87 } |
75 | 88 |
76 AutofillPopupViewGtk::~AutofillPopupViewGtk() { | 89 AutofillPopupViewGtk::~AutofillPopupViewGtk() { |
77 g_object_unref(layout_); | 90 g_object_unref(layout_); |
78 gtk_widget_destroy(window_); | 91 gtk_widget_destroy(window_); |
79 } | 92 } |
80 | 93 |
81 void AutofillPopupViewGtk::ShowInternal() { | 94 void AutofillPopupViewGtk::ShowInternal() { |
82 SetBounds(); | 95 SetBounds(); |
83 gtk_window_move(GTK_WINDOW(window_), bounds_.x(), bounds_.y()); | 96 gtk_window_move(GTK_WINDOW(window_), bounds_.x(), bounds_.y()); |
84 | 97 |
85 ResizePopup(); | 98 ResizePopup(); |
86 | 99 |
87 render_view_host_->AddKeyboardListener(this); | 100 render_view_host_->AddKeyboardListener(this); |
88 | 101 |
89 gtk_widget_show(window_); | 102 gtk_widget_show(window_); |
90 | 103 |
91 GtkWidget* toplevel = gtk_widget_get_toplevel(parent_); | 104 GtkWidget* toplevel = gtk_widget_get_toplevel(parent_); |
92 CHECK(gtk_widget_is_toplevel(toplevel)); | 105 CHECK(gtk_widget_is_toplevel(toplevel)); |
93 ui::StackPopupWindow(window_, toplevel); | 106 ui::StackPopupWindow(window_, toplevel); |
94 } | 107 } |
95 | 108 |
96 void AutofillPopupViewGtk::HideInternal() { | 109 void AutofillPopupViewGtk::HideInternal() { |
97 render_view_host_->RemoveKeyboardListener(this); | 110 render_view_host_->RemoveKeyboardListener(this); |
98 | 111 |
99 gtk_widget_hide(window_); | 112 gtk_widget_hide(window_); |
100 } | 113 } |
101 | 114 |
102 void AutofillPopupViewGtk::InvalidateRow(size_t row) { | 115 void AutofillPopupViewGtk::InvalidateRow(size_t row) { |
103 GdkRectangle row_rect = GetRectForRow( | 116 GdkRectangle row_rect = GetRectForRow(row, bounds_.width()).ToGdkRectangle(); |
104 row, bounds_.width(), row_height_).ToGdkRectangle(); | |
105 GdkWindow* gdk_window = gtk_widget_get_window(window_); | 117 GdkWindow* gdk_window = gtk_widget_get_window(window_); |
106 gdk_window_invalidate_rect(gdk_window, &row_rect, FALSE); | 118 gdk_window_invalidate_rect(gdk_window, &row_rect, FALSE); |
107 } | 119 } |
108 | 120 |
109 void AutofillPopupViewGtk::ResizePopup() { | 121 void AutofillPopupViewGtk::ResizePopup() { |
110 gtk_widget_set_size_request( | 122 gtk_widget_set_size_request( |
111 window_, | 123 window_, |
112 GetPopupRequiredWidth(), | 124 GetPopupRequiredWidth(), |
113 row_height_ * autofill_values().size()); | 125 kRowHeight * autofill_values().size()); |
114 } | 126 } |
115 | 127 |
116 gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget, | 128 gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget, |
117 GdkEventButton* event) { | 129 GdkEventButton* event) { |
118 // We only care about the left click. | 130 // We only care about the left click. |
119 if (event->button != 1) | 131 if (event->button != 1) |
120 return FALSE; | 132 return FALSE; |
121 | 133 |
122 DCHECK_EQ(selected_line(), LineFromY(event->y)); | 134 DCHECK_EQ(selected_line(), LineFromY(event->y)); |
123 | 135 |
(...skipping 21 matching lines...) Expand all Loading... |
145 cairo_stroke(cr); | 157 cairo_stroke(cr); |
146 | 158 |
147 SetupLayout(window_rect, kTextColor); | 159 SetupLayout(window_rect, kTextColor); |
148 | 160 |
149 int actual_content_width, actual_content_height; | 161 int actual_content_width, actual_content_height; |
150 pango_layout_get_size(layout_, &actual_content_width, &actual_content_height); | 162 pango_layout_get_size(layout_, &actual_content_width, &actual_content_height); |
151 actual_content_width /= PANGO_SCALE; | 163 actual_content_width /= PANGO_SCALE; |
152 actual_content_height /= PANGO_SCALE; | 164 actual_content_height /= PANGO_SCALE; |
153 | 165 |
154 for (size_t i = 0; i < autofill_values().size(); ++i) { | 166 for (size_t i = 0; i < autofill_values().size(); ++i) { |
155 gfx::Rect line_rect = GetRectForRow(i, window_rect.width(), row_height_); | 167 gfx::Rect line_rect = GetRectForRow(i, window_rect.width()); |
156 // Only repaint and layout damaged lines. | 168 // Only repaint and layout damaged lines. |
157 if (!line_rect.Intersects(damage_rect)) | 169 if (!line_rect.Intersects(damage_rect)) |
158 continue; | 170 continue; |
159 | 171 |
160 if (IsSeparatorIndex(i)) { | 172 if (IsSeparatorIndex(i)) { |
161 int line_y = i * row_height_; | 173 int line_y = i * kRowHeight; |
162 | 174 |
163 cairo_save(cr); | 175 cairo_save(cr); |
164 cairo_move_to(cr, 0, line_y); | 176 cairo_move_to(cr, 0, line_y); |
165 cairo_line_to(cr, window_rect.width(), line_y); | 177 cairo_line_to(cr, window_rect.width(), line_y); |
166 cairo_stroke(cr); | 178 cairo_stroke(cr); |
167 cairo_restore(cr); | 179 cairo_restore(cr); |
168 } | 180 } |
169 | 181 |
170 if (selected_line() == static_cast<int>(i)) { | 182 if (selected_line() == static_cast<int>(i)) { |
171 gdk_cairo_set_source_color(cr, &kHoveredBackgroundColor); | 183 gdk_cairo_set_source_color(cr, &kHoveredBackgroundColor); |
172 cairo_rectangle(cr, line_rect.x(), line_rect.y(), | 184 cairo_rectangle(cr, line_rect.x(), line_rect.y(), |
173 line_rect.width(), line_rect.height()); | 185 line_rect.width(), line_rect.height()); |
174 cairo_fill(cr); | 186 cairo_fill(cr); |
175 } | 187 } |
176 | 188 |
177 // Center the text within the line. | 189 // Center the text within the line. |
178 int content_y = std::max( | 190 int content_y = std::max( |
179 line_rect.y(), | 191 line_rect.y(), |
180 line_rect.y() + ((row_height_ - actual_content_height) / 2)); | 192 line_rect.y() + ((kRowHeight - actual_content_height) / 2)); |
181 | 193 |
182 // Draw the value. | 194 // Draw the value. |
183 gtk_util::SetLayoutText(layout_, autofill_values()[i]); | 195 gtk_util::SetLayoutText(layout_, autofill_values()[i]); |
184 | 196 |
185 cairo_save(cr); | 197 cairo_save(cr); |
186 cairo_move_to(cr, 0, content_y); | 198 cairo_move_to(cr, 0, content_y); |
187 pango_cairo_show_layout(cr, layout_); | 199 pango_cairo_show_layout(cr, layout_); |
188 cairo_restore(cr); | 200 cairo_restore(cr); |
189 | 201 |
190 // Draw the label. | 202 // Draw the label. |
191 int x_align_left = window_rect.width() - | 203 int x_align_left = window_rect.width() - |
192 font_.GetStringWidth(autofill_labels()[i]); | 204 font_.GetStringWidth(autofill_labels()[i]); |
| 205 |
| 206 if (!autofill_icons()[i].empty()) |
| 207 x_align_left -= 2 * kIconPadding + kIconWidth; |
| 208 |
193 gtk_util::SetLayoutText(layout_, autofill_labels()[i]); | 209 gtk_util::SetLayoutText(layout_, autofill_labels()[i]); |
194 | 210 |
195 cairo_save(cr); | 211 cairo_save(cr); |
196 cairo_move_to(cr, x_align_left, line_rect.y()); | 212 cairo_move_to(cr, x_align_left, content_y); |
197 pango_cairo_show_layout(cr, layout_); | 213 pango_cairo_show_layout(cr, layout_); |
198 cairo_restore(cr); | 214 cairo_restore(cr); |
| 215 |
| 216 // Draw the icon, if one exists |
| 217 if (!autofill_icons()[i].empty()) { |
| 218 int icon = GetIconResourceID(autofill_icons()[i]); |
| 219 DCHECK_NE(-1, icon); |
| 220 int icon_y = line_rect.y() + ((kRowHeight - kIconHeight) / 2); |
| 221 |
| 222 cairo_save(cr); |
| 223 gtk_util::DrawFullImage(cr, |
| 224 window_, |
| 225 theme_service_->GetImageNamed(icon), |
| 226 window_rect.width() - |
| 227 (kIconPadding + kIconWidth), |
| 228 icon_y); |
| 229 cairo_restore(cr); |
| 230 } |
199 } | 231 } |
200 | 232 |
201 cairo_destroy(cr); | 233 cairo_destroy(cr); |
202 | 234 |
203 return TRUE; | 235 return TRUE; |
204 } | 236 } |
205 | 237 |
206 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, | 238 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, |
207 GdkEventMotion* event) { | 239 GdkEventMotion* event) { |
208 int line = LineFromY(event->y); | 240 int line = LineFromY(event->y); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 return AcceptSelectedLine(); | 272 return AcceptSelectedLine(); |
241 } | 273 } |
242 | 274 |
243 return false; | 275 return false; |
244 } | 276 } |
245 | 277 |
246 void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, | 278 void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, |
247 const GdkColor& text_color) { | 279 const GdkColor& text_color) { |
248 int allocated_content_width = window_rect.width(); | 280 int allocated_content_width = window_rect.width(); |
249 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); | 281 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); |
250 pango_layout_set_height(layout_, row_height_ * PANGO_SCALE); | 282 pango_layout_set_height(layout_, kRowHeight * PANGO_SCALE); |
251 | 283 |
252 PangoAttrList* attrs = pango_attr_list_new(); | 284 PangoAttrList* attrs = pango_attr_list_new(); |
253 | 285 |
254 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, | 286 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, |
255 text_color.green, | 287 text_color.green, |
256 text_color.blue); | 288 text_color.blue); |
257 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. | 289 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. |
258 | 290 |
259 | 291 |
260 pango_layout_set_attributes(layout_, attrs); // Ref taken. | 292 pango_layout_set_attributes(layout_, attrs); // Ref taken. |
261 pango_attr_list_unref(attrs); | 293 pango_attr_list_unref(attrs); |
262 } | 294 } |
263 | 295 |
264 void AutofillPopupViewGtk::SetBounds() { | 296 void AutofillPopupViewGtk::SetBounds() { |
265 gint origin_x, origin_y; | 297 gint origin_x, origin_y; |
266 gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); | 298 gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); |
267 | 299 |
268 GdkScreen* screen = gtk_widget_get_screen(parent_); | 300 GdkScreen* screen = gtk_widget_get_screen(parent_); |
269 gint screen_height = gdk_screen_get_height(screen); | 301 gint screen_height = gdk_screen_get_height(screen); |
270 | 302 |
271 int bottom_of_field = origin_y + element_bounds().y() + | 303 int bottom_of_field = origin_y + element_bounds().y() + |
272 element_bounds().height(); | 304 element_bounds().height(); |
273 int popup_size = row_height_ * autofill_values().size(); | 305 int popup_size = kRowHeight * autofill_values().size(); |
274 | 306 |
275 // Find the correct top position of the popup so that is doesn't go off | 307 // Find the correct top position of the popup so that is doesn't go off |
276 // the screen. | 308 // the screen. |
277 int top_of_popup = 0; | 309 int top_of_popup = 0; |
278 if (screen_height < bottom_of_field + popup_size) { | 310 if (screen_height < bottom_of_field + popup_size) { |
279 // The popup must appear above the field. | 311 // The popup must appear above the field. |
280 top_of_popup = origin_y + element_bounds().y() - popup_size; | 312 top_of_popup = origin_y + element_bounds().y() - popup_size; |
281 } else { | 313 } else { |
282 // The popup can appear below the field. | 314 // The popup can appear below the field. |
283 top_of_popup = bottom_of_field; | 315 top_of_popup = bottom_of_field; |
284 } | 316 } |
285 | 317 |
286 bounds_.SetRect( | 318 bounds_.SetRect( |
287 origin_x + element_bounds().x(), | 319 origin_x + element_bounds().x(), |
288 top_of_popup, | 320 top_of_popup, |
289 GetPopupRequiredWidth(), | 321 GetPopupRequiredWidth(), |
290 popup_size); | 322 popup_size); |
291 } | 323 } |
292 | 324 |
293 int AutofillPopupViewGtk::GetPopupRequiredWidth() { | 325 int AutofillPopupViewGtk::GetPopupRequiredWidth() { |
294 // TODO(csharp): Once the icon is also displayed it will affect the required | |
295 // size so it will need to be included in the calculation. | |
296 int popup_width = element_bounds().width(); | 326 int popup_width = element_bounds().width(); |
297 DCHECK_EQ(autofill_values().size(), autofill_labels().size()); | 327 DCHECK_EQ(autofill_values().size(), autofill_labels().size()); |
298 for (size_t i = 0; i < autofill_values().size(); ++i) { | 328 for (size_t i = 0; i < autofill_values().size(); ++i) { |
299 popup_width = std::max(popup_width, | 329 int row_size = font_.GetStringWidth(autofill_values()[i]) + |
300 font_.GetStringWidth(autofill_values()[i]) + | 330 kMiddlePadding + |
301 kMiddlePadding + | 331 font_.GetStringWidth(autofill_labels()[i]); |
302 font_.GetStringWidth(autofill_labels()[i])); | 332 |
| 333 // Add the icon size if required. |
| 334 if (!autofill_icons()[i].empty()) |
| 335 row_size += kIconWidth + 2 * kIconPadding; |
| 336 |
| 337 popup_width = std::max(popup_width, row_size); |
303 } | 338 } |
304 | 339 |
305 return popup_width; | 340 return popup_width; |
306 } | 341 } |
307 | 342 |
308 int AutofillPopupViewGtk::LineFromY(int y) { | 343 int AutofillPopupViewGtk::LineFromY(int y) { |
309 return y / row_height_; | 344 return y / kRowHeight; |
310 } | 345 } |
OLD | NEW |