Index: chrome/browser/ui/views/tabs/tab.cc |
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc |
index 2b4392ccd588810ba7dd052c5d4aa19c003d030d..1db349eaea78a8fb36fc99b6b16de9605a3597a6 100644 |
--- a/chrome/browser/ui/views/tabs/tab.cc |
+++ b/chrome/browser/ui/views/tabs/tab.cc |
@@ -45,6 +45,7 @@ |
#include "ui/views/controls/button/image_button.h" |
#include "ui/views/controls/label.h" |
#include "ui/views/rect_based_targeting_utils.h" |
+#include "ui/views/view_targeter.h" |
#include "ui/views/widget/tooltip_manager.h" |
#include "ui/views/widget/widget.h" |
#include "ui/views/window/non_client_view.h" |
@@ -231,12 +232,19 @@ class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, |
// |
// This is a Button subclass that causes middle clicks to be forwarded to the |
// parent View by explicitly not handling them in OnMousePressed. |
-class Tab::TabCloseButton : public views::ImageButton { |
+class Tab::TabCloseButton : public views::ImageButton, |
+ public views::MaskedTargeterDelegate { |
public: |
- explicit TabCloseButton(Tab* tab) : views::ImageButton(tab), tab_(tab) {} |
+ explicit TabCloseButton(Tab* tab) |
+ : views::ImageButton(tab), |
+ tab_(tab) { |
+ SetEventTargeter( |
+ scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); |
+ } |
+ |
virtual ~TabCloseButton() {} |
- // Overridden from views::View. |
+ // views::View: |
virtual View* GetEventHandlerForRect(const gfx::Rect& rect) OVERRIDE { |
if (!views::UsePointBasedTargeting(rect)) |
return View::GetEventHandlerForRect(rect); |
@@ -252,7 +260,6 @@ class Tab::TabCloseButton : public views::ImageButton { |
return contents_bounds.Intersects(rect) ? this : parent(); |
} |
- // Overridden from views::View. |
virtual View* GetTooltipHandlerForPoint(const gfx::Point& point) OVERRIDE { |
// Tab close button has no children, so tooltip handler should be the same |
// as the event handler. |
@@ -289,26 +296,30 @@ class Tab::TabCloseButton : public views::ImageButton { |
event->SetHandled(); |
} |
- virtual bool HasHitTestMask() const OVERRIDE { |
- return true; |
+ virtual const char* GetClassName() const OVERRIDE { |
+ return kTabCloseButtonName; |
} |
- virtual void GetHitTestMaskDeprecated(HitTestSource source, |
- gfx::Path* path) const OVERRIDE { |
- // Use the button's contents bounds (which does not include padding) |
- // and the hit test mask of our parent |tab_| to determine if the |
- // button is hidden behind another tab. |
+ private: |
+ // Returns the rectangular bounds of parent tab's visible region in the |
+ // local coordinate space of |this|. |
+ gfx::Rect GetTabBounds() const { |
gfx::Path tab_mask; |
- tab_->GetHitTestMaskDeprecated(source, &tab_mask); |
+ tab_->GetHitTestMask(&tab_mask); |
- gfx::Rect button_bounds(GetContentsBounds()); |
- button_bounds.set_x(GetMirroredXForRect(button_bounds)); |
gfx::RectF tab_bounds_f(gfx::SkRectToRectF(tab_mask.getBounds())); |
views::View::ConvertRectToTarget(tab_, this, &tab_bounds_f); |
- gfx::Rect tab_bounds = gfx::ToEnclosingRect(tab_bounds_f); |
+ return gfx::ToEnclosingRect(tab_bounds_f); |
+ } |
+ |
+ // Returns the rectangular bounds of the tab close button in the local |
+ // coordinate space of |this|, not including clipped regions on the top |
+ // or bottom of the button. |tab_bounds| is the rectangular bounds of |
+ // the parent tab's visible region in the local coordinate space of |this|. |
+ gfx::Rect GetTabCloseButtonBounds(const gfx::Rect& tab_bounds) const { |
+ gfx::Rect button_bounds(GetContentsBounds()); |
+ button_bounds.set_x(GetMirroredXForRect(button_bounds)); |
- // If either the top or bottom of the tab close button is clipped, |
- // do not consider these regions to be part of the button's bounds. |
int top_overflow = tab_bounds.y() - button_bounds.y(); |
int bottom_overflow = button_bounds.bottom() - tab_bounds.bottom(); |
if (top_overflow > 0) |
@@ -316,29 +327,52 @@ class Tab::TabCloseButton : public views::ImageButton { |
else if (bottom_overflow > 0) |
button_bounds.set_height(button_bounds.height() - bottom_overflow); |
- // If the hit test request is in response to a gesture, |path| should be |
- // empty unless the entire tab close button is visible to the user. Hit |
- // test requests in response to a mouse event should always set |path| |
- // to be the visible portion of the tab close button, even if it is |
- // partially hidden behind another tab. |
- path->reset(); |
+ return button_bounds; |
+ } |
+ |
+ // views:MaskedTargeterDelegate: |
+ virtual bool GetHitTestMask(gfx::Path* mask) const OVERRIDE { |
+ DCHECK(mask); |
+ mask->reset(); |
+ |
+ // The parent tab may be partially occluded by another tab if we are |
+ // in stacked tab mode, which means that the tab close button may also |
+ // be partially occluded. Define the hit test mask of the tab close |
+ // button to be the intersection of the parent tab's visible bounds |
+ // and the bounds of the tab close button. |
+ gfx::Rect tab_bounds(GetTabBounds()); |
+ gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); |
gfx::Rect intersection(gfx::IntersectRects(tab_bounds, button_bounds)); |
- if (!intersection.IsEmpty()) { |
- // TODO(tdanderson): Consider always returning the intersection if |
- // the non-rectangular shape of the tabs can be accounted for. |
- if (source == HIT_TEST_SOURCE_TOUCH && |
- !tab_bounds.Contains(button_bounds)) |
- return; |
- path->addRect(RectToSkRect(intersection)); |
+ if (!intersection.IsEmpty()) { |
+ mask->addRect(RectToSkRect(intersection)); |
+ return true; |
} |
+ |
+ return false; |
} |
- virtual const char* GetClassName() const OVERRIDE { |
- return kTabCloseButtonName; |
+ virtual bool DoesIntersectRect(const View* target, |
+ const gfx::Rect& rect) const OVERRIDE { |
+ CHECK_EQ(target, this); |
+ |
+ // If the request is not made in response to a gesture, use the |
+ // default implementation. |
+ if (views::UsePointBasedTargeting(rect)) |
+ return MaskedTargeterDelegate::DoesIntersectRect(target, rect); |
+ |
+ // The hit test request is in response to a gesture. Return false if any |
+ // part of the tab close button is hidden from the user. |
+ // TODO(tdanderson): Consider always returning the intersection if the |
+ // non-rectangular shape of the tab can be accounted for. |
+ gfx::Rect tab_bounds(GetTabBounds()); |
+ gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); |
+ if (!tab_bounds.Contains(button_bounds)) |
+ return false; |
+ |
+ return MaskedTargeterDelegate::DoesIntersectRect(target, rect); |
} |
- private: |
Tab* tab_; |
DISALLOW_COPY_AND_ASSIGN(TabCloseButton); |
@@ -401,6 +435,9 @@ Tab::Tab(TabController* controller) |
title_->SetText(CoreTabHelper::GetDefaultTitle()); |
AddChildView(title_); |
+ SetEventTargeter( |
+ scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); |
+ |
// Add the Close Button. |
close_button_ = new TabCloseButton(this); |
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
@@ -655,6 +692,35 @@ void Tab::ShowContextMenuForView(views::View* source, |
} |
//////////////////////////////////////////////////////////////////////////////// |
+// Tab, views::MaskedTargeterDelegate overrides: |
+ |
+bool Tab::GetHitTestMask(gfx::Path* mask) const { |
+ DCHECK(mask); |
+ |
+ // When the window is maximized we don't want to shave off the edges or top |
+ // shadow of the tab, such that the user can click anywhere along the top |
+ // edge of the screen to select a tab. Ditto for immersive fullscreen. |
+ const views::Widget* widget = GetWidget(); |
+ bool include_top_shadow = |
+ widget && (widget->IsMaximized() || widget->IsFullscreen()); |
+ TabResources::GetHitTestMask(width(), height(), include_top_shadow, mask); |
+ |
+ // It is possible for a portion of the tab to be occluded if tabs are |
+ // stacked, so modify the hit test mask to only include the visible |
+ // region of the tab. |
+ gfx::Rect clip; |
+ controller_->ShouldPaintTab(this, &clip); |
+ if (clip.size().GetArea()) { |
+ SkRect intersection(mask->getBounds()); |
+ intersection.intersect(RectToSkRect(clip)); |
+ mask->reset(); |
+ mask->addRect(intersection); |
+ } |
+ |
+ return true; |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
// Tab, views::View overrides: |
void Tab::OnPaint(gfx::Canvas* canvas) { |
@@ -803,33 +869,6 @@ const char* Tab::GetClassName() const { |
return kViewClassName; |
} |
-bool Tab::HasHitTestMask() const { |
- return true; |
-} |
- |
-void Tab::GetHitTestMaskDeprecated(HitTestSource source, |
- gfx::Path* path) const { |
- // When the window is maximized we don't want to shave off the edges or top |
- // shadow of the tab, such that the user can click anywhere along the top |
- // edge of the screen to select a tab. Ditto for immersive fullscreen. |
- const views::Widget* widget = GetWidget(); |
- bool include_top_shadow = |
- widget && (widget->IsMaximized() || widget->IsFullscreen()); |
- TabResources::GetHitTestMask(width(), height(), include_top_shadow, path); |
- |
- // It is possible for a portion of the tab to be occluded if tabs are |
- // stacked, so modify the hit test mask to only include the visible |
- // region of the tab. |
- gfx::Rect clip; |
- controller_->ShouldPaintTab(this, &clip); |
- if (clip.size().GetArea()) { |
- SkRect intersection(path->getBounds()); |
- intersection.intersect(RectToSkRect(clip)); |
- path->reset(); |
- path->addRect(intersection); |
- } |
-} |
- |
bool Tab::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { |
// Note: Anything that affects the tooltip text should be accounted for when |
// calling TooltipTextChanged() from Tab::DataChanged(). |