| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/views/tabs/tab_strip.h" | 5 #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| 6 | 6 |
| 7 #include "base/macros.h" | 7 #include "base/macros.h" |
| 8 #include "base/message_loop/message_loop.h" | 8 #include "base/message_loop/message_loop.h" |
| 9 #include "chrome/browser/ui/layout_constants.h" |
| 9 #include "chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h" | 10 #include "chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h" |
| 10 #include "chrome/browser/ui/views/tabs/tab.h" | 11 #include "chrome/browser/ui/views/tabs/tab.h" |
| 11 #include "chrome/browser/ui/views/tabs/tab_renderer_data.h" | 12 #include "chrome/browser/ui/views/tabs/tab_renderer_data.h" |
| 12 #include "chrome/browser/ui/views/tabs/tab_strip.h" | 13 #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| 13 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" | 14 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" |
| 14 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h" | 15 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h" |
| 15 #include "chrome/test/base/testing_profile.h" | 16 #include "chrome/test/base/testing_profile.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
| 17 #include "ui/base/material_design/material_design_controller.h" | 18 #include "ui/base/material_design/material_design_controller.h" |
| 18 #include "ui/gfx/canvas.h" | 19 #include "ui/gfx/canvas.h" |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 widget_.reset(); | 127 widget_.reset(); |
| 127 views::ViewsTestBase::TearDown(); | 128 views::ViewsTestBase::TearDown(); |
| 128 } | 129 } |
| 129 | 130 |
| 130 protected: | 131 protected: |
| 131 bool IsShowingPinnedTabTitleChangedIndicator(int model_index) { | 132 bool IsShowingPinnedTabTitleChangedIndicator(int model_index) { |
| 132 return tab_strip_->tab_at(model_index) | 133 return tab_strip_->tab_at(model_index) |
| 133 ->showing_pinned_tab_title_changed_indicator_; | 134 ->showing_pinned_tab_title_changed_indicator_; |
| 134 } | 135 } |
| 135 | 136 |
| 136 // Returns the rectangular hit test region of |tab| in |tab|'s local | |
| 137 // coordinate space. | |
| 138 gfx::Rect GetTabHitTestMask(Tab* tab) { | |
| 139 views::ViewTargeter* targeter = tab->targeter(); | |
| 140 DCHECK(targeter); | |
| 141 views::MaskedTargeterDelegate* delegate = | |
| 142 static_cast<views::MaskedTargeterDelegate*>(tab); | |
| 143 | |
| 144 gfx::Path mask; | |
| 145 bool valid_mask = delegate->GetHitTestMask(&mask); | |
| 146 DCHECK(valid_mask); | |
| 147 | |
| 148 return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask.getBounds()))); | |
| 149 } | |
| 150 | |
| 151 // Returns the rectangular hit test region of the tab close button of | |
| 152 // |tab| in |tab|'s coordinate space (including padding if |padding| | |
| 153 // is true). | |
| 154 gfx::Rect GetTabCloseHitTestMask(Tab* tab, bool padding) { | |
| 155 gfx::RectF bounds_f = gfx::RectF(tab->close_button_->GetContentsBounds()); | |
| 156 if (padding) | |
| 157 bounds_f = gfx::RectF(tab->close_button_->GetLocalBounds()); | |
| 158 views::View::ConvertRectToTarget(tab->close_button_, tab, &bounds_f); | |
| 159 return gfx::ToEnclosingRect(bounds_f); | |
| 160 } | |
| 161 | |
| 162 // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point | 137 // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point |
| 163 // is in |tab_strip_| coordinates. | 138 // is in |tab_strip_| coordinates. |
| 164 bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords) { | 139 bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords) { |
| 165 gfx::Point point_in_tab_coords(point_in_tabstrip_coords); | 140 gfx::Point point_in_tab_coords(point_in_tabstrip_coords); |
| 166 views::View::ConvertPointToTarget(tab_strip_, tab, &point_in_tab_coords); | 141 views::View::ConvertPointToTarget(tab_strip_, tab, &point_in_tab_coords); |
| 167 return tab->HitTestPoint(point_in_tab_coords); | 142 return tab->HitTestPoint(point_in_tab_coords); |
| 168 } | 143 } |
| 169 | 144 |
| 170 void DoLayout() { tab_strip_->DoLayout(); } | 145 void DoLayout() { tab_strip_->DoLayout(); } |
| 171 | 146 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 EXPECT_TRUE(tab_strip_->IsImmersiveStyle()); | 294 EXPECT_TRUE(tab_strip_->IsImmersiveStyle()); |
| 320 | 295 |
| 321 // Now tabs have the immersive height. | 296 // Now tabs have the immersive height. |
| 322 int immersive_height = Tab::GetImmersiveHeight(); | 297 int immersive_height = Tab::GetImmersiveHeight(); |
| 323 EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height()); | 298 EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height()); |
| 324 | 299 |
| 325 // Sanity-check immersive tabs are shorter than normal tabs. | 300 // Sanity-check immersive tabs are shorter than normal tabs. |
| 326 EXPECT_LT(immersive_height, normal_height); | 301 EXPECT_LT(immersive_height, normal_height); |
| 327 } | 302 } |
| 328 | 303 |
| 329 // Creates a tab strip in stacked layout mode and verifies the correctness | 304 // Creates a tab strip in stacked layout mode and verifies that as we move |
| 330 // of hit tests against the visible/occluded regions of a tab and the tab | 305 // across the strip at the top, middle, and bottom, events will target each tab |
| 331 // close button of the active tab. | 306 // in order. |
| 332 // TODO(pkasting): Update the test for Material Design layout. | 307 TEST_F(TabStripTest, TabForEventWhenStacked) { |
| 333 // crbug.com/575327 | 308 tab_strip_->SetBounds(0, 0, 200, GetLayoutConstant(TAB_HEIGHT)); |
| 334 TEST_F(TabStripTest, DISABLED_TabHitTestMaskWhenStacked) { | |
| 335 tab_strip_->SetBounds(0, 0, 300, 20); | |
| 336 | 309 |
| 337 controller_->AddTab(0, false); | 310 controller_->AddTab(0, false); |
| 338 controller_->AddTab(1, true); | 311 controller_->AddTab(1, true); |
| 339 controller_->AddTab(2, false); | 312 controller_->AddTab(2, false); |
| 340 controller_->AddTab(3, false); | 313 controller_->AddTab(3, false); |
| 341 ASSERT_EQ(4, tab_strip_->tab_count()); | 314 ASSERT_EQ(4, tab_strip_->tab_count()); |
| 342 | 315 |
| 343 Tab* left_tab = tab_strip_->tab_at(0); | |
| 344 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); | |
| 345 | |
| 346 Tab* active_tab = tab_strip_->tab_at(1); | |
| 347 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20))); | |
| 348 ASSERT_TRUE(active_tab->IsActive()); | |
| 349 | |
| 350 Tab* right_tab = tab_strip_->tab_at(2); | |
| 351 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20))); | |
| 352 | |
| 353 Tab* most_right_tab = tab_strip_->tab_at(3); | |
| 354 most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0), | |
| 355 gfx::Size(200, 20))); | |
| 356 | |
| 357 // Switch to stacked layout mode and force a layout to ensure tabs stack. | 316 // Switch to stacked layout mode and force a layout to ensure tabs stack. |
| 358 tab_strip_->SetStackedLayout(true); | 317 tab_strip_->SetStackedLayout(true); |
| 359 tab_strip_->DoLayout(); | 318 tab_strip_->DoLayout(); |
| 360 | 319 |
| 361 // Tests involving |left_tab|, which has part of its bounds occluded by | 320 gfx::Point p; |
| 362 // |active_tab|. | 321 for (int y : {0, tab_strip_->height() / 2, tab_strip_->height() - 1}) { |
| 363 | 322 p.set_y(y); |
| 364 // Bounds of the tab's hit test mask. | 323 int previous_tab = -1; |
| 365 gfx::Rect tab_bounds = GetTabHitTestMask(left_tab); | 324 for (int x = 0; x < tab_strip_->width(); ++x) { |
| 366 EXPECT_EQ(gfx::Rect(6, 2, 61, 27).ToString(), tab_bounds.ToString()); | 325 p.set_x(x); |
| 367 | 326 int tab = tab_strip_->GetModelIndexOfTab(tab_strip_->FindTabForEvent(p)); |
| 368 // Hit tests in the non-occuluded region of the tab. | 327 if (tab == previous_tab) |
| 369 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 2, 2))); | 328 continue; |
| 370 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 1, 1))); | 329 if ((tab != -1) || (previous_tab != tab_strip_->tab_count() - 1)) |
| 371 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 1, 1))); | 330 EXPECT_EQ(previous_tab + 1, tab) << "p = " << p.ToString(); |
| 372 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 25, 35))); | 331 previous_tab = tab; |
| 373 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(-10, -5, 20, 30))); | 332 } |
| 374 | 333 } |
| 375 // Hit tests in the occluded region of the tab. | |
| 376 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, 15, 2, 2))); | |
| 377 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, -15, 30, 40))); | |
| 378 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(87, 20, 5, 3))); | |
| 379 | |
| 380 // Hit tests completely outside of the tab. | |
| 381 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 1, 1))); | |
| 382 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 3, 19))); | |
| 383 | |
| 384 | |
| 385 // Tests involving |active_tab|, which is completely visible. | |
| 386 | |
| 387 tab_bounds = GetTabHitTestMask(active_tab); | |
| 388 EXPECT_EQ(gfx::Rect(6, 2, 108, 27).ToString(), tab_bounds.ToString()); | |
| 389 gfx::Rect contents_bounds = GetTabCloseHitTestMask(active_tab, false); | |
| 390 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved. | |
| 391 // EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString()); | |
| 392 | |
| 393 // Verify that the tab close button is not occluded. | |
| 394 EXPECT_TRUE(tab_bounds.Contains(contents_bounds)); | |
| 395 | |
| 396 // Bounds of the tab close button (with padding) in the tab's coordinate | |
| 397 // space. | |
| 398 gfx::Rect local_bounds = GetTabCloseHitTestMask(active_tab, true); | |
| 399 EXPECT_EQ(gfx::Rect(82, 0, 38, 29).ToString(), local_bounds.ToString()); | |
| 400 | |
| 401 // Hit tests within the tab. | |
| 402 EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 1, 1))); | |
| 403 EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 2, 2))); | |
| 404 | |
| 405 // Hit tests against the tab close button. Note that hit tests from either | |
| 406 // mouse or touch should both fail if they are strictly contained within | |
| 407 // the button's padding. | |
| 408 views::ImageButton* active_close = active_tab->close_button_; | |
| 409 EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 1, 1))); | |
| 410 EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 2, 2))); | |
| 411 EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 1, 1))); | |
| 412 EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 25, 35))); | |
| 413 | |
| 414 | |
| 415 // Tests involving |most_right_tab|, which has part of its bounds occluded | |
| 416 // by |right_tab|. | |
| 417 | |
| 418 tab_bounds = GetTabHitTestMask(most_right_tab); | |
| 419 EXPECT_EQ(gfx::Rect(84, 2, 30, 27).ToString(), tab_bounds.ToString()); | |
| 420 | |
| 421 // Hit tests in the occluded region of the tab. | |
| 422 EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 1, 1))); | |
| 423 EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 5, 6))); | |
| 424 | |
| 425 // Hit tests in the non-occluded region of the tab. | |
| 426 EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 1, 1))); | |
| 427 EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 2, 2))); | |
| 428 } | 334 } |
| 429 | 335 |
| 430 // Tests that the tab close buttons of non-active tabs are hidden when | 336 // Tests that the tab close buttons of non-active tabs are hidden when |
| 431 // the tabstrip is in stacked tab mode. | 337 // the tabstrip is in stacked tab mode. |
| 432 TEST_F(TabStripTest, TabCloseButtonVisibilityWhenStacked) { | 338 TEST_F(TabStripTest, TabCloseButtonVisibilityWhenStacked) { |
| 433 tab_strip_->SetBounds(0, 0, 300, 20); | 339 tab_strip_->SetBounds(0, 0, 300, 20); |
| 434 controller_->AddTab(0, false); | 340 controller_->AddTab(0, false); |
| 435 controller_->AddTab(1, true); | 341 controller_->AddTab(1, true); |
| 436 controller_->AddTab(2, false); | 342 controller_->AddTab(2, false); |
| 437 ASSERT_EQ(3, tab_strip_->tab_count()); | 343 ASSERT_EQ(3, tab_strip_->tab_count()); |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 | 558 |
| 653 // Change the title of the second tab (first tab is selected). | 559 // Change the title of the second tab (first tab is selected). |
| 654 tab_strip_->TabTitleChangedNotLoading(1); | 560 tab_strip_->TabTitleChangedNotLoading(1); |
| 655 // Indicator should be shown. | 561 // Indicator should be shown. |
| 656 EXPECT_TRUE(IsShowingPinnedTabTitleChangedIndicator(1)); | 562 EXPECT_TRUE(IsShowingPinnedTabTitleChangedIndicator(1)); |
| 657 // Select the second tab. | 563 // Select the second tab. |
| 658 controller_->SelectTab(1); | 564 controller_->SelectTab(1); |
| 659 // Indicator should hide. | 565 // Indicator should hide. |
| 660 EXPECT_FALSE(IsShowingPinnedTabTitleChangedIndicator(1)); | 566 EXPECT_FALSE(IsShowingPinnedTabTitleChangedIndicator(1)); |
| 661 } | 567 } |
| OLD | NEW |