Commit d6c65442 authored by Thomas Lukaszewicz's avatar Thomas Lukaszewicz Committed by Commit Bot

Tab Strip: Fix caption area hit testing for TabStripRegionView

Currently if Tab Scrolling is enabled the TabStrip is allowed to grow
beyond the visible bounds of the TabStripRegionView with the help of
a ScrollView.

Existing caption hit testing uses the TabStrip view to determine
whether a rect or point belongs to the caption area. This results in
a bug where areas of the browser window are no longer recognized as
part of the caption area.

This is due to the non-visible parts of the TabStrip continuing to
contribute to the hit testing logic as it overflows.

This patch updates the TabStripRegionView to support the necessary
hit testing logic and ensure that only the visible portion of the
TabStrip is used.

Bug: 1129703

Change-Id: I49ccf9ba573afba84921a66249ef38723787cbba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2411704
Commit-Queue: Thomas Lukaszewicz <tluk@chromium.org>
Reviewed-by: default avatarTaylor Bergquist <tbergquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808579}
parent b987ed06
...@@ -321,24 +321,29 @@ bool BrowserNonClientFrameView::DoesIntersectRect(const views::View* target, ...@@ -321,24 +321,29 @@ bool BrowserNonClientFrameView::DoesIntersectRect(const views::View* target,
if (!frame_->client_view()->HitTestRect(rect_in_client_view_coords)) if (!frame_->client_view()->HitTestRect(rect_in_client_view_coords))
return true; return true;
// Otherwise, claim |rect| only if it is above the bottom of the tabstrip in // Otherwise, claim |rect| only if it is above the bottom of the tab strip
// a non-tab portion. // region view in a non-tab portion.
TabStrip* tabstrip = browser_view_->tabstrip(); TabStripRegionView* tab_strip_region_view =
// The tabstrip may not be in a Widget (e.g. when switching into immersive browser_view_->tab_strip_region_view();
// reveal).
if (tabstrip->GetWidget()) { // The |tab_strip_region_view| may not be in a Widget (e.g. when switching
gfx::RectF rect_in_tabstrip_coords_f(rect); // into immersive reveal the BrowserView's TopContainerView is reparented).
View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f); if (tab_strip_region_view->GetWidget()) {
gfx::Rect rect_in_tabstrip_coords = gfx::RectF rect_in_region_view_coords_f(rect);
gfx::ToEnclosingRect(rect_in_tabstrip_coords_f); View::ConvertRectToTarget(this, tab_strip_region_view,
if (rect_in_tabstrip_coords.y() >= tabstrip->GetLocalBounds().bottom()) { &rect_in_region_view_coords_f);
// |rect| is below the tabstrip. gfx::Rect rect_in_region_view_coords =
gfx::ToEnclosingRect(rect_in_region_view_coords_f);
if (rect_in_region_view_coords.y() >=
tab_strip_region_view->GetLocalBounds().bottom()) {
// |rect| is below the tab_strip_region_view.
return false; return false;
} }
if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) { if (tab_strip_region_view->HitTestRect(rect_in_region_view_coords)) {
// Claim |rect| if it is in a non-tab portion of the tabstrip. // Claim |rect| if it is in a non-tab portion of the tabstrip.
return tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords); return tab_strip_region_view->IsRectInWindowCaption(
rect_in_region_view_coords);
} }
} }
......
...@@ -135,7 +135,7 @@ BrowserViewLayout::BrowserViewLayout( ...@@ -135,7 +135,7 @@ BrowserViewLayout::BrowserViewLayout(
gfx::NativeView host_view, gfx::NativeView host_view,
BrowserView* browser_view, BrowserView* browser_view,
views::View* top_container, views::View* top_container,
views::View* tab_strip_region_view, TabStripRegionView* tab_strip_region_view,
TabStrip* tab_strip, TabStrip* tab_strip,
views::View* toolbar, views::View* toolbar,
InfoBarContainerView* infobar_container, InfoBarContainerView* infobar_container,
...@@ -233,10 +233,10 @@ int BrowserViewLayout::NonClientHitTest(const gfx::Point& point) { ...@@ -233,10 +233,10 @@ int BrowserViewLayout::NonClientHitTest(const gfx::Point& point) {
// Determine if the TabStrip exists and is capable of being clicked on. We // Determine if the TabStrip exists and is capable of being clicked on. We
// might be a popup window without a TabStrip. // might be a popup window without a TabStrip.
if (delegate_->IsTabStripVisible()) { if (delegate_->IsTabStripVisible()) {
// See if the mouse pointer is within the bounds of the TabStrip. // See if the mouse pointer is within the bounds of the TabStripRegionView.
gfx::Point test_point(point); gfx::Point test_point(point);
if (ConvertedHitTest(parent, tab_strip_, &test_point)) { if (ConvertedHitTest(parent, tab_strip_region_view_, &test_point)) {
if (tab_strip_->IsPositionInWindowCaption(test_point)) if (tab_strip_region_view_->IsPositionInWindowCaption(test_point))
return HTCAPTION; return HTCAPTION;
return HTCLIENT; return HTCLIENT;
} }
...@@ -244,10 +244,12 @@ int BrowserViewLayout::NonClientHitTest(const gfx::Point& point) { ...@@ -244,10 +244,12 @@ int BrowserViewLayout::NonClientHitTest(const gfx::Point& point) {
// The top few pixels of the TabStrip are a drop-shadow - as we're pretty // The top few pixels of the TabStrip are a drop-shadow - as we're pretty
// starved of dragable area, let's give it to window dragging (this also // starved of dragable area, let's give it to window dragging (this also
// makes sense visually). // makes sense visually).
// TODO(tluk): Investigate the impact removing this has on draggable area
// given the tab strip no longer uses shadows.
views::Widget* widget = browser_view_->GetWidget(); views::Widget* widget = browser_view_->GetWidget();
if (!(widget->IsMaximized() || widget->IsFullscreen()) && if (!(widget->IsMaximized() || widget->IsFullscreen()) &&
(point_in_browser_view_coords.y() < (point_in_browser_view_coords.y() <
(tab_strip_->y() + kTabShadowSize))) { (tab_strip_region_view_->y() + kTabShadowSize))) {
// We return HTNOWHERE as this is a signal to our containing // We return HTNOWHERE as this is a signal to our containing
// NonClientView that it should figure out what the correct hit-test // NonClientView that it should figure out what the correct hit-test
// code is given the mouse position... // code is given the mouse position...
......
...@@ -21,6 +21,7 @@ class BrowserViewLayoutDelegate; ...@@ -21,6 +21,7 @@ class BrowserViewLayoutDelegate;
class ImmersiveModeController; class ImmersiveModeController;
class InfoBarContainerView; class InfoBarContainerView;
class TabStrip; class TabStrip;
class TabStripRegionView;
namespace gfx { namespace gfx {
class Point; class Point;
...@@ -52,7 +53,7 @@ class BrowserViewLayout : public views::LayoutManager { ...@@ -52,7 +53,7 @@ class BrowserViewLayout : public views::LayoutManager {
gfx::NativeView host_view, gfx::NativeView host_view,
BrowserView* browser_view, BrowserView* browser_view,
views::View* top_container, views::View* top_container,
views::View* tab_strip_region_view, TabStripRegionView* tab_strip_region_view,
TabStrip* tab_strip, TabStrip* tab_strip,
views::View* toolbar, views::View* toolbar,
InfoBarContainerView* infobar_container, InfoBarContainerView* infobar_container,
...@@ -146,7 +147,7 @@ class BrowserViewLayout : public views::LayoutManager { ...@@ -146,7 +147,7 @@ class BrowserViewLayout : public views::LayoutManager {
// NOTE: If you add a view, try to add it as a views::View, which makes // NOTE: If you add a view, try to add it as a views::View, which makes
// testing much easier. // testing much easier.
views::View* const top_container_; views::View* const top_container_;
views::View* const tab_strip_region_view_; TabStripRegionView* const tab_strip_region_view_;
views::View* const toolbar_; views::View* const toolbar_;
InfoBarContainerView* const infobar_container_; InfoBarContainerView* const infobar_container_;
views::View* const contents_container_; views::View* const contents_container_;
......
...@@ -172,8 +172,8 @@ class BrowserViewLayoutTest : public ChromeViewsTestBase { ...@@ -172,8 +172,8 @@ class BrowserViewLayoutTest : public ChromeViewsTestBase {
auto tab_strip = std::make_unique<TabStrip>( auto tab_strip = std::make_unique<TabStrip>(
std::make_unique<FakeBaseTabStripController>()); std::make_unique<FakeBaseTabStripController>());
tab_strip_ = tab_strip.get(); tab_strip_ = tab_strip.get();
views::View* tab_strip_region_view = TabStripRegionView* tab_strip_region_view = top_container_->AddChildView(
top_container_->AddChildView(std::move(tab_strip)); std::make_unique<TabStripRegionView>(std::move(tab_strip)));
webui_tab_strip_ = webui_tab_strip_ =
top_container_->AddChildView(CreateFixedSizeView(gfx::Size(800, 200))); top_container_->AddChildView(CreateFixedSizeView(gfx::Size(800, 200)));
webui_tab_strip_->SetVisible(false); webui_tab_strip_->SetVisible(false);
......
...@@ -38,6 +38,30 @@ TabStripRegionView::TabStripRegionView(std::unique_ptr<TabStrip> tab_strip) { ...@@ -38,6 +38,30 @@ TabStripRegionView::TabStripRegionView(std::unique_ptr<TabStrip> tab_strip) {
TabStripRegionView::~TabStripRegionView() = default; TabStripRegionView::~TabStripRegionView() = default;
bool TabStripRegionView::IsRectInWindowCaption(const gfx::Rect& rect) {
const auto get_target_rect = [&](views::View* target) {
gfx::RectF rect_in_target_coords_f(rect);
View::ConvertRectToTarget(this, target, &rect_in_target_coords_f);
return gfx::ToEnclosingRect(rect_in_target_coords_f);
};
// Perform a hit test against the |tab_strip_container_| to ensure that the
// rect is within the visible portion of the |tab_strip_| before calling the
// tab strip's |IsRectInWindowCaption()|.
// TODO(tluk): Address edge case where |rect| might partially intersect with
// the |tab_strip_container_| and the |tab_strip_| but not over the same
// pixels. This could lead to this returning false when it should be returning
// true.
if (tab_strip_container_->HitTestRect(get_target_rect(tab_strip_container_)))
return tab_strip_->IsRectInWindowCaption(get_target_rect(tab_strip_));
return true;
}
bool TabStripRegionView::IsPositionInWindowCaption(const gfx::Point& point) {
return IsRectInWindowCaption(gfx::Rect(point, gfx::Size(1, 1)));
}
const char* TabStripRegionView::GetClassName() const { const char* TabStripRegionView::GetClassName() const {
return "TabStripRegionView"; return "TabStripRegionView";
} }
......
...@@ -15,6 +15,16 @@ class TabStripRegionView final : public views::View { ...@@ -15,6 +15,16 @@ class TabStripRegionView final : public views::View {
explicit TabStripRegionView(std::unique_ptr<TabStrip> tab_strip); explicit TabStripRegionView(std::unique_ptr<TabStrip> tab_strip);
~TabStripRegionView() override; ~TabStripRegionView() override;
// Returns true if the specified rect intersects the window caption area of
// the browser window. |rect| is in the local coordinate space
// of |this|.
bool IsRectInWindowCaption(const gfx::Rect& rect);
// A convenience function which calls |IsRectInWindowCaption()| with a rect of
// size 1x1 and an origin of |point|. |point| is in the local coordinate space
// of |this|.
bool IsPositionInWindowCaption(const gfx::Point& point);
// views::View overrides: // views::View overrides:
const char* GetClassName() const override; const char* GetClassName() const override;
void ChildPreferredSizeChanged(views::View* child) override; void ChildPreferredSizeChanged(views::View* child) override;
...@@ -28,7 +38,7 @@ class TabStripRegionView final : public views::View { ...@@ -28,7 +38,7 @@ class TabStripRegionView final : public views::View {
int CalculateTabStripAvailableWidth(); int CalculateTabStripAvailableWidth();
views::View* tab_strip_container_; views::View* tab_strip_container_;
views::View* tab_strip_; TabStrip* tab_strip_;
}; };
#endif // CHROME_BROWSER_UI_VIEWS_FRAME_TAB_STRIP_REGION_VIEW_H_ #endif // CHROME_BROWSER_UI_VIEWS_FRAME_TAB_STRIP_REGION_VIEW_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment