1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/corewm/tooltip_controller.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "ui/aura/client/cursor_client.h" 9 #include "ui/aura/client/screen_position_client.h" 10 #include "ui/aura/env.h" 11 #include "ui/aura/test/aura_test_base.h" 12 #include "ui/aura/test/event_generator.h" 13 #include "ui/aura/test/test_screen.h" 14 #include "ui/aura/test/test_window_delegate.h" 15 #include "ui/aura/window.h" 16 #include "ui/aura/window_event_dispatcher.h" 17 #include "ui/base/resource/resource_bundle.h" 18 #include "ui/gfx/font.h" 19 #include "ui/gfx/point.h" 20 #include "ui/gfx/screen.h" 21 #include "ui/gfx/screen_type_delegate.h" 22 #include "ui/gfx/text_elider.h" 23 #include "ui/views/corewm/tooltip_aura.h" 24 #include "ui/views/corewm/tooltip_controller_test_helper.h" 25 #include "ui/views/test/desktop_test_views_delegate.h" 26 #include "ui/views/test/test_views_delegate.h" 27 #include "ui/views/view.h" 28 #include "ui/views/widget/tooltip_manager.h" 29 #include "ui/views/widget/widget.h" 30 #include "ui/wm/core/default_activation_client.h" 31 #include "ui/wm/core/wm_state.h" 32 #include "ui/wm/public/tooltip_client.h" 33 #include "ui/wm/public/window_types.h" 34 35 #if defined(OS_WIN) 36 #include "ui/base/win/scoped_ole_initializer.h" 37 #endif 38 39 #if !defined(OS_CHROMEOS) 40 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 41 #include "ui/views/widget/desktop_aura/desktop_screen.h" 42 #endif 43 44 using base::ASCIIToUTF16; 45 46 namespace views { 47 namespace corewm { 48 namespace test { 49 namespace { 50 51 views::Widget* CreateWidget(aura::Window* root) { 52 views::Widget* widget = new views::Widget; 53 views::Widget::InitParams params; 54 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; 55 params.accept_events = true; 56 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 57 #if defined(OS_CHROMEOS) 58 params.parent = root; 59 #else 60 params.native_widget = new DesktopNativeWidgetAura(widget); 61 #endif 62 params.bounds = gfx::Rect(0, 0, 200, 100); 63 widget->Init(params); 64 widget->Show(); 65 return widget; 66 } 67 68 TooltipController* GetController(Widget* widget) { 69 return static_cast<TooltipController*>( 70 aura::client::GetTooltipClient( 71 widget->GetNativeWindow()->GetRootWindow())); 72 } 73 74 } // namespace 75 76 class TooltipControllerTest : public aura::test::AuraTestBase { 77 public: 78 TooltipControllerTest() : view_(NULL) {} 79 virtual ~TooltipControllerTest() {} 80 81 virtual void SetUp() OVERRIDE { 82 #if defined(OS_CHROMEOS) 83 views_delegate_.reset(new TestViewsDelegate); 84 #else 85 views_delegate_.reset(new DesktopTestViewsDelegate); 86 #endif 87 88 aura::test::AuraTestBase::SetUp(); 89 new wm::DefaultActivationClient(root_window()); 90 #if defined(OS_CHROMEOS) 91 controller_.reset(new TooltipController( 92 scoped_ptr<views::corewm::Tooltip>( 93 new views::corewm::TooltipAura(gfx::SCREEN_TYPE_ALTERNATE)))); 94 root_window()->AddPreTargetHandler(controller_.get()); 95 SetTooltipClient(root_window(), controller_.get()); 96 #endif 97 widget_.reset(CreateWidget(root_window())); 98 widget_->SetContentsView(new View); 99 view_ = new TooltipTestView; 100 widget_->GetContentsView()->AddChildView(view_); 101 view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds()); 102 helper_.reset(new TooltipControllerTestHelper( 103 GetController(widget_.get()))); 104 generator_.reset(new aura::test::EventGenerator(GetRootWindow())); 105 } 106 107 virtual void TearDown() OVERRIDE { 108 #if defined(OS_CHROMEOS) 109 root_window()->RemovePreTargetHandler(controller_.get()); 110 aura::client::SetTooltipClient(root_window(), NULL); 111 controller_.reset(); 112 #endif 113 generator_.reset(); 114 helper_.reset(); 115 widget_.reset(); 116 aura::test::AuraTestBase::TearDown(); 117 views_delegate_.reset(); 118 } 119 120 protected: 121 aura::Window* GetWindow() { 122 return widget_->GetNativeWindow(); 123 } 124 125 aura::Window* GetRootWindow() { 126 return GetWindow()->GetRootWindow(); 127 } 128 129 TooltipTestView* PrepareSecondView() { 130 TooltipTestView* view2 = new TooltipTestView; 131 widget_->GetContentsView()->AddChildView(view2); 132 view_->SetBounds(0, 0, 100, 100); 133 view2->SetBounds(100, 0, 100, 100); 134 return view2; 135 } 136 137 scoped_ptr<views::Widget> widget_; 138 TooltipTestView* view_; 139 scoped_ptr<TooltipControllerTestHelper> helper_; 140 scoped_ptr<aura::test::EventGenerator> generator_; 141 142 private: 143 scoped_ptr<TooltipController> controller_; 144 145 scoped_ptr<views::TestViewsDelegate> views_delegate_; 146 147 #if defined(OS_WIN) 148 ui::ScopedOleInitializer ole_initializer_; 149 #endif 150 151 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest); 152 }; 153 154 TEST_F(TooltipControllerTest, ViewTooltip) { 155 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 156 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 157 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 158 generator_->MoveMouseToCenterOf(GetWindow()); 159 160 EXPECT_EQ(GetWindow(), GetRootWindow()->GetEventHandlerForPoint( 161 generator_->current_location())); 162 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 163 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 164 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 165 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 166 167 // Fire tooltip timer so tooltip becomes visible. 168 helper_->FireTooltipTimer(); 169 170 EXPECT_TRUE(helper_->IsTooltipVisible()); 171 generator_->MoveMouseBy(1, 0); 172 173 EXPECT_TRUE(helper_->IsTooltipVisible()); 174 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 175 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 176 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 177 } 178 179 TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { 180 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 181 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 182 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 183 184 PrepareSecondView(); 185 aura::Window* window = GetWindow(); 186 aura::Window* root_window = GetRootWindow(); 187 188 // Fire tooltip timer so tooltip becomes visible. 189 generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); 190 helper_->FireTooltipTimer(); 191 EXPECT_TRUE(helper_->IsTooltipVisible()); 192 for (int i = 0; i < 49; ++i) { 193 generator_->MoveMouseBy(1, 0); 194 EXPECT_TRUE(helper_->IsTooltipVisible()); 195 EXPECT_EQ(window, root_window->GetEventHandlerForPoint( 196 generator_->current_location())); 197 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 198 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 199 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 200 EXPECT_EQ(window, helper_->GetTooltipWindow()); 201 } 202 for (int i = 0; i < 49; ++i) { 203 generator_->MoveMouseBy(1, 0); 204 EXPECT_FALSE(helper_->IsTooltipVisible()); 205 EXPECT_EQ(window, root_window->GetEventHandlerForPoint( 206 generator_->current_location())); 207 base::string16 expected_tooltip; // = "" 208 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 209 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 210 EXPECT_EQ(window, helper_->GetTooltipWindow()); 211 } 212 } 213 214 TEST_F(TooltipControllerTest, EnableOrDisableTooltips) { 215 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 216 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 217 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 218 219 generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint()); 220 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 221 222 // Fire tooltip timer so tooltip becomes visible. 223 helper_->FireTooltipTimer(); 224 EXPECT_TRUE(helper_->IsTooltipVisible()); 225 226 // Disable tooltips and check again. 227 helper_->controller()->SetTooltipsEnabled(false); 228 EXPECT_FALSE(helper_->IsTooltipVisible()); 229 helper_->FireTooltipTimer(); 230 EXPECT_FALSE(helper_->IsTooltipVisible()); 231 232 // Enable tooltips back and check again. 233 helper_->controller()->SetTooltipsEnabled(true); 234 EXPECT_FALSE(helper_->IsTooltipVisible()); 235 helper_->FireTooltipTimer(); 236 EXPECT_TRUE(helper_->IsTooltipVisible()); 237 } 238 239 // Verifies tooltip isn't shown if tooltip text consists entirely of whitespace. 240 TEST_F(TooltipControllerTest, DontShowEmptyTooltips) { 241 view_->set_tooltip_text(ASCIIToUTF16(" ")); 242 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 243 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 244 245 generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint()); 246 247 helper_->FireTooltipTimer(); 248 EXPECT_FALSE(helper_->IsTooltipVisible()); 249 } 250 251 TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { 252 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); 253 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 254 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 255 256 TooltipTestView* view2 = PrepareSecondView(); 257 view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2")); 258 259 aura::Window* window = GetWindow(); 260 261 // Fire tooltip timer so tooltip becomes visible. 262 generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); 263 helper_->FireTooltipTimer(); 264 EXPECT_TRUE(helper_->IsTooltipVisible()); 265 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 266 267 generator_->PressKey(ui::VKEY_1, 0); 268 EXPECT_FALSE(helper_->IsTooltipVisible()); 269 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 270 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 271 272 // Moving the mouse inside |view1| should not change the state of the tooltip 273 // or the timers. 274 for (int i = 0; i < 49; i++) { 275 generator_->MoveMouseBy(1, 0); 276 EXPECT_FALSE(helper_->IsTooltipVisible()); 277 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 278 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 279 EXPECT_EQ(window, 280 GetRootWindow()->GetEventHandlerForPoint( 281 generator_->current_location())); 282 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); 283 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 284 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 285 EXPECT_EQ(window, helper_->GetTooltipWindow()); 286 } 287 288 // Now we move the mouse on to |view2|. It should re-start the tooltip timer. 289 generator_->MoveMouseBy(1, 0); 290 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 291 helper_->FireTooltipTimer(); 292 EXPECT_TRUE(helper_->IsTooltipVisible()); 293 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 294 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); 295 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 296 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 297 EXPECT_EQ(window, helper_->GetTooltipWindow()); 298 } 299 300 TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { 301 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); 302 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 303 EXPECT_EQ(NULL, helper_->GetTooltipWindow()); 304 305 TooltipTestView* view2 = PrepareSecondView(); 306 view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2")); 307 308 aura::Window* window = GetWindow(); 309 310 // Fire tooltip timer so tooltip becomes visible. 311 generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); 312 helper_->FireTooltipTimer(); 313 EXPECT_TRUE(helper_->IsTooltipVisible()); 314 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 315 316 helper_->FireTooltipShownTimer(); 317 EXPECT_FALSE(helper_->IsTooltipVisible()); 318 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 319 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 320 321 // Moving the mouse inside |view1| should not change the state of the tooltip 322 // or the timers. 323 for (int i = 0; i < 49; ++i) { 324 generator_->MoveMouseBy(1, 0); 325 EXPECT_FALSE(helper_->IsTooltipVisible()); 326 EXPECT_FALSE(helper_->IsTooltipTimerRunning()); 327 EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); 328 EXPECT_EQ(window, GetRootWindow()->GetEventHandlerForPoint( 329 generator_->current_location())); 330 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); 331 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 332 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 333 EXPECT_EQ(window, helper_->GetTooltipWindow()); 334 } 335 336 // Now we move the mouse on to |view2|. It should re-start the tooltip timer. 337 generator_->MoveMouseBy(1, 0); 338 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 339 helper_->FireTooltipTimer(); 340 EXPECT_TRUE(helper_->IsTooltipVisible()); 341 EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); 342 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); 343 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); 344 EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); 345 EXPECT_EQ(window, helper_->GetTooltipWindow()); 346 } 347 348 // Verifies a mouse exit event hides the tooltips. 349 TEST_F(TooltipControllerTest, HideOnExit) { 350 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 351 generator_->MoveMouseToCenterOf(GetWindow()); 352 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 353 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 354 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 355 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 356 357 // Fire tooltip timer so tooltip becomes visible. 358 helper_->FireTooltipTimer(); 359 360 EXPECT_TRUE(helper_->IsTooltipVisible()); 361 generator_->SendMouseExit(); 362 EXPECT_FALSE(helper_->IsTooltipVisible()); 363 } 364 365 TEST_F(TooltipControllerTest, ReshowOnClickAfterEnterExit) { 366 // Owned by |view_|. 367 TooltipTestView* v1 = new TooltipTestView; 368 TooltipTestView* v2 = new TooltipTestView; 369 view_->AddChildView(v1); 370 view_->AddChildView(v2); 371 gfx::Rect view_bounds(view_->GetLocalBounds()); 372 view_bounds.set_height(view_bounds.height() / 2); 373 v1->SetBoundsRect(view_bounds); 374 view_bounds.set_y(view_bounds.height()); 375 v2->SetBoundsRect(view_bounds); 376 const base::string16 v1_tt(ASCIIToUTF16("v1")); 377 const base::string16 v2_tt(ASCIIToUTF16("v2")); 378 v1->set_tooltip_text(v1_tt); 379 v2->set_tooltip_text(v2_tt); 380 381 gfx::Point v1_point(1, 1); 382 View::ConvertPointToWidget(v1, &v1_point); 383 generator_->MoveMouseRelativeTo(GetWindow(), v1_point); 384 385 // Fire tooltip timer so tooltip becomes visible. 386 helper_->FireTooltipTimer(); 387 EXPECT_TRUE(helper_->IsTooltipVisible()); 388 EXPECT_EQ(v1_tt, helper_->GetTooltipText()); 389 390 // Press the mouse, move to v2 and back to v1. 391 generator_->ClickLeftButton(); 392 393 gfx::Point v2_point(1, 1); 394 View::ConvertPointToWidget(v2, &v2_point); 395 generator_->MoveMouseRelativeTo(GetWindow(), v2_point); 396 generator_->MoveMouseRelativeTo(GetWindow(), v1_point); 397 398 helper_->FireTooltipTimer(); 399 EXPECT_TRUE(helper_->IsTooltipVisible()); 400 EXPECT_EQ(v1_tt, helper_->GetTooltipText()); 401 } 402 403 namespace { 404 405 // Returns the index of |window| in its parent's children. 406 int IndexInParent(const aura::Window* window) { 407 aura::Window::Windows::const_iterator i = 408 std::find(window->parent()->children().begin(), 409 window->parent()->children().end(), 410 window); 411 return i == window->parent()->children().end() ? -1 : 412 static_cast<int>(i - window->parent()->children().begin()); 413 } 414 415 class TestScreenPositionClient : public aura::client::ScreenPositionClient { 416 public: 417 TestScreenPositionClient() {} 418 virtual ~TestScreenPositionClient() {} 419 420 // ScreenPositionClient overrides: 421 virtual void ConvertPointToScreen(const aura::Window* window, 422 gfx::Point* point) OVERRIDE { 423 } 424 virtual void ConvertPointFromScreen(const aura::Window* window, 425 gfx::Point* point) OVERRIDE { 426 } 427 virtual void ConvertHostPointToScreen(aura::Window* root_gwindow, 428 gfx::Point* point) OVERRIDE { 429 NOTREACHED(); 430 } 431 virtual void SetBounds(aura::Window* window, 432 const gfx::Rect& bounds, 433 const gfx::Display& display) OVERRIDE { 434 window->SetBounds(bounds); 435 } 436 437 private: 438 DISALLOW_COPY_AND_ASSIGN(TestScreenPositionClient); 439 }; 440 441 } // namespace 442 443 class TooltipControllerCaptureTest : public TooltipControllerTest { 444 public: 445 TooltipControllerCaptureTest() {} 446 virtual ~TooltipControllerCaptureTest() {} 447 448 virtual void SetUp() OVERRIDE { 449 TooltipControllerTest::SetUp(); 450 aura::client::SetScreenPositionClient(GetRootWindow(), 451 &screen_position_client_); 452 #if !defined(OS_CHROMEOS) 453 desktop_screen_.reset(CreateDesktopScreen()); 454 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, 455 desktop_screen_.get()); 456 #endif 457 } 458 459 virtual void TearDown() OVERRIDE { 460 #if !defined(OS_CHROMEOS) 461 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen()); 462 desktop_screen_.reset(); 463 #endif 464 aura::client::SetScreenPositionClient(GetRootWindow(), NULL); 465 TooltipControllerTest::TearDown(); 466 } 467 468 private: 469 TestScreenPositionClient screen_position_client_; 470 scoped_ptr<gfx::Screen> desktop_screen_; 471 472 DISALLOW_COPY_AND_ASSIGN(TooltipControllerCaptureTest); 473 }; 474 475 // Verifies when capture is released the TooltipController resets state. 476 TEST_F(TooltipControllerCaptureTest, CloseOnCaptureLost) { 477 view_->GetWidget()->SetCapture(view_); 478 RunAllPendingInMessageLoop(); 479 view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); 480 generator_->MoveMouseToCenterOf(GetWindow()); 481 base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); 482 EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); 483 EXPECT_EQ(base::string16(), helper_->GetTooltipText()); 484 EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); 485 486 // Fire tooltip timer so tooltip becomes visible. 487 helper_->FireTooltipTimer(); 488 489 EXPECT_TRUE(helper_->IsTooltipVisible()); 490 view_->GetWidget()->ReleaseCapture(); 491 EXPECT_FALSE(helper_->IsTooltipVisible()); 492 EXPECT_TRUE(helper_->GetTooltipWindow() == NULL); 493 } 494 495 // Disabled on linux as DesktopScreenX11::GetWindowAtScreenPoint() doesn't 496 // consider z-order. 497 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 498 #define MAYBE_Capture DISABLED_Capture 499 #else 500 #define MAYBE_Capture Capture 501 #endif 502 // Verifies the correct window is found for tooltips when there is a capture. 503 TEST_F(TooltipControllerCaptureTest, MAYBE_Capture) { 504 const base::string16 tooltip_text(ASCIIToUTF16("1")); 505 const base::string16 tooltip_text2(ASCIIToUTF16("2")); 506 507 widget_->SetBounds(gfx::Rect(0, 0, 200, 200)); 508 view_->set_tooltip_text(tooltip_text); 509 510 scoped_ptr<views::Widget> widget2(CreateWidget(root_window())); 511 widget2->SetContentsView(new View); 512 TooltipTestView* view2 = new TooltipTestView; 513 widget2->GetContentsView()->AddChildView(view2); 514 view2->set_tooltip_text(tooltip_text2); 515 widget2->SetBounds(gfx::Rect(0, 0, 200, 200)); 516 view2->SetBoundsRect(widget2->GetContentsView()->GetLocalBounds()); 517 518 widget_->SetCapture(view_); 519 EXPECT_TRUE(widget_->HasCapture()); 520 widget2->Show(); 521 EXPECT_GE(IndexInParent(widget2->GetNativeWindow()), 522 IndexInParent(widget_->GetNativeWindow())); 523 524 generator_->MoveMouseRelativeTo(widget_->GetNativeWindow(), 525 view_->bounds().CenterPoint()); 526 527 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 528 helper_->FireTooltipTimer(); 529 // Even though the mouse is over a window with a tooltip it shouldn't be 530 // picked up because the windows don't have the same value for 531 // |TooltipManager::kGroupingPropertyKey|. 532 EXPECT_TRUE(helper_->GetTooltipText().empty()); 533 534 // Now make both the windows have same transient value for 535 // kGroupingPropertyKey. In this case the tooltip should be picked up from 536 // |widget2| (because the mouse is over it). 537 const int grouping_key = 1; 538 widget_->SetNativeWindowProperty(TooltipManager::kGroupingPropertyKey, 539 reinterpret_cast<void*>(grouping_key)); 540 widget2->SetNativeWindowProperty(TooltipManager::kGroupingPropertyKey, 541 reinterpret_cast<void*>(grouping_key)); 542 generator_->MoveMouseBy(1, 10); 543 EXPECT_TRUE(helper_->IsTooltipTimerRunning()); 544 helper_->FireTooltipTimer(); 545 EXPECT_EQ(tooltip_text2, helper_->GetTooltipText()); 546 547 widget2.reset(); 548 } 549 550 namespace { 551 552 class TestTooltip : public Tooltip { 553 public: 554 TestTooltip() : is_visible_(false) {} 555 virtual ~TestTooltip() {} 556 557 const base::string16& tooltip_text() const { return tooltip_text_; } 558 559 // Tooltip: 560 virtual void SetText(aura::Window* window, 561 const base::string16& tooltip_text, 562 const gfx::Point& location) OVERRIDE { 563 tooltip_text_ = tooltip_text; 564 location_ = location; 565 } 566 virtual void Show() OVERRIDE { 567 is_visible_ = true; 568 } 569 virtual void Hide() OVERRIDE { 570 is_visible_ = false; 571 } 572 virtual bool IsVisible() OVERRIDE { 573 return is_visible_; 574 } 575 const gfx::Point& location() { return location_; } 576 577 private: 578 bool is_visible_; 579 base::string16 tooltip_text_; 580 gfx::Point location_; 581 582 DISALLOW_COPY_AND_ASSIGN(TestTooltip); 583 }; 584 585 } // namespace 586 587 // Use for tests that don't depend upon views. 588 class TooltipControllerTest2 : public aura::test::AuraTestBase { 589 public: 590 TooltipControllerTest2() : test_tooltip_(new TestTooltip) {} 591 virtual ~TooltipControllerTest2() {} 592 593 virtual void SetUp() OVERRIDE { 594 wm_state_.reset(new wm::WMState); 595 aura::test::AuraTestBase::SetUp(); 596 new wm::DefaultActivationClient(root_window()); 597 controller_.reset(new TooltipController( 598 scoped_ptr<corewm::Tooltip>(test_tooltip_))); 599 root_window()->AddPreTargetHandler(controller_.get()); 600 SetTooltipClient(root_window(), controller_.get()); 601 helper_.reset(new TooltipControllerTestHelper(controller_.get())); 602 generator_.reset(new aura::test::EventGenerator(root_window())); 603 } 604 605 virtual void TearDown() OVERRIDE { 606 root_window()->RemovePreTargetHandler(controller_.get()); 607 aura::client::SetTooltipClient(root_window(), NULL); 608 controller_.reset(); 609 generator_.reset(); 610 helper_.reset(); 611 aura::test::AuraTestBase::TearDown(); 612 wm_state_.reset(); 613 } 614 615 protected: 616 // Owned by |controller_|. 617 TestTooltip* test_tooltip_; 618 scoped_ptr<TooltipControllerTestHelper> helper_; 619 scoped_ptr<aura::test::EventGenerator> generator_; 620 621 private: 622 scoped_ptr<TooltipController> controller_; 623 scoped_ptr<wm::WMState> wm_state_; 624 625 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest2); 626 }; 627 628 TEST_F(TooltipControllerTest2, VerifyLeadingTrailingWhitespaceStripped) { 629 aura::test::TestWindowDelegate test_delegate; 630 scoped_ptr<aura::Window> window( 631 CreateNormalWindow(100, root_window(), &test_delegate)); 632 window->SetBounds(gfx::Rect(0, 0, 300, 300)); 633 base::string16 tooltip_text(ASCIIToUTF16(" \nx ")); 634 aura::client::SetTooltipText(window.get(), &tooltip_text); 635 generator_->MoveMouseToCenterOf(window.get()); 636 helper_->FireTooltipTimer(); 637 EXPECT_EQ(ASCIIToUTF16("x"), test_tooltip_->tooltip_text()); 638 } 639 640 // Verifies that tooltip is hidden and tooltip window closed upon cancel mode. 641 TEST_F(TooltipControllerTest2, CloseOnCancelMode) { 642 aura::test::TestWindowDelegate test_delegate; 643 scoped_ptr<aura::Window> window( 644 CreateNormalWindow(100, root_window(), &test_delegate)); 645 window->SetBounds(gfx::Rect(0, 0, 300, 300)); 646 base::string16 tooltip_text(ASCIIToUTF16("Tooltip Text")); 647 aura::client::SetTooltipText(window.get(), &tooltip_text); 648 generator_->MoveMouseToCenterOf(window.get()); 649 650 // Fire tooltip timer so tooltip becomes visible. 651 helper_->FireTooltipTimer(); 652 EXPECT_TRUE(helper_->IsTooltipVisible()); 653 654 // Send OnCancelMode event and verify that tooltip becomes invisible and 655 // the tooltip window is closed. 656 ui::CancelModeEvent event; 657 helper_->controller()->OnCancelMode(&event); 658 EXPECT_FALSE(helper_->IsTooltipVisible()); 659 EXPECT_TRUE(helper_->GetTooltipWindow() == NULL); 660 } 661 662 // Use for tests that need both views and a TestTooltip. 663 class TooltipControllerTest3 : public aura::test::AuraTestBase { 664 public: 665 TooltipControllerTest3() : test_tooltip_(new TestTooltip) {} 666 virtual ~TooltipControllerTest3() {} 667 668 virtual void SetUp() OVERRIDE { 669 wm_state_.reset(new wm::WMState); 670 aura::test::AuraTestBase::SetUp(); 671 new wm::DefaultActivationClient(root_window()); 672 673 widget_.reset(CreateWidget(root_window())); 674 widget_->SetContentsView(new View); 675 view_ = new TooltipTestView; 676 widget_->GetContentsView()->AddChildView(view_); 677 view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds()); 678 679 generator_.reset(new aura::test::EventGenerator(GetRootWindow())); 680 controller_.reset(new TooltipController( 681 scoped_ptr<views::corewm::Tooltip>(test_tooltip_))); 682 GetRootWindow()->RemovePreTargetHandler( 683 static_cast<TooltipController*>(aura::client::GetTooltipClient( 684 widget_->GetNativeWindow()->GetRootWindow()))); 685 GetRootWindow()->AddPreTargetHandler(controller_.get()); 686 helper_.reset(new TooltipControllerTestHelper(controller_.get())); 687 SetTooltipClient(GetRootWindow(), controller_.get()); 688 } 689 690 virtual void TearDown() OVERRIDE { 691 GetRootWindow()->RemovePreTargetHandler(controller_.get()); 692 aura::client::SetTooltipClient(GetRootWindow(), NULL); 693 694 controller_.reset(); 695 generator_.reset(); 696 helper_.reset(); 697 widget_.reset(); 698 aura::test::AuraTestBase::TearDown(); 699 wm_state_.reset(); 700 } 701 702 aura::Window* GetWindow() { return widget_->GetNativeWindow(); } 703 704 protected: 705 // Owned by |controller_|. 706 TestTooltip* test_tooltip_; 707 scoped_ptr<TooltipControllerTestHelper> helper_; 708 scoped_ptr<aura::test::EventGenerator> generator_; 709 scoped_ptr<views::Widget> widget_; 710 TooltipTestView* view_; 711 712 private: 713 scoped_ptr<TooltipController> controller_; 714 scoped_ptr<wm::WMState> wm_state_; 715 716 #if defined(OS_WIN) 717 ui::ScopedOleInitializer ole_initializer_; 718 #endif 719 720 aura::Window* GetRootWindow() { return GetWindow()->GetRootWindow(); } 721 722 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest3); 723 }; 724 725 TEST_F(TooltipControllerTest3, TooltipPositionChangesOnTwoViewsWithSameLabel) { 726 // Owned by |view_|. 727 // These two views have the same tooltip text 728 TooltipTestView* v1 = new TooltipTestView; 729 TooltipTestView* v2 = new TooltipTestView; 730 // v1_1 is a view inside v1 that has an identical tooltip text to that of v1 731 // and v2 732 TooltipTestView* v1_1 = new TooltipTestView; 733 // v2_1 is a view inside v2 that has an identical tooltip text to that of v1 734 // and v2 735 TooltipTestView* v2_1 = new TooltipTestView; 736 // v2_2 is a view inside v2 with the tooltip text different from all the 737 // others 738 TooltipTestView* v2_2 = new TooltipTestView; 739 740 // Setup all the views' relations 741 view_->AddChildView(v1); 742 view_->AddChildView(v2); 743 v1->AddChildView(v1_1); 744 v2->AddChildView(v2_1); 745 v2->AddChildView(v2_2); 746 const base::string16 reference_string( 747 base::ASCIIToUTF16("Identical Tooltip Text")); 748 const base::string16 alternative_string( 749 base::ASCIIToUTF16("Another Shrubbery")); 750 v1->set_tooltip_text(reference_string); 751 v2->set_tooltip_text(reference_string); 752 v1_1->set_tooltip_text(reference_string); 753 v2_1->set_tooltip_text(reference_string); 754 v2_2->set_tooltip_text(alternative_string); 755 756 // Set views' bounds 757 gfx::Rect view_bounds(view_->GetLocalBounds()); 758 view_bounds.set_height(view_bounds.height() / 2); 759 v1->SetBoundsRect(view_bounds); 760 v1_1->SetBounds(0, 0, 3, 3); 761 view_bounds.set_y(view_bounds.height()); 762 v2->SetBoundsRect(view_bounds); 763 v2_2->SetBounds(view_bounds.width() - 3, view_bounds.height() - 3, 3, 3); 764 v2_1->SetBounds(0, 0, 3, 3); 765 766 // Test whether a toolbar appears on v1 767 gfx::Point center = v1->bounds().CenterPoint(); 768 generator_->MoveMouseRelativeTo(GetWindow(), center); 769 helper_->FireTooltipTimer(); 770 EXPECT_TRUE(helper_->IsTooltipVisible()); 771 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 772 gfx::Point tooltip_bounds1 = test_tooltip_->location(); 773 774 // Test whether the toolbar changes position on mouse over v2 775 center = v2->bounds().CenterPoint(); 776 generator_->MoveMouseRelativeTo(GetWindow(), center); 777 helper_->FireTooltipTimer(); 778 EXPECT_TRUE(helper_->IsTooltipVisible()); 779 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 780 gfx::Point tooltip_bounds2 = test_tooltip_->location(); 781 782 EXPECT_NE(tooltip_bounds1, gfx::Point()); 783 EXPECT_NE(tooltip_bounds2, gfx::Point()); 784 EXPECT_NE(tooltip_bounds1, tooltip_bounds2); 785 786 // Test if the toolbar does not change position on encountering a contained 787 // view with the same tooltip text 788 center = v2_1->GetLocalBounds().CenterPoint(); 789 views::View::ConvertPointToTarget(v2_1, view_, ¢er); 790 generator_->MoveMouseRelativeTo(GetWindow(), center); 791 helper_->FireTooltipTimer(); 792 gfx::Point tooltip_bounds2_1 = test_tooltip_->location(); 793 794 EXPECT_NE(tooltip_bounds2, tooltip_bounds2_1); 795 EXPECT_TRUE(helper_->IsTooltipVisible()); 796 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 797 798 // Test if the toolbar changes position on encountering a contained 799 // view with a different tooltip text 800 center = v2_2->GetLocalBounds().CenterPoint(); 801 views::View::ConvertPointToTarget(v2_2, view_, ¢er); 802 generator_->MoveMouseRelativeTo(GetWindow(), center); 803 helper_->FireTooltipTimer(); 804 gfx::Point tooltip_bounds2_2 = test_tooltip_->location(); 805 806 EXPECT_NE(tooltip_bounds2_1, tooltip_bounds2_2); 807 EXPECT_TRUE(helper_->IsTooltipVisible()); 808 EXPECT_EQ(alternative_string, helper_->GetTooltipText()); 809 810 // Test if moving from a view that is contained by a larger view, both with 811 // the same tooltip text, does not change tooltip's position. 812 center = v1_1->GetLocalBounds().CenterPoint(); 813 views::View::ConvertPointToTarget(v1_1, view_, ¢er); 814 generator_->MoveMouseRelativeTo(GetWindow(), center); 815 helper_->FireTooltipTimer(); 816 gfx::Point tooltip_bounds1_1 = test_tooltip_->location(); 817 818 EXPECT_TRUE(helper_->IsTooltipVisible()); 819 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 820 821 center = v1->bounds().CenterPoint(); 822 generator_->MoveMouseRelativeTo(GetWindow(), center); 823 helper_->FireTooltipTimer(); 824 tooltip_bounds1 = test_tooltip_->location(); 825 826 EXPECT_NE(tooltip_bounds1_1, tooltip_bounds1); 827 EXPECT_EQ(reference_string, helper_->GetTooltipText()); 828 } 829 830 } // namespace test 831 } // namespace corewm 832 } // namespace views 833