Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / chrome / browser / ui / gtk / autofill / autofill_popup_view_gtk.cc
blob9fd01804c23930378e0ee80269089ae8fe28d911
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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h"
7 #include <gdk/gdkkeysyms.h>
8 #include <pango/pango.h>
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
13 #include "chrome/browser/ui/gtk/gtk_util.h"
14 #include "grit/ui_resources.h"
15 #include "third_party/WebKit/public/web/WebAutofillClient.h"
16 #include "ui/base/gtk/gtk_hig_constants.h"
17 #include "ui/base/gtk/gtk_windowing.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/gfx/gtk_compat.h"
20 #include "ui/gfx/image/image.h"
21 #include "ui/gfx/native_widget_types.h"
22 #include "ui/gfx/pango_util.h"
23 #include "ui/gfx/rect.h"
24 #include "ui/gfx/text_utils.h"
26 using blink::WebAutofillClient;
28 namespace {
30 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce);
31 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0xcd, 0xcd, 0xcd);
32 const GdkColor kNameColor = GDK_COLOR_RGB(0x00, 0x00, 0x00);
33 const GdkColor kWarningColor = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f);
34 const GdkColor kSubtextColor = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f);
36 } // namespace
38 namespace autofill {
40 AutofillPopupViewGtk::AutofillPopupViewGtk(
41 AutofillPopupController* controller)
42 : controller_(controller),
43 window_(gtk_window_new(GTK_WINDOW_POPUP)) {
44 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
45 gtk_widget_set_app_paintable(window_, TRUE);
46 gtk_widget_set_double_buffered(window_, TRUE);
48 // Setup the window to ensure it receives the expose event.
49 gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK |
50 GDK_BUTTON_RELEASE_MASK |
51 GDK_EXPOSURE_MASK |
52 GDK_POINTER_MOTION_MASK);
54 GtkWidget* toplevel_window = gtk_widget_get_toplevel(
55 controller->container_view());
56 signals_.Connect(toplevel_window, "configure-event",
57 G_CALLBACK(HandleConfigureThunk), this);
58 g_signal_connect(window_, "expose-event",
59 G_CALLBACK(HandleExposeThunk), this);
60 g_signal_connect(window_, "leave-notify-event",
61 G_CALLBACK(HandleLeaveThunk), this);
62 g_signal_connect(window_, "motion-notify-event",
63 G_CALLBACK(HandleMotionThunk), this);
64 g_signal_connect(window_, "button-release-event",
65 G_CALLBACK(HandleButtonReleaseThunk), this);
67 // Cache the layout so we don't have to create it for every expose.
68 layout_ = gtk_widget_create_pango_layout(window_, NULL);
71 AutofillPopupViewGtk::~AutofillPopupViewGtk() {
72 g_object_unref(layout_);
73 gtk_widget_destroy(window_);
76 void AutofillPopupViewGtk::Hide() {
77 delete this;
80 void AutofillPopupViewGtk::Show() {
81 UpdateBoundsAndRedrawPopup();
83 gtk_widget_show(window_);
85 GtkWidget* parent_window =
86 gtk_widget_get_toplevel(controller_->container_view());
87 ui::StackPopupWindow(window_, parent_window);
90 void AutofillPopupViewGtk::InvalidateRow(size_t row) {
91 GdkRectangle row_rect = controller_->GetRowBounds(row).ToGdkRectangle();
92 GdkWindow* gdk_window = gtk_widget_get_window(window_);
93 gdk_window_invalidate_rect(gdk_window, &row_rect, FALSE);
96 void AutofillPopupViewGtk::UpdateBoundsAndRedrawPopup() {
97 gtk_widget_set_size_request(window_,
98 controller_->popup_bounds().width(),
99 controller_->popup_bounds().height());
100 gtk_window_move(GTK_WINDOW(window_),
101 controller_->popup_bounds().x(),
102 controller_->popup_bounds().y());
104 GdkWindow* gdk_window = gtk_widget_get_window(window_);
105 GdkRectangle popup_rect = controller_->popup_bounds().ToGdkRectangle();
106 if (gdk_window != NULL)
107 gdk_window_invalidate_rect(gdk_window, &popup_rect, FALSE);
110 gboolean AutofillPopupViewGtk::HandleConfigure(GtkWidget* widget,
111 GdkEventConfigure* event) {
112 controller_->Hide();
113 return FALSE;
116 gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget,
117 GdkEventButton* event) {
118 // We only care about the left click.
119 if (event->button != 1)
120 return FALSE;
122 controller_->LineAcceptedAtPoint(event->x, event->y);
123 return TRUE;
126 gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget,
127 GdkEventExpose* event) {
128 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget)));
129 gdk_cairo_rectangle(cr, &event->area);
130 cairo_clip(cr);
132 // This assert is kinda ugly, but it would be more currently unneeded work
133 // to support painting a border that isn't 1 pixel thick. There is no point
134 // in writing that code now, and explode if that day ever comes.
135 DCHECK_EQ(1, kBorderThickness);
136 // Draw the 1px border around the entire window.
137 gdk_cairo_set_source_color(cr, &kBorderColor);
138 gdk_cairo_rectangle(cr, &widget->allocation);
139 cairo_stroke(cr);
140 SetUpLayout();
142 gfx::Rect damage_rect(event->area);
144 for (size_t i = 0; i < controller_->names().size(); ++i) {
145 gfx::Rect line_rect = controller_->GetRowBounds(i);
146 // Only repaint and layout damaged lines.
147 if (!line_rect.Intersects(damage_rect))
148 continue;
150 if (controller_->identifiers()[i] == WebAutofillClient::MenuItemIDSeparator)
151 DrawSeparator(cr, line_rect);
152 else
153 DrawAutofillEntry(cr, i, line_rect);
156 cairo_destroy(cr);
158 return TRUE;
161 gboolean AutofillPopupViewGtk::HandleLeave(GtkWidget* widget,
162 GdkEventCrossing* event) {
163 controller_->SelectionCleared();
165 return FALSE;
168 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget,
169 GdkEventMotion* event) {
170 controller_->LineSelectedAtPoint(event->x, event->y);
172 return TRUE;
175 void AutofillPopupViewGtk::SetUpLayout() {
176 pango_layout_set_width(layout_, window_->allocation.width * PANGO_SCALE);
177 pango_layout_set_height(layout_, window_->allocation.height * PANGO_SCALE);
180 void AutofillPopupViewGtk::SetLayoutText(const base::string16& text,
181 const gfx::FontList& font_list,
182 const GdkColor text_color) {
183 PangoAttrList* attrs = pango_attr_list_new();
185 PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red,
186 text_color.green,
187 text_color.blue);
188 pango_attr_list_insert(attrs, fg_attr); // Ownership taken.
190 pango_layout_set_attributes(layout_, attrs); // Ref taken.
191 pango_attr_list_unref(attrs);
193 gfx::ScopedPangoFontDescription font_description(
194 pango_font_description_from_string(
195 font_list.GetFontDescriptionString().c_str()));
196 pango_layout_set_font_description(layout_, font_description.get());
198 gtk_util::SetLayoutText(layout_, text);
200 // The popup is already the correct size for the text, so set the width to -1
201 // to prevent additional wrapping or ellipsization.
202 pango_layout_set_width(layout_, -1);
205 void AutofillPopupViewGtk::DrawSeparator(cairo_t* cairo_context,
206 const gfx::Rect& separator_rect) {
207 cairo_save(cairo_context);
208 cairo_move_to(cairo_context, 0, separator_rect.y());
209 cairo_line_to(cairo_context,
210 separator_rect.width(),
211 separator_rect.y() + separator_rect.height());
212 cairo_stroke(cairo_context);
213 cairo_restore(cairo_context);
216 void AutofillPopupViewGtk::DrawAutofillEntry(cairo_t* cairo_context,
217 size_t index,
218 const gfx::Rect& entry_rect) {
219 if (controller_->selected_line() == static_cast<int>(index)) {
220 gdk_cairo_set_source_color(cairo_context, &kHoveredBackgroundColor);
221 cairo_rectangle(cairo_context, entry_rect.x(), entry_rect.y(),
222 entry_rect.width(), entry_rect.height());
223 cairo_fill(cairo_context);
226 // Draw the value.
227 SetLayoutText(controller_->names()[index],
228 controller_->GetNameFontListForRow(index),
229 controller_->IsWarning(index) ? kWarningColor : kNameColor);
230 int value_text_width =
231 gfx::GetStringWidth(controller_->names()[index],
232 controller_->GetNameFontListForRow(index));
234 // Center the text within the line.
235 int row_height = entry_rect.height();
236 int value_content_y = std::max(
237 entry_rect.y(),
238 entry_rect.y() +
239 (row_height -
240 controller_->GetNameFontListForRow(index).GetHeight()) / 2);
242 bool is_rtl = controller_->IsRTL();
243 int value_content_x = is_rtl ?
244 entry_rect.width() - value_text_width - kEndPadding : kEndPadding;
246 cairo_save(cairo_context);
247 cairo_move_to(cairo_context, value_content_x, value_content_y);
248 pango_cairo_show_layout(cairo_context, layout_);
249 cairo_restore(cairo_context);
251 // Use this to figure out where all the other Autofill items should be placed.
252 int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding;
254 // Draw the Autofill icon, if one exists
255 if (!controller_->icons()[index].empty()) {
256 int icon = controller_->GetIconResourceID(controller_->icons()[index]);
257 DCHECK_NE(-1, icon);
258 const gfx::Image& image =
259 ui::ResourceBundle::GetSharedInstance().GetImageNamed(icon);
260 int icon_y = entry_rect.y() + (row_height - image.Height()) / 2;
262 x_align_left += is_rtl ? 0 : -image.Width();
264 cairo_save(cairo_context);
265 gtk_util::DrawFullImage(cairo_context,
266 window_,
267 image,
268 x_align_left,
269 icon_y);
270 cairo_restore(cairo_context);
272 x_align_left += is_rtl ? image.Width() + kIconPadding : -kIconPadding;
275 // Draw the subtext.
276 SetLayoutText(controller_->subtexts()[index],
277 controller_->subtext_font_list(),
278 kSubtextColor);
279 if (!is_rtl) {
280 x_align_left -= gfx::GetStringWidth(controller_->subtexts()[index],
281 controller_->subtext_font_list());
284 // Center the text within the line.
285 int subtext_content_y = std::max(
286 entry_rect.y(),
287 entry_rect.y() +
288 (row_height - controller_->subtext_font_list().GetHeight()) / 2);
290 cairo_save(cairo_context);
291 cairo_move_to(cairo_context, x_align_left, subtext_content_y);
292 pango_cairo_show_layout(cairo_context, layout_);
293 cairo_restore(cairo_context);
296 AutofillPopupView* AutofillPopupView::Create(
297 AutofillPopupController* controller) {
298 return new AutofillPopupViewGtk(controller);
301 } // namespace autofill