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/views/autocomplete/autocomplete_popup_contents_view.
h" | 5 #include "chrome/browser/ui/views/autocomplete/autocomplete_popup_contents_view.
h" |
6 | 6 |
7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
8 #include "base/i18n/bidi_line_iterator.h" | |
9 #include "base/i18n/rtl.h" | |
10 #include "base/utf_string_conversions.h" | 8 #include "base/utf_string_conversions.h" |
11 #include "chrome/browser/autocomplete/autocomplete_edit_view.h" | 9 #include "chrome/browser/autocomplete/autocomplete_edit_view.h" |
12 #include "chrome/browser/autocomplete/autocomplete_match.h" | |
13 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" | 10 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" |
14 #include "chrome/browser/instant/instant_confirm_dialog.h" | 11 #include "chrome/browser/instant/instant_confirm_dialog.h" |
15 #include "chrome/browser/instant/promo_counter.h" | 12 #include "chrome/browser/instant/promo_counter.h" |
16 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" |
17 #include "chrome/browser/ui/views/bubble_border.h" | 14 #include "chrome/browser/ui/views/bubble_border.h" |
18 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" | 15 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
19 #include "grit/chromium_strings.h" | 16 #include "grit/chromium_strings.h" |
20 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
21 #include "grit/theme_resources.h" | 18 #include "grit/theme_resources.h" |
22 #include "third_party/skia/include/core/SkShader.h" | 19 #include "third_party/skia/include/core/SkShader.h" |
23 #include "ui/base/l10n/l10n_util.h" | 20 #include "ui/base/l10n/l10n_util.h" |
24 #include "ui/base/resource/resource_bundle.h" | 21 #include "ui/base/resource/resource_bundle.h" |
25 #include "ui/base/text/text_elider.h" | |
26 #include "ui/base/theme_provider.h" | 22 #include "ui/base/theme_provider.h" |
27 #include "ui/gfx/canvas_skia.h" | 23 #include "ui/gfx/canvas_skia.h" |
28 #include "ui/gfx/color_utils.h" | |
29 #include "ui/gfx/insets.h" | 24 #include "ui/gfx/insets.h" |
30 #include "ui/gfx/path.h" | 25 #include "ui/gfx/path.h" |
31 #include "unicode/ubidi.h" | 26 #include "unicode/ubidi.h" |
32 #include "views/controls/button/text_button.h" | 27 #include "views/controls/button/text_button.h" |
33 #include "views/controls/label.h" | 28 #include "views/controls/label.h" |
34 #include "views/layout/grid_layout.h" | 29 #include "views/layout/grid_layout.h" |
35 #include "views/layout/layout_constants.h" | 30 #include "views/layout/layout_constants.h" |
36 #include "views/painter.h" | 31 #include "views/painter.h" |
37 #include "views/widget/widget.h" | 32 #include "views/widget/widget.h" |
38 #include "views/window/window.h" | 33 #include "views/window/window.h" |
39 | 34 |
40 #if defined(OS_WIN) | 35 #if defined(OS_WIN) |
41 #include <commctrl.h> | 36 #include <commctrl.h> |
42 #include <dwmapi.h> | 37 #include <dwmapi.h> |
43 #include <objidl.h> | 38 #include <objidl.h> |
44 | 39 |
45 #include "base/win/scoped_gdi_object.h" | 40 #include "base/win/scoped_gdi_object.h" |
46 #include "views/widget/widget_win.h" | 41 #include "views/widget/widget_win.h" |
47 #endif | 42 #endif |
48 | 43 |
49 #if defined(OS_LINUX) | 44 #if defined(OS_LINUX) |
50 #include "chrome/browser/ui/gtk/gtk_util.h" | |
51 #include "ui/gfx/skia_utils_gtk.h" | 45 #include "ui/gfx/skia_utils_gtk.h" |
52 #endif | 46 #endif |
53 | 47 |
54 namespace { | 48 namespace { |
55 | 49 |
56 enum ResultViewState { | |
57 NORMAL = 0, | |
58 SELECTED, | |
59 HOVERED, | |
60 NUM_STATES | |
61 }; | |
62 | |
63 enum ColorKind { | |
64 BACKGROUND = 0, | |
65 TEXT, | |
66 DIMMED_TEXT, | |
67 URL, | |
68 NUM_KINDS | |
69 }; | |
70 | |
71 SkColor GetColor(ResultViewState state, ColorKind kind) { | |
72 static bool initialized = false; | |
73 static SkColor colors[NUM_STATES][NUM_KINDS]; | |
74 if (!initialized) { | |
75 #if defined(OS_WIN) | |
76 colors[NORMAL][BACKGROUND] = color_utils::GetSysSkColor(COLOR_WINDOW); | |
77 colors[SELECTED][BACKGROUND] = color_utils::GetSysSkColor(COLOR_HIGHLIGHT); | |
78 colors[NORMAL][TEXT] = color_utils::GetSysSkColor(COLOR_WINDOWTEXT); | |
79 colors[SELECTED][TEXT] = color_utils::GetSysSkColor(COLOR_HIGHLIGHTTEXT); | |
80 #elif defined(OS_LINUX) | |
81 GdkColor bg_color, selected_bg_color, text_color, selected_text_color; | |
82 gtk_util::GetTextColors( | |
83 &bg_color, &selected_bg_color, &text_color, &selected_text_color); | |
84 colors[NORMAL][BACKGROUND] = gfx::GdkColorToSkColor(bg_color); | |
85 colors[SELECTED][BACKGROUND] = gfx::GdkColorToSkColor(selected_bg_color); | |
86 colors[NORMAL][TEXT] = gfx::GdkColorToSkColor(text_color); | |
87 colors[SELECTED][TEXT] = gfx::GdkColorToSkColor(selected_text_color); | |
88 #else | |
89 // TODO(beng): source from theme provider. | |
90 colors[NORMAL][BACKGROUND] = SK_ColorWHITE; | |
91 colors[SELECTED][BACKGROUND] = SK_ColorBLUE; | |
92 colors[NORMAL][TEXT] = SK_ColorBLACK; | |
93 colors[SELECTED][TEXT] = SK_ColorWHITE; | |
94 #endif | |
95 colors[HOVERED][BACKGROUND] = | |
96 color_utils::AlphaBlend(colors[SELECTED][BACKGROUND], | |
97 colors[NORMAL][BACKGROUND], 64); | |
98 colors[HOVERED][TEXT] = colors[NORMAL][TEXT]; | |
99 for (int i = 0; i < NUM_STATES; ++i) { | |
100 colors[i][DIMMED_TEXT] = | |
101 color_utils::AlphaBlend(colors[i][TEXT], colors[i][BACKGROUND], 128); | |
102 colors[i][URL] = color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0), | |
103 colors[i][BACKGROUND]); | |
104 } | |
105 initialized = true; | |
106 } | |
107 | |
108 return colors[state][kind]; | |
109 } | |
110 | |
111 const char16 kEllipsis[] = { 0x2026 }; | |
112 | |
113 const SkAlpha kGlassPopupAlpha = 240; | 50 const SkAlpha kGlassPopupAlpha = 240; |
114 const SkAlpha kOpaquePopupAlpha = 255; | 51 const SkAlpha kOpaquePopupAlpha = 255; |
115 // The minimum distance between the top and bottom of the icon and the top or | |
116 // bottom of the row. "Minimum" is used because the vertical padding may be | |
117 // larger, depending on the size of the text. | |
118 const int kIconVerticalPadding = 2; | |
119 // The minimum distance between the top and bottom of the text and the top or | |
120 // bottom of the row. See comment about the use of "minimum" for | |
121 // kIconVerticalPadding. | |
122 const int kTextVerticalPadding = 3; | |
123 // The size delta between the font used for the edit and the result rows. Passed | 52 // The size delta between the font used for the edit and the result rows. Passed |
124 // to gfx::Font::DeriveFont. | 53 // to gfx::Font::DeriveFont. |
125 #if defined(OS_CHROMEOS) | 54 #if defined(OS_CHROMEOS) |
126 // Don't adjust the size on Chrome OS (http://crbug.com/61433). | 55 // Don't adjust the size on Chrome OS (http://crbug.com/61433). |
127 const int kEditFontAdjust = 0; | 56 const int kEditFontAdjust = 0; |
128 #else | 57 #else |
129 const int kEditFontAdjust = -1; | 58 const int kEditFontAdjust = -1; |
130 #endif | 59 #endif |
131 | 60 |
132 // Horizontal padding between the buttons on the opt in promo. | 61 // Horizontal padding between the buttons on the opt in promo. |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 button->set_animate_on_state_change(false); | 189 button->set_animate_on_state_change(false); |
261 return button; | 190 return button; |
262 } | 191 } |
263 | 192 |
264 AutocompletePopupContentsView* contents_view_; | 193 AutocompletePopupContentsView* contents_view_; |
265 scoped_ptr<views::Painter> bg_painter_; | 194 scoped_ptr<views::Painter> bg_painter_; |
266 | 195 |
267 DISALLOW_COPY_AND_ASSIGN(InstantOptInView); | 196 DISALLOW_COPY_AND_ASSIGN(InstantOptInView); |
268 }; | 197 }; |
269 | 198 |
270 class AutocompleteResultView : public views::View { | |
271 public: | |
272 AutocompleteResultView(AutocompleteResultViewModel* model, | |
273 int model_index, | |
274 const gfx::Font& font, | |
275 const gfx::Font& bold_font); | |
276 virtual ~AutocompleteResultView(); | |
277 | |
278 // Updates the match used to paint the contents of this result view. We copy | |
279 // the match so that we can continue to paint the last result even after the | |
280 // model has changed. | |
281 void set_match(const AutocompleteMatch& match) { match_ = match; } | |
282 | |
283 // Overridden from views::View: | |
284 virtual void Paint(gfx::Canvas* canvas); | |
285 virtual void Layout(); | |
286 virtual gfx::Size GetPreferredSize(); | |
287 | |
288 // Returns the preferred height for a single row. | |
289 static int GetPreferredHeight(const gfx::Font& font, | |
290 const gfx::Font& bold_font); | |
291 | |
292 private: | |
293 // Precalculated data used to draw the portion of a match classification that | |
294 // fits entirely within one run. | |
295 struct ClassificationData { | |
296 string16 text; | |
297 const gfx::Font* font; | |
298 SkColor color; | |
299 int pixel_width; | |
300 }; | |
301 typedef std::vector<ClassificationData> Classifications; | |
302 | |
303 // Precalculated data used to draw a complete visual run within the match. | |
304 // This will include all or part of at leasdt one, and possibly several, | |
305 // classifications. | |
306 struct RunData { | |
307 size_t run_start; // Offset within the match text where this run begins. | |
308 int visual_order; // Where this run occurs in visual order. The earliest | |
309 // run drawn is run 0. | |
310 bool is_rtl; | |
311 int pixel_width; | |
312 Classifications classifications; // Classification pieces within this run, | |
313 // in logical order. | |
314 }; | |
315 typedef std::vector<RunData> Runs; | |
316 | |
317 // Predicate functions for use when sorting the runs. | |
318 static bool SortRunsLogically(const RunData& lhs, const RunData& rhs); | |
319 static bool SortRunsVisually(const RunData& lhs, const RunData& rhs); | |
320 | |
321 ResultViewState GetState() const; | |
322 | |
323 const SkBitmap* GetIcon() const; | |
324 | |
325 // Draws the specified |text| into the canvas, using highlighting provided by | |
326 // |classifications|. If |force_dim| is true, ACMatchClassification::DIM is | |
327 // added to all of the classifications. Returns the x position to the right | |
328 // of the string. | |
329 int DrawString(gfx::Canvas* canvas, | |
330 const string16& text, | |
331 const ACMatchClassifications& classifications, | |
332 bool force_dim, | |
333 int x, | |
334 int y); | |
335 | |
336 // Elides |runs| to fit in |remaining_width|. The runs in |runs| should be in | |
337 // logical order. | |
338 // | |
339 // When we need to elide a run, the ellipsis will be placed at the end of that | |
340 // run. This means that if we elide a run whose visual direction is opposite | |
341 // that of the drawing context, the ellipsis will not be at the "end" of the | |
342 // drawn string. For example, if in an LTR context we have the LTR run | |
343 // "LTR_STRING" and the RTL run "RTL_STRING", the unelided text would be drawn | |
344 // like: | |
345 // LTR_STRING GNIRTS_LTR | |
346 // If we need to elide the RTL run, then it will be drawn like: | |
347 // LTR_STRING ...RTS_LTR | |
348 // Instead of: | |
349 // LTR_STRING RTS_LTR... | |
350 void Elide(Runs* runs, int remaining_width) const; | |
351 | |
352 // This row's model and model index. | |
353 AutocompleteResultViewModel* model_; | |
354 size_t model_index_; | |
355 | |
356 const gfx::Font normal_font_; | |
357 const gfx::Font bold_font_; | |
358 | |
359 // Width of the ellipsis in the normal font. | |
360 int ellipsis_width_; | |
361 | |
362 // A context used for mirroring regions. | |
363 class MirroringContext; | |
364 scoped_ptr<MirroringContext> mirroring_context_; | |
365 | |
366 // Layout rects for various sub-components of the view. | |
367 gfx::Rect icon_bounds_; | |
368 gfx::Rect text_bounds_; | |
369 | |
370 static int icon_size_; | |
371 | |
372 AutocompleteMatch match_; | |
373 | |
374 DISALLOW_COPY_AND_ASSIGN(AutocompleteResultView); | |
375 }; | |
376 | |
377 // static | |
378 int AutocompleteResultView::icon_size_ = 0; | |
379 | |
380 // This class is a utility class for calculations affected by whether the result | |
381 // view is horizontally mirrored. The drawing functions can be written as if | |
382 // all drawing occurs left-to-right, and then use this class to get the actual | |
383 // coordinates to begin drawing onscreen. | |
384 class AutocompleteResultView::MirroringContext { | |
385 public: | |
386 MirroringContext() : center_(0), right_(0) {} | |
387 | |
388 // Tells the mirroring context to use the provided range as the physical | |
389 // bounds of the drawing region. When coordinate mirroring is needed, the | |
390 // mirror point will be the center of this range. | |
391 void Initialize(int x, int width) { | |
392 center_ = x + width / 2; | |
393 right_ = x + width; | |
394 } | |
395 | |
396 // Given a logical range within the drawing region, returns the coordinate of | |
397 // the possibly-mirrored "left" side. (This functions exactly like | |
398 // View::MirroredLeftPointForRect().) | |
399 int mirrored_left_coord(int left, int right) const { | |
400 return base::i18n::IsRTL() ? (center_ + (center_ - right)) : left; | |
401 } | |
402 | |
403 // Given a logical coordinate within the drawing region, returns the remaining | |
404 // width available. | |
405 int remaining_width(int x) const { | |
406 return right_ - x; | |
407 } | |
408 | |
409 private: | |
410 int center_; | |
411 int right_; | |
412 | |
413 DISALLOW_COPY_AND_ASSIGN(MirroringContext); | |
414 }; | |
415 | |
416 AutocompleteResultView::AutocompleteResultView( | |
417 AutocompleteResultViewModel* model, | |
418 int model_index, | |
419 const gfx::Font& font, | |
420 const gfx::Font& bold_font) | |
421 : model_(model), | |
422 model_index_(model_index), | |
423 normal_font_(font), | |
424 bold_font_(bold_font), | |
425 ellipsis_width_(font.GetStringWidth(string16(kEllipsis))), | |
426 mirroring_context_(new MirroringContext()), | |
427 match_(NULL, 0, false, AutocompleteMatch::URL_WHAT_YOU_TYPED) { | |
428 CHECK(model_index >= 0); | |
429 if (icon_size_ == 0) { | |
430 icon_size_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
431 AutocompleteMatch::TypeToIcon(AutocompleteMatch::URL_WHAT_YOU_TYPED))-> | |
432 width(); | |
433 } | |
434 } | |
435 | |
436 AutocompleteResultView::~AutocompleteResultView() { | |
437 } | |
438 | |
439 void AutocompleteResultView::Paint(gfx::Canvas* canvas) { | |
440 const ResultViewState state = GetState(); | |
441 if (state != NORMAL) | |
442 canvas->AsCanvasSkia()->drawColor(GetColor(state, BACKGROUND)); | |
443 | |
444 // Paint the icon. | |
445 canvas->DrawBitmapInt(*GetIcon(), GetMirroredXForRect(icon_bounds_), | |
446 icon_bounds_.y()); | |
447 | |
448 // Paint the text. | |
449 int x = GetMirroredXForRect(text_bounds_); | |
450 mirroring_context_->Initialize(x, text_bounds_.width()); | |
451 x = DrawString(canvas, match_.contents, match_.contents_class, false, x, | |
452 text_bounds_.y()); | |
453 | |
454 // Paint the description. | |
455 // TODO(pkasting): Because we paint in multiple separate pieces, we can wind | |
456 // up with no space even for an ellipsis for one or both of these pieces. | |
457 // Instead, we should paint the entire match as a single long string. This | |
458 // would also let us use a more properly-localizable string than we get with | |
459 // just the IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR. | |
460 if (!match_.description.empty()) { | |
461 string16 separator = | |
462 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); | |
463 ACMatchClassifications classifications; | |
464 classifications.push_back( | |
465 ACMatchClassification(0, ACMatchClassification::NONE)); | |
466 x = DrawString(canvas, separator, classifications, true, x, | |
467 text_bounds_.y()); | |
468 | |
469 DrawString(canvas, match_.description, match_.description_class, true, x, | |
470 text_bounds_.y()); | |
471 } | |
472 } | |
473 | |
474 void AutocompleteResultView::Layout() { | |
475 icon_bounds_.SetRect(LocationBarView::kEdgeItemPadding, | |
476 (height() - icon_size_) / 2, icon_size_, icon_size_); | |
477 int text_x = icon_bounds_.right() + LocationBarView::kItemPadding; | |
478 int font_height = std::max(normal_font_.GetHeight(), bold_font_.GetHeight()); | |
479 text_bounds_.SetRect(text_x, std::max(0, (height() - font_height) / 2), | |
480 std::max(bounds().width() - text_x - LocationBarView::kEdgeItemPadding, | |
481 0), font_height); | |
482 } | |
483 | |
484 gfx::Size AutocompleteResultView::GetPreferredSize() { | |
485 return gfx::Size(0, GetPreferredHeight(normal_font_, bold_font_)); | |
486 } | |
487 | |
488 // static | |
489 int AutocompleteResultView::GetPreferredHeight( | |
490 const gfx::Font& font, | |
491 const gfx::Font& bold_font) { | |
492 int text_height = std::max(font.GetHeight(), bold_font.GetHeight()) + | |
493 (kTextVerticalPadding * 2); | |
494 int icon_height = icon_size_ + (kIconVerticalPadding * 2); | |
495 return std::max(icon_height, text_height); | |
496 } | |
497 | |
498 // static | |
499 bool AutocompleteResultView::SortRunsLogically(const RunData& lhs, | |
500 const RunData& rhs) { | |
501 return lhs.run_start < rhs.run_start; | |
502 } | |
503 | |
504 // static | |
505 bool AutocompleteResultView::SortRunsVisually(const RunData& lhs, | |
506 const RunData& rhs) { | |
507 return lhs.visual_order < rhs.visual_order; | |
508 } | |
509 | |
510 ResultViewState AutocompleteResultView::GetState() const { | |
511 if (model_->IsSelectedIndex(model_index_)) | |
512 return SELECTED; | |
513 return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; | |
514 } | |
515 | |
516 const SkBitmap* AutocompleteResultView::GetIcon() const { | |
517 const SkBitmap* bitmap = model_->GetSpecialIcon(model_index_); | |
518 if (bitmap) | |
519 return bitmap; | |
520 | |
521 int icon = match_.starred ? | |
522 IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type); | |
523 if (model_->IsSelectedIndex(model_index_)) { | |
524 switch (icon) { | |
525 case IDR_OMNIBOX_HTTP: icon = IDR_OMNIBOX_HTTP_SELECTED; break; | |
526 case IDR_OMNIBOX_HISTORY: icon = IDR_OMNIBOX_HISTORY_SELECTED; break; | |
527 case IDR_OMNIBOX_SEARCH: icon = IDR_OMNIBOX_SEARCH_SELECTED; break; | |
528 case IDR_OMNIBOX_STAR: icon = IDR_OMNIBOX_STAR_SELECTED; break; | |
529 default: NOTREACHED(); break; | |
530 } | |
531 } | |
532 return ResourceBundle::GetSharedInstance().GetBitmapNamed(icon); | |
533 } | |
534 | |
535 int AutocompleteResultView::DrawString( | |
536 gfx::Canvas* canvas, | |
537 const string16& text, | |
538 const ACMatchClassifications& classifications, | |
539 bool force_dim, | |
540 int x, | |
541 int y) { | |
542 if (text.empty()) | |
543 return x; | |
544 | |
545 // Check whether or not this text is a URL. URLs are always displayed LTR | |
546 // regardless of locale. | |
547 bool is_url = true; | |
548 for (ACMatchClassifications::const_iterator i(classifications.begin()); | |
549 i != classifications.end(); ++i) { | |
550 if (!(i->style & ACMatchClassification::URL)) { | |
551 is_url = false; | |
552 break; | |
553 } | |
554 } | |
555 | |
556 // Split the text into visual runs. We do this first so that we don't need to | |
557 // worry about whether our eliding might change the visual display in | |
558 // unintended ways, e.g. by removing directional markings or by adding an | |
559 // ellipsis that's not enclosed in appropriate markings. | |
560 base::i18n::BiDiLineIterator bidi_line; | |
561 if (!bidi_line.Open(text, base::i18n::IsRTL(), is_url)) | |
562 return x; | |
563 const int num_runs = bidi_line.CountRuns(); | |
564 Runs runs; | |
565 for (int run = 0; run < num_runs; ++run) { | |
566 int run_start_int = 0, run_length_int = 0; | |
567 // The index we pass to GetVisualRun corresponds to the position of the run | |
568 // in the displayed text. For example, the string "Google in HEBREW" (where | |
569 // HEBREW is text in the Hebrew language) has two runs: "Google in " which | |
570 // is an LTR run, and "HEBREW" which is an RTL run. In an LTR context, the | |
571 // run "Google in " has the index 0 (since it is the leftmost run | |
572 // displayed). In an RTL context, the same run has the index 1 because it | |
573 // is the rightmost run. This is why the order in which we traverse the | |
574 // runs is different depending on the locale direction. | |
575 const UBiDiDirection run_direction = bidi_line.GetVisualRun( | |
576 (base::i18n::IsRTL() && !is_url) ? (num_runs - run - 1) : run, | |
577 &run_start_int, &run_length_int); | |
578 DCHECK_GT(run_length_int, 0); | |
579 runs.push_back(RunData()); | |
580 RunData* current_run = &runs.back(); | |
581 current_run->run_start = run_start_int; | |
582 const size_t run_end = current_run->run_start + run_length_int; | |
583 current_run->visual_order = run; | |
584 current_run->is_rtl = !is_url && (run_direction == UBIDI_RTL); | |
585 current_run->pixel_width = 0; | |
586 | |
587 // Compute classifications for this run. | |
588 for (size_t i = 0; i < classifications.size(); ++i) { | |
589 const size_t text_start = | |
590 std::max(classifications[i].offset, current_run->run_start); | |
591 if (text_start >= run_end) | |
592 break; // We're past the last classification in the run. | |
593 | |
594 const size_t text_end = (i < (classifications.size() - 1)) ? | |
595 std::min(classifications[i + 1].offset, run_end) : run_end; | |
596 if (text_end <= current_run->run_start) | |
597 continue; // We haven't reached the first classification in the run. | |
598 | |
599 current_run->classifications.push_back(ClassificationData()); | |
600 ClassificationData* current_data = | |
601 ¤t_run->classifications.back(); | |
602 current_data->text = text.substr(text_start, text_end - text_start); | |
603 | |
604 // Calculate style-related data. | |
605 const int style = classifications[i].style; | |
606 const bool use_bold_font = !!(style & ACMatchClassification::MATCH); | |
607 current_data->font = &(use_bold_font ? bold_font_ : normal_font_); | |
608 const ResultViewState state = GetState(); | |
609 if (style & ACMatchClassification::URL) | |
610 current_data->color = GetColor(state, URL); | |
611 else if (style & ACMatchClassification::DIM) | |
612 current_data->color = GetColor(state, DIMMED_TEXT); | |
613 else | |
614 current_data->color = GetColor(state, force_dim ? DIMMED_TEXT : TEXT); | |
615 current_data->pixel_width = | |
616 current_data->font->GetStringWidth(current_data->text); | |
617 current_run->pixel_width += current_data->pixel_width; | |
618 } | |
619 DCHECK(!current_run->classifications.empty()); | |
620 } | |
621 DCHECK(!runs.empty()); | |
622 | |
623 // Sort into logical order so we can elide logically. | |
624 std::sort(runs.begin(), runs.end(), &SortRunsLogically); | |
625 | |
626 // Now determine what to elide, if anything. Several subtle points: | |
627 // * Because we have the run data, we can get edge cases correct, like | |
628 // whether to place an ellipsis before or after the end of a run when the | |
629 // text needs to be elided at the run boundary. | |
630 // * The "or one before it" comments below refer to cases where an earlier | |
631 // classification fits completely, but leaves too little space for an | |
632 // ellipsis that turns out to be needed later. These cases are commented | |
633 // more completely in Elide(). | |
634 int remaining_width = mirroring_context_->remaining_width(x); | |
635 for (Runs::iterator i(runs.begin()); i != runs.end(); ++i) { | |
636 if (i->pixel_width > remaining_width) { | |
637 // This run or one before it needs to be elided. | |
638 for (Classifications::iterator j(i->classifications.begin()); | |
639 j != i->classifications.end(); ++j) { | |
640 if (j->pixel_width > remaining_width) { | |
641 // This classification or one before it needs to be elided. Erase all | |
642 // further classifications and runs so Elide() can simply reverse- | |
643 // iterate over everything to find the specific classification to | |
644 // elide. | |
645 i->classifications.erase(++j, i->classifications.end()); | |
646 runs.erase(++i, runs.end()); | |
647 Elide(&runs, remaining_width); | |
648 break; | |
649 } | |
650 remaining_width -= j->pixel_width; | |
651 } | |
652 break; | |
653 } | |
654 remaining_width -= i->pixel_width; | |
655 } | |
656 | |
657 // Sort back into visual order so we can display the runs correctly. | |
658 std::sort(runs.begin(), runs.end(), &SortRunsVisually); | |
659 | |
660 // Draw the runs. | |
661 for (Runs::iterator i(runs.begin()); i != runs.end(); ++i) { | |
662 const bool reverse_visible_order = (i->is_rtl != base::i18n::IsRTL()); | |
663 int flags = gfx::Canvas::NO_ELLIPSIS; // We've already elided. | |
664 if (reverse_visible_order) { | |
665 std::reverse(i->classifications.begin(), i->classifications.end()); | |
666 if (i->is_rtl) | |
667 flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY; | |
668 } | |
669 for (Classifications::const_iterator j(i->classifications.begin()); | |
670 j != i->classifications.end(); ++j) { | |
671 int left = mirroring_context_->mirrored_left_coord(x, x + j->pixel_width); | |
672 canvas->DrawStringInt(j->text, *j->font, j->color, left, | |
673 y, j->pixel_width, j->font->GetHeight(), flags); | |
674 x += j->pixel_width; | |
675 } | |
676 } | |
677 | |
678 return x; | |
679 } | |
680 | |
681 void AutocompleteResultView::Elide(Runs* runs, int remaining_width) const { | |
682 // The complexity of this function is due to edge cases like the following: | |
683 // We have 100 px of available space, an initial classification that takes 86 | |
684 // px, and a font that has a 15 px wide ellipsis character. Now if the first | |
685 // classification is followed by several very narrow classifications (e.g. 3 | |
686 // px wide each), we don't know whether we need to elide or not at the time we | |
687 // see the first classification -- it depends on how many subsequent | |
688 // classifications follow, and some of those may be in the next run (or | |
689 // several runs!). This is why instead we let our caller move forward until | |
690 // we know we definitely need to elide, and then in this function we move | |
691 // backward again until we find a string that we can successfully do the | |
692 // eliding on. | |
693 bool first_classification = true; | |
694 for (Runs::reverse_iterator i(runs->rbegin()); i != runs->rend(); ++i) { | |
695 for (Classifications::reverse_iterator j(i->classifications.rbegin()); | |
696 j != i->classifications.rend(); ++j) { | |
697 if (!first_classification) { | |
698 // For all but the first classification we consider, we need to append | |
699 // an ellipsis, since there isn't enough room to draw it after this | |
700 // classification. | |
701 j->text += kEllipsis; | |
702 | |
703 // We also add this classification's width (sans ellipsis) back to the | |
704 // available width since we want to consider the available space we'll | |
705 // have when we draw this classification. | |
706 remaining_width += j->pixel_width; | |
707 } | |
708 first_classification = false; | |
709 | |
710 // Can we fit at least an ellipsis? | |
711 string16 elided_text = | |
712 ui::ElideText(j->text, *j->font, remaining_width, false); | |
713 Classifications::reverse_iterator prior_classification(j); | |
714 ++prior_classification; | |
715 const bool on_first_classification = | |
716 (prior_classification == i->classifications.rend()); | |
717 if (elided_text.empty() && (remaining_width >= ellipsis_width_) && | |
718 on_first_classification) { | |
719 // Edge case: This classification is bold, we can't fit a bold ellipsis | |
720 // but we can fit a normal one, and this is the first classification in | |
721 // the run. We should display a lone normal ellipsis, because appending | |
722 // one to the end of the previous run might put it in the wrong visual | |
723 // location (if the previous run is reversed from the normal visual | |
724 // order). | |
725 // NOTE: If this isn't the first classification in the run, we don't | |
726 // need to bother with this; see note below. | |
727 elided_text = kEllipsis; | |
728 } | |
729 if (!elided_text.empty()) { | |
730 // Success. Elide this classification and stop. | |
731 j->text = elided_text; | |
732 | |
733 // If we could only fit an ellipsis, then only make it bold if there was | |
734 // an immediate prior classification in this run that was also bold, or | |
735 // it will look orphaned. | |
736 if ((elided_text.length() == 1) && | |
737 (on_first_classification || | |
738 (prior_classification->font == &normal_font_))) | |
739 j->font = &normal_font_; | |
740 | |
741 j->pixel_width = j->font->GetStringWidth(elided_text); | |
742 | |
743 // Erase any other classifications that come after the elided one. | |
744 i->classifications.erase(j.base(), i->classifications.end()); | |
745 runs->erase(i.base(), runs->end()); | |
746 return; | |
747 } | |
748 | |
749 // We couldn't fit an ellipsis. Move back one classification, | |
750 // append an ellipsis, and try again. | |
751 // NOTE: In the edge case that a bold ellipsis doesn't fit but a | |
752 // normal one would, and we reach here, then there is a previous | |
753 // classification in this run, and so either: | |
754 // * It's normal, and will be able to draw successfully with the | |
755 // ellipsis we'll append to it, or | |
756 // * It is also bold, in which case we don't want to fall back | |
757 // to a normal ellipsis anyway (see comment above). | |
758 } | |
759 } | |
760 | |
761 // We couldn't draw anything. | |
762 runs->clear(); | |
763 } | |
764 | |
765 //////////////////////////////////////////////////////////////////////////////// | 199 //////////////////////////////////////////////////////////////////////////////// |
766 // AutocompletePopupContentsView, public: | 200 // AutocompletePopupContentsView, public: |
767 | 201 |
768 AutocompletePopupContentsView::AutocompletePopupContentsView( | 202 AutocompletePopupContentsView::AutocompletePopupContentsView( |
769 const gfx::Font& font, | 203 const gfx::Font& font, |
770 AutocompleteEditView* edit_view, | 204 AutocompleteEditView* edit_view, |
771 AutocompleteEditModel* edit_model, | 205 AutocompleteEditModel* edit_model, |
772 Profile* profile, | 206 Profile* profile, |
773 const views::View* location_bar) | 207 const views::View* location_bar) |
774 : model_(new AutocompletePopupModel(this, edit_model, profile)), | 208 : model_(new AutocompletePopupModel(this, edit_model, profile)), |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
829 // destroying the popup would cause us to read garbage when we unwind back | 263 // destroying the popup would cause us to read garbage when we unwind back |
830 // to that level. | 264 // to that level. |
831 popup_->Close(); // This will eventually delete the popup. | 265 popup_->Close(); // This will eventually delete the popup. |
832 popup_.reset(); | 266 popup_.reset(); |
833 } | 267 } |
834 return; | 268 return; |
835 } | 269 } |
836 | 270 |
837 // Update the match cached by each row, in the process of doing so make sure | 271 // Update the match cached by each row, in the process of doing so make sure |
838 // we have enough row views. | 272 // we have enough row views. |
839 int total_child_height = 0; | |
840 size_t child_rv_count = child_count(); | 273 size_t child_rv_count = child_count(); |
841 if (opt_in_view_) { | 274 if (opt_in_view_) { |
842 DCHECK(child_rv_count > 0); | 275 DCHECK(child_rv_count > 0); |
843 child_rv_count--; | 276 child_rv_count--; |
844 } | 277 } |
845 for (size_t i = 0; i < model_->result().size(); ++i) { | 278 for (size_t i = 0; i < model_->result().size(); ++i) { |
846 AutocompleteResultView* result_view; | 279 AutocompleteResultView* result_view; |
847 if (i >= child_rv_count) { | 280 if (i >= child_rv_count) { |
848 result_view = | 281 result_view = |
849 new AutocompleteResultView(this, i, result_font_, result_bold_font_); | 282 CreateResultView(this, i, result_font_, result_bold_font_); |
850 AddChildViewAt(result_view, static_cast<int>(i)); | 283 AddChildViewAt(result_view, static_cast<int>(i)); |
851 } else { | 284 } else { |
852 result_view = static_cast<AutocompleteResultView*>(GetChildViewAt(i)); | 285 result_view = static_cast<AutocompleteResultView*>(GetChildViewAt(i)); |
853 result_view->SetVisible(true); | 286 result_view->SetVisible(true); |
854 } | 287 } |
855 result_view->set_match(GetMatchAtIndex(i)); | 288 result_view->set_match(GetMatchAtIndex(i)); |
856 total_child_height += result_view->GetPreferredSize().height(); | |
857 } | 289 } |
858 for (size_t i = model_->result().size(); i < child_rv_count; ++i) | 290 for (size_t i = model_->result().size(); i < child_rv_count; ++i) |
859 GetChildViewAt(i)->SetVisible(false); | 291 GetChildViewAt(i)->SetVisible(false); |
860 | 292 |
861 PromoCounter* counter = model_->profile()->GetInstantPromoCounter(); | 293 PromoCounter* counter = model_->profile()->GetInstantPromoCounter(); |
862 if (!opt_in_view_ && counter && counter->ShouldShow(base::Time::Now())) { | 294 if (!opt_in_view_ && counter && counter->ShouldShow(base::Time::Now())) { |
863 opt_in_view_ = new InstantOptInView(this, result_bold_font_, result_font_); | 295 opt_in_view_ = new InstantOptInView(this, result_bold_font_, result_font_); |
864 AddChildView(opt_in_view_); | 296 AddChildView(opt_in_view_); |
865 } else if (opt_in_view_ && (!counter || | 297 } else if (opt_in_view_ && (!counter || |
866 !counter->ShouldShow(base::Time::Now()))) { | 298 !counter->ShouldShow(base::Time::Now()))) { |
867 delete opt_in_view_; | 299 delete opt_in_view_; |
868 opt_in_view_ = NULL; | 300 opt_in_view_ = NULL; |
869 } | 301 } |
870 | 302 |
871 if (opt_in_view_) | 303 gfx::Rect new_target_bounds = CalculateTargetBounds(CalculatePopupHeight()); |
872 total_child_height += opt_in_view_->GetPreferredSize().height(); | |
873 | |
874 gfx::Rect new_target_bounds = CalculateTargetBounds(total_child_height); | |
875 | 304 |
876 // If we're animating and our target height changes, reset the animation. | 305 // If we're animating and our target height changes, reset the animation. |
877 // NOTE: If we just reset blindly on _every_ update, then when the user types | 306 // NOTE: If we just reset blindly on _every_ update, then when the user types |
878 // rapidly we could get "stuck" trying repeatedly to animate shrinking by the | 307 // rapidly we could get "stuck" trying repeatedly to animate shrinking by the |
879 // last few pixels to get to one visible result. | 308 // last few pixels to get to one visible result. |
880 if (new_target_bounds.height() != target_bounds_.height()) | 309 if (new_target_bounds.height() != target_bounds_.height()) |
881 size_animation_.Reset(); | 310 size_animation_.Reset(); |
882 target_bounds_ = new_target_bounds; | 311 target_bounds_ = new_target_bounds; |
883 | 312 |
884 if (popup_ == NULL) { | 313 if (popup_ == NULL) { |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
950 // | 379 // |
951 // Because the border of this view creates an anti-aliased round-rect region | 380 // Because the border of this view creates an anti-aliased round-rect region |
952 // for the contents, we need to render our rectangular result child views into | 381 // for the contents, we need to render our rectangular result child views into |
953 // this round rect region. We can't use a simple clip because clipping is | 382 // this round rect region. We can't use a simple clip because clipping is |
954 // 1-bit and we get nasty jagged edges. | 383 // 1-bit and we get nasty jagged edges. |
955 // | 384 // |
956 // Instead, we paint all our children into a second canvas and use that as a | 385 // Instead, we paint all our children into a second canvas and use that as a |
957 // shader to fill a path representing the round-rect clipping region. This | 386 // shader to fill a path representing the round-rect clipping region. This |
958 // yields a nice anti-aliased edge. | 387 // yields a nice anti-aliased edge. |
959 gfx::CanvasSkia contents_canvas(width(), height(), true); | 388 gfx::CanvasSkia contents_canvas(width(), height(), true); |
960 contents_canvas.drawColor(GetColor(NORMAL, BACKGROUND)); | 389 PaintChildren(&contents_canvas); |
961 View::PaintChildren(&contents_canvas); | 390 |
962 // We want the contents background to be slightly transparent so we can see | 391 // We want the contents background to be slightly transparent so we can see |
963 // the blurry glass effect on DWM systems behind. We do this _after_ we paint | 392 // the blurry glass effect on DWM systems behind. We do this _after_ we paint |
964 // the children since they paint text, and GDI will reset this alpha data if | 393 // the children since they paint text, and GDI will reset this alpha data if |
965 // we paint text after this call. | 394 // we paint text after this call. |
966 MakeCanvasTransparent(&contents_canvas); | 395 MakeCanvasTransparent(&contents_canvas); |
967 | 396 |
968 // Now paint the contents of the contents canvas into the actual canvas. | 397 // Now paint the contents of the contents canvas into the actual canvas. |
969 SkPaint paint; | 398 SkPaint paint; |
970 paint.setAntiAlias(true); | 399 paint.setAntiAlias(true); |
971 | 400 |
972 SkShader* shader = SkShader::CreateBitmapShader( | 401 SkShader* shader = SkShader::CreateBitmapShader( |
973 contents_canvas.getDevice()->accessBitmap(false), | 402 contents_canvas.getDevice()->accessBitmap(false), |
974 SkShader::kClamp_TileMode, | 403 SkShader::kClamp_TileMode, |
975 SkShader::kClamp_TileMode); | 404 SkShader::kClamp_TileMode); |
976 paint.setShader(shader); | 405 paint.setShader(shader); |
977 shader->unref(); | 406 shader->unref(); |
978 | 407 |
979 gfx::Path path; | 408 gfx::Path path; |
980 MakeContentsPath(&path, GetContentsBounds()); | 409 MakeContentsPath(&path, GetContentsBounds()); |
981 canvas->AsCanvasSkia()->drawPath(path, paint); | 410 canvas->AsCanvasSkia()->drawPath(path, paint); |
982 | 411 |
983 // Now we paint the border, so it will be alpha-blended atop the contents. | 412 // Now we paint the border, so it will be alpha-blended atop the contents. |
984 // This looks slightly better in the corners than drawing the contents atop | 413 // This looks slightly better in the corners than drawing the contents atop |
985 // the border. | 414 // the border. |
986 PaintBorder(canvas); | 415 PaintBorder(canvas); |
987 } | 416 } |
988 | 417 |
| 418 void AutocompletePopupContentsView::PaintChildren(gfx::CanvasSkia* canvas) { |
| 419 canvas->drawColor(AutocompleteResultView::GetColor( |
| 420 AutocompleteResultView::NORMAL, AutocompleteResultView::BACKGROUND)); |
| 421 View::PaintChildren(canvas); |
| 422 } |
| 423 |
989 void AutocompletePopupContentsView::Layout() { | 424 void AutocompletePopupContentsView::Layout() { |
990 UpdateBlurRegion(); | 425 UpdateBlurRegion(); |
991 | 426 |
992 // Size our children to the available content area. | 427 // Size our children to the available content area. |
| 428 LayoutChildren(); |
| 429 |
| 430 // We need to manually schedule a paint here since we are a layered window and |
| 431 // won't implicitly require painting until we ask for one. |
| 432 SchedulePaint(); |
| 433 } |
| 434 |
| 435 void AutocompletePopupContentsView::LayoutChildren() { |
993 gfx::Rect contents_rect = GetContentsBounds(); | 436 gfx::Rect contents_rect = GetContentsBounds(); |
994 int top = contents_rect.y(); | 437 int top = contents_rect.y(); |
995 for (int i = 0; i < child_count(); ++i) { | 438 for (int i = 0; i < child_count(); ++i) { |
996 View* v = GetChildViewAt(i); | 439 View* v = GetChildViewAt(i); |
997 if (v->IsVisible()) { | 440 if (v->IsVisible()) { |
998 v->SetBounds(contents_rect.x(), top, contents_rect.width(), | 441 v->SetBounds(contents_rect.x(), top, contents_rect.width(), |
999 v->GetPreferredSize().height()); | 442 v->GetPreferredSize().height()); |
1000 top = v->bounds().bottom(); | 443 top = v->bounds().bottom(); |
1001 } | 444 } |
1002 } | 445 } |
1003 | |
1004 // We need to manually schedule a paint here since we are a layered window and | |
1005 // won't implicitly require painting until we ask for one. | |
1006 SchedulePaint(); | |
1007 } | 446 } |
1008 | 447 |
1009 | |
1010 void AutocompletePopupContentsView::OnMouseEntered( | 448 void AutocompletePopupContentsView::OnMouseEntered( |
1011 const views::MouseEvent& event) { | 449 const views::MouseEvent& event) { |
1012 model_->SetHoveredLine(GetIndexForPoint(event.location())); | 450 model_->SetHoveredLine(GetIndexForPoint(event.location())); |
1013 } | 451 } |
1014 | 452 |
1015 void AutocompletePopupContentsView::OnMouseMoved( | 453 void AutocompletePopupContentsView::OnMouseMoved( |
1016 const views::MouseEvent& event) { | 454 const views::MouseEvent& event) { |
1017 model_->SetHoveredLine(GetIndexForPoint(event.location())); | 455 model_->SetHoveredLine(GetIndexForPoint(event.location())); |
1018 } | 456 } |
1019 | 457 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1067 if (!opt_in_view_) | 505 if (!opt_in_view_) |
1068 return this; | 506 return this; |
1069 | 507 |
1070 views::View* child = views::View::GetViewForPoint(point); | 508 views::View* child = views::View::GetViewForPoint(point); |
1071 views::View* ancestor = child; | 509 views::View* ancestor = child; |
1072 while (ancestor && ancestor != opt_in_view_) | 510 while (ancestor && ancestor != opt_in_view_) |
1073 ancestor = ancestor->parent(); | 511 ancestor = ancestor->parent(); |
1074 return ancestor ? child : this; | 512 return ancestor ? child : this; |
1075 } | 513 } |
1076 | 514 |
| 515 //////////////////////////////////////////////////////////////////////////////// |
| 516 // AutocompletePopupContentsView, protected: |
| 517 |
| 518 int AutocompletePopupContentsView::CalculatePopupHeight() { |
| 519 DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size()); |
| 520 int popup_height = 0; |
| 521 for (size_t i = 0; i < model_->result().size(); ++i) |
| 522 popup_height += GetChildViewAt(i)->GetPreferredSize().height(); |
| 523 return popup_height + |
| 524 (opt_in_view_ ? opt_in_view_->GetPreferredSize().height() : 0); |
| 525 } |
| 526 |
| 527 AutocompleteResultView* AutocompletePopupContentsView::CreateResultView( |
| 528 AutocompleteResultViewModel* model, |
| 529 int model_index, |
| 530 const gfx::Font& font, |
| 531 const gfx::Font& bold_font) { |
| 532 return new AutocompleteResultView(model, model_index, font, bold_font); |
| 533 } |
1077 | 534 |
1078 //////////////////////////////////////////////////////////////////////////////// | 535 //////////////////////////////////////////////////////////////////////////////// |
1079 // AutocompletePopupContentsView, private: | 536 // AutocompletePopupContentsView, private: |
1080 | 537 |
1081 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const { | 538 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const { |
1082 return index < model_->result().size(); | 539 return index < model_->result().size(); |
1083 } | 540 } |
1084 | 541 |
1085 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex( | 542 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex( |
1086 size_t index) const { | 543 size_t index) const { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1126 bb.hRgnBlur = popup_region.Get(); | 583 bb.hRgnBlur = popup_region.Get(); |
1127 DwmEnableBlurBehindWindow(GetWidget()->GetNativeView(), &bb); | 584 DwmEnableBlurBehindWindow(GetWidget()->GetNativeView(), &bb); |
1128 #endif | 585 #endif |
1129 } | 586 } |
1130 | 587 |
1131 void AutocompletePopupContentsView::MakeCanvasTransparent( | 588 void AutocompletePopupContentsView::MakeCanvasTransparent( |
1132 gfx::Canvas* canvas) { | 589 gfx::Canvas* canvas) { |
1133 // Allow the window blur effect to show through the popup background. | 590 // Allow the window blur effect to show through the popup background. |
1134 SkAlpha alpha = GetThemeProvider()->ShouldUseNativeFrame() ? | 591 SkAlpha alpha = GetThemeProvider()->ShouldUseNativeFrame() ? |
1135 kGlassPopupAlpha : kOpaquePopupAlpha; | 592 kGlassPopupAlpha : kOpaquePopupAlpha; |
1136 canvas->AsCanvasSkia()->drawColor( | 593 canvas->AsCanvasSkia()->drawColor(SkColorSetA( |
1137 SkColorSetA(GetColor(NORMAL, BACKGROUND), alpha), | 594 AutocompleteResultView::GetColor(AutocompleteResultView::NORMAL, |
1138 SkXfermode::kDstIn_Mode); | 595 AutocompleteResultView::BACKGROUND), alpha), SkXfermode::kDstIn_Mode); |
1139 } | 596 } |
1140 | 597 |
1141 void AutocompletePopupContentsView::OpenIndex( | 598 void AutocompletePopupContentsView::OpenIndex( |
1142 size_t index, | 599 size_t index, |
1143 WindowOpenDisposition disposition) { | 600 WindowOpenDisposition disposition) { |
1144 if (!HasMatchAt(index)) | 601 if (!HasMatchAt(index)) |
1145 return; | 602 return; |
1146 | 603 |
1147 const AutocompleteMatch& match = model_->result().match_at(index); | 604 const AutocompleteMatch& match = model_->result().match_at(index); |
1148 // OpenURL() may close the popup, which will clear the result set and, by | 605 // OpenURL() may close the popup, which will clear the result set and, by |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1200 opt_in_view_ = NULL; | 657 opt_in_view_ = NULL; |
1201 PromoCounter* counter = model_->profile()->GetInstantPromoCounter(); | 658 PromoCounter* counter = model_->profile()->GetInstantPromoCounter(); |
1202 DCHECK(counter); | 659 DCHECK(counter); |
1203 counter->Hide(); | 660 counter->Hide(); |
1204 if (opt_in) { | 661 if (opt_in) { |
1205 browser::ShowInstantConfirmDialogIfNecessary( | 662 browser::ShowInstantConfirmDialogIfNecessary( |
1206 location_bar_->GetWindow()->GetNativeWindow(), model_->profile()); | 663 location_bar_->GetWindow()->GetNativeWindow(), model_->profile()); |
1207 } | 664 } |
1208 UpdatePopupAppearance(); | 665 UpdatePopupAppearance(); |
1209 } | 666 } |
OLD | NEW |