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