OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this |
2 // source code is governed by a BSD-style license that can be found in the | 2 // source code is governed by a BSD-style license that can be found in the |
3 // LICENSE file. | 3 // LICENSE file. |
4 | 4 |
5 #include "chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h" | 5 #include "chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h" |
6 | 6 |
7 #include "app/bidi_line_iterator.h" | 7 #include "app/bidi_line_iterator.h" |
8 #include "app/gfx/canvas.h" | 8 #include "app/gfx/canvas.h" |
9 #include "app/gfx/color_utils.h" | 9 #include "app/gfx/color_utils.h" |
10 #include "app/gfx/insets.h" | 10 #include "app/gfx/insets.h" |
11 #include "app/gfx/path.h" | 11 #include "app/gfx/path.h" |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 | 117 |
118 // Updates the match used to paint the contents of this result view. We copy | 118 // Updates the match used to paint the contents of this result view. We copy |
119 // the match so that we can continue to paint the last result even after the | 119 // the match so that we can continue to paint the last result even after the |
120 // model has changed. | 120 // model has changed. |
121 void set_match(const AutocompleteMatch& match) { match_ = match; } | 121 void set_match(const AutocompleteMatch& match) { match_ = match; } |
122 | 122 |
123 // Overridden from views::View: | 123 // Overridden from views::View: |
124 virtual void Paint(gfx::Canvas* canvas); | 124 virtual void Paint(gfx::Canvas* canvas); |
125 virtual void Layout(); | 125 virtual void Layout(); |
126 virtual gfx::Size GetPreferredSize(); | 126 virtual gfx::Size GetPreferredSize(); |
127 virtual void OnMouseEntered(const views::MouseEvent& event); | |
128 virtual void OnMouseMoved(const views::MouseEvent& event); | |
129 virtual void OnMouseExited(const views::MouseEvent& event); | |
130 virtual bool OnMousePressed(const views::MouseEvent& event); | |
131 virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled); | |
132 virtual bool OnMouseDragged(const views::MouseEvent& event); | |
133 | 127 |
134 private: | 128 private: |
135 ResultViewState GetState() const; | 129 ResultViewState GetState() const; |
136 | 130 |
137 SkBitmap* GetIcon() const; | 131 SkBitmap* GetIcon() const; |
138 | 132 |
139 // Draws the specified |text| into the canvas, using highlighting provided by | 133 // Draws the specified |text| into the canvas, using highlighting provided by |
140 // |classifications|. If |force_dim| is true, ACMatchClassification::DIM is | 134 // |classifications|. If |force_dim| is true, ACMatchClassification::DIM is |
141 // added to all of the classifications. Returns the x position to the right | 135 // added to all of the classifications. Returns the x position to the right |
142 // of the string. | 136 // of the string. |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 std::max(0, bounds().right() - text_x - kRowRightPadding), | 373 std::max(0, bounds().right() - text_x - kRowRightPadding), |
380 font_.height()); | 374 font_.height()); |
381 } | 375 } |
382 | 376 |
383 gfx::Size AutocompleteResultView::GetPreferredSize() { | 377 gfx::Size AutocompleteResultView::GetPreferredSize() { |
384 int text_height = font_.height() + 2 * kTextVerticalPadding; | 378 int text_height = font_.height() + 2 * kTextVerticalPadding; |
385 int icon_height = icon_size_ + 2 * kIconVerticalPadding; | 379 int icon_height = icon_size_ + 2 * kIconVerticalPadding; |
386 return gfx::Size(0, std::max(icon_height, text_height)); | 380 return gfx::Size(0, std::max(icon_height, text_height)); |
387 } | 381 } |
388 | 382 |
389 void AutocompleteResultView::OnMouseEntered(const views::MouseEvent& event) { | |
390 model_->SetHoveredLine(model_index_); | |
391 } | |
392 | |
393 void AutocompleteResultView::OnMouseMoved(const views::MouseEvent& event) { | |
394 model_->SetHoveredLine(model_index_); | |
395 if (event.IsLeftMouseButton()) | |
396 model_->SetSelectedLine(model_index_, false); | |
397 } | |
398 | |
399 void AutocompleteResultView::OnMouseExited(const views::MouseEvent& event) { | |
400 model_->SetHoveredLine(AutocompletePopupModel::kNoMatch); | |
401 } | |
402 | |
403 bool AutocompleteResultView::OnMousePressed(const views::MouseEvent& event) { | |
404 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) { | |
405 model_->SetHoveredLine(model_index_); | |
406 if (event.IsLeftMouseButton()) | |
407 model_->SetSelectedLine(model_index_, false); | |
408 } | |
409 return true; | |
410 } | |
411 | |
412 void AutocompleteResultView::OnMouseReleased(const views::MouseEvent& event, | |
413 bool canceled) { | |
414 if (canceled) | |
415 return; | |
416 if (event.IsOnlyMiddleMouseButton()) | |
417 model_->OpenIndex(model_index_, NEW_BACKGROUND_TAB); | |
418 else if (event.IsOnlyLeftMouseButton()) | |
419 model_->OpenIndex(model_index_, CURRENT_TAB); | |
420 } | |
421 | |
422 bool AutocompleteResultView::OnMouseDragged(const views::MouseEvent& event) { | |
423 // TODO(beng): move all message handling into the contents view and override | |
424 // GetViewForPoint. | |
425 return false; | |
426 } | |
427 | 383 |
428 ResultViewState AutocompleteResultView::GetState() const { | 384 ResultViewState AutocompleteResultView::GetState() const { |
429 if (model_->IsSelectedIndex(model_index_)) | 385 if (model_->IsSelectedIndex(model_index_)) |
430 return SELECTED; | 386 return SELECTED; |
431 return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; | 387 return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; |
432 } | 388 } |
433 | 389 |
434 SkBitmap* AutocompleteResultView::GetIcon() const { | 390 SkBitmap* AutocompleteResultView::GetIcon() const { |
435 bool selected = model_->IsSelectedIndex(model_index_); | 391 bool selected = model_->IsSelectedIndex(model_index_); |
436 if (match_.starred) | 392 if (match_.starred) |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 // AutocompletePopupContentsView, AutocompleteResultViewModel implementation: | 667 // AutocompletePopupContentsView, AutocompleteResultViewModel implementation: |
712 | 668 |
713 bool AutocompletePopupContentsView::IsSelectedIndex(size_t index) const { | 669 bool AutocompletePopupContentsView::IsSelectedIndex(size_t index) const { |
714 return HasMatchAt(index) ? index == model_->selected_line() : false; | 670 return HasMatchAt(index) ? index == model_->selected_line() : false; |
715 } | 671 } |
716 | 672 |
717 bool AutocompletePopupContentsView::IsHoveredIndex(size_t index) const { | 673 bool AutocompletePopupContentsView::IsHoveredIndex(size_t index) const { |
718 return HasMatchAt(index) ? index == model_->hovered_line() : false; | 674 return HasMatchAt(index) ? index == model_->hovered_line() : false; |
719 } | 675 } |
720 | 676 |
721 void AutocompletePopupContentsView::OpenIndex( | |
722 size_t index, | |
723 WindowOpenDisposition disposition) { | |
724 if (!HasMatchAt(index)) | |
725 return; | |
726 | |
727 const AutocompleteMatch& match = model_->result().match_at(index); | |
728 // OpenURL() may close the popup, which will clear the result set and, by | |
729 // extension, |match| and its contents. So copy the relevant strings out to | |
730 // make sure they stay alive until the call completes. | |
731 const GURL url(match.destination_url); | |
732 std::wstring keyword; | |
733 const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword); | |
734 edit_view_->OpenURL(url, disposition, match.transition, GURL(), index, | |
735 is_keyword_hint ? std::wstring() : keyword); | |
736 } | |
737 | |
738 void AutocompletePopupContentsView::SetHoveredLine(size_t index) { | |
739 if (HasMatchAt(index)) | |
740 model_->SetHoveredLine(index); | |
741 } | |
742 | |
743 void AutocompletePopupContentsView::SetSelectedLine(size_t index, | |
744 bool revert_to_default) { | |
745 if (HasMatchAt(index)) | |
746 model_->SetSelectedLine(index, revert_to_default); | |
747 } | |
748 | |
749 //////////////////////////////////////////////////////////////////////////////// | 677 //////////////////////////////////////////////////////////////////////////////// |
750 // AutocompletePopupContentsView, AnimationDelegate implementation: | 678 // AutocompletePopupContentsView, AnimationDelegate implementation: |
751 | 679 |
752 void AutocompletePopupContentsView::AnimationProgressed( | 680 void AutocompletePopupContentsView::AnimationProgressed( |
753 const Animation* animation) { | 681 const Animation* animation) { |
754 // We should only be running the animation when the popup is already visible. | 682 // We should only be running the animation when the popup is already visible. |
755 DCHECK(popup_ != NULL); | 683 DCHECK(popup_ != NULL); |
756 popup_->SetBounds(GetPopupBounds()); | 684 popup_->SetBounds(GetPopupBounds()); |
757 } | 685 } |
758 | 686 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
812 v->SetBounds(contents_rect.x(), top, contents_rect.width(), | 740 v->SetBounds(contents_rect.x(), top, contents_rect.width(), |
813 v->GetPreferredSize().height()); | 741 v->GetPreferredSize().height()); |
814 top = v->bounds().bottom(); | 742 top = v->bounds().bottom(); |
815 } | 743 } |
816 | 744 |
817 // We need to manually schedule a paint here since we are a layered window and | 745 // We need to manually schedule a paint here since we are a layered window and |
818 // won't implicitly require painting until we ask for one. | 746 // won't implicitly require painting until we ask for one. |
819 SchedulePaint(); | 747 SchedulePaint(); |
820 } | 748 } |
821 | 749 |
| 750 |
| 751 void AutocompletePopupContentsView::OnMouseEntered( |
| 752 const views::MouseEvent& event) { |
| 753 model_->SetHoveredLine(GetIndexForPoint(event.location())); |
| 754 } |
| 755 |
| 756 void AutocompletePopupContentsView::OnMouseMoved( |
| 757 const views::MouseEvent& event) { |
| 758 model_->SetHoveredLine(GetIndexForPoint(event.location())); |
| 759 } |
| 760 |
| 761 void AutocompletePopupContentsView::OnMouseExited( |
| 762 const views::MouseEvent& event) { |
| 763 model_->SetHoveredLine(AutocompletePopupModel::kNoMatch); |
| 764 } |
| 765 |
| 766 bool AutocompletePopupContentsView::OnMousePressed( |
| 767 const views::MouseEvent& event) { |
| 768 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) { |
| 769 int index = GetIndexForPoint(event.location()); |
| 770 model_->SetHoveredLine(index); |
| 771 if (HasMatchAt(index) && event.IsLeftMouseButton()) |
| 772 model_->SetSelectedLine(index, false); |
| 773 } |
| 774 return true; |
| 775 } |
| 776 |
| 777 void AutocompletePopupContentsView::OnMouseReleased( |
| 778 const views::MouseEvent& event, |
| 779 bool canceled) { |
| 780 if (canceled) |
| 781 return; |
| 782 int index = GetIndexForPoint(event.location()); |
| 783 if (index == AutocompletePopupModel::kNoMatch) |
| 784 return; |
| 785 if (event.IsOnlyMiddleMouseButton()) |
| 786 OpenIndex(index, NEW_BACKGROUND_TAB); |
| 787 else if (event.IsOnlyLeftMouseButton()) |
| 788 OpenIndex(index, CURRENT_TAB); |
| 789 } |
| 790 |
| 791 bool AutocompletePopupContentsView::OnMouseDragged( |
| 792 const views::MouseEvent& event) { |
| 793 if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) { |
| 794 int index = GetIndexForPoint(event.location()); |
| 795 model_->SetHoveredLine(index); |
| 796 if (HasMatchAt(index) && event.IsLeftMouseButton()) |
| 797 model_->SetSelectedLine(index, false); |
| 798 } |
| 799 return true; |
| 800 } |
| 801 |
| 802 views::View* AutocompletePopupContentsView::GetViewForPoint( |
| 803 const gfx::Point& /*point*/) { |
| 804 // This View takes control of the mouse events, so it should be considered the |
| 805 // active view for any point inside of it. |
| 806 return this; |
| 807 } |
| 808 |
| 809 |
822 //////////////////////////////////////////////////////////////////////////////// | 810 //////////////////////////////////////////////////////////////////////////////// |
823 // AutocompletePopupContentsView, private: | 811 // AutocompletePopupContentsView, private: |
824 | 812 |
825 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const { | 813 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const { |
826 return index < model_->result().size(); | 814 return index < model_->result().size(); |
827 } | 815 } |
828 | 816 |
829 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex( | 817 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex( |
830 size_t index) const { | 818 size_t index) const { |
831 return model_->result().match_at(index); | 819 return model_->result().match_at(index); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
874 | 862 |
875 void AutocompletePopupContentsView::MakeCanvasTransparent( | 863 void AutocompletePopupContentsView::MakeCanvasTransparent( |
876 gfx::Canvas* canvas) { | 864 gfx::Canvas* canvas) { |
877 // Allow the window blur effect to show through the popup background. | 865 // Allow the window blur effect to show through the popup background. |
878 SkAlpha alpha = GetThemeProvider()->ShouldUseNativeFrame() ? | 866 SkAlpha alpha = GetThemeProvider()->ShouldUseNativeFrame() ? |
879 kGlassPopupAlpha : kOpaquePopupAlpha; | 867 kGlassPopupAlpha : kOpaquePopupAlpha; |
880 canvas->drawColor(SkColorSetA(GetColor(NORMAL, BACKGROUND), alpha), | 868 canvas->drawColor(SkColorSetA(GetColor(NORMAL, BACKGROUND), alpha), |
881 SkXfermode::kDstIn_Mode); | 869 SkXfermode::kDstIn_Mode); |
882 } | 870 } |
883 | 871 |
| 872 void AutocompletePopupContentsView::OpenIndex( |
| 873 size_t index, |
| 874 WindowOpenDisposition disposition) { |
| 875 if (!HasMatchAt(index)) |
| 876 return; |
| 877 |
| 878 const AutocompleteMatch& match = model_->result().match_at(index); |
| 879 // OpenURL() may close the popup, which will clear the result set and, by |
| 880 // extension, |match| and its contents. So copy the relevant strings out to |
| 881 // make sure they stay alive until the call completes. |
| 882 const GURL url(match.destination_url); |
| 883 std::wstring keyword; |
| 884 const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword); |
| 885 edit_view_->OpenURL(url, disposition, match.transition, GURL(), index, |
| 886 is_keyword_hint ? std::wstring() : keyword); |
| 887 } |
| 888 |
| 889 int AutocompletePopupContentsView::GetIndexForPoint(const gfx::Point& point) { |
| 890 if (!HitTest(point)) |
| 891 return AutocompletePopupModel::kNoMatch; |
| 892 |
| 893 int nb_match = model_->result().size(); |
| 894 DCHECK(nb_match <= GetChildViewCount()); |
| 895 for (int i = 0; i < nb_match; ++i) { |
| 896 views::View* child = GetChildViewAt(i); |
| 897 gfx::Point point_in_child_coords(point); |
| 898 View::ConvertPointToView(this, child, &point_in_child_coords); |
| 899 if (child->HitTest(point_in_child_coords)) |
| 900 return i; |
| 901 } |
| 902 return AutocompletePopupModel::kNoMatch; |
| 903 } |
| 904 |
884 // static | 905 // static |
885 AutocompletePopupView* AutocompletePopupView::CreatePopupView( | 906 AutocompletePopupView* AutocompletePopupView::CreatePopupView( |
886 const gfx::Font& font, | 907 const gfx::Font& font, |
887 AutocompleteEditView* edit_view, | 908 AutocompleteEditView* edit_view, |
888 AutocompleteEditModel* edit_model, | 909 AutocompleteEditModel* edit_model, |
889 Profile* profile, | 910 Profile* profile, |
890 const BubblePositioner* bubble_positioner) { | 911 const BubblePositioner* bubble_positioner) { |
891 return new AutocompletePopupContentsView(font, edit_view, edit_model, | 912 return new AutocompletePopupContentsView(font, edit_view, edit_model, |
892 profile, bubble_positioner); | 913 profile, bubble_positioner); |
893 } | 914 } |
OLD | NEW |