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 73e01ea1492f8dfe4f4edbac3d2069bddc837bbe..8c71f31028565fdf43f9e999589acba71db69b60 100644 |
--- a/chrome/browser/ui/views/tabs/tab.cc |
+++ b/chrome/browser/ui/views/tabs/tab.cc |
@@ -39,8 +39,11 @@ |
#include "ui/gfx/font.h" |
#include "ui/gfx/image/image_skia_operations.h" |
#include "ui/gfx/path.h" |
+#include "ui/gfx/rect_conversions.h" |
+#include "ui/gfx/skia_util.h" |
#include "ui/gfx/text_elider.h" |
#include "ui/views/controls/button/image_button.h" |
+#include "ui/views/rect_based_targeting_utils.h" |
#include "ui/views/widget/tooltip_manager.h" |
#include "ui/views/widget/widget.h" |
#include "ui/views/window/non_client_view.h" |
@@ -242,6 +245,8 @@ const double kImmersiveTabMinThrobOpacity = 0.66; |
// Number of steps in the immersive mode loading animation. |
const int kImmersiveLoadingStepCount = 32; |
+const char kTabCloseButtonName[] = "TabCloseButton"; |
+ |
void DrawIconAtLocation(gfx::Canvas* canvas, |
const gfx::ImageSkia& image, |
int image_offset, |
@@ -351,21 +356,26 @@ class Tab::TabCloseButton : public views::ImageButton { |
virtual ~TabCloseButton() {} |
// Overridden from views::View. |
- virtual View* GetEventHandlerForPoint(const gfx::Point& point) OVERRIDE { |
+ virtual View* GetEventHandlerForRect(const gfx::Rect& rect) OVERRIDE { |
+ if (!views::UsePointBasedTargeting(rect)) |
+ return View::GetEventHandlerForRect(rect); |
+ |
// Ignore the padding set on the button. |
- gfx::Rect rect = GetContentsBounds(); |
- rect.set_x(GetMirroredXForRect(rect)); |
+ gfx::Rect contents_bounds = GetContentsBounds(); |
+ contents_bounds.set_x(GetMirroredXForRect(contents_bounds)); |
+ // TODO(tdanderson): Remove this ifdef if rect-based targeting |
+ // is turned on by default. |
#if defined(USE_ASH) |
// Include the padding in hit-test for touch events. |
if (aura::Env::GetInstance()->is_touch_down()) |
- rect = GetLocalBounds(); |
+ contents_bounds = GetLocalBounds(); |
#elif defined(OS_WIN) |
// TODO(sky): Use local-bounds if a touch-point is active. |
// http://crbug.com/145258 |
#endif |
- return rect.Contains(point) ? this : parent(); |
+ return contents_bounds.Intersects(rect) ? this : parent(); |
} |
// Overridden from views::View. |
@@ -408,6 +418,39 @@ class Tab::TabCloseButton : public views::ImageButton { |
event->SetHandled(); |
} |
+ virtual bool HasHitTestMask() const OVERRIDE { |
+ return true; |
+ } |
+ |
+ virtual void GetHitTestMask(gfx::Path* path, |
+ HitTestSource source = HIT_TEST_SOURCE_MOUSE) 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 completely hidden behind another tab. |
+ gfx::Path tab_mask; |
+ tab_->GetHitTestMask(&tab_mask, source); |
+ |
+ gfx::Rect button_bounds(GetContentsBounds()); |
+ 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); |
+ |
+ // If the button is completely hidden behind another tab, the hit test |
+ // mask is empty. Otherwise set the hit test mask to be the contents bounds. |
+ path->reset(); |
+ if (tab_bounds.Contains(button_bounds)) { |
+ // Include the padding in the hit test mask for touch events. |
+ if (source == HIT_TEST_SOURCE_TOUCH) |
+ button_bounds = GetLocalBounds(); |
+ |
+ path->addRect(RectToSkRect(button_bounds)); |
+ } |
+ } |
+ |
+ virtual const char* GetClassName() const OVERRIDE { |
+ return kTabCloseButtonName; |
+ } |
+ |
private: |
Tab* tab_; |
@@ -872,7 +915,7 @@ bool Tab::HasHitTestMask() const { |
return true; |
} |
-void Tab::GetHitTestMask(gfx::Path* path) const { |
+void Tab::GetHitTestMask(gfx::Path* path, HitTestSource source) 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. |
@@ -880,6 +923,20 @@ void Tab::GetHitTestMask(gfx::Path* path) const { |
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. |
+ if (controller()) { |
+ 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, string16* tooltip) const { |