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 "chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h" | 5 #include "chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <string> | 10 #include <string> |
(...skipping 13 matching lines...) Expand all Loading... |
24 #include "chrome/browser/ui/gtk/gtk_theme_service.h" | 24 #include "chrome/browser/ui/gtk/gtk_theme_service.h" |
25 #include "chrome/browser/ui/gtk/gtk_util.h" | 25 #include "chrome/browser/ui/gtk/gtk_util.h" |
26 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" | 26 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" |
27 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" | 27 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" |
28 #include "chrome/browser/ui/omnibox/omnibox_view.h" | 28 #include "chrome/browser/ui/omnibox/omnibox_view.h" |
29 #include "content/public/browser/notification_source.h" | 29 #include "content/public/browser/notification_source.h" |
30 #include "grit/theme_resources.h" | 30 #include "grit/theme_resources.h" |
31 #include "ui/base/gtk/gtk_compat.h" | 31 #include "ui/base/gtk/gtk_compat.h" |
32 #include "ui/base/gtk/gtk_hig_constants.h" | 32 #include "ui/base/gtk/gtk_hig_constants.h" |
33 #include "ui/base/gtk/gtk_screen_util.h" | 33 #include "ui/base/gtk/gtk_screen_util.h" |
34 #include "ui/base/gtk/gtk_signal_registrar.h" | |
35 #include "ui/base/gtk/gtk_windowing.h" | 34 #include "ui/base/gtk/gtk_windowing.h" |
36 #include "ui/gfx/color_utils.h" | 35 #include "ui/gfx/color_utils.h" |
37 #include "ui/gfx/font.h" | 36 #include "ui/gfx/font.h" |
38 #include "ui/gfx/gtk_util.h" | 37 #include "ui/gfx/gtk_util.h" |
39 #include "ui/gfx/image/cairo_cached_surface.h" | 38 #include "ui/gfx/image/cairo_cached_surface.h" |
40 #include "ui/gfx/image/image.h" | 39 #include "ui/gfx/image/image.h" |
41 #include "ui/gfx/rect.h" | 40 #include "ui/gfx/rect.h" |
42 #include "ui/gfx/skia_utils_gtk.h" | 41 #include "ui/gfx/skia_utils_gtk.h" |
43 | 42 |
44 namespace { | 43 namespace { |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 | 259 |
261 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); | 260 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); |
262 pango_layout_set_attributes(layout, attrs); // Ref taken. | 261 pango_layout_set_attributes(layout, attrs); // Ref taken. |
263 pango_attr_list_unref(attrs); | 262 pango_attr_list_unref(attrs); |
264 } | 263 } |
265 | 264 |
266 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, | 265 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, |
267 OmniboxView* omnibox_view, | 266 OmniboxView* omnibox_view, |
268 OmniboxEditModel* edit_model, | 267 OmniboxEditModel* edit_model, |
269 GtkWidget* location_bar) | 268 GtkWidget* location_bar) |
270 : signal_registrar_(new ui::GtkSignalRegistrar), | 269 : model_(new OmniboxPopupModel(this, edit_model)), |
271 model_(new OmniboxPopupModel(this, edit_model)), | |
272 omnibox_view_(omnibox_view), | 270 omnibox_view_(omnibox_view), |
273 location_bar_(location_bar), | 271 location_bar_(location_bar), |
274 window_(gtk_window_new(GTK_WINDOW_POPUP)), | 272 window_(gtk_window_new(GTK_WINDOW_POPUP)), |
275 layout_(NULL), | 273 layout_(NULL), |
276 theme_service_(GtkThemeService::GetFrom(edit_model->profile())), | 274 theme_service_(GtkThemeService::GetFrom(edit_model->profile())), |
277 font_(font.DeriveFont(kEditFontAdjust)), | 275 font_(font.DeriveFont(kEditFontAdjust)), |
278 ignore_mouse_drag_(false), | 276 ignore_mouse_drag_(false), |
279 opened_(false) { | 277 opened_(false) { |
280 gtk_widget_set_can_focus(window_, FALSE); | 278 gtk_widget_set_can_focus(window_, FALSE); |
281 // Don't allow the window to be resized. This also forces the window to | 279 // Don't allow the window to be resized. This also forces the window to |
282 // shrink down to the size of its child contents. | 280 // shrink down to the size of its child contents. |
283 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 281 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
284 gtk_widget_set_app_paintable(window_, TRUE); | 282 gtk_widget_set_app_paintable(window_, TRUE); |
285 // Have GTK double buffer around the expose signal. | 283 // Have GTK double buffer around the expose signal. |
286 gtk_widget_set_double_buffered(window_, TRUE); | 284 gtk_widget_set_double_buffered(window_, TRUE); |
287 | 285 |
288 // Cache the layout so we don't have to create it for every expose. If we | 286 // Cache the layout so we don't have to create it for every expose. If we |
289 // were a real widget we should handle changing directions, but we're not | 287 // were a real widget we should handle changing directions, but we're not |
290 // doing RTL or anything yet, so it shouldn't be important now. | 288 // doing RTL or anything yet, so it shouldn't be important now. |
291 layout_ = gtk_widget_create_pango_layout(window_, NULL); | 289 layout_ = gtk_widget_create_pango_layout(window_, NULL); |
292 // We don't want the layout of search results depending on their language. | 290 // We don't want the layout of search results depending on their language. |
293 pango_layout_set_auto_dir(layout_, FALSE); | 291 pango_layout_set_auto_dir(layout_, FALSE); |
294 // We always ellipsize when drawing our text runs. | 292 // We always ellipsize when drawing our text runs. |
295 pango_layout_set_ellipsize(layout_, PANGO_ELLIPSIZE_END); | 293 pango_layout_set_ellipsize(layout_, PANGO_ELLIPSIZE_END); |
296 | 294 |
297 gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK | | 295 gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK | |
298 GDK_POINTER_MOTION_MASK | | 296 GDK_POINTER_MOTION_MASK | |
299 GDK_BUTTON_PRESS_MASK | | 297 GDK_BUTTON_PRESS_MASK | |
300 GDK_BUTTON_RELEASE_MASK); | 298 GDK_BUTTON_RELEASE_MASK); |
301 signal_registrar_->Connect(window_, "motion-notify-event", | 299 g_signal_connect(window_, "motion-notify-event", |
302 G_CALLBACK(HandleMotionThunk), this); | 300 G_CALLBACK(HandleMotionThunk), this); |
303 signal_registrar_->Connect(window_, "button-press-event", | 301 g_signal_connect(window_, "button-press-event", |
304 G_CALLBACK(HandleButtonPressThunk), this); | 302 G_CALLBACK(HandleButtonPressThunk), this); |
305 signal_registrar_->Connect(window_, "button-release-event", | 303 g_signal_connect(window_, "button-release-event", |
306 G_CALLBACK(HandleButtonReleaseThunk), this); | 304 G_CALLBACK(HandleButtonReleaseThunk), this); |
307 signal_registrar_->Connect(window_, "expose-event", | 305 g_signal_connect(window_, "expose-event", |
308 G_CALLBACK(HandleExposeThunk), this); | 306 G_CALLBACK(HandleExposeThunk), this); |
309 | 307 |
310 registrar_.Add(this, | 308 registrar_.Add(this, |
311 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | 309 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, |
312 content::Source<ThemeService>(theme_service_)); | 310 content::Source<ThemeService>(theme_service_)); |
313 theme_service_->InitThemesFor(this); | 311 theme_service_->InitThemesFor(this); |
314 | 312 |
315 // TODO(erg): There appears to be a bug somewhere in something which shows | 313 // TODO(erg): There appears to be a bug somewhere in something which shows |
316 // itself when we're in NX. Previously, we called | 314 // itself when we're in NX. Previously, we called |
317 // gtk_util::ActAsRoundedWindow() to make this popup have rounded | 315 // gtk_util::ActAsRoundedWindow() to make this popup have rounded |
318 // corners. This worked on the standard xorg server (both locally and | 316 // corners. This worked on the standard xorg server (both locally and |
319 // remotely), but broke over NX. My current hypothesis is that it can't | 317 // remotely), but broke over NX. My current hypothesis is that it can't |
320 // handle shaping top-level windows during an expose event, but I'm not sure | 318 // handle shaping top-level windows during an expose event, but I'm not sure |
321 // how else to get accurate shaping information. | 319 // how else to get accurate shaping information. |
322 // | 320 // |
323 // r25080 (the original patch that added rounded corners here) should | 321 // r25080 (the original patch that added rounded corners here) should |
324 // eventually be cherry picked once I know what's going | 322 // eventually be cherry picked once I know what's going |
325 // on. http://crbug.com/22015. | 323 // on. http://crbug.com/22015. |
326 } | 324 } |
327 | 325 |
328 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { | 326 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { |
329 // Stop listening to our signals before we destroy the model. I suspect that | |
330 // we can race window destruction, otherwise. | |
331 signal_registrar_.reset(); | |
332 | |
333 // Explicitly destroy our model here, before we destroy our GTK widgets. | 327 // Explicitly destroy our model here, before we destroy our GTK widgets. |
334 // This is because the model destructor can call back into us, and we need | 328 // This is because the model destructor can call back into us, and we need |
335 // to make sure everything is still valid when it does. | 329 // to make sure everything is still valid when it does. |
336 model_.reset(); | 330 model_.reset(); |
337 g_object_unref(layout_); | 331 g_object_unref(layout_); |
338 gtk_widget_destroy(window_); | 332 gtk_widget_destroy(window_); |
339 } | 333 } |
340 | 334 |
341 bool OmniboxPopupViewGtk::IsOpen() const { | 335 bool OmniboxPopupViewGtk::IsOpen() const { |
342 return opened_; | 336 return opened_; |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
460 | 454 |
461 void OmniboxPopupViewGtk::StackWindow() { | 455 void OmniboxPopupViewGtk::StackWindow() { |
462 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); | 456 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); |
463 DCHECK(GTK_IS_WIDGET(omnibox_view)); | 457 DCHECK(GTK_IS_WIDGET(omnibox_view)); |
464 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); | 458 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); |
465 DCHECK(gtk_widget_is_toplevel(toplevel)); | 459 DCHECK(gtk_widget_is_toplevel(toplevel)); |
466 ui::StackPopupWindow(window_, toplevel); | 460 ui::StackPopupWindow(window_, toplevel); |
467 } | 461 } |
468 | 462 |
469 size_t OmniboxPopupViewGtk::LineFromY(int y) { | 463 size_t OmniboxPopupViewGtk::LineFromY(int y) { |
| 464 DCHECK_NE(0U, model_->result().size()); |
470 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; | 465 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; |
471 return std::min(line, model_->result().size() - 1); | 466 return std::min(line, model_->result().size() - 1); |
472 } | 467 } |
473 | 468 |
474 void OmniboxPopupViewGtk::AcceptLine(size_t line, | 469 void OmniboxPopupViewGtk::AcceptLine(size_t line, |
475 WindowOpenDisposition disposition) { | 470 WindowOpenDisposition disposition) { |
476 // OpenMatch() may close the popup, which will clear the result set and, by | 471 // OpenMatch() may close the popup, which will clear the result set and, by |
477 // extension, |match| and its contents. So copy the relevant match out to | 472 // extension, |match| and its contents. So copy the relevant match out to |
478 // make sure it stays alive until the call completes. | 473 // make sure it stays alive until the call completes. |
479 AutocompleteMatch match = model_->result().match_at(line); | 474 AutocompleteMatch match = model_->result().match_at(line); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
535 *is_selected_keyword = true; | 530 *is_selected_keyword = true; |
536 return; | 531 return; |
537 } | 532 } |
538 | 533 |
539 *match = &result.match_at(index); | 534 *match = &result.match_at(index); |
540 *is_selected_keyword = false; | 535 *is_selected_keyword = false; |
541 } | 536 } |
542 | 537 |
543 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, | 538 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, |
544 GdkEventMotion* event) { | 539 GdkEventMotion* event) { |
| 540 if (!IsOpen()) |
| 541 return FALSE; |
| 542 |
545 // TODO(deanm): Windows has a bunch of complicated logic here. | 543 // TODO(deanm): Windows has a bunch of complicated logic here. |
546 size_t line = LineFromY(static_cast<int>(event->y)); | 544 size_t line = LineFromY(static_cast<int>(event->y)); |
547 // There is both a hovered and selected line, hovered just means your mouse | 545 // There is both a hovered and selected line, hovered just means your mouse |
548 // is over it, but selected is what's showing in the location edit. | 546 // is over it, but selected is what's showing in the location edit. |
549 model_->SetHoveredLine(line); | 547 model_->SetHoveredLine(line); |
550 // Select the line if the user has the left mouse button down. | 548 // Select the line if the user has the left mouse button down. |
551 if (!ignore_mouse_drag_ && (event->state & GDK_BUTTON1_MASK)) | 549 if (!ignore_mouse_drag_ && (event->state & GDK_BUTTON1_MASK)) |
552 model_->SetSelectedLine(line, false, false); | 550 model_->SetSelectedLine(line, false, false); |
553 return TRUE; | 551 return TRUE; |
554 } | 552 } |
555 | 553 |
556 gboolean OmniboxPopupViewGtk::HandleButtonPress(GtkWidget* widget, | 554 gboolean OmniboxPopupViewGtk::HandleButtonPress(GtkWidget* widget, |
557 GdkEventButton* event) { | 555 GdkEventButton* event) { |
| 556 if (!IsOpen()) |
| 557 return FALSE; |
| 558 |
558 ignore_mouse_drag_ = false; | 559 ignore_mouse_drag_ = false; |
559 // Very similar to HandleMotion. | 560 // Very similar to HandleMotion. |
560 size_t line = LineFromY(static_cast<int>(event->y)); | 561 size_t line = LineFromY(static_cast<int>(event->y)); |
561 model_->SetHoveredLine(line); | 562 model_->SetHoveredLine(line); |
562 if (event->button == 1) | 563 if (event->button == 1) |
563 model_->SetSelectedLine(line, false, false); | 564 model_->SetSelectedLine(line, false, false); |
564 return TRUE; | 565 return TRUE; |
565 } | 566 } |
566 | 567 |
567 gboolean OmniboxPopupViewGtk::HandleButtonRelease(GtkWidget* widget, | 568 gboolean OmniboxPopupViewGtk::HandleButtonRelease(GtkWidget* widget, |
568 GdkEventButton* event) { | 569 GdkEventButton* event) { |
| 570 if (!IsOpen()) |
| 571 return FALSE; |
| 572 |
569 if (ignore_mouse_drag_) { | 573 if (ignore_mouse_drag_) { |
570 // See header comment about this flag. | 574 // See header comment about this flag. |
571 ignore_mouse_drag_ = false; | 575 ignore_mouse_drag_ = false; |
572 return TRUE; | 576 return TRUE; |
573 } | 577 } |
574 | 578 |
575 size_t line = LineFromY(static_cast<int>(event->y)); | 579 size_t line = LineFromY(static_cast<int>(event->y)); |
576 switch (event->button) { | 580 switch (event->button) { |
577 case 1: // Left click. | 581 case 1: // Left click. |
578 AcceptLine(line, CURRENT_TAB); | 582 AcceptLine(line, CURRENT_TAB); |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
721 theme_service_->GetImageNamed( | 725 theme_service_->GetImageNamed( |
722 is_selected ? IDR_OMNIBOX_TTS_DARK : | 726 is_selected ? IDR_OMNIBOX_TTS_DARK : |
723 IDR_OMNIBOX_TTS), | 727 IDR_OMNIBOX_TTS), |
724 icon_start_x, line_rect.y() + kIconTopPadding); | 728 icon_start_x, line_rect.y() + kIconTopPadding); |
725 } | 729 } |
726 } | 730 } |
727 | 731 |
728 cairo_destroy(cr); | 732 cairo_destroy(cr); |
729 return TRUE; | 733 return TRUE; |
730 } | 734 } |
OLD | NEW |