Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(119)

Side by Side Diff: chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.cc

Issue 22679003: InstantExtended(gtk): Hide top match if told to so. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add unit tests. Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 // UTF-8 Left-to-right embedding. 91 // UTF-8 Left-to-right embedding.
92 const char* kLRE = "\xe2\x80\xaa"; 92 const char* kLRE = "\xe2\x80\xaa";
93 93
94 // Return a Rect covering the whole area of |window|. 94 // Return a Rect covering the whole area of |window|.
95 gfx::Rect GetWindowRect(GdkWindow* window) { 95 gfx::Rect GetWindowRect(GdkWindow* window) {
96 gint width = gdk_window_get_width(window); 96 gint width = gdk_window_get_width(window);
97 gint height = gdk_window_get_height(window); 97 gint height = gdk_window_get_height(window);
98 return gfx::Rect(width, height); 98 return gfx::Rect(width, height);
99 } 99 }
100 100
101 // Return a Rect for the space for a result line. This excludes the border,
102 // but includes the padding. This is the area that is colored for a selection.
103 gfx::Rect GetRectForLine(size_t line, int width) {
104 return gfx::Rect(kBorderThickness,
105 (line * kHeightPerResult) + kBorderThickness,
106 width - (kBorderThickness * 2),
107 kHeightPerResult);
108 }
109
110 // TODO(deanm): Find some better home for this, and make it more efficient. 101 // TODO(deanm): Find some better home for this, and make it more efficient.
111 size_t GetUTF8Offset(const string16& text, size_t text_offset) { 102 size_t GetUTF8Offset(const string16& text, size_t text_offset) {
112 return UTF16ToUTF8(text.substr(0, text_offset)).length(); 103 return UTF16ToUTF8(text.substr(0, text_offset)).length();
113 } 104 }
114 105
115 // Generates the normal URL color, a green color used in unhighlighted URL 106 // Generates the normal URL color, a green color used in unhighlighted URL
116 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the 107 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the
117 // selected text color, it is more important to match the qualities of the 108 // selected text color, it is more important to match the qualities of the
118 // foreground typeface color instead of taking the background into account. 109 // foreground typeface color instead of taking the background into account.
119 GdkColor NormalURLColor(GdkColor foreground) { 110 GdkColor NormalURLColor(GdkColor foreground) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 // The luminance should match the luminance of the foreground text. Again, 154 // The luminance should match the luminance of the foreground text. Again,
164 // we clamp so as to have at some amount of color (green) in the text. 155 // we clamp so as to have at some amount of color (green) in the text.
165 double opposite_l = fg_hsl.l; 156 double opposite_l = fg_hsl.l;
166 double l = std::max(0.1, std::min(0.9, opposite_l)); 157 double l = std::max(0.1, std::min(0.9, opposite_l));
167 158
168 color_utils::HSL output = { hue_hsl.h, s, l }; 159 color_utils::HSL output = { hue_hsl.h, s, l };
169 return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); 160 return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255));
170 } 161 }
171 } // namespace 162 } // namespace
172 163
164 // static
173 void OmniboxPopupViewGtk::SetupLayoutForMatch( 165 void OmniboxPopupViewGtk::SetupLayoutForMatch(
174 PangoLayout* layout, 166 PangoLayout* layout,
175 const string16& text, 167 const string16& text,
176 const AutocompleteMatch::ACMatchClassifications& classifications, 168 const AutocompleteMatch::ACMatchClassifications& classifications,
177 const GdkColor* base_color, 169 const GdkColor* base_color,
178 const GdkColor* dim_color, 170 const GdkColor* dim_color,
179 const GdkColor* url_color, 171 const GdkColor* url_color,
180 const std::string& prefix_text) { 172 const std::string& prefix_text) {
181 // In RTL, mark text with left-to-right embedding mark if there is no strong 173 // In RTL, mark text with left-to-right embedding mark if there is no strong
182 // RTL characters inside it, so the ending punctuation displays correctly 174 // RTL characters inside it, so the ending punctuation displays correctly
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); 253 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length());
262 pango_layout_set_attributes(layout, attrs); // Ref taken. 254 pango_layout_set_attributes(layout, attrs); // Ref taken.
263 pango_attr_list_unref(attrs); 255 pango_attr_list_unref(attrs);
264 } 256 }
265 257
266 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, 258 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font,
267 OmniboxView* omnibox_view, 259 OmniboxView* omnibox_view,
268 OmniboxEditModel* edit_model, 260 OmniboxEditModel* edit_model,
269 GtkWidget* location_bar) 261 GtkWidget* location_bar)
270 : signal_registrar_(new ui::GtkSignalRegistrar), 262 : signal_registrar_(new ui::GtkSignalRegistrar),
271 model_(new OmniboxPopupModel(this, edit_model)),
272 omnibox_view_(omnibox_view), 263 omnibox_view_(omnibox_view),
273 location_bar_(location_bar), 264 location_bar_(location_bar),
274 window_(gtk_window_new(GTK_WINDOW_POPUP)), 265 window_(gtk_window_new(GTK_WINDOW_POPUP)),
275 layout_(NULL), 266 layout_(NULL),
276 theme_service_(GtkThemeService::GetFrom(edit_model->profile())), 267 theme_service_(NULL),
277 font_(font.DeriveFont(kEditFontAdjust)), 268 font_(font.DeriveFont(kEditFontAdjust)),
278 ignore_mouse_drag_(false), 269 ignore_mouse_drag_(false),
279 opened_(false) { 270 opened_(false) {
271 // edit_model may be NULL in unit tests.
272 if (edit_model) {
273 model_.reset(new OmniboxPopupModel(this, edit_model));
274 theme_service_ = GtkThemeService::GetFrom(edit_model->profile());
275 }
276 }
277
278 void OmniboxPopupViewGtk::Init() {
280 gtk_widget_set_can_focus(window_, FALSE); 279 gtk_widget_set_can_focus(window_, FALSE);
281 // Don't allow the window to be resized. This also forces the window to 280 // Don't allow the window to be resized. This also forces the window to
282 // shrink down to the size of its child contents. 281 // shrink down to the size of its child contents.
283 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); 282 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
284 gtk_widget_set_app_paintable(window_, TRUE); 283 gtk_widget_set_app_paintable(window_, TRUE);
285 // Have GTK double buffer around the expose signal. 284 // Have GTK double buffer around the expose signal.
286 gtk_widget_set_double_buffered(window_, TRUE); 285 gtk_widget_set_double_buffered(window_, TRUE);
287 286
288 // Cache the layout so we don't have to create it for every expose. If we 287 // 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 288 // were a real widget we should handle changing directions, but we're not
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
327 326
328 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { 327 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() {
329 // Stop listening to our signals before we destroy the model. I suspect that 328 // Stop listening to our signals before we destroy the model. I suspect that
330 // we can race window destruction, otherwise. 329 // we can race window destruction, otherwise.
331 signal_registrar_.reset(); 330 signal_registrar_.reset();
332 331
333 // Explicitly destroy our model here, before we destroy our GTK widgets. 332 // 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 333 // 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. 334 // to make sure everything is still valid when it does.
336 model_.reset(); 335 model_.reset();
337 g_object_unref(layout_); 336 // layout_ may be NULL in unit tests.
338 gtk_widget_destroy(window_); 337 if (layout_) {
338 g_object_unref(layout_);
339 gtk_widget_destroy(window_);
340 }
339 } 341 }
340 342
341 bool OmniboxPopupViewGtk::IsOpen() const { 343 bool OmniboxPopupViewGtk::IsOpen() const {
342 return opened_; 344 return opened_;
343 } 345 }
344 346
345 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { 347 void OmniboxPopupViewGtk::InvalidateLine(size_t line) {
346 // TODO(deanm): Is it possible to use some constant for the width, instead 348 // TODO(deanm): Is it possible to use some constant for the width, instead
347 // of having to query the width of the window? 349 // of having to query the width of the window?
348 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); 350 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
349 GdkRectangle line_rect = GetRectForLine( 351 GdkRectangle line_rect = GetRectForLine(
350 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); 352 line, GetWindowRect(gdk_window).width()).ToGdkRectangle();
351 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); 353 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE);
352 } 354 }
353 355
354 void OmniboxPopupViewGtk::UpdatePopupAppearance() { 356 void OmniboxPopupViewGtk::UpdatePopupAppearance() {
355 const AutocompleteResult& result = model_->result(); 357 const AutocompleteResult& result = GetResult();
356 if (result.empty()) { 358 const size_t hidden_matches = GetHiddenMatchCount();
359 if (result.size() <= hidden_matches) {
357 Hide(); 360 Hide();
358 return; 361 return;
359 } 362 }
360 363
361 Show(result.size()); 364 Show(result.size() - hidden_matches);
362 gtk_widget_queue_draw(window_); 365 gtk_widget_queue_draw(window_);
363 } 366 }
364 367
365 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { 368 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() {
366 if (!gtk_widget_get_realized(window_)) 369 if (!gtk_widget_get_realized(window_))
367 return gfx::Rect(); 370 return gfx::Rect();
368 371
369 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); 372 gfx::Rect retval = ui::GetWidgetScreenBounds(window_);
370 373
371 // The widget bounds don't update synchronously so may be out of sync with 374 // The widget bounds don't update synchronously so may be out of sync with
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 void OmniboxPopupViewGtk::StackWindow() { 464 void OmniboxPopupViewGtk::StackWindow() {
462 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); 465 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView();
463 DCHECK(GTK_IS_WIDGET(omnibox_view)); 466 DCHECK(GTK_IS_WIDGET(omnibox_view));
464 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); 467 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view);
465 DCHECK(gtk_widget_is_toplevel(toplevel)); 468 DCHECK(gtk_widget_is_toplevel(toplevel));
466 ui::StackPopupWindow(window_, toplevel); 469 ui::StackPopupWindow(window_, toplevel);
467 } 470 }
468 471
469 size_t OmniboxPopupViewGtk::LineFromY(int y) { 472 size_t OmniboxPopupViewGtk::LineFromY(int y) {
470 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; 473 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult;
471 return std::min(line, model_->result().size() - 1); 474 return std::min(line + GetHiddenMatchCount(), GetResult().size() - 1);
472 } 475 }
473 476
474 void OmniboxPopupViewGtk::AcceptLine(size_t line, 477 void OmniboxPopupViewGtk::AcceptLine(size_t line,
475 WindowOpenDisposition disposition) { 478 WindowOpenDisposition disposition) {
476 // OpenMatch() may close the popup, which will clear the result set and, by 479 // 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 480 // extension, |match| and its contents. So copy the relevant match out to
478 // make sure it stays alive until the call completes. 481 // make sure it stays alive until the call completes.
479 AutocompleteMatch match = model_->result().match_at(line); 482 AutocompleteMatch match = GetResult().match_at(line);
480 omnibox_view_->OpenMatch(match, disposition, GURL(), line); 483 omnibox_view_->OpenMatch(match, disposition, GURL(), line);
481 } 484 }
482 485
483 gfx::Image OmniboxPopupViewGtk::IconForMatch( 486 gfx::Image OmniboxPopupViewGtk::IconForMatch(
484 const AutocompleteMatch& match, 487 const AutocompleteMatch& match,
485 bool selected, 488 bool selected,
486 bool is_selected_keyword) { 489 bool is_selected_keyword) {
487 const gfx::Image image = model_->GetIconIfExtensionMatch(match); 490 const gfx::Image image = model_->GetIconIfExtensionMatch(match);
488 if (!image.IsEmpty()) 491 if (!image.IsEmpty())
489 return image; 492 return image;
(...skipping 29 matching lines...) Expand all
519 } 522 }
520 } 523 }
521 524
522 return theme_service_->GetImageNamed(icon); 525 return theme_service_->GetImageNamed(icon);
523 } 526 }
524 527
525 void OmniboxPopupViewGtk::GetVisibleMatchForInput( 528 void OmniboxPopupViewGtk::GetVisibleMatchForInput(
526 size_t index, 529 size_t index,
527 const AutocompleteMatch** match, 530 const AutocompleteMatch** match,
528 bool* is_selected_keyword) { 531 bool* is_selected_keyword) {
529 const AutocompleteResult& result = model_->result(); 532 const AutocompleteResult& result = GetResult();
530 533
531 if (result.match_at(index).associated_keyword.get() && 534 if (result.match_at(index).associated_keyword.get() &&
532 model_->selected_line() == index && 535 model_->selected_line() == index &&
533 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { 536 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) {
534 *match = result.match_at(index).associated_keyword.get(); 537 *match = result.match_at(index).associated_keyword.get();
535 *is_selected_keyword = true; 538 *is_selected_keyword = true;
536 return; 539 return;
537 } 540 }
538 541
539 *match = &result.match_at(index); 542 *match = &result.match_at(index);
540 *is_selected_keyword = false; 543 *is_selected_keyword = false;
541 } 544 }
542 545
546 gfx::Rect OmniboxPopupViewGtk::GetRectForLine(size_t line, int width) {
547 size_t visible_line = line - GetHiddenMatchCount();
548 return gfx::Rect(kBorderThickness,
549 (visible_line * kHeightPerResult) + kBorderThickness,
550 width - (kBorderThickness * 2),
551 kHeightPerResult);
552 }
553
554 size_t OmniboxPopupViewGtk::GetHiddenMatchCount() {
555 return GetResult().ShouldHideTopMatch() ? 1 : 0;
556 }
557
558 const AutocompleteResult& OmniboxPopupViewGtk::GetResult() const {
Evan Stade 2013/08/15 01:05:12 make the definition order match the declaration or
Jered 2013/08/15 16:08:49 Done.
559 return model_->result();
560 }
561
543 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget, 562 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget,
544 GdkEventMotion* event) { 563 GdkEventMotion* event) {
545 // TODO(deanm): Windows has a bunch of complicated logic here. 564 // TODO(deanm): Windows has a bunch of complicated logic here.
546 size_t line = LineFromY(static_cast<int>(event->y)); 565 size_t line = LineFromY(static_cast<int>(event->y));
547 // There is both a hovered and selected line, hovered just means your mouse 566 // 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. 567 // is over it, but selected is what's showing in the location edit.
549 model_->SetHoveredLine(line); 568 model_->SetHoveredLine(line);
550 // Select the line if the user has the left mouse button down. 569 // Select the line if the user has the left mouse button down.
551 if (!ignore_mouse_drag_ && (event->state & GDK_BUTTON1_MASK)) 570 if (!ignore_mouse_drag_ && (event->state & GDK_BUTTON1_MASK))
552 model_->SetSelectedLine(line, false, false); 571 model_->SetSelectedLine(line, false, false);
(...skipping 30 matching lines...) Expand all
583 default: 602 default:
584 // Don't open the result. 603 // Don't open the result.
585 break; 604 break;
586 } 605 }
587 return TRUE; 606 return TRUE;
588 } 607 }
589 608
590 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, 609 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget,
591 GdkEventExpose* event) { 610 GdkEventExpose* event) {
592 bool ltr = !base::i18n::IsRTL(); 611 bool ltr = !base::i18n::IsRTL();
593 const AutocompleteResult& result = model_->result(); 612 const AutocompleteResult& result = GetResult();
594 613
595 gfx::Rect window_rect = GetWindowRect(event->window); 614 gfx::Rect window_rect = GetWindowRect(event->window);
596 gfx::Rect damage_rect = gfx::Rect(event->area); 615 gfx::Rect damage_rect = gfx::Rect(event->area);
597 // Handle when our window is super narrow. A bunch of the calculations 616 // Handle when our window is super narrow. A bunch of the calculations
598 // below would go negative, and really we're not going to fit anything 617 // below would go negative, and really we're not going to fit anything
599 // useful in such a small window anyway. Just don't paint anything. 618 // useful in such a small window anyway. Just don't paint anything.
600 // This means we won't draw the border, but, yeah, whatever. 619 // This means we won't draw the border, but, yeah, whatever.
601 // TODO(deanm): Make the code more robust and remove this check. 620 // TODO(deanm): Make the code more robust and remove this check.
602 if (window_rect.width() < (kIconAreaWidth * 3)) 621 if (window_rect.width() < (kIconAreaWidth * 3))
603 return TRUE; 622 return TRUE;
604 623
605 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); 624 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
606 gdk_cairo_rectangle(cr, &event->area); 625 gdk_cairo_rectangle(cr, &event->area);
607 cairo_clip(cr); 626 cairo_clip(cr);
608 627
609 // This assert is kinda ugly, but it would be more currently unneeded work 628 // This assert is kinda ugly, but it would be more currently unneeded work
610 // to support painting a border that isn't 1 pixel thick. There is no point 629 // to support painting a border that isn't 1 pixel thick. There is no point
611 // in writing that code now, and explode if that day ever comes. 630 // in writing that code now, and explode if that day ever comes.
612 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); 631 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied);
613 // Draw the 1px border around the entire window. 632 // Draw the 1px border around the entire window.
614 gdk_cairo_set_source_color(cr, &border_color_); 633 gdk_cairo_set_source_color(cr, &border_color_);
615 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); 634 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height());
616 cairo_stroke(cr); 635 cairo_stroke(cr);
617 636
618 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); 637 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE);
619 638
620 for (size_t i = 0; i < result.size(); ++i) { 639 for (size_t i = GetHiddenMatchCount(); i < result.size(); ++i) {
621 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); 640 gfx::Rect line_rect = GetRectForLine(i, window_rect.width());
622 // Only repaint and layout damaged lines. 641 // Only repaint and layout damaged lines.
623 if (!line_rect.Intersects(damage_rect)) 642 if (!line_rect.Intersects(damage_rect))
624 continue; 643 continue;
625 644
626 const AutocompleteMatch* match = NULL; 645 const AutocompleteMatch* match = NULL;
627 bool is_selected_keyword = false; 646 bool is_selected_keyword = false;
628 GetVisibleMatchForInput(i, &match, &is_selected_keyword); 647 GetVisibleMatchForInput(i, &match, &is_selected_keyword);
629 bool is_selected = (model_->selected_line() == i); 648 bool is_selected = (model_->selected_line() == i);
630 bool is_hovered = (model_->hovered_line() == i); 649 bool is_hovered = (model_->hovered_line() == i);
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 theme_service_->GetImageNamed( 740 theme_service_->GetImageNamed(
722 is_selected ? IDR_OMNIBOX_TTS_DARK : 741 is_selected ? IDR_OMNIBOX_TTS_DARK :
723 IDR_OMNIBOX_TTS), 742 IDR_OMNIBOX_TTS),
724 icon_start_x, line_rect.y() + kIconTopPadding); 743 icon_start_x, line_rect.y() + kIconTopPadding);
725 } 744 }
726 } 745 }
727 746
728 cairo_destroy(cr); 747 cairo_destroy(cr);
729 return TRUE; 748 return TRUE;
730 } 749 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698