| 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 |