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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/autocomplete/autocomplete_popup_contents_view.h" 6 7 #include "base/compiler_specific.h" 8 #include "base/utf_string_conversions.h" 9 #include "chrome/browser/autocomplete/autocomplete_edit_view.h" 10 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" 11 #include "chrome/browser/instant/instant_confirm_dialog.h" 12 #include "chrome/browser/instant/promo_counter.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/ui/views/autocomplete/autocomplete_result_view.h" 15 #include "chrome/browser/ui/views/bubble/bubble_border.h" 16 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" 17 #include "grit/chromium_strings.h" 18 #include "grit/generated_resources.h" 19 #include "grit/theme_resources.h" 20 #include "third_party/skia/include/core/SkShader.h" 21 #include "ui/base/l10n/l10n_util.h" 22 #include "ui/base/resource/resource_bundle.h" 23 #include "ui/base/theme_provider.h" 24 #include "ui/gfx/canvas_skia.h" 25 #include "ui/gfx/insets.h" 26 #include "ui/gfx/path.h" 27 #include "unicode/ubidi.h" 28 #include "views/controls/button/text_button.h" 29 #include "views/controls/label.h" 30 #include "views/layout/grid_layout.h" 31 #include "views/layout/layout_constants.h" 32 #include "views/painter.h" 33 #include "views/widget/widget.h" 34 #include "views/window/window.h" 35 36 #if defined(OS_WIN) 37 #include <commctrl.h> 38 #include <dwmapi.h> 39 #include <objidl.h> 40 41 #include "base/win/scoped_gdi_object.h" 42 #include "views/widget/widget_win.h" 43 #endif 44 45 #if defined(OS_LINUX) 46 #include "ui/gfx/skia_utils_gtk.h" 47 #endif 48 49 namespace { 50 51 const SkAlpha kGlassPopupAlpha = 240; 52 const SkAlpha kOpaquePopupAlpha = 255; 53 // The size delta between the font used for the edit and the result rows. Passed 54 // to gfx::Font::DeriveFont. 55 #if defined(OS_CHROMEOS) 56 // Don't adjust the size on Chrome OS (http://crbug.com/61433). 57 const int kEditFontAdjust = 0; 58 #else 59 const int kEditFontAdjust = -1; 60 #endif 61 62 // Horizontal padding between the buttons on the opt in promo. 63 const int kOptInButtonPadding = 2; 64 65 // Padding around the opt in view. 66 const int kOptInLeftPadding = 12; 67 const int kOptInRightPadding = 10; 68 const int kOptInTopPadding = 6; 69 const int kOptInBottomPadding = 5; 70 71 // Horizontal/Vertical inset of the promo background. 72 const int kOptInBackgroundHInset = 6; 73 const int kOptInBackgroundVInset = 2; 74 75 // Border for instant opt-in buttons. Consists of two 9 patch painters: one for 76 // the normal state, the other for the pressed state. 77 class OptInButtonBorder : public views::Border { 78 public: 79 OptInButtonBorder() { 80 border_painter_.reset(CreatePainter(IDR_OPT_IN_BUTTON)); 81 border_pushed_painter_.reset(CreatePainter(IDR_OPT_IN_BUTTON_P)); 82 } 83 84 virtual void Paint(const views::View& view, gfx::Canvas* canvas) const { 85 views::Painter* painter; 86 if (static_cast<const views::CustomButton&>(view).state() == 87 views::CustomButton::BS_PUSHED) { 88 painter = border_pushed_painter_.get(); 89 } else { 90 painter = border_painter_.get(); 91 } 92 painter->Paint(view.width(), view.height(), canvas); 93 } 94 95 virtual void GetInsets(gfx::Insets* insets) const { 96 insets->Set(3, 8, 3, 8); 97 } 98 99 private: 100 // Creates 9 patch painter from the image with the id |image_id|. 101 views::Painter* CreatePainter(int image_id) { 102 SkBitmap* image = 103 ResourceBundle::GetSharedInstance().GetBitmapNamed(image_id); 104 int w = image->width() / 2; 105 if (image->width() % 2 == 0) 106 w--; 107 int h = image->height() / 2; 108 if (image->height() % 2 == 0) 109 h--; 110 gfx::Insets insets(h, w, h, w); 111 return views::Painter::CreateImagePainter(*image, insets, true); 112 } 113 114 scoped_ptr<views::Painter> border_painter_; 115 scoped_ptr<views::Painter> border_pushed_painter_; 116 117 DISALLOW_COPY_AND_ASSIGN(OptInButtonBorder); 118 }; 119 120 } // namespace 121 122 class AutocompletePopupContentsView::InstantOptInView 123 : public views::View, 124 public views::ButtonListener { 125 public: 126 InstantOptInView(AutocompletePopupContentsView* contents_view, 127 const gfx::Font& label_font, 128 const gfx::Font& button_font) 129 : contents_view_(contents_view), 130 bg_painter_(views::Painter::CreateVerticalGradient( 131 SkColorSetRGB(255, 242, 183), 132 SkColorSetRGB(250, 230, 145))) { 133 views::Label* label = new views::Label( 134 UTF16ToWide(l10n_util::GetStringUTF16(IDS_INSTANT_OPT_IN_LABEL))); 135 label->SetFont(label_font); 136 137 views::GridLayout* layout = new views::GridLayout(this); 138 layout->SetInsets(kOptInTopPadding, kOptInLeftPadding, 139 kOptInBottomPadding, kOptInRightPadding); 140 SetLayoutManager(layout); 141 142 const int first_column_set = 1; 143 views::GridLayout::Alignment v_align = views::GridLayout::CENTER; 144 views::ColumnSet* column_set = layout->AddColumnSet(first_column_set); 145 column_set->AddColumn(views::GridLayout::TRAILING, v_align, 1, 146 views::GridLayout::USE_PREF, 0, 0); 147 column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); 148 column_set->AddColumn(views::GridLayout::CENTER, v_align, 0, 149 views::GridLayout::USE_PREF, 0, 0); 150 column_set->AddPaddingColumn(0, kOptInButtonPadding); 151 column_set->AddColumn(views::GridLayout::CENTER, v_align, 0, 152 views::GridLayout::USE_PREF, 0, 0); 153 column_set->LinkColumnSizes(2, 4, -1); 154 layout->StartRow(0, first_column_set); 155 layout->AddView(label); 156 layout->AddView(CreateButton(IDS_INSTANT_OPT_IN_ENABLE, button_font)); 157 layout->AddView(CreateButton(IDS_INSTANT_OPT_IN_NO_THANKS, button_font)); 158 } 159 160 virtual void ButtonPressed(views::Button* sender, const views::Event& event) { 161 contents_view_->UserPressedOptIn( 162 sender->tag() == IDS_INSTANT_OPT_IN_ENABLE); 163 // WARNING: we've been deleted. 164 } 165 166 virtual void OnPaint(gfx::Canvas* canvas) { 167 canvas->Save(); 168 canvas->TranslateInt(kOptInBackgroundHInset, kOptInBackgroundVInset); 169 bg_painter_->Paint(width() - kOptInBackgroundHInset * 2, 170 height() - kOptInBackgroundVInset * 2, canvas); 171 canvas->DrawRectInt(ResourceBundle::toolbar_separator_color, 0, 0, 172 width() - kOptInBackgroundHInset * 2, 173 height() - kOptInBackgroundVInset * 2); 174 canvas->Restore(); 175 } 176 177 private: 178 // Creates and returns a button configured for the opt-in promo. 179 views::View* CreateButton(int id, const gfx::Font& font) { 180 // NOTE: we can't use NativeButton as the popup is a layered window and 181 // native buttons don't draw in layered windows. 182 // TODO: these buttons look crap. Figure out the right border/background to 183 // use. 184 views::TextButton* button = 185 new views::TextButton(this, UTF16ToWide(l10n_util::GetStringUTF16(id))); 186 button->set_border(new OptInButtonBorder()); 187 button->SetNormalHasBorder(true); 188 button->set_tag(id); 189 button->SetFont(font); 190 button->set_animate_on_state_change(false); 191 return button; 192 } 193 194 AutocompletePopupContentsView* contents_view_; 195 scoped_ptr<views::Painter> bg_painter_; 196 197 DISALLOW_COPY_AND_ASSIGN(InstantOptInView); 198 }; 199 200 //////////////////////////////////////////////////////////////////////////////// 201 // AutocompletePopupContentsView, public: 202 203 AutocompletePopupContentsView::AutocompletePopupContentsView( 204 const gfx::Font& font, 205 AutocompleteEditView* edit_view, 206 AutocompleteEditModel* edit_model, 207 Profile* profile, 208 const views::View* location_bar) 209 : model_(new AutocompletePopupModel(this, edit_model, profile)), 210 opt_in_view_(NULL), 211 edit_view_(edit_view), 212 location_bar_(location_bar), 213 result_font_(font.DeriveFont(kEditFontAdjust)), 214 result_bold_font_(result_font_.DeriveFont(0, gfx::Font::BOLD)), 215 ignore_mouse_drag_(false), 216 ALLOW_THIS_IN_INITIALIZER_LIST(size_animation_(this)) { 217 // The following little dance is required because set_border() requires a 218 // pointer to a non-const object. 219 BubbleBorder* bubble_border = new BubbleBorder(BubbleBorder::NONE); 220 bubble_border_ = bubble_border; 221 set_border(bubble_border); 222 // The contents is owned by the LocationBarView. 223 set_parent_owned(false); 224 } 225 226 AutocompletePopupContentsView::~AutocompletePopupContentsView() { 227 // We don't need to do anything with |popup_| here. The OS either has already 228 // closed the window, in which case it's been deleted, or it will soon, in 229 // which case there's nothing we need to do. 230 } 231 232 gfx::Rect AutocompletePopupContentsView::GetPopupBounds() const { 233 if (!size_animation_.is_animating()) 234 return target_bounds_; 235 236 gfx::Rect current_frame_bounds = start_bounds_; 237 int total_height_delta = target_bounds_.height() - start_bounds_.height(); 238 // Round |current_height_delta| instead of truncating so we won't leave single 239 // white pixels at the bottom of the popup as long when animating very small 240 // height differences. 241 int current_height_delta = static_cast<int>( 242 size_animation_.GetCurrentValue() * total_height_delta - 0.5); 243 current_frame_bounds.set_height( 244 current_frame_bounds.height() + current_height_delta); 245 return current_frame_bounds; 246 } 247 248 void AutocompletePopupContentsView::LayoutChildren() { 249 gfx::Rect contents_rect = GetContentsBounds(); 250 int top = contents_rect.y(); 251 for (int i = 0; i < child_count(); ++i) { 252 View* v = GetChildViewAt(i); 253 if (v->IsVisible()) { 254 v->SetBounds(contents_rect.x(), top, contents_rect.width(), 255 v->GetPreferredSize().height()); 256 top = v->bounds().bottom(); 257 } 258 } 259 } 260 261 //////////////////////////////////////////////////////////////////////////////// 262 // AutocompletePopupContentsView, AutocompletePopupView overrides: 263 264 bool AutocompletePopupContentsView::IsOpen() const { 265 return (popup_ != NULL); 266 } 267 268 void AutocompletePopupContentsView::InvalidateLine(size_t line) { 269 GetChildViewAt(static_cast<int>(line))->SchedulePaint(); 270 } 271 272 void AutocompletePopupContentsView::UpdatePopupAppearance() { 273 if (model_->result().empty()) { 274 // No matches, close any existing popup. 275 if (popup_ != NULL) { 276 size_animation_.Stop(); 277 // NOTE: Do NOT use CloseNow() here, as we may be deep in a callstack 278 // triggered by the popup receiving a message (e.g. LBUTTONUP), and 279 // destroying the popup would cause us to read garbage when we unwind back 280 // to that level. 281 popup_->Close(); // This will eventually delete the popup. 282 popup_.reset(); 283 } 284 return; 285 } 286 287 // Update the match cached by each row, in the process of doing so make sure 288 // we have enough row views. 289 size_t child_rv_count = child_count(); 290 if (opt_in_view_) { 291 DCHECK(child_rv_count > 0); 292 child_rv_count--; 293 } 294 for (size_t i = 0; i < model_->result().size(); ++i) { 295 AutocompleteResultView* result_view; 296 if (i >= child_rv_count) { 297 result_view = 298 CreateResultView(this, i, result_font_, result_bold_font_); 299 AddChildViewAt(result_view, static_cast<int>(i)); 300 } else { 301 result_view = static_cast<AutocompleteResultView*>(GetChildViewAt(i)); 302 result_view->SetVisible(true); 303 } 304 result_view->SetMatch(GetMatchAtIndex(i)); 305 } 306 for (size_t i = model_->result().size(); i < child_rv_count; ++i) 307 GetChildViewAt(i)->SetVisible(false); 308 309 PromoCounter* counter = model_->profile()->GetInstantPromoCounter(); 310 if (!opt_in_view_ && counter && counter->ShouldShow(base::Time::Now())) { 311 opt_in_view_ = new InstantOptInView(this, result_bold_font_, result_font_); 312 AddChildView(opt_in_view_); 313 } else if (opt_in_view_ && (!counter || 314 !counter->ShouldShow(base::Time::Now()))) { 315 delete opt_in_view_; 316 opt_in_view_ = NULL; 317 } 318 319 gfx::Rect new_target_bounds = CalculateTargetBounds(CalculatePopupHeight()); 320 321 // If we're animating and our target height changes, reset the animation. 322 // NOTE: If we just reset blindly on _every_ update, then when the user types 323 // rapidly we could get "stuck" trying repeatedly to animate shrinking by the 324 // last few pixels to get to one visible result. 325 if (new_target_bounds.height() != target_bounds_.height()) 326 size_animation_.Reset(); 327 target_bounds_ = new_target_bounds; 328 329 if (popup_ == NULL) { 330 // If the popup is currently closed, we need to create it. 331 popup_ = (new AutocompletePopupClass)->AsWeakPtr(); 332 views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); 333 params.can_activate = false; 334 params.transparent = true; 335 popup_->SetCreateParams(params); 336 popup_->Init(location_bar_->GetWidget()->GetNativeView(), GetPopupBounds()); 337 popup_->SetContentsView(this); 338 popup_->MoveAbove(popup_->GetRelativeWindowForPopup( 339 edit_view_->GetNativeView())); 340 popup_->Show(); 341 } else { 342 // Animate the popup shrinking, but don't animate growing larger since that 343 // would make the popup feel less responsive. 344 start_bounds_ = GetWidget()->GetWindowScreenBounds(); 345 if (target_bounds_.height() < start_bounds_.height()) 346 size_animation_.Show(); 347 else 348 start_bounds_ = target_bounds_; 349 popup_->SetBounds(GetPopupBounds()); 350 } 351 352 SchedulePaint(); 353 } 354 355 gfx::Rect AutocompletePopupContentsView::GetTargetBounds() { 356 return target_bounds_; 357 } 358 359 void AutocompletePopupContentsView::PaintUpdatesNow() { 360 // TODO(beng): remove this from the interface. 361 } 362 363 void AutocompletePopupContentsView::OnDragCanceled() { 364 ignore_mouse_drag_ = true; 365 } 366 367 //////////////////////////////////////////////////////////////////////////////// 368 // AutocompletePopupContentsView, AutocompleteResultViewModel implementation: 369 370 bool AutocompletePopupContentsView::IsSelectedIndex(size_t index) const { 371 return HasMatchAt(index) ? index == model_->selected_line() : false; 372 } 373 374 bool AutocompletePopupContentsView::IsHoveredIndex(size_t index) const { 375 return HasMatchAt(index) ? index == model_->hovered_line() : false; 376 } 377 378 const SkBitmap* AutocompletePopupContentsView::GetIconIfExtensionMatch( 379 size_t index) const { 380 if (!HasMatchAt(index)) 381 return NULL; 382 return model_->GetIconIfExtensionMatch(GetMatchAtIndex(index)); 383 } 384 385 //////////////////////////////////////////////////////////////////////////////// 386 // AutocompletePopupContentsView, AnimationDelegate implementation: 387 388 void AutocompletePopupContentsView::AnimationProgressed( 389 const ui::Animation* animation) { 390 // We should only be running the animation when the popup is already visible. 391 DCHECK(popup_ != NULL); 392 popup_->SetBounds(GetPopupBounds()); 393 } 394 395 //////////////////////////////////////////////////////////////////////////////// 396 // AutocompletePopupContentsView, views::View overrides: 397 398 void AutocompletePopupContentsView::Layout() { 399 UpdateBlurRegion(); 400 401 // Size our children to the available content area. 402 LayoutChildren(); 403 404 // We need to manually schedule a paint here since we are a layered window and 405 // won't implicitly require painting until we ask for one. 406 SchedulePaint(); 407 } 408 409 views::View* AutocompletePopupContentsView::GetEventHandlerForPoint( 410 const gfx::Point& point) { 411 // If there is no opt in view, then we want all mouse events. Otherwise let 412 // any descendants of the opt-in view get mouse events. 413 if (!opt_in_view_) 414 return this; 415 416 views::View* child = views::View::GetEventHandlerForPoint(point); 417 views::View* ancestor = child; 418 while (ancestor && ancestor != opt_in_view_) 419 ancestor = ancestor->parent(); 420 return ancestor ? child : this; 421 } 422 423 bool AutocompletePopupContentsView::OnMousePressed( 424 const views::MouseEvent& event) { 425 ignore_mouse_drag_ = false; // See comment on |ignore_mouse_drag_| in header. 426 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) { 427 size_t index = GetIndexForPoint(event.location()); 428 model_->SetHoveredLine(index); 429 if (HasMatchAt(index) && event.IsLeftMouseButton()) 430 model_->SetSelectedLine(index, false, false); 431 } 432 return true; 433 } 434 435 bool AutocompletePopupContentsView::OnMouseDragged( 436 const views::MouseEvent& event) { 437 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) { 438 size_t index = GetIndexForPoint(event.location()); 439 model_->SetHoveredLine(index); 440 if (!ignore_mouse_drag_ && HasMatchAt(index) && event.IsLeftMouseButton()) 441 model_->SetSelectedLine(index, false, false); 442 } 443 return true; 444 } 445 446 void AutocompletePopupContentsView::OnMouseReleased( 447 const views::MouseEvent& event) { 448 if (ignore_mouse_drag_) { 449 OnMouseCaptureLost(); 450 return; 451 } 452 453 size_t index = GetIndexForPoint(event.location()); 454 if (event.IsOnlyMiddleMouseButton()) 455 OpenIndex(index, NEW_BACKGROUND_TAB); 456 else if (event.IsOnlyLeftMouseButton()) 457 OpenIndex(index, CURRENT_TAB); 458 } 459 460 void AutocompletePopupContentsView::OnMouseCaptureLost() { 461 ignore_mouse_drag_ = false; 462 } 463 464 void AutocompletePopupContentsView::OnMouseMoved( 465 const views::MouseEvent& event) { 466 model_->SetHoveredLine(GetIndexForPoint(event.location())); 467 } 468 469 void AutocompletePopupContentsView::OnMouseEntered( 470 const views::MouseEvent& event) { 471 model_->SetHoveredLine(GetIndexForPoint(event.location())); 472 } 473 474 void AutocompletePopupContentsView::OnMouseExited( 475 const views::MouseEvent& event) { 476 model_->SetHoveredLine(AutocompletePopupModel::kNoMatch); 477 } 478 479 //////////////////////////////////////////////////////////////////////////////// 480 // AutocompletePopupContentsView, protected: 481 482 void AutocompletePopupContentsView::PaintResultViews(gfx::CanvasSkia* canvas) { 483 canvas->drawColor(AutocompleteResultView::GetColor( 484 AutocompleteResultView::NORMAL, AutocompleteResultView::BACKGROUND)); 485 View::PaintChildren(canvas); 486 } 487 488 int AutocompletePopupContentsView::CalculatePopupHeight() { 489 DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size()); 490 int popup_height = 0; 491 for (size_t i = 0; i < model_->result().size(); ++i) 492 popup_height += GetChildViewAt(i)->GetPreferredSize().height(); 493 return popup_height + 494 (opt_in_view_ ? opt_in_view_->GetPreferredSize().height() : 0); 495 } 496 497 AutocompleteResultView* AutocompletePopupContentsView::CreateResultView( 498 AutocompleteResultViewModel* model, 499 int model_index, 500 const gfx::Font& font, 501 const gfx::Font& bold_font) { 502 return new AutocompleteResultView(model, model_index, font, bold_font); 503 } 504 505 //////////////////////////////////////////////////////////////////////////////// 506 // AutocompletePopupContentsView, views::View overrides, protected: 507 508 void AutocompletePopupContentsView::OnPaint(gfx::Canvas* canvas) { 509 // We paint our children in an unconventional way. 510 // 511 // Because the border of this view creates an anti-aliased round-rect region 512 // for the contents, we need to render our rectangular result child views into 513 // this round rect region. We can't use a simple clip because clipping is 514 // 1-bit and we get nasty jagged edges. 515 // 516 // Instead, we paint all our children into a second canvas and use that as a 517 // shader to fill a path representing the round-rect clipping region. This 518 // yields a nice anti-aliased edge. 519 gfx::CanvasSkia contents_canvas(width(), height(), true); 520 PaintResultViews(&contents_canvas); 521 522 // We want the contents background to be slightly transparent so we can see 523 // the blurry glass effect on DWM systems behind. We do this _after_ we paint 524 // the children since they paint text, and GDI will reset this alpha data if 525 // we paint text after this call. 526 MakeCanvasTransparent(&contents_canvas); 527 528 // Now paint the contents of the contents canvas into the actual canvas. 529 SkPaint paint; 530 paint.setAntiAlias(true); 531 532 SkShader* shader = SkShader::CreateBitmapShader( 533 contents_canvas.getDevice()->accessBitmap(false), 534 SkShader::kClamp_TileMode, 535 SkShader::kClamp_TileMode); 536 paint.setShader(shader); 537 shader->unref(); 538 539 gfx::Path path; 540 MakeContentsPath(&path, GetContentsBounds()); 541 canvas->AsCanvasSkia()->drawPath(path, paint); 542 543 // Now we paint the border, so it will be alpha-blended atop the contents. 544 // This looks slightly better in the corners than drawing the contents atop 545 // the border. 546 OnPaintBorder(canvas); 547 } 548 549 void AutocompletePopupContentsView::PaintChildren(gfx::Canvas* canvas) { 550 // We paint our children inside OnPaint(). 551 } 552 553 //////////////////////////////////////////////////////////////////////////////// 554 // AutocompletePopupContentsView, private: 555 556 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const { 557 return index < model_->result().size(); 558 } 559 560 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex( 561 size_t index) const { 562 return model_->result().match_at(index); 563 } 564 565 void AutocompletePopupContentsView::MakeContentsPath( 566 gfx::Path* path, 567 const gfx::Rect& bounding_rect) { 568 SkRect rect; 569 rect.set(SkIntToScalar(bounding_rect.x()), 570 SkIntToScalar(bounding_rect.y()), 571 SkIntToScalar(bounding_rect.right()), 572 SkIntToScalar(bounding_rect.bottom())); 573 574 SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius()); 575 path->addRoundRect(rect, radius, radius); 576 } 577 578 void AutocompletePopupContentsView::UpdateBlurRegion() { 579 #if defined(OS_WIN) 580 // We only support background blurring on Vista with Aero-Glass enabled. 581 if (!views::WidgetWin::IsAeroGlassEnabled() || !GetWidget()) 582 return; 583 584 // Provide a blurred background effect within the contents region of the 585 // popup. 586 DWM_BLURBEHIND bb = {0}; 587 bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; 588 bb.fEnable = true; 589 590 // Translate the contents rect into widget coordinates, since that's what 591 // DwmEnableBlurBehindWindow expects a region in. 592 gfx::Rect contents_rect = GetContentsBounds(); 593 gfx::Point origin(contents_rect.origin()); 594 views::View::ConvertPointToWidget(this, &origin); 595 contents_rect.set_origin(origin); 596 597 gfx::Path contents_path; 598 MakeContentsPath(&contents_path, contents_rect); 599 base::win::ScopedGDIObject<HRGN> popup_region; 600 popup_region.Set(contents_path.CreateNativeRegion()); 601 bb.hRgnBlur = popup_region.Get(); 602 DwmEnableBlurBehindWindow(GetWidget()->GetNativeView(), &bb); 603 #endif 604 } 605 606 void AutocompletePopupContentsView::MakeCanvasTransparent( 607 gfx::Canvas* canvas) { 608 // Allow the window blur effect to show through the popup background. 609 SkAlpha alpha = GetThemeProvider()->ShouldUseNativeFrame() ? 610 kGlassPopupAlpha : kOpaquePopupAlpha; 611 canvas->AsCanvasSkia()->drawColor(SkColorSetA( 612 AutocompleteResultView::GetColor(AutocompleteResultView::NORMAL, 613 AutocompleteResultView::BACKGROUND), alpha), SkXfermode::kDstIn_Mode); 614 } 615 616 void AutocompletePopupContentsView::OpenIndex( 617 size_t index, 618 WindowOpenDisposition disposition) { 619 if (!HasMatchAt(index)) 620 return; 621 622 const AutocompleteMatch& match = model_->result().match_at(index); 623 // OpenURL() may close the popup, which will clear the result set and, by 624 // extension, |match| and its contents. So copy the relevant strings out to 625 // make sure they stay alive until the call completes. 626 const GURL url(match.destination_url); 627 string16 keyword; 628 const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword); 629 edit_view_->OpenURL(url, disposition, match.transition, GURL(), index, 630 is_keyword_hint ? string16() : keyword); 631 } 632 633 size_t AutocompletePopupContentsView::GetIndexForPoint( 634 const gfx::Point& point) { 635 if (!HitTest(point)) 636 return AutocompletePopupModel::kNoMatch; 637 638 int nb_match = model_->result().size(); 639 DCHECK(nb_match <= child_count()); 640 for (int i = 0; i < nb_match; ++i) { 641 views::View* child = GetChildViewAt(i); 642 gfx::Point point_in_child_coords(point); 643 View::ConvertPointToView(this, child, &point_in_child_coords); 644 if (child->HitTest(point_in_child_coords)) 645 return i; 646 } 647 return AutocompletePopupModel::kNoMatch; 648 } 649 650 gfx::Rect AutocompletePopupContentsView::CalculateTargetBounds(int h) { 651 gfx::Rect location_bar_bounds(location_bar_->GetContentsBounds()); 652 const views::Border* border = location_bar_->border(); 653 if (border) { 654 // Adjust for the border so that the bubble and location bar borders are 655 // aligned. 656 gfx::Insets insets; 657 border->GetInsets(&insets); 658 location_bar_bounds.Inset(insets.left(), 0, insets.right(), 0); 659 } else { 660 // The normal location bar is drawn using a background graphic that includes 661 // the border, so we inset by enough to make the edges line up, and the 662 // bubble appear at the same height as the Star bubble. 663 location_bar_bounds.Inset(LocationBarView::kNormalHorizontalEdgeThickness, 664 0); 665 } 666 gfx::Point location_bar_origin(location_bar_bounds.origin()); 667 views::View::ConvertPointToScreen(location_bar_, &location_bar_origin); 668 location_bar_bounds.set_origin(location_bar_origin); 669 return bubble_border_->GetBounds( 670 location_bar_bounds, gfx::Size(location_bar_bounds.width(), h)); 671 } 672 673 void AutocompletePopupContentsView::UserPressedOptIn(bool opt_in) { 674 delete opt_in_view_; 675 opt_in_view_ = NULL; 676 PromoCounter* counter = model_->profile()->GetInstantPromoCounter(); 677 DCHECK(counter); 678 counter->Hide(); 679 if (opt_in) { 680 browser::ShowInstantConfirmDialogIfNecessary( 681 location_bar_->GetWindow()->GetNativeWindow(), model_->profile()); 682 } 683 UpdatePopupAppearance(); 684 } 685