Chromium Code Reviews| 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 "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/utf_string_conversions.h" | |
| 9 #include "ui/gfx/rect.h" | |
| 10 #include "ui/gfx/native_widget_types.h" | |
| 11 #include "ui/base/gtk/gtk_hig_constants.h" | |
| 8 #include "ui/base/gtk/gtk_windowing.h" | 12 #include "ui/base/gtk/gtk_windowing.h" |
| 13 #include "ui/gfx/font.h" | |
| 14 | |
| 15 namespace { | |
| 16 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); | |
| 17 const GdkColor kTextColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); | |
| 18 | |
| 19 // The amount of minimum padding between the autofill value and label in pixels. | |
| 20 const int kMiddlePadding = 10; | |
| 21 | |
| 22 // We have a 1 pixel border around the entire results popup. | |
| 23 const int kBorderThickness = 1; | |
| 24 | |
| 25 // Returns the |window| rectangle as a gfx::Rect. | |
| 26 gfx::Rect GetWindowRect(GdkWindow* window) { | |
| 27 gint width, height; | |
| 28 gdk_drawable_get_size(GDK_DRAWABLE(window), &width, &height); | |
|
Elliot Glaysher
2012/01/12 20:34:11
include gtk_compat.h and use gdk_window_get_width/
csharp
2012/01/13 18:09:03
Done. I moved SetTextToDraw to gtk_util and rename
| |
| 29 return gfx::Rect(width, height); | |
| 30 } | |
| 31 | |
| 32 // Returns the rectangle containing the item at position |index| in the popup. | |
| 33 gfx::Rect GetRectForItemByIndex(size_t index, int width, int height) { | |
|
Ilya Sherman
2012/01/13 02:45:37
nit: Another possible (shorter!) name: GetRectForR
csharp
2012/01/13 18:09:03
Done.
| |
| 34 return gfx::Rect(0, (index * height), width, height); | |
| 35 } | |
| 36 | |
| 37 // Sets the text to be displayed by |layout|. | |
| 38 void SetTextToDraw(PangoLayout* layout, const string16& text) { | |
| 39 // Pango is really easy to overflow and send into a computational death | |
| 40 // spiral that can corrupt the screen. Assume that we'll never have more than | |
| 41 // 2000 characters, which should be a safe assumption until we all get robot | |
| 42 // eyes. http://crbug.com/66576 | |
| 43 std::string text_utf8 = UTF16ToUTF8(text); | |
| 44 if (text_utf8.length() > 2000) | |
| 45 text_utf8 = text_utf8.substr(0, 2000); | |
| 46 | |
| 47 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); | |
| 48 } | |
| 49 | |
| 50 } // namespace | |
| 9 | 51 |
| 10 AutofillPopupViewGtk::AutofillPopupViewGtk(content::WebContents* web_contents, | 52 AutofillPopupViewGtk::AutofillPopupViewGtk(content::WebContents* web_contents, |
| 11 GtkWidget* parent) | 53 GtkWidget* parent) |
| 12 : AutofillPopupView(web_contents), | 54 : AutofillPopupView(web_contents), |
| 13 parent_(parent), | 55 parent_(parent), |
| 14 window_(gtk_window_new(GTK_WINDOW_POPUP)) { | 56 window_(gtk_window_new(GTK_WINDOW_POPUP)) { |
| 15 CHECK(parent != NULL); | 57 CHECK(parent != NULL); |
| 16 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 58 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
| 17 gtk_widget_set_app_paintable(window_, TRUE); | 59 gtk_widget_set_app_paintable(window_, TRUE); |
| 18 gtk_widget_set_double_buffered(window_, TRUE); | 60 gtk_widget_set_double_buffered(window_, TRUE); |
| 19 | 61 |
| 20 // Setup the window to ensure it recieves the expose event. | 62 // Setup the window to ensure it recieves the expose event. |
| 21 gtk_widget_add_events(window_, GDK_EXPOSURE_MASK); | 63 gtk_widget_add_events(window_, GDK_EXPOSURE_MASK); |
| 22 g_signal_connect(window_, "expose-event", | 64 g_signal_connect(window_, "expose-event", |
| 23 G_CALLBACK(HandleExposeThunk), this); | 65 G_CALLBACK(HandleExposeThunk), this); |
| 66 | |
| 67 // Cache the layout so we don't have to create it for every expose. | |
| 68 layout_ = gtk_widget_create_pango_layout(window_, NULL); | |
| 69 | |
| 70 row_height_ = font_.GetHeight(); | |
| 24 } | 71 } |
| 25 | 72 |
| 26 AutofillPopupViewGtk::~AutofillPopupViewGtk() { | 73 AutofillPopupViewGtk::~AutofillPopupViewGtk() { |
| 74 g_object_unref(layout_); | |
| 27 gtk_widget_destroy(window_); | 75 gtk_widget_destroy(window_); |
| 28 } | 76 } |
| 29 | 77 |
| 30 void AutofillPopupViewGtk::Hide() { | 78 void AutofillPopupViewGtk::Hide() { |
| 31 gtk_widget_hide(window_); | 79 gtk_widget_hide(window_); |
| 32 } | 80 } |
| 33 | 81 |
| 34 // TODO(csharp): Actually show the values. | 82 void AutofillPopupViewGtk::ShowInternal() { |
| 35 void AutofillPopupViewGtk::Show(const std::vector<string16>& autofill_values, | |
| 36 const std::vector<string16>& autofill_labels, | |
| 37 const std::vector<string16>& autofill_icons, | |
| 38 const std::vector<int>& autofill_unique_ids, | |
| 39 int separator_index) { | |
| 40 gint origin_x, origin_y; | 83 gint origin_x, origin_y; |
| 41 gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); | 84 gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); |
| 42 | 85 |
| 86 // Move the popup to appear right below the text field it is using. | |
| 43 gtk_window_move(GTK_WINDOW(window_), | 87 gtk_window_move(GTK_WINDOW(window_), |
| 44 origin_x + element_bounds().x(), | 88 origin_x + element_bounds().x(), |
| 45 origin_y + element_bounds().y() + element_bounds().height()); | 89 origin_y + element_bounds().y() + element_bounds().height()); |
| 46 | 90 |
| 91 // Find out the maximum bounds required by the popup. | |
| 92 // TODO(csharp): Once the icon is also displayed it will affect the required | |
| 93 // size so it will need to be include in the calculation. | |
| 94 int popup_width = element_bounds().width(); | |
| 95 DCHECK_EQ(autofill_values().size(), autofill_labels().size()); | |
| 96 for (size_t i = 0; i < autofill_values().size(); ++i) { | |
| 97 popup_width = std::max(popup_width, | |
| 98 font_.GetStringWidth(autofill_values()[i]) + | |
| 99 kMiddlePadding + | |
| 100 font_.GetStringWidth(autofill_labels()[i])); | |
| 101 } | |
| 102 | |
| 47 gtk_widget_set_size_request( | 103 gtk_widget_set_size_request( |
| 48 window_, | 104 window_, |
| 49 element_bounds().width(), | 105 popup_width, |
| 50 element_bounds().height() * autofill_values.size()); | 106 row_height_ * autofill_values().size()); |
| 51 | 107 |
| 52 gtk_widget_show(window_); | 108 gtk_widget_show(window_); |
| 53 | 109 |
| 54 GtkWidget* toplevel = gtk_widget_get_toplevel(parent_); | 110 GtkWidget* toplevel = gtk_widget_get_toplevel(parent_); |
| 55 CHECK(gtk_widget_is_toplevel(toplevel)); | 111 CHECK(gtk_widget_is_toplevel(toplevel)); |
| 56 ui::StackPopupWindow(window_, toplevel); | 112 ui::StackPopupWindow(window_, toplevel); |
| 57 } | 113 } |
| 58 | 114 |
| 59 gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, | 115 gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, |
| 60 GdkEventExpose* event) { | 116 GdkEventExpose* event) { |
| 117 gfx::Rect window_rect = GetWindowRect(event->window); | |
| 118 gfx::Rect damage_rect = gfx::Rect(event->area); | |
| 119 | |
| 120 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget))); | |
|
Elliot Glaysher
2012/01/12 20:34:11
When I said above to cut GDK_DRAWABLE, please don'
| |
| 121 gdk_cairo_rectangle(cr, &event->area); | |
| 122 cairo_clip(cr); | |
| 123 | |
| 124 // This assert is kinda ugly, but it would be more currently unneeded work | |
| 125 // to support painting a border that isn't 1 pixel thick. There is no point | |
| 126 // in writing that code now, and explode if that day ever comes. | |
| 127 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); | |
| 128 // Draw the 1px border around the entire window. | |
| 129 gdk_cairo_set_source_color(cr, &kBorderColor); | |
| 130 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); | |
| 131 cairo_stroke(cr); | |
| 132 | |
| 133 SetupLayout(window_rect, kTextColor); | |
| 134 | |
| 135 int actual_content_width, actual_content_height; | |
| 136 pango_layout_get_size(layout_, &actual_content_width, &actual_content_height); | |
| 137 actual_content_width /= PANGO_SCALE; | |
| 138 actual_content_height /= PANGO_SCALE; | |
| 139 | |
| 140 for (size_t i = 0; i < autofill_values().size(); ++i) { | |
| 141 gfx::Rect line_rect = | |
| 142 GetRectForItemByIndex(i, window_rect.width(), row_height_); | |
| 143 // Only repaint and layout damaged lines. | |
| 144 if (!line_rect.Intersects(damage_rect)) | |
| 145 continue; | |
| 146 | |
| 147 if (separator_index() == static_cast<int>(i)) { | |
| 148 int line_y = i * row_height_; | |
| 149 | |
| 150 cairo_save(cr); | |
| 151 cairo_move_to(cr, 0, line_y); | |
| 152 cairo_line_to(cr, window_rect.width(), line_y); | |
| 153 cairo_stroke(cr); | |
| 154 cairo_restore(cr); | |
| 155 } | |
| 156 | |
| 157 // Center the text within the line. | |
| 158 int content_y = std::max( | |
| 159 line_rect.y(), | |
| 160 line_rect.y() + ((row_height_ - actual_content_height) / 2)); | |
| 161 | |
| 162 // Draw the autofill value. | |
| 163 SetTextToDraw(layout_, autofill_values()[i]); | |
| 164 | |
| 165 cairo_save(cr); | |
| 166 cairo_move_to(cr, 0, content_y); | |
| 167 pango_cairo_show_layout(cr, layout_); | |
| 168 cairo_restore(cr); | |
| 169 | |
| 170 // Draw the autofill label. | |
| 171 int x_align_left = window_rect.width() - | |
| 172 font_.GetStringWidth(autofill_labels()[i]); | |
| 173 SetTextToDraw(layout_, autofill_labels()[i]); | |
| 174 | |
| 175 cairo_save(cr); | |
| 176 cairo_move_to(cr, x_align_left, line_rect.y()); | |
| 177 pango_cairo_show_layout(cr, layout_); | |
| 178 cairo_restore(cr); | |
| 179 } | |
| 180 | |
| 181 cairo_destroy(cr); | |
| 182 | |
| 61 return TRUE; | 183 return TRUE; |
| 62 } | 184 } |
| 185 | |
| 186 void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, | |
| 187 const GdkColor& text_color) { | |
| 188 int allocated_content_width = window_rect.width(); | |
| 189 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); | |
| 190 pango_layout_set_height(layout_, row_height_ * PANGO_SCALE); | |
| 191 | |
| 192 PangoAttrList* attrs = pango_attr_list_new(); | |
| 193 | |
| 194 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red, | |
| 195 text_color.green, | |
| 196 text_color.blue); | |
| 197 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. | |
| 198 | |
| 199 | |
| 200 pango_layout_set_attributes(layout_, attrs); // Ref taken. | |
| 201 pango_attr_list_unref(attrs); | |
| 202 } | |
| OLD | NEW |