| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/location_bar/icon_label_bubble_view.h" | 5 #include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h" |
| 6 | 6 |
| 7 #include "base/strings/utf_string_conversions.h" | 7 #include "base/strings/utf_string_conversions.h" |
| 8 #include "chrome/browser/ui/layout_constants.h" | 8 #include "chrome/browser/ui/layout_constants.h" |
| 9 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" | 9 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" | 10 #include "testing/gtest/include/gtest/gtest.h" |
| 11 #include "ui/events/base_event_utils.h" |
| 12 #include "ui/events/gesture_detection/gesture_configuration.h" |
| 13 #include "ui/events/test/event_generator.h" |
| 14 #include "ui/views/animation/test/ink_drop_host_view_test_api.h" |
| 15 #include "ui/views/animation/test/test_ink_drop.h" |
| 11 #include "ui/views/controls/image_view.h" | 16 #include "ui/views/controls/image_view.h" |
| 12 #include "ui/views/test/views_test_base.h" | 17 #include "ui/views/test/views_test_base.h" |
| 13 | 18 |
| 14 #if defined(USE_ASH) | 19 #if defined(USE_ASH) |
| 15 #include "ui/aura/window.h" | 20 #include "ui/aura/window.h" |
| 16 #endif | 21 #endif |
| 17 | 22 |
| 23 using views::test::InkDropHostViewTestApi; |
| 24 using views::test::TestInkDrop; |
| 25 |
| 18 namespace { | 26 namespace { |
| 19 | 27 |
| 20 const int kStayOpenTimeMS = 100; | 28 const int kStayOpenTimeMS = 100; |
| 21 const int kOpenTimeMS = 100; | 29 const int kOpenTimeMS = 100; |
| 22 const int kAnimationDurationMS = (kOpenTimeMS * 2) + kStayOpenTimeMS; | 30 const int kAnimationDurationMS = (kOpenTimeMS * 2) + kStayOpenTimeMS; |
| 23 const int kImageSize = 15; | 31 const int kImageSize = 15; |
| 24 const SkColor kTestColor = SkColorSetRGB(64, 64, 64); | 32 const SkColor kTestColor = SkColorSetRGB(64, 64, 64); |
| 25 const int kNumberOfSteps = 300; | 33 const int kNumberOfSteps = 300; |
| 26 | 34 |
| 27 class TestIconLabelBubbleView : public IconLabelBubbleView { | 35 class TestIconLabelBubbleView : public IconLabelBubbleView { |
| 28 public: | 36 public: |
| 29 enum State { | 37 enum State { |
| 30 GROWING, | 38 GROWING, |
| 31 STEADY, | 39 STEADY, |
| 32 SHRINKING, | 40 SHRINKING, |
| 33 }; | 41 }; |
| 34 | 42 |
| 35 explicit TestIconLabelBubbleView(const gfx::FontList& font_list) | 43 explicit TestIconLabelBubbleView(const gfx::FontList& font_list) |
| 36 : IconLabelBubbleView(font_list, false), value_(0) { | 44 : IconLabelBubbleView(font_list, false), |
| 45 value_(0), |
| 46 is_bubble_showing_(false) { |
| 37 GetImageView()->SetImageSize(gfx::Size(kImageSize, kImageSize)); | 47 GetImageView()->SetImageSize(gfx::Size(kImageSize, kImageSize)); |
| 38 SetLabel(base::ASCIIToUTF16("Label")); | 48 SetLabel(base::ASCIIToUTF16("Label")); |
| 39 } | 49 } |
| 40 | 50 |
| 41 void SetCurrentAnimationValue(int value) { | 51 void SetCurrentAnimationValue(int value) { |
| 42 value_ = value; | 52 value_ = value; |
| 43 SizeToPreferredSize(); | 53 SizeToPreferredSize(); |
| 44 } | 54 } |
| 45 | 55 |
| 46 int width() const { return bounds().width(); } | 56 int width() const { return bounds().width(); } |
| 47 bool IsLabelVisible() const { return label()->visible(); } | 57 bool IsLabelVisible() const { return label()->visible(); } |
| 48 void SetLabelVisible(bool visible) { label()->SetVisible(visible); } | 58 void SetLabelVisible(bool visible) { label()->SetVisible(visible); } |
| 49 const gfx::Rect& GetLabelBounds() const { return label()->bounds(); } | 59 const gfx::Rect& GetLabelBounds() const { return label()->bounds(); } |
| 50 | 60 |
| 51 State state() const { | 61 State state() const { |
| 52 const double kOpenFraction = | 62 const double kOpenFraction = |
| 53 static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; | 63 static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; |
| 54 double state = value_ / (double)kNumberOfSteps; | 64 double state = value_ / (double)kNumberOfSteps; |
| 55 if (state < kOpenFraction) | 65 if (state < kOpenFraction) |
| 56 return GROWING; | 66 return GROWING; |
| 57 if (state > (1.0 - kOpenFraction)) | 67 if (state > (1.0 - kOpenFraction)) |
| 58 return SHRINKING; | 68 return SHRINKING; |
| 59 return STEADY; | 69 return STEADY; |
| 60 } | 70 } |
| 61 | 71 |
| 72 void HideBubble() { |
| 73 OnWidgetVisibilityChanged(nullptr, false); |
| 74 is_bubble_showing_ = false; |
| 75 } |
| 76 |
| 77 bool IsBubbleShowing() const override { return is_bubble_showing_; } |
| 78 |
| 62 protected: | 79 protected: |
| 63 // IconLabelBubbleView: | 80 // IconLabelBubbleView: |
| 64 SkColor GetTextColor() const override { return kTestColor; } | 81 SkColor GetTextColor() const override { return kTestColor; } |
| 65 | 82 |
| 66 bool ShouldShowLabel() const override { | 83 bool ShouldShowLabel() const override { |
| 67 return !IsShrinking() || | 84 return !IsShrinking() || |
| 68 (width() > (image()->GetPreferredSize().width() + | 85 (width() > (image()->GetPreferredSize().width() + |
| 69 2 * LocationBarView::kIconInteriorPadding + | 86 2 * LocationBarView::kIconInteriorPadding + |
| 70 2 * GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING))); | 87 2 * GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING))); |
| 71 } | 88 } |
| 72 | 89 |
| 73 double WidthMultiplier() const override { | 90 double WidthMultiplier() const override { |
| 74 const double kOpenFraction = | 91 const double kOpenFraction = |
| 75 static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; | 92 static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; |
| 76 double fraction = value_ / (double)kNumberOfSteps; | 93 double fraction = value_ / (double)kNumberOfSteps; |
| 77 switch (state()) { | 94 switch (state()) { |
| 78 case GROWING: | 95 case GROWING: |
| 79 return fraction / kOpenFraction; | 96 return fraction / kOpenFraction; |
| 80 case STEADY: | 97 case STEADY: |
| 81 return 1.0; | 98 return 1.0; |
| 82 case SHRINKING: | 99 case SHRINKING: |
| 83 return (1.0 - fraction) / kOpenFraction; | 100 return (1.0 - fraction) / kOpenFraction; |
| 84 } | 101 } |
| 85 NOTREACHED(); | 102 NOTREACHED(); |
| 86 return 1.0; | 103 return 1.0; |
| 87 } | 104 } |
| 88 | 105 |
| 89 bool IsShrinking() const override { return state() == SHRINKING; } | 106 bool IsShrinking() const override { return state() == SHRINKING; } |
| 90 | 107 |
| 108 bool ShowBubble(const ui::Event& event) override { |
| 109 is_bubble_showing_ = true; |
| 110 return true; |
| 111 } |
| 112 |
| 91 private: | 113 private: |
| 92 int value_; | 114 int value_; |
| 115 bool is_bubble_showing_; |
| 93 DISALLOW_COPY_AND_ASSIGN(TestIconLabelBubbleView); | 116 DISALLOW_COPY_AND_ASSIGN(TestIconLabelBubbleView); |
| 94 }; | 117 }; |
| 95 | 118 |
| 96 } // namespace | 119 } // namespace |
| 97 | 120 |
| 98 class IconLabelBubbleViewTest : public views::ViewsTestBase { | 121 class IconLabelBubbleViewTest : public views::ViewsTestBase { |
| 99 public: | 122 public: |
| 100 IconLabelBubbleViewTest() | 123 IconLabelBubbleViewTest() |
| 101 : views::ViewsTestBase(), | 124 : views::ViewsTestBase(), |
| 125 widget_(nullptr), |
| 126 view_(nullptr), |
| 127 ink_drop_(nullptr), |
| 102 steady_reached_(false), | 128 steady_reached_(false), |
| 103 shrinking_reached_(false), | 129 shrinking_reached_(false), |
| 104 minimum_size_reached_(false), | 130 minimum_size_reached_(false), |
| 105 previous_width_(0), | 131 previous_width_(0), |
| 106 initial_image_x_(0) {} | 132 initial_image_x_(0) {} |
| 107 ~IconLabelBubbleViewTest() override {} | 133 ~IconLabelBubbleViewTest() override {} |
| 108 | 134 |
| 109 protected: | 135 protected: |
| 110 // views::ViewsTestBase: | 136 // views::ViewsTestBase: |
| 111 void SetUp() override { | 137 void SetUp() override { |
| 112 views::ViewsTestBase::SetUp(); | 138 views::ViewsTestBase::SetUp(); |
| 113 gfx::FontList font_list; | 139 gfx::FontList font_list; |
| 114 view_.reset(new TestIconLabelBubbleView(font_list)); | 140 |
| 141 CreateWidget(); |
| 142 generator_.reset(new ui::test::EventGenerator(widget_->GetNativeWindow())); |
| 143 view_ = new TestIconLabelBubbleView(font_list); |
| 144 view_->SetBoundsRect(gfx::Rect(0, 0, 24, 24)); |
| 145 widget_->SetContentsView(view_); |
| 146 |
| 147 widget_->Show(); |
| 148 } |
| 149 |
| 150 void TearDown() override { |
| 151 generator_.reset(); |
| 152 if (widget_ && !widget_->IsClosed()) |
| 153 widget_->Close(); |
| 154 |
| 155 ViewsTestBase::TearDown(); |
| 115 } | 156 } |
| 116 | 157 |
| 117 void VerifyWithAnimationStep(int step) { | 158 void VerifyWithAnimationStep(int step) { |
| 118 Reset(); | 159 Reset(); |
| 119 for (int value = 0; value < kNumberOfSteps; value += step) { | 160 for (int value = 0; value < kNumberOfSteps; value += step) { |
| 120 SetValue(value); | 161 SetValue(value); |
| 121 VerifyAnimationStep(); | 162 VerifyAnimationStep(); |
| 122 } | 163 } |
| 123 view_->SetLabelVisible(false); | 164 view_->SetLabelVisible(false); |
| 124 } | 165 } |
| 125 | 166 |
| 167 TestInkDrop* ink_drop() { return ink_drop_; } |
| 168 |
| 169 TestIconLabelBubbleView* view() { return view_; } |
| 170 |
| 171 ui::test::EventGenerator* generator() { return generator_.get(); } |
| 172 |
| 173 void AttachInkDrop() { |
| 174 ink_drop_ = new TestInkDrop(); |
| 175 InkDropHostViewTestApi(view_).SetInkDrop(base::WrapUnique(ink_drop_)); |
| 176 } |
| 177 |
| 126 private: | 178 private: |
| 179 void CreateWidget() { |
| 180 DCHECK(!widget_); |
| 181 |
| 182 widget_ = new views::Widget; |
| 183 views::Widget::InitParams params = |
| 184 CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| 185 params.bounds = gfx::Rect(0, 0, 200, 200); |
| 186 widget_->Init(params); |
| 187 } |
| 188 |
| 127 void Reset() { | 189 void Reset() { |
| 128 view_->SetLabelVisible(true); | 190 view_->SetLabelVisible(true); |
| 129 SetValue(0); | 191 SetValue(0); |
| 130 steady_reached_ = false; | 192 steady_reached_ = false; |
| 131 shrinking_reached_ = false; | 193 shrinking_reached_ = false; |
| 132 minimum_size_reached_ = false; | 194 minimum_size_reached_ = false; |
| 133 previous_width_ = 0; | 195 previous_width_ = 0; |
| 134 initial_image_x_ = GetImageBounds().x(); | 196 initial_image_x_ = GetImageBounds().x(); |
| 135 EXPECT_EQ(0, initial_image_x_); | 197 EXPECT_EQ(0, initial_image_x_); |
| 136 } | 198 } |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 195 int width() { return view_->width(); } | 257 int width() { return view_->width(); } |
| 196 | 258 |
| 197 bool IsLabelVisible() { return view_->IsLabelVisible(); } | 259 bool IsLabelVisible() { return view_->IsLabelVisible(); } |
| 198 | 260 |
| 199 const gfx::Rect& GetLabelBounds() const { return view_->GetLabelBounds(); } | 261 const gfx::Rect& GetLabelBounds() const { return view_->GetLabelBounds(); } |
| 200 | 262 |
| 201 const gfx::Rect& GetImageBounds() const { | 263 const gfx::Rect& GetImageBounds() const { |
| 202 return view_->GetImageView()->bounds(); | 264 return view_->GetImageView()->bounds(); |
| 203 } | 265 } |
| 204 | 266 |
| 205 std::unique_ptr<TestIconLabelBubbleView> view_; | 267 views::Widget* widget_; |
| 268 TestIconLabelBubbleView* view_; |
| 269 TestInkDrop* ink_drop_; |
| 270 std::unique_ptr<ui::test::EventGenerator> generator_; |
| 206 | 271 |
| 207 bool steady_reached_; | 272 bool steady_reached_; |
| 208 bool shrinking_reached_; | 273 bool shrinking_reached_; |
| 209 bool minimum_size_reached_; | 274 bool minimum_size_reached_; |
| 210 int previous_width_; | 275 int previous_width_; |
| 211 int initial_image_x_; | 276 int initial_image_x_; |
| 212 }; | 277 }; |
| 213 | 278 |
| 214 // Tests layout rules for IconLabelBubbleView while simulating animation. | 279 // Tests layout rules for IconLabelBubbleView while simulating animation. |
| 215 // The animation is first growing the bubble from zero, then keeping its size | 280 // The animation is first growing the bubble from zero, then keeping its size |
| 216 // constant and finally shrinking it down to its minimum size which is the image | 281 // constant and finally shrinking it down to its minimum size which is the image |
| 217 // size. | 282 // size. |
| 218 // Various step sizes during animation simulate different possible timing. | 283 // Various step sizes during animation simulate different possible timing. |
| 219 TEST_F(IconLabelBubbleViewTest, AnimateLayout) { | 284 TEST_F(IconLabelBubbleViewTest, AnimateLayout) { |
| 220 VerifyWithAnimationStep(1); | 285 VerifyWithAnimationStep(1); |
| 221 VerifyWithAnimationStep(5); | 286 VerifyWithAnimationStep(5); |
| 222 VerifyWithAnimationStep(10); | 287 VerifyWithAnimationStep(10); |
| 223 VerifyWithAnimationStep(25); | 288 VerifyWithAnimationStep(25); |
| 224 } | 289 } |
| 225 | 290 |
| 291 // Verify that clicking the view a second time hides its bubble. |
| 292 TEST_F(IconLabelBubbleViewTest, SecondClick) { |
| 293 generator()->PressLeftButton(); |
| 294 EXPECT_FALSE(view()->IsBubbleShowing()); |
| 295 generator()->ReleaseLeftButton(); |
| 296 EXPECT_TRUE(view()->IsBubbleShowing()); |
| 297 |
| 298 // Hide the bubble manually. In the browser this would normally happen during |
| 299 // the event processing. |
| 300 generator()->PressLeftButton(); |
| 301 view()->HideBubble(); |
| 302 EXPECT_FALSE(view()->IsBubbleShowing()); |
| 303 generator()->ReleaseLeftButton(); |
| 304 } |
| 305 |
| 306 TEST_F(IconLabelBubbleViewTest, MouseInkDropState) { |
| 307 AttachInkDrop(); |
| 308 generator()->PressLeftButton(); |
| 309 EXPECT_EQ(views::InkDropState::ACTION_PENDING, |
| 310 ink_drop()->GetTargetInkDropState()); |
| 311 generator()->ReleaseLeftButton(); |
| 312 EXPECT_EQ(views::InkDropState::ACTIVATED, |
| 313 ink_drop()->GetTargetInkDropState()); |
| 314 view()->HideBubble(); |
| 315 EXPECT_EQ(views::InkDropState::HIDDEN, ink_drop()->GetTargetInkDropState()); |
| 316 |
| 317 // If the bubble is shown, the InkDropState should not change to |
| 318 // ACTION_PENDING. |
| 319 generator()->PressLeftButton(); |
| 320 EXPECT_EQ(views::InkDropState::ACTION_PENDING, |
| 321 ink_drop()->GetTargetInkDropState()); |
| 322 generator()->ReleaseLeftButton(); |
| 323 EXPECT_EQ(views::InkDropState::ACTIVATED, |
| 324 ink_drop()->GetTargetInkDropState()); |
| 325 generator()->PressLeftButton(); |
| 326 EXPECT_NE(views::InkDropState::ACTION_PENDING, |
| 327 ink_drop()->GetTargetInkDropState()); |
| 328 } |
| 329 |
| 330 #if !defined(OS_MACOSX) |
| 331 TEST_F(IconLabelBubbleViewTest, GestureInkDropState) { |
| 332 AttachInkDrop(); |
| 333 generator()->GestureTapAt(gfx::Point()); |
| 334 EXPECT_EQ(views::InkDropState::ACTIVATED, |
| 335 ink_drop()->GetTargetInkDropState()); |
| 336 view()->HideBubble(); |
| 337 EXPECT_EQ(views::InkDropState::HIDDEN, ink_drop()->GetTargetInkDropState()); |
| 338 |
| 339 // If the bubble is shown, the InkDropState should not change to |
| 340 // ACTIVATED. |
| 341 generator()->GestureTapAt(gfx::Point()); |
| 342 EXPECT_EQ(views::InkDropState::ACTIVATED, |
| 343 ink_drop()->GetTargetInkDropState()); |
| 344 generator()->GestureTapAt(gfx::Point()); |
| 345 view()->HideBubble(); |
| 346 EXPECT_EQ(views::InkDropState::HIDDEN, ink_drop()->GetTargetInkDropState()); |
| 347 } |
| 348 #endif |
| 349 |
| 226 #if defined(USE_ASH) | 350 #if defined(USE_ASH) |
| 227 // Verifies IconLabelBubbleView::GetPreferredSize() doesn't crash when there is | 351 // Verifies IconLabelBubbleView::GetPreferredSize() doesn't crash when there is |
| 228 // a widget but no compositor. | 352 // a widget but no compositor. |
| 229 using IconLabelBubbleViewCrashTest = views::ViewsTestBase; | 353 using IconLabelBubbleViewCrashTest = views::ViewsTestBase; |
| 230 | 354 |
| 231 TEST_F(IconLabelBubbleViewCrashTest, | 355 TEST_F(IconLabelBubbleViewCrashTest, |
| 232 GetPreferredSizeDoesntCrashWhenNoCompositor) { | 356 GetPreferredSizeDoesntCrashWhenNoCompositor) { |
| 233 gfx::FontList font_list; | 357 gfx::FontList font_list; |
| 234 views::Widget::InitParams params = | 358 views::Widget::InitParams params = |
| 235 CreateParams(views::Widget::InitParams::TYPE_WINDOW); | 359 CreateParams(views::Widget::InitParams::TYPE_WINDOW); |
| 236 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 360 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 237 views::Widget widget; | 361 views::Widget widget; |
| 238 widget.Init(params); | 362 widget.Init(params); |
| 239 IconLabelBubbleView* icon_label_bubble_view = | 363 IconLabelBubbleView* icon_label_bubble_view = |
| 240 new TestIconLabelBubbleView(font_list); | 364 new TestIconLabelBubbleView(font_list); |
| 241 icon_label_bubble_view->SetLabel(base::ASCIIToUTF16("x")); | 365 icon_label_bubble_view->SetLabel(base::ASCIIToUTF16("x")); |
| 242 widget.GetContentsView()->AddChildView(icon_label_bubble_view); | 366 widget.GetContentsView()->AddChildView(icon_label_bubble_view); |
| 243 aura::Window* widget_native_view = widget.GetNativeView(); | 367 aura::Window* widget_native_view = widget.GetNativeView(); |
| 244 // Remove the window from its parent. This means GetWidget() in | 368 // Remove the window from its parent. This means GetWidget() in |
| 245 // IconLabelBubbleView will return non-null, but GetWidget()->GetCompositor() | 369 // IconLabelBubbleView will return non-null, but GetWidget()->GetCompositor() |
| 246 // will return null. | 370 // will return null. |
| 247 ASSERT_TRUE(widget_native_view->parent()); | 371 ASSERT_TRUE(widget_native_view->parent()); |
| 248 widget_native_view->parent()->RemoveChild(widget_native_view); | 372 widget_native_view->parent()->RemoveChild(widget_native_view); |
| 249 static_cast<views::View*>(icon_label_bubble_view)->GetPreferredSize(); | 373 static_cast<views::View*>(icon_label_bubble_view)->GetPreferredSize(); |
| 250 } | 374 } |
| 251 #endif | 375 #endif |
| OLD | NEW |