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
;
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);
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
|
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() {
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
) {
116 gboolean
AutofillPopupViewGtk::HandleButtonRelease(GtkWidget
* widget
,
117 GdkEventButton
* event
) {
118 // We only care about the left click.
119 if (event
->button
!= 1)
122 controller_
->LineAcceptedAtPoint(event
->x
, event
->y
);
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
);
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
);
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
))
150 if (controller_
->identifiers()[i
] == WebAutofillClient::MenuItemIDSeparator
)
151 DrawSeparator(cr
, line_rect
);
153 DrawAutofillEntry(cr
, i
, line_rect
);
161 gboolean
AutofillPopupViewGtk::HandleLeave(GtkWidget
* widget
,
162 GdkEventCrossing
* event
) {
163 controller_
->SelectionCleared();
168 gboolean
AutofillPopupViewGtk::HandleMotion(GtkWidget
* widget
,
169 GdkEventMotion
* event
) {
170 controller_
->LineSelectedAtPoint(event
->x
, event
->y
);
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
,
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
,
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
);
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(
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
]);
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
,
270 cairo_restore(cairo_context
);
272 x_align_left
+= is_rtl
? image
.Width() + kIconPadding
: -kIconPadding
;
276 SetLayoutText(controller_
->subtexts()[index
],
277 controller_
->subtext_font_list(),
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(
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