OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 16 matching lines...) Expand all Loading... |
27 #include "chrome/browser/ui/omnibox/omnibox_view.h" | 27 #include "chrome/browser/ui/omnibox/omnibox_view.h" |
28 #include "chrome/common/chrome_notification_types.h" | 28 #include "chrome/common/chrome_notification_types.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_windowing.h" | 33 #include "ui/base/gtk/gtk_windowing.h" |
34 #include "ui/gfx/color_utils.h" | 34 #include "ui/gfx/color_utils.h" |
35 #include "ui/gfx/font.h" | 35 #include "ui/gfx/font.h" |
36 #include "ui/gfx/gtk_util.h" | 36 #include "ui/gfx/gtk_util.h" |
| 37 #include "ui/gfx/image/cairo_cached_surface.h" |
| 38 #include "ui/gfx/image/image.h" |
37 #include "ui/gfx/rect.h" | 39 #include "ui/gfx/rect.h" |
38 #include "ui/gfx/skia_utils_gtk.h" | 40 #include "ui/gfx/skia_utils_gtk.h" |
39 | 41 |
40 namespace { | 42 namespace { |
41 | 43 |
42 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); | 44 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); |
43 const GdkColor kBackgroundColor = GDK_COLOR_RGB(0xff, 0xff, 0xff); | 45 const GdkColor kBackgroundColor = GDK_COLOR_RGB(0xff, 0xff, 0xff); |
44 const GdkColor kSelectedBackgroundColor = GDK_COLOR_RGB(0xdf, 0xe6, 0xf6); | 46 const GdkColor kSelectedBackgroundColor = GDK_COLOR_RGB(0xdf, 0xe6, 0xf6); |
45 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0xef, 0xf2, 0xfa); | 47 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0xef, 0xf2, 0xfa); |
46 | 48 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 // Return a Rect for the space for a result line. This excludes the border, | 99 // Return a Rect for the space for a result line. This excludes the border, |
98 // but includes the padding. This is the area that is colored for a selection. | 100 // but includes the padding. This is the area that is colored for a selection. |
99 gfx::Rect GetRectForLine(size_t line, int width) { | 101 gfx::Rect GetRectForLine(size_t line, int width) { |
100 return gfx::Rect(kBorderThickness, | 102 return gfx::Rect(kBorderThickness, |
101 (line * kHeightPerResult) + kBorderThickness, | 103 (line * kHeightPerResult) + kBorderThickness, |
102 width - (kBorderThickness * 2), | 104 width - (kBorderThickness * 2), |
103 kHeightPerResult); | 105 kHeightPerResult); |
104 } | 106 } |
105 | 107 |
106 // Helper for drawing an entire pixbuf without dithering. | 108 // Helper for drawing an entire pixbuf without dithering. |
107 void DrawFullPixbuf(GdkDrawable* drawable, GdkGC* gc, GdkPixbuf* pixbuf, | 109 void DrawFullImage(cairo_t* cr, GtkWidget* widget, const gfx::Image* image, |
108 gint dest_x, gint dest_y) { | 110 gint dest_x, gint dest_y) { |
109 gdk_draw_pixbuf(drawable, gc, pixbuf, | 111 gfx::CairoCachedSurface* surface = image->ToCairo(); |
110 0, 0, // Source. | 112 surface->SetSource(cr, widget, dest_x, dest_y); |
111 dest_x, dest_y, // Dest. | 113 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); |
112 -1, -1, // Width/height (auto). | 114 cairo_rectangle(cr, dest_x, dest_y, surface->Width(), surface->Height()); |
113 GDK_RGB_DITHER_NONE, 0, 0); // Don't dither. | 115 cairo_fill(cr); |
114 } | 116 } |
115 | 117 |
116 // TODO(deanm): Find some better home for this, and make it more efficient. | 118 // TODO(deanm): Find some better home for this, and make it more efficient. |
117 size_t GetUTF8Offset(const string16& text, size_t text_offset) { | 119 size_t GetUTF8Offset(const string16& text, size_t text_offset) { |
118 return UTF16ToUTF8(text.substr(0, text_offset)).length(); | 120 return UTF16ToUTF8(text.substr(0, text_offset)).length(); |
119 } | 121 } |
120 | 122 |
121 // Generates the normal URL color, a green color used in unhighlighted URL | 123 // Generates the normal URL color, a green color used in unhighlighted URL |
122 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the | 124 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the |
123 // selected text color, it is more important to match the qualities of the | 125 // selected text color, it is more important to match the qualities of the |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 } | 333 } |
332 | 334 |
333 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { | 335 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { |
334 // Explicitly destroy our model here, before we destroy our GTK widgets. | 336 // Explicitly destroy our model here, before we destroy our GTK widgets. |
335 // This is because the model destructor can call back into us, and we need | 337 // This is because the model destructor can call back into us, and we need |
336 // to make sure everything is still valid when it does. | 338 // to make sure everything is still valid when it does. |
337 model_.reset(); | 339 model_.reset(); |
338 g_object_unref(layout_); | 340 g_object_unref(layout_); |
339 gtk_widget_destroy(window_); | 341 gtk_widget_destroy(window_); |
340 | 342 |
341 for (PixbufMap::iterator it = pixbufs_.begin(); it != pixbufs_.end(); ++it) | 343 for (ImageMap::iterator it = images_.begin(); it != images_.end(); ++it) |
342 g_object_unref(it->second); | 344 delete it->second; |
343 } | 345 } |
344 | 346 |
345 bool OmniboxPopupViewGtk::IsOpen() const { | 347 bool OmniboxPopupViewGtk::IsOpen() const { |
346 return opened_; | 348 return opened_; |
347 } | 349 } |
348 | 350 |
349 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { | 351 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { |
350 // TODO(deanm): Is it possible to use some constant for the width, instead | 352 // TODO(deanm): Is it possible to use some constant for the width, instead |
351 // of having to query the width of the window? | 353 // of having to query the width of the window? |
352 GdkRectangle line_rect = GetRectForLine( | 354 GdkRectangle line_rect = GetRectForLine( |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
476 // OpenMatch() may close the popup, which will clear the result set and, by | 478 // 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 | 479 // extension, |match| and its contents. So copy the relevant match out to |
478 // make sure it stays alive until the call completes. | 480 // make sure it stays alive until the call completes. |
479 AutocompleteMatch match = model_->result().match_at(line); | 481 AutocompleteMatch match = model_->result().match_at(line); |
480 string16 keyword; | 482 string16 keyword; |
481 const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword); | 483 const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword); |
482 omnibox_view_->OpenMatch(match, disposition, GURL(), line, | 484 omnibox_view_->OpenMatch(match, disposition, GURL(), line, |
483 is_keyword_hint ? string16() : keyword); | 485 is_keyword_hint ? string16() : keyword); |
484 } | 486 } |
485 | 487 |
486 GdkPixbuf* OmniboxPopupViewGtk::IconForMatch(const AutocompleteMatch& match, | 488 const gfx::Image* OmniboxPopupViewGtk::IconForMatch( |
487 bool selected) { | 489 const AutocompleteMatch& match, bool selected) { |
488 const SkBitmap* bitmap = model_->GetIconIfExtensionMatch(match); | 490 const SkBitmap* bitmap = model_->GetIconIfExtensionMatch(match); |
489 if (bitmap) { | 491 if (bitmap) { |
490 if (!ContainsKey(pixbufs_, bitmap)) | 492 if (!ContainsKey(images_, bitmap)) { |
491 pixbufs_[bitmap] = gfx::GdkPixbufFromSkBitmap(bitmap); | 493 // gfx::Image wants ownership of bitmaps given to it, and we might as |
492 return pixbufs_[bitmap]; | 494 // well make the bitmap copy a format that will be used. |
| 495 images_[bitmap] = new gfx::Image(gfx::GdkPixbufFromSkBitmap(bitmap)); |
| 496 } |
| 497 return images_[bitmap]; |
493 } | 498 } |
494 | 499 |
495 int icon = match.starred ? | 500 int icon = match.starred ? |
496 IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match.type); | 501 IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match.type); |
497 if (selected) { | 502 if (selected) { |
498 switch (icon) { | 503 switch (icon) { |
499 case IDR_OMNIBOX_EXTENSION_APP: | 504 case IDR_OMNIBOX_EXTENSION_APP: |
500 icon = IDR_OMNIBOX_EXTENSION_APP_DARK; | 505 icon = IDR_OMNIBOX_EXTENSION_APP_DARK; |
501 break; | 506 break; |
502 case IDR_OMNIBOX_HTTP: | 507 case IDR_OMNIBOX_HTTP: |
503 icon = IDR_OMNIBOX_HTTP_DARK; | 508 icon = IDR_OMNIBOX_HTTP_DARK; |
504 break; | 509 break; |
505 case IDR_OMNIBOX_HISTORY: | 510 case IDR_OMNIBOX_HISTORY: |
506 icon = IDR_OMNIBOX_HISTORY_DARK; | 511 icon = IDR_OMNIBOX_HISTORY_DARK; |
507 break; | 512 break; |
508 case IDR_OMNIBOX_SEARCH: | 513 case IDR_OMNIBOX_SEARCH: |
509 icon = IDR_OMNIBOX_SEARCH_DARK; | 514 icon = IDR_OMNIBOX_SEARCH_DARK; |
510 break; | 515 break; |
511 case IDR_OMNIBOX_STAR: | 516 case IDR_OMNIBOX_STAR: |
512 icon = IDR_OMNIBOX_STAR_DARK; | 517 icon = IDR_OMNIBOX_STAR_DARK; |
513 break; | 518 break; |
514 default: | 519 default: |
515 NOTREACHED(); | 520 NOTREACHED(); |
516 break; | 521 break; |
517 } | 522 } |
518 } | 523 } |
519 | 524 |
520 // TODO(estade): Do we want to flip these for RTL? (Windows doesn't). | 525 return theme_service_->GetImageNamed(icon); |
521 return theme_service_->GetPixbufNamed(icon); | |
522 } | 526 } |
523 | 527 |
524 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, | 528 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, |
525 GdkEventMotion* event) { | 529 GdkEventMotion* event) { |
526 // TODO(deanm): Windows has a bunch of complicated logic here. | 530 // TODO(deanm): Windows has a bunch of complicated logic here. |
527 size_t line = LineFromY(static_cast<int>(event->y)); | 531 size_t line = LineFromY(static_cast<int>(event->y)); |
528 // There is both a hovered and selected line, hovered just means your mouse | 532 // There is both a hovered and selected line, hovered just means your mouse |
529 // is over it, but selected is what's showing in the location edit. | 533 // is over it, but selected is what's showing in the location edit. |
530 model_->SetHoveredLine(line); | 534 model_->SetHoveredLine(line); |
531 // Select the line if the user has the left mouse button down. | 535 // Select the line if the user has the left mouse button down. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 gfx::Rect window_rect = GetWindowRect(event->window); | 580 gfx::Rect window_rect = GetWindowRect(event->window); |
577 gfx::Rect damage_rect = gfx::Rect(event->area); | 581 gfx::Rect damage_rect = gfx::Rect(event->area); |
578 // Handle when our window is super narrow. A bunch of the calculations | 582 // Handle when our window is super narrow. A bunch of the calculations |
579 // below would go negative, and really we're not going to fit anything | 583 // below would go negative, and really we're not going to fit anything |
580 // useful in such a small window anyway. Just don't paint anything. | 584 // useful in such a small window anyway. Just don't paint anything. |
581 // This means we won't draw the border, but, yeah, whatever. | 585 // This means we won't draw the border, but, yeah, whatever. |
582 // TODO(deanm): Make the code more robust and remove this check. | 586 // TODO(deanm): Make the code more robust and remove this check. |
583 if (window_rect.width() < (kIconAreaWidth * 3)) | 587 if (window_rect.width() < (kIconAreaWidth * 3)) |
584 return TRUE; | 588 return TRUE; |
585 | 589 |
586 GdkDrawable* drawable = GDK_DRAWABLE(event->window); | 590 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window)); |
587 GdkGC* gc = gdk_gc_new(drawable); | 591 gdk_cairo_rectangle(cr, &event->area); |
588 | 592 cairo_clip(cr); |
589 // kBorderColor is unallocated, so use the GdkRGB routine. | |
590 gdk_gc_set_rgb_fg_color(gc, &border_color_); | |
591 | 593 |
592 // This assert is kinda ugly, but it would be more currently unneeded work | 594 // This assert is kinda ugly, but it would be more currently unneeded work |
593 // to support painting a border that isn't 1 pixel thick. There is no point | 595 // to support painting a border that isn't 1 pixel thick. There is no point |
594 // in writing that code now, and explode if that day ever comes. | 596 // in writing that code now, and explode if that day ever comes. |
595 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); | 597 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); |
596 // Draw the 1px border around the entire window. | 598 // Draw the 1px border around the entire window. |
597 gdk_draw_rectangle(drawable, gc, FALSE, | 599 gdk_cairo_set_source_color(cr, &border_color_); |
598 0, 0, | 600 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); |
599 window_rect.width() - 1, window_rect.height() - 1); | 601 cairo_stroke(cr); |
600 | 602 |
601 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); | 603 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); |
602 | 604 |
603 for (size_t i = 0; i < result.size(); ++i) { | 605 for (size_t i = 0; i < result.size(); ++i) { |
604 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); | 606 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); |
605 // Only repaint and layout damaged lines. | 607 // Only repaint and layout damaged lines. |
606 if (!line_rect.Intersects(damage_rect)) | 608 if (!line_rect.Intersects(damage_rect)) |
607 continue; | 609 continue; |
608 | 610 |
609 const AutocompleteMatch& match = result.match_at(i); | 611 const AutocompleteMatch& match = result.match_at(i); |
610 bool is_selected = (model_->selected_line() == i); | 612 bool is_selected = (model_->selected_line() == i); |
611 bool is_hovered = (model_->hovered_line() == i); | 613 bool is_hovered = (model_->hovered_line() == i); |
612 if (is_selected || is_hovered) { | 614 if (is_selected || is_hovered) { |
613 gdk_gc_set_rgb_fg_color(gc, is_selected ? &selected_background_color_ : | 615 gdk_cairo_set_source_color(cr, is_selected ? &selected_background_color_ : |
614 &hovered_background_color_); | 616 &hovered_background_color_); |
615 // This entry is selected or hovered, fill a rect with the color. | 617 // This entry is selected or hovered, fill a rect with the color. |
616 gdk_draw_rectangle(drawable, gc, TRUE, | 618 cairo_rectangle(cr, line_rect.x(), line_rect.y(), |
617 line_rect.x(), line_rect.y(), | 619 line_rect.width(), line_rect.height()); |
618 line_rect.width(), line_rect.height()); | 620 cairo_fill(cr); |
619 } | 621 } |
620 | 622 |
621 int icon_start_x = ltr ? kIconLeftPadding : | 623 int icon_start_x = ltr ? kIconLeftPadding : |
622 (line_rect.width() - kIconLeftPadding - kIconWidth); | 624 (line_rect.width() - kIconLeftPadding - kIconWidth); |
623 // Draw the icon for this result. | 625 // Draw the icon for this result. |
624 DrawFullPixbuf(drawable, gc, | 626 DrawFullImage(cr, widget, |
625 IconForMatch(match, is_selected), | 627 IconForMatch(match, is_selected), |
626 icon_start_x, line_rect.y() + kIconTopPadding); | 628 icon_start_x, line_rect.y() + kIconTopPadding); |
627 | 629 |
628 // Draw the results text vertically centered in the results space. | 630 // Draw the results text vertically centered in the results space. |
629 // First draw the contents / url, but don't let it take up the whole width | 631 // First draw the contents / url, but don't let it take up the whole width |
630 // if there is also a description to be shown. | 632 // if there is also a description to be shown. |
631 bool has_description = !match.description.empty(); | 633 bool has_description = !match.description.empty(); |
632 int text_width = window_rect.width() - (kIconAreaWidth + kRightPadding); | 634 int text_width = window_rect.width() - (kIconAreaWidth + kRightPadding); |
633 int allocated_content_width = has_description ? | 635 int allocated_content_width = has_description ? |
634 static_cast<int>(text_width * kContentWidthPercentage) : text_width; | 636 static_cast<int>(text_width * kContentWidthPercentage) : text_width; |
635 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); | 637 pango_layout_set_width(layout_, allocated_content_width * PANGO_SCALE); |
636 | 638 |
(...skipping 11 matching lines...) Expand all Loading... |
648 pango_layout_get_size(layout_, | 650 pango_layout_get_size(layout_, |
649 &actual_content_width, &actual_content_height); | 651 &actual_content_width, &actual_content_height); |
650 actual_content_width /= PANGO_SCALE; | 652 actual_content_width /= PANGO_SCALE; |
651 actual_content_height /= PANGO_SCALE; | 653 actual_content_height /= PANGO_SCALE; |
652 | 654 |
653 // DCHECK_LT(actual_content_height, kHeightPerResult); // Font is too tall. | 655 // DCHECK_LT(actual_content_height, kHeightPerResult); // Font is too tall. |
654 // Center the text within the line. | 656 // Center the text within the line. |
655 int content_y = std::max(line_rect.y(), | 657 int content_y = std::max(line_rect.y(), |
656 line_rect.y() + ((kHeightPerResult - actual_content_height) / 2)); | 658 line_rect.y() + ((kHeightPerResult - actual_content_height) / 2)); |
657 | 659 |
658 gdk_draw_layout(drawable, gc, | 660 cairo_save(cr); |
659 ltr ? kIconAreaWidth : | 661 cairo_move_to(cr, |
| 662 ltr ? kIconAreaWidth : |
660 (text_width - actual_content_width), | 663 (text_width - actual_content_width), |
661 content_y, layout_); | 664 content_y); |
| 665 pango_cairo_show_layout(cr, layout_); |
| 666 cairo_restore(cr); |
662 | 667 |
663 if (has_description) { | 668 if (has_description) { |
664 pango_layout_set_width(layout_, | 669 pango_layout_set_width(layout_, |
665 (text_width - actual_content_width) * PANGO_SCALE); | 670 (text_width - actual_content_width) * PANGO_SCALE); |
666 | 671 |
667 // In Windows, a boolean "force_dim" is passed as true for the | 672 // In Windows, a boolean "force_dim" is passed as true for the |
668 // description. Here, we pass the dim text color for both normal and dim, | 673 // description. Here, we pass the dim text color for both normal and dim, |
669 // to accomplish the same thing. | 674 // to accomplish the same thing. |
670 SetupLayoutForMatch(layout_, match.description, match.description_class, | 675 SetupLayoutForMatch(layout_, match.description, match.description_class, |
671 is_selected ? &selected_content_dim_text_color_ : | 676 is_selected ? &selected_content_dim_text_color_ : |
672 &content_dim_text_color_, | 677 &content_dim_text_color_, |
673 is_selected ? &selected_content_dim_text_color_ : | 678 is_selected ? &selected_content_dim_text_color_ : |
674 &content_dim_text_color_, | 679 &content_dim_text_color_, |
675 is_selected ? &url_selected_text_color_ : | 680 is_selected ? &url_selected_text_color_ : |
676 &url_text_color_, | 681 &url_text_color_, |
677 std::string(" - ")); | 682 std::string(" - ")); |
678 gint actual_description_width; | 683 gint actual_description_width; |
679 pango_layout_get_size(layout_, &actual_description_width, NULL); | 684 pango_layout_get_size(layout_, &actual_description_width, NULL); |
680 gdk_draw_layout(drawable, gc, ltr ? | 685 |
681 (kIconAreaWidth + actual_content_width) : | 686 cairo_save(cr); |
682 (text_width - actual_content_width - | 687 cairo_move_to(cr, ltr ? |
683 (actual_description_width / PANGO_SCALE)), | 688 (kIconAreaWidth + actual_content_width) : |
684 content_y, layout_); | 689 (text_width - actual_content_width - |
| 690 (actual_description_width / PANGO_SCALE)), |
| 691 content_y); |
| 692 pango_cairo_show_layout(cr, layout_); |
| 693 cairo_restore(cr); |
685 } | 694 } |
686 } | 695 } |
687 | 696 |
688 g_object_unref(gc); | 697 cairo_destroy(cr); |
689 | |
690 return TRUE; | 698 return TRUE; |
691 } | 699 } |
OLD | NEW |