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. | |
Ilya Sherman
2012/05/11 22:12:17
nit: "in pixels"
csharp
2012/05/14 14:19:03
Done.
| |
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. | |
Ilya Sherman
2012/05/11 22:12:17
nit: "in pixels"
csharp
2012/05/14 14:19:03
Done.
| |
40 const int kIconWidth = 25; | |
41 | |
42 // Height of the icons | |
Ilya Sherman
2012/05/11 22:12:17
nit: "in pixels"
csharp
2012/05/14 14:19:03
Done.
| |
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 Profile* profile, | |
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_(GtkThemeService::GetFrom(profile)), | |
Ilya Sherman
2012/05/11 22:12:17
nit: How about passing the GtkThemeService directl
csharp
2012/05/14 14:19:03
Done.
| |
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 (has_icons()) | |
Ilya Sherman
2012/05/11 22:12:17
Hmm, interesting. Did you try the other option, o
csharp
2012/05/14 14:19:03
In my quick testing I don't think there is output
Ilya Sherman
2012/05/14 20:38:22
The only way that I can think of to have mixed ico
| |
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] != string16()) { | |
Ilya Sherman
2012/05/11 22:12:17
nit: if (!autofill_icons()[i].empty())
csharp
2012/05/14 14:19:03
Done.
| |
218 int icon = GetIconResourceID(autofill_icons()[i].c_str()); | |
Ilya Sherman
2012/05/11 22:12:17
nit: Why are you passing the c_str()?
csharp
2012/05/14 14:19:03
Good catch, that was from when I was trying differ
| |
219 if (icon != -1) { | |
Ilya Sherman
2012/05/11 22:12:17
nit: Can this be a DCHECK rather than an if-stmt?
csharp
2012/05/14 14:19:03
Yup, I had thought GetImageNamed might crash with
| |
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 } | |
231 | |
232 } | |
199 } | 233 } |
200 | 234 |
201 cairo_destroy(cr); | 235 cairo_destroy(cr); |
202 | 236 |
203 return TRUE; | 237 return TRUE; |
204 } | 238 } |
205 | 239 |
206 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, | 240 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, |
207 GdkEventMotion* event) { | 241 GdkEventMotion* event) { |
208 int line = LineFromY(event->y); | 242 int line = LineFromY(event->y); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
240 return AcceptSelectedLine(); | 274 return AcceptSelectedLine(); |
241 } | 275 } |
242 | 276 |
243 return false; | 277 return false; |
244 } | 278 } |
245 | 279 |
246 void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, | 280 void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, |
247 const GdkColor& text_color) { | 281 const GdkColor& text_color) { |
248 int allocated_content_width = window_rect.width(); | 282 int allocated_content_width = window_rect.width(); |
249 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); | 283 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); |
250 pango_layout_set_height(layout_, row_height_ * PANGO_SCALE); | 284 pango_layout_set_height(layout_, kRowHeight * PANGO_SCALE); |
251 | 285 |
252 PangoAttrList* attrs = pango_attr_list_new(); | 286 PangoAttrList* attrs = pango_attr_list_new(); |
253 | 287 |
254 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, | 288 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, |
255 text_color.green, | 289 text_color.green, |
256 text_color.blue); | 290 text_color.blue); |
257 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. | 291 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. |
258 | 292 |
259 | 293 |
260 pango_layout_set_attributes(layout_, attrs); // Ref taken. | 294 pango_layout_set_attributes(layout_, attrs); // Ref taken. |
261 pango_attr_list_unref(attrs); | 295 pango_attr_list_unref(attrs); |
262 } | 296 } |
263 | 297 |
264 void AutofillPopupViewGtk::SetBounds() { | 298 void AutofillPopupViewGtk::SetBounds() { |
265 gint origin_x, origin_y; | 299 gint origin_x, origin_y; |
266 gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); | 300 gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); |
267 | 301 |
268 GdkScreen* screen = gtk_widget_get_screen(parent_); | 302 GdkScreen* screen = gtk_widget_get_screen(parent_); |
269 gint screen_height = gdk_screen_get_height(screen); | 303 gint screen_height = gdk_screen_get_height(screen); |
270 | 304 |
271 int bottom_of_field = origin_y + element_bounds().y() + | 305 int bottom_of_field = origin_y + element_bounds().y() + |
272 element_bounds().height(); | 306 element_bounds().height(); |
273 int popup_size = row_height_ * autofill_values().size(); | 307 int popup_size = kRowHeight * autofill_values().size(); |
274 | 308 |
275 // Find the correct top position of the popup so that is doesn't go off | 309 // Find the correct top position of the popup so that is doesn't go off |
276 // the screen. | 310 // the screen. |
277 int top_of_popup = 0; | 311 int top_of_popup = 0; |
278 if (screen_height < bottom_of_field + popup_size) { | 312 if (screen_height < bottom_of_field + popup_size) { |
279 // The popup must appear above the field. | 313 // The popup must appear above the field. |
280 top_of_popup = origin_y + element_bounds().y() - popup_size; | 314 top_of_popup = origin_y + element_bounds().y() - popup_size; |
281 } else { | 315 } else { |
282 // The popup can appear below the field. | 316 // The popup can appear below the field. |
283 top_of_popup = bottom_of_field; | 317 top_of_popup = bottom_of_field; |
284 } | 318 } |
285 | 319 |
286 bounds_.SetRect( | 320 bounds_.SetRect( |
287 origin_x + element_bounds().x(), | 321 origin_x + element_bounds().x(), |
288 top_of_popup, | 322 top_of_popup, |
289 GetPopupRequiredWidth(), | 323 GetPopupRequiredWidth(), |
290 popup_size); | 324 popup_size); |
291 } | 325 } |
292 | 326 |
293 int AutofillPopupViewGtk::GetPopupRequiredWidth() { | 327 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(); | 328 int popup_width = element_bounds().width(); |
297 DCHECK_EQ(autofill_values().size(), autofill_labels().size()); | 329 DCHECK_EQ(autofill_values().size(), autofill_labels().size()); |
298 for (size_t i = 0; i < autofill_values().size(); ++i) { | 330 for (size_t i = 0; i < autofill_values().size(); ++i) { |
299 popup_width = std::max(popup_width, | 331 int row_size = font_.GetStringWidth(autofill_values()[i]) + |
300 font_.GetStringWidth(autofill_values()[i]) + | 332 kMiddlePadding + |
301 kMiddlePadding + | 333 font_.GetStringWidth(autofill_labels()[i]); |
302 font_.GetStringWidth(autofill_labels()[i])); | 334 |
335 // Add the icon size if required. | |
336 if (autofill_icons()[i] != string16()) | |
Ilya Sherman
2012/05/11 22:12:17
nit: if (!autofill_icons()[i].empty())
csharp
2012/05/14 14:19:03
Done.
| |
337 row_size += kIconWidth + 2 * kIconPadding; | |
338 | |
339 popup_width = std::max(popup_width, row_size); | |
303 } | 340 } |
304 | 341 |
305 return popup_width; | 342 return popup_width; |
306 } | 343 } |
307 | 344 |
308 int AutofillPopupViewGtk::LineFromY(int y) { | 345 int AutofillPopupViewGtk::LineFromY(int y) { |
309 return y / row_height_; | 346 return y / kRowHeight; |
310 } | 347 } |
OLD | NEW |