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
3 // found in the LICENSE file.
5 #include "ash/launcher/launcher_view.h"
10 #include "ash/ash_switches.h"
11 #include "ash/launcher/launcher.h"
12 #include "ash/launcher/launcher_button.h"
13 #include "ash/launcher/launcher_icon_observer.h"
14 #include "ash/launcher/launcher_model.h"
15 #include "ash/launcher/launcher_tooltip_manager.h"
16 #include "ash/root_window_controller.h"
17 #include "ash/shelf/shelf_layout_manager.h"
18 #include "ash/shelf/shelf_widget.h"
19 #include "ash/shell.h"
20 #include "ash/shell_window_ids.h"
21 #include "ash/test/ash_test_base.h"
22 #include "ash/test/launcher_view_test_api.h"
23 #include "ash/test/shell_test_api.h"
24 #include "ash/test/test_launcher_delegate.h"
25 #include "base/basictypes.h"
26 #include "base/command_line.h"
27 #include "base/compiler_specific.h"
28 #include "base/memory/scoped_ptr.h"
29 #include "grit/ash_resources.h"
30 #include "ui/aura/root_window.h"
31 #include "ui/aura/test/aura_test_base.h"
32 #include "ui/aura/test/event_generator.h"
33 #include "ui/aura/window.h"
34 #include "ui/base/events/event.h"
35 #include "ui/base/events/event_constants.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/compositor/layer.h"
38 #include "ui/views/view_model.h"
39 #include "ui/views/widget/widget.h"
40 #include "ui/views/widget/widget_delegate.h"
45 ////////////////////////////////////////////////////////////////////////////////
46 // LauncherIconObserver tests.
48 class TestLauncherIconObserver
: public LauncherIconObserver
{
50 explicit TestLauncherIconObserver(Launcher
* launcher
)
51 : launcher_(launcher
),
52 change_notified_(false) {
54 launcher_
->AddIconObserver(this);
57 virtual ~TestLauncherIconObserver() {
59 launcher_
->RemoveIconObserver(this);
62 // LauncherIconObserver implementation.
63 virtual void OnLauncherIconPositionsChanged() OVERRIDE
{
64 change_notified_
= true;
67 int change_notified() const { return change_notified_
; }
68 void Reset() { change_notified_
= false; }
72 bool change_notified_
;
74 DISALLOW_COPY_AND_ASSIGN(TestLauncherIconObserver
);
77 class LauncherViewIconObserverTest
: public ash::test::AshTestBase
{
79 LauncherViewIconObserverTest() {}
80 virtual ~LauncherViewIconObserverTest() {}
82 virtual void SetUp() OVERRIDE
{
84 Launcher
* launcher
= Launcher::ForPrimaryDisplay();
85 observer_
.reset(new TestLauncherIconObserver(launcher
));
87 launcher_view_test_
.reset(new LauncherViewTestAPI(
88 launcher
->GetLauncherViewForTest()));
89 launcher_view_test_
->SetAnimationDuration(1);
92 virtual void TearDown() OVERRIDE
{
94 AshTestBase::TearDown();
97 TestLauncherIconObserver
* observer() { return observer_
.get(); }
99 LauncherViewTestAPI
* launcher_view_test() {
100 return launcher_view_test_
.get();
103 Launcher
* LauncherForSecondaryDisplay() {
104 return Launcher::ForWindow(Shell::GetAllRootWindows()[1]);
108 scoped_ptr
<TestLauncherIconObserver
> observer_
;
109 scoped_ptr
<LauncherViewTestAPI
> launcher_view_test_
;
111 DISALLOW_COPY_AND_ASSIGN(LauncherViewIconObserverTest
);
114 TEST_F(LauncherViewIconObserverTest
, AddRemove
) {
115 ash::test::TestLauncherDelegate
* launcher_delegate
=
116 ash::test::TestLauncherDelegate::instance();
117 ASSERT_TRUE(launcher_delegate
);
119 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
120 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
121 params
.bounds
= gfx::Rect(0, 0, 200, 200);
122 params
.context
= CurrentContext();
124 scoped_ptr
<views::Widget
> widget(new views::Widget());
125 widget
->Init(params
);
126 launcher_delegate
->AddLauncherItem(widget
->GetNativeWindow());
127 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
128 EXPECT_TRUE(observer()->change_notified());
132 widget
->GetNativeWindow()->parent()->RemoveChild(widget
->GetNativeWindow());
133 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
134 EXPECT_TRUE(observer()->change_notified());
138 // Sometimes fails on trybots on win7_aura. http://crbug.com/177135
140 #define MAYBE_AddRemoveWithMultipleDisplays \
141 DISABLED_AddRemoveWithMultipleDisplays
143 #define MAYBE_AddRemoveWithMultipleDisplays \
144 AddRemoveWithMultipleDisplays
146 // Make sure creating/deleting an window on one displays notifies a
147 // launcher on external display as well as one on primary.
148 TEST_F(LauncherViewIconObserverTest
, MAYBE_AddRemoveWithMultipleDisplays
) {
149 UpdateDisplay("400x400,400x400");
150 TestLauncherIconObserver
second_observer(LauncherForSecondaryDisplay());
152 ash::test::TestLauncherDelegate
* launcher_delegate
=
153 ash::test::TestLauncherDelegate::instance();
154 ASSERT_TRUE(launcher_delegate
);
156 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
157 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
158 params
.bounds
= gfx::Rect(0, 0, 200, 200);
159 params
.context
= CurrentContext();
161 scoped_ptr
<views::Widget
> widget(new views::Widget());
162 widget
->Init(params
);
163 launcher_delegate
->AddLauncherItem(widget
->GetNativeWindow());
164 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
165 EXPECT_TRUE(observer()->change_notified());
166 EXPECT_TRUE(second_observer
.change_notified());
168 second_observer
.Reset();
170 widget
->GetNativeWindow()->parent()->RemoveChild(widget
->GetNativeWindow());
171 launcher_view_test()->RunMessageLoopUntilAnimationsDone();
172 EXPECT_TRUE(observer()->change_notified());
173 EXPECT_TRUE(second_observer
.change_notified());
176 second_observer
.Reset();
179 TEST_F(LauncherViewIconObserverTest
, BoundsChanged
) {
180 ash::ShelfWidget
* shelf
= Shell::GetPrimaryRootWindowController()->shelf();
181 Launcher
* launcher
= Launcher::ForPrimaryDisplay();
182 gfx::Size shelf_size
=
183 shelf
->GetWindowBoundsInScreen().size();
184 shelf_size
.set_width(shelf_size
.width() / 2);
185 ASSERT_GT(shelf_size
.width(), 0);
186 launcher
->SetLauncherViewBounds(gfx::Rect(shelf_size
));
187 // No animation happens for LauncherView bounds change.
188 EXPECT_TRUE(observer()->change_notified());
192 ////////////////////////////////////////////////////////////////////////////////
193 // LauncherView tests.
195 class LauncherViewTest
: public AshTestBase
{
197 LauncherViewTest() : model_(NULL
), launcher_view_(NULL
) {}
198 virtual ~LauncherViewTest() {}
200 virtual void SetUp() OVERRIDE
{
201 AshTestBase::SetUp();
202 test::ShellTestApi
test_api(Shell::GetInstance());
203 model_
= test_api
.launcher_model();
204 Launcher
* launcher
= Launcher::ForPrimaryDisplay();
205 launcher_view_
= launcher
->GetLauncherViewForTest();
207 // The bounds should be big enough for 4 buttons + overflow chevron.
208 launcher_view_
->SetBounds(0, 0, 500, 50);
210 test_api_
.reset(new LauncherViewTestAPI(launcher_view_
));
211 test_api_
->SetAnimationDuration(1); // Speeds up animation for test.
213 // Add browser shortcut launcher item at index 0 for test.
214 AddBrowserShortcut();
217 virtual void TearDown() OVERRIDE
{
219 AshTestBase::TearDown();
223 LauncherID
AddBrowserShortcut() {
224 LauncherItem browser_shortcut
;
225 browser_shortcut
.type
= TYPE_BROWSER_SHORTCUT
;
226 browser_shortcut
.is_incognito
= false;
228 LauncherID id
= model_
->next_id();
229 model_
->AddAt(0, browser_shortcut
);
230 test_api_
->RunMessageLoopUntilAnimationsDone();
234 LauncherID
AddAppShortcut() {
236 item
.type
= TYPE_APP_SHORTCUT
;
237 item
.status
= STATUS_CLOSED
;
239 LauncherID id
= model_
->next_id();
241 test_api_
->RunMessageLoopUntilAnimationsDone();
245 LauncherID
AddTabbedBrowserNoWait() {
247 item
.type
= TYPE_TABBED
;
248 item
.status
= STATUS_RUNNING
;
250 LauncherID id
= model_
->next_id();
255 LauncherID
AddTabbedBrowser() {
256 LauncherID id
= AddTabbedBrowserNoWait();
257 test_api_
->RunMessageLoopUntilAnimationsDone();
261 LauncherID
AddPanel() {
262 LauncherID id
= AddPanelNoWait();
263 test_api_
->RunMessageLoopUntilAnimationsDone();
267 LauncherID
AddPlatformAppNoWait() {
269 item
.type
= TYPE_PLATFORM_APP
;
270 item
.status
= STATUS_RUNNING
;
272 LauncherID id
= model_
->next_id();
277 LauncherID
AddPanelNoWait() {
279 item
.type
= TYPE_APP_PANEL
;
280 item
.status
= STATUS_RUNNING
;
282 LauncherID id
= model_
->next_id();
287 LauncherID
AddPlatformApp() {
288 LauncherID id
= AddPlatformAppNoWait();
289 test_api_
->RunMessageLoopUntilAnimationsDone();
293 void RemoveByID(LauncherID id
) {
294 model_
->RemoveItemAt(model_
->ItemIndexByID(id
));
295 test_api_
->RunMessageLoopUntilAnimationsDone();
298 internal::LauncherButton
* GetButtonByID(LauncherID id
) {
299 int index
= model_
->ItemIndexByID(id
);
300 return test_api_
->GetButton(index
);
303 LauncherItem
GetItemByID(LauncherID id
) {
304 LauncherItems::const_iterator items
= model_
->ItemByID(id
);
309 const std::vector
<std::pair
<LauncherID
, views::View
*> >& id_map
) {
310 size_t map_index
= 0;
311 for (size_t model_index
= 0;
312 model_index
< model_
->items().size();
314 ash::LauncherItem item
= model_
->items()[model_index
];
315 ash::LauncherID id
= item
.id
;
316 EXPECT_EQ(id_map
[map_index
].first
, id
);
317 EXPECT_EQ(id_map
[map_index
].second
, GetButtonByID(id
));
320 ASSERT_EQ(map_index
, id_map
.size());
323 void VerifyLauncherItemBoundsAreValid() {
324 for (int i
=0;i
<= test_api_
->GetLastVisibleIndex(); ++i
) {
325 if (test_api_
->GetButton(i
)) {
326 gfx::Rect launcher_view_bounds
= launcher_view_
->GetLocalBounds();
327 gfx::Rect item_bounds
= test_api_
->GetBoundsByIndex(i
);
328 EXPECT_TRUE(item_bounds
.x() >= 0);
329 EXPECT_TRUE(item_bounds
.y() >= 0);
330 EXPECT_TRUE(item_bounds
.right() <= launcher_view_bounds
.width());
331 EXPECT_TRUE(item_bounds
.bottom() <= launcher_view_bounds
.height());
336 views::View
* SimulateDrag(internal::LauncherButtonHost::Pointer pointer
,
338 int destination_index
) {
339 internal::LauncherButtonHost
* button_host
= launcher_view_
;
342 views::View
* button
= test_api_
->GetButton(button_index
);
343 ui::MouseEvent
click_event(ui::ET_MOUSE_PRESSED
,
344 button
->bounds().origin(),
345 button
->bounds().origin(), 0);
346 button_host
->PointerPressedOnButton(button
, pointer
, click_event
);
349 views::View
* destination
= test_api_
->GetButton(destination_index
);
350 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
,
351 destination
->bounds().origin(),
352 destination
->bounds().origin(), 0);
353 button_host
->PointerDraggedOnButton(button
, pointer
, drag_event
);
357 void SetupForDragTest(
358 std::vector
<std::pair
<LauncherID
, views::View
*> >* id_map
) {
359 // Initialize |id_map| with the automatically-created launcher buttons.
360 for (size_t i
= 0; i
< model_
->items().size(); ++i
) {
361 internal::LauncherButton
* button
= test_api_
->GetButton(i
);
362 id_map
->push_back(std::make_pair(model_
->items()[i
].id
, button
));
364 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map
));
366 // Add 5 app launcher buttons for testing.
367 for (int i
= 0; i
< 5; ++i
) {
368 LauncherID id
= AddAppShortcut();
369 // browser shortcut is located at index 0. So we should start to add app
370 // shortcut at index 1.
371 id_map
->insert(id_map
->begin() + (i
+ 1),
372 std::make_pair(id
, GetButtonByID(id
)));
374 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map
));
377 views::View
* GetTooltipAnchorView() {
378 return launcher_view_
->tooltip_manager()->anchor_
;
382 launcher_view_
->tooltip_manager()->ShowInternal();
385 LauncherModel
* model_
;
386 internal::LauncherView
* launcher_view_
;
388 scoped_ptr
<LauncherViewTestAPI
> test_api_
;
391 DISALLOW_COPY_AND_ASSIGN(LauncherViewTest
);
394 class LauncherViewTextDirectionTest
395 : public LauncherViewTest
,
396 public testing::WithParamInterface
<bool> {
398 LauncherViewTextDirectionTest() : is_rtl_(GetParam()) {}
399 virtual ~LauncherViewTextDirectionTest() {}
401 virtual void SetUp() OVERRIDE
{
402 LauncherViewTest::SetUp();
403 original_locale_
= l10n_util::GetApplicationLocale(std::string());
405 base::i18n::SetICUDefaultLocale("he");
406 ASSERT_EQ(is_rtl_
, base::i18n::IsRTL());
409 virtual void TearDown() OVERRIDE
{
411 base::i18n::SetICUDefaultLocale(original_locale_
);
412 LauncherViewTest::TearDown();
417 std::string original_locale_
;
419 DISALLOW_COPY_AND_ASSIGN(LauncherViewTextDirectionTest
);
422 // Checks that the ideal item icon bounds match the view's bounds in the screen
423 // in both LTR and RTL.
424 TEST_P(LauncherViewTextDirectionTest
, IdealBoundsOfItemIcon
) {
425 LauncherID id
= AddTabbedBrowser();
426 internal::LauncherButton
* button
= GetButtonByID(id
);
427 gfx::Rect item_bounds
= button
->GetBoundsInScreen();
428 gfx::Point icon_offset
= button
->GetIconBounds().origin();
429 item_bounds
.Offset(icon_offset
.OffsetFromOrigin());
430 gfx::Rect ideal_bounds
= launcher_view_
->GetIdealBoundsOfItemIcon(id
);
431 gfx::Point screen_origin
;
432 views::View::ConvertPointToScreen(launcher_view_
, &screen_origin
);
433 ideal_bounds
.Offset(screen_origin
.x(), screen_origin
.y());
434 EXPECT_EQ(item_bounds
.x(), ideal_bounds
.x());
435 EXPECT_EQ(item_bounds
.y(), ideal_bounds
.y());
438 // Adds browser button until overflow and verifies that the last added browser
440 TEST_F(LauncherViewTest
, AddBrowserUntilOverflow
) {
441 // All buttons should be visible.
442 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
443 test_api_
->GetButtonCount());
445 // Add tabbed browser until overflow.
447 LauncherID last_added
= AddTabbedBrowser();
448 while (!test_api_
->IsOverflowButtonVisible()) {
449 // Added button is visible after animation while in this loop.
450 EXPECT_TRUE(GetButtonByID(last_added
)->visible());
452 last_added
= AddTabbedBrowser();
454 ASSERT_LT(items_added
, 10000);
457 // The last added button should be invisible.
458 EXPECT_FALSE(GetButtonByID(last_added
)->visible());
461 // Adds one browser button then adds app shortcut until overflow. Verifies that
462 // the browser button gets hidden on overflow and last added app shortcut is
464 TEST_F(LauncherViewTest
, AddAppShortcutWithBrowserButtonUntilOverflow
) {
465 // All buttons should be visible.
466 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
467 test_api_
->GetButtonCount());
469 LauncherID browser_button_id
= AddTabbedBrowser();
471 // Add app shortcut until overflow.
473 LauncherID last_added
= AddAppShortcut();
474 while (!test_api_
->IsOverflowButtonVisible()) {
475 // Added button is visible after animation while in this loop.
476 EXPECT_TRUE(GetButtonByID(last_added
)->visible());
478 last_added
= AddAppShortcut();
480 ASSERT_LT(items_added
, 10000);
483 // The last added app short button should be visible.
484 EXPECT_TRUE(GetButtonByID(last_added
)->visible());
485 // And the browser button is invisible.
486 EXPECT_FALSE(GetButtonByID(browser_button_id
)->visible());
489 TEST_F(LauncherViewTest
, AddPanelHidesTabbedBrowser
) {
490 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
491 test_api_
->GetButtonCount());
493 // Add tabbed browser until overflow, remember last visible tabbed browser.
495 LauncherID first_added
= AddTabbedBrowser();
496 EXPECT_TRUE(GetButtonByID(first_added
)->visible());
497 LauncherID last_visible
= first_added
;
499 LauncherID added
= AddTabbedBrowser();
500 if (test_api_
->IsOverflowButtonVisible()) {
501 EXPECT_FALSE(GetButtonByID(added
)->visible());
504 last_visible
= added
;
506 ASSERT_LT(items_added
, 10000);
509 LauncherID panel
= AddPanel();
510 EXPECT_TRUE(GetButtonByID(panel
)->visible());
511 EXPECT_FALSE(GetButtonByID(last_visible
)->visible());
514 EXPECT_TRUE(GetButtonByID(last_visible
)->visible());
517 // When there are more panels then browsers we should hide panels rather
519 TEST_F(LauncherViewTest
, BrowserHidesExcessPanels
) {
520 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
521 test_api_
->GetButtonCount());
523 // Add tabbed browser.
524 LauncherID browser
= AddTabbedBrowser();
525 LauncherID first_panel
= AddPanel();
527 EXPECT_TRUE(GetButtonByID(browser
)->visible());
528 EXPECT_TRUE(GetButtonByID(first_panel
)->visible());
530 // Add panels until there is an overflow.
531 LauncherID last_panel
= first_panel
;
533 while (!test_api_
->IsOverflowButtonVisible()) {
534 last_panel
= AddPanel();
536 ASSERT_LT(items_added
, 10000);
539 // The first panel should now be hidden by the new browsers needing space.
540 EXPECT_FALSE(GetButtonByID(first_panel
)->visible());
541 EXPECT_TRUE(GetButtonByID(last_panel
)->visible());
542 EXPECT_TRUE(GetButtonByID(browser
)->visible());
544 // Adding browsers should eventually begin to hide browsers. We will add
545 // browsers until either the last panel or browser is hidden.
547 while (GetButtonByID(browser
)->visible() &&
548 GetButtonByID(last_panel
)->visible()) {
549 browser
= AddTabbedBrowser();
551 ASSERT_LT(items_added
, 10000);
553 EXPECT_TRUE(GetButtonByID(last_panel
)->visible());
554 EXPECT_FALSE(GetButtonByID(browser
)->visible());
557 // Adds button until overflow then removes first added one. Verifies that
558 // the last added one changes from invisible to visible and overflow
560 TEST_F(LauncherViewTest
, RemoveButtonRevealsOverflowed
) {
561 // All buttons should be visible.
562 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
563 test_api_
->GetButtonCount());
565 // Add tabbed browser until overflow.
567 LauncherID first_added
= AddTabbedBrowser();
568 LauncherID last_added
= first_added
;
569 while (!test_api_
->IsOverflowButtonVisible()) {
570 last_added
= AddTabbedBrowser();
572 ASSERT_LT(items_added
, 10000);
575 // Expect add more than 1 button. First added is visible and last is not.
576 EXPECT_NE(first_added
, last_added
);
577 EXPECT_TRUE(GetButtonByID(first_added
)->visible());
578 EXPECT_FALSE(GetButtonByID(last_added
)->visible());
580 // Remove first added.
581 RemoveByID(first_added
);
583 // Last added button becomes visible and overflow chevron is gone.
584 EXPECT_TRUE(GetButtonByID(last_added
)->visible());
585 EXPECT_EQ(1.0f
, GetButtonByID(last_added
)->layer()->opacity());
586 EXPECT_FALSE(test_api_
->IsOverflowButtonVisible());
589 // Verifies that remove last overflowed button should hide overflow chevron.
590 TEST_F(LauncherViewTest
, RemoveLastOverflowed
) {
591 // All buttons should be visible.
592 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
593 test_api_
->GetButtonCount());
595 // Add tabbed browser until overflow.
597 LauncherID last_added
= AddTabbedBrowser();
598 while (!test_api_
->IsOverflowButtonVisible()) {
599 last_added
= AddTabbedBrowser();
601 ASSERT_LT(items_added
, 10000);
604 RemoveByID(last_added
);
605 EXPECT_FALSE(test_api_
->IsOverflowButtonVisible());
608 // Adds browser button without waiting for animation to finish and verifies
609 // that all added buttons are visible.
610 TEST_F(LauncherViewTest
, AddButtonQuickly
) {
611 // All buttons should be visible.
612 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
613 test_api_
->GetButtonCount());
615 // Add a few tabbed browser quickly without wait for animation.
617 while (!test_api_
->IsOverflowButtonVisible()) {
618 AddTabbedBrowserNoWait();
620 ASSERT_LT(added_count
, 10000);
623 // LauncherView should be big enough to hold at least 3 new buttons.
624 ASSERT_GE(added_count
, 3);
626 // Wait for the last animation to finish.
627 test_api_
->RunMessageLoopUntilAnimationsDone();
629 // Verifies non-overflow buttons are visible.
630 for (int i
= 0; i
<= test_api_
->GetLastVisibleIndex(); ++i
) {
631 internal::LauncherButton
* button
= test_api_
->GetButton(i
);
633 EXPECT_TRUE(button
->visible()) << "button index=" << i
;
634 EXPECT_EQ(1.0f
, button
->layer()->opacity()) << "button index=" << i
;
639 // Check that model changes are handled correctly while a launcher icon is being
641 TEST_F(LauncherViewTest
, ModelChangesWhileDragging
) {
642 internal::LauncherButtonHost
* button_host
= launcher_view_
;
644 std::vector
<std::pair
<LauncherID
, views::View
*> > id_map
;
645 SetupForDragTest(&id_map
);
647 // Dragging browser shortcut at index 0.
648 EXPECT_TRUE(model_
->items()[0].type
== TYPE_BROWSER_SHORTCUT
);
649 views::View
* dragged_button
= SimulateDrag(
650 internal::LauncherButtonHost::MOUSE
, 0, 2);
651 std::rotate(id_map
.begin(),
654 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
655 button_host
->PointerReleasedOnButton(dragged_button
,
656 internal::LauncherButtonHost::MOUSE
,
658 EXPECT_TRUE(model_
->items()[2].type
== TYPE_BROWSER_SHORTCUT
);
660 // Dragging changes model order.
661 dragged_button
= SimulateDrag(
662 internal::LauncherButtonHost::MOUSE
, 0, 2);
663 std::rotate(id_map
.begin(),
666 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
668 // Cancelling the drag operation restores previous order.
669 button_host
->PointerReleasedOnButton(dragged_button
,
670 internal::LauncherButtonHost::MOUSE
,
672 std::rotate(id_map
.begin(),
675 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
677 // Deleting an item keeps the remaining intact.
678 dragged_button
= SimulateDrag(internal::LauncherButtonHost::MOUSE
, 0, 2);
679 model_
->RemoveItemAt(1);
680 id_map
.erase(id_map
.begin() + 1);
681 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
682 button_host
->PointerReleasedOnButton(dragged_button
,
683 internal::LauncherButtonHost::MOUSE
,
686 // Adding a launcher item cancels the drag and respects the order.
687 dragged_button
= SimulateDrag(internal::LauncherButtonHost::MOUSE
, 0, 2);
688 LauncherID new_id
= AddAppShortcut();
689 id_map
.insert(id_map
.begin() + 5,
690 std::make_pair(new_id
, GetButtonByID(new_id
)));
691 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
692 button_host
->PointerReleasedOnButton(dragged_button
,
693 internal::LauncherButtonHost::MOUSE
,
696 // Adding a launcher item at the end (i.e. a panel) canels drag and respects
698 dragged_button
= SimulateDrag(internal::LauncherButtonHost::MOUSE
, 0, 2);
700 id_map
.insert(id_map
.begin() + 7,
701 std::make_pair(new_id
, GetButtonByID(new_id
)));
702 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
703 button_host
->PointerReleasedOnButton(dragged_button
,
704 internal::LauncherButtonHost::MOUSE
,
708 // Check that 2nd drag from the other pointer would be ignored.
709 TEST_F(LauncherViewTest
, SimultaneousDrag
) {
710 internal::LauncherButtonHost
* button_host
= launcher_view_
;
712 std::vector
<std::pair
<LauncherID
, views::View
*> > id_map
;
713 SetupForDragTest(&id_map
);
715 // Start a mouse drag.
716 views::View
* dragged_button_mouse
= SimulateDrag(
717 internal::LauncherButtonHost::MOUSE
, 0, 2);
718 std::rotate(id_map
.begin(),
721 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
722 // Attempt a touch drag before the mouse drag finishes.
723 views::View
* dragged_button_touch
= SimulateDrag(
724 internal::LauncherButtonHost::TOUCH
, 3, 1);
726 // Nothing changes since 2nd drag is ignored.
727 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
729 // Finish the mouse drag.
730 button_host
->PointerReleasedOnButton(dragged_button_mouse
,
731 internal::LauncherButtonHost::MOUSE
,
733 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
735 // Now start a touch drag.
736 dragged_button_touch
= SimulateDrag(
737 internal::LauncherButtonHost::TOUCH
, 3, 1);
738 std::rotate(id_map
.begin() + 2,
741 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
743 // And attempt a mouse drag before the touch drag finishes.
744 dragged_button_mouse
= SimulateDrag(
745 internal::LauncherButtonHost::MOUSE
, 0, 1);
747 // Nothing changes since 2nd drag is ignored.
748 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
750 button_host
->PointerReleasedOnButton(dragged_button_touch
,
751 internal::LauncherButtonHost::TOUCH
,
753 ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map
));
756 // Confirm that item status changes are reflected in the buttons.
757 TEST_F(LauncherViewTest
, LauncherItemStatus
) {
758 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
759 test_api_
->GetButtonCount());
761 // Add tabbed browser.
762 LauncherID last_added
= AddTabbedBrowser();
763 LauncherItem item
= GetItemByID(last_added
);
764 int index
= model_
->ItemIndexByID(last_added
);
765 internal::LauncherButton
* button
= GetButtonByID(last_added
);
766 ASSERT_EQ(internal::LauncherButton::STATE_RUNNING
, button
->state());
767 item
.status
= ash::STATUS_ACTIVE
;
768 model_
->Set(index
, item
);
769 ASSERT_EQ(internal::LauncherButton::STATE_ACTIVE
, button
->state());
770 item
.status
= ash::STATUS_ATTENTION
;
771 model_
->Set(index
, item
);
772 ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION
, button
->state());
775 TEST_F(LauncherViewTest
, LauncherItemPositionReflectedOnStateChanged
) {
776 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
777 test_api_
->GetButtonCount());
779 // Add 2 items to the launcher.
780 LauncherID item1_id
= AddTabbedBrowser();
781 LauncherID item2_id
= AddPlatformAppNoWait();
782 internal::LauncherButton
* item1_button
= GetButtonByID(item1_id
);
783 internal::LauncherButton
* item2_button
= GetButtonByID(item2_id
);
785 internal::LauncherButton::State state_mask
=
786 static_cast<internal::LauncherButton::State
>
787 (internal::LauncherButton::STATE_NORMAL
|
788 internal::LauncherButton::STATE_HOVERED
|
789 internal::LauncherButton::STATE_RUNNING
|
790 internal::LauncherButton::STATE_ACTIVE
|
791 internal::LauncherButton::STATE_ATTENTION
|
792 internal::LauncherButton::STATE_FOCUSED
);
794 // Clear the button states.
795 item1_button
->ClearState(state_mask
);
796 item2_button
->ClearState(state_mask
);
798 // Since default alignment in tests is bottom, state is reflected in y-axis.
799 ASSERT_EQ(item1_button
->GetIconBounds().y(),
800 item2_button
->GetIconBounds().y());
801 item1_button
->AddState(internal::LauncherButton::STATE_HOVERED
);
802 ASSERT_NE(item1_button
->GetIconBounds().y(),
803 item2_button
->GetIconBounds().y());
804 item1_button
->ClearState(internal::LauncherButton::STATE_HOVERED
);
806 // Enable the alternate shelf layout.
807 CommandLine::ForCurrentProcess()->AppendSwitch(
808 ash::switches::kAshUseAlternateShelfLayout
);
809 launcher_view_
->Layout();
811 // Since default alignment in tests is bottom, state is reflected in y-axis.
812 // In alternate shelf layout there is no visible hovered state.
813 ASSERT_EQ(item1_button
->GetIconBounds().y(),
814 item2_button
->GetIconBounds().y());
815 item1_button
->AddState(internal::LauncherButton::STATE_HOVERED
);
816 ASSERT_EQ(item1_button
->GetIconBounds().y(),
817 item2_button
->GetIconBounds().y());
820 // Confirm that item status changes are reflected in the buttons
821 // for platform apps.
822 TEST_F(LauncherViewTest
, LauncherItemStatusPlatformApp
) {
823 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
824 test_api_
->GetButtonCount());
826 // Add tabbed browser.
827 LauncherID last_added
= AddPlatformApp();
828 LauncherItem item
= GetItemByID(last_added
);
829 int index
= model_
->ItemIndexByID(last_added
);
830 internal::LauncherButton
* button
= GetButtonByID(last_added
);
831 ASSERT_EQ(internal::LauncherButton::STATE_RUNNING
, button
->state());
832 item
.status
= ash::STATUS_ACTIVE
;
833 model_
->Set(index
, item
);
834 ASSERT_EQ(internal::LauncherButton::STATE_ACTIVE
, button
->state());
835 item
.status
= ash::STATUS_ATTENTION
;
836 model_
->Set(index
, item
);
837 ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION
, button
->state());
840 // Confirm that launcher item bounds are correctly updated on shelf changes.
841 TEST_F(LauncherViewTest
, LauncherItemBoundsCheck
) {
842 internal::ShelfLayoutManager
* shelf_layout_manager
=
843 Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
844 VerifyLauncherItemBoundsAreValid();
845 shelf_layout_manager
->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS
);
846 test_api_
->RunMessageLoopUntilAnimationsDone();
847 VerifyLauncherItemBoundsAreValid();
848 shelf_layout_manager
->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER
);
849 test_api_
->RunMessageLoopUntilAnimationsDone();
850 VerifyLauncherItemBoundsAreValid();
853 TEST_F(LauncherViewTest
, LauncherTooltipTest
) {
854 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
855 test_api_
->GetButtonCount());
857 // Prepare some items to the launcher.
858 LauncherID app_button_id
= AddAppShortcut();
859 LauncherID tab_button_id
= AddTabbedBrowser();
861 internal::LauncherButton
* app_button
= GetButtonByID(app_button_id
);
862 internal::LauncherButton
* tab_button
= GetButtonByID(tab_button_id
);
864 internal::LauncherButtonHost
* button_host
= launcher_view_
;
865 internal::LauncherTooltipManager
* tooltip_manager
=
866 launcher_view_
->tooltip_manager();
868 button_host
->MouseEnteredButton(app_button
);
869 // There's a delay to show the tooltip, so it's not visible yet.
870 EXPECT_FALSE(tooltip_manager
->IsVisible());
871 EXPECT_EQ(app_button
, GetTooltipAnchorView());
874 EXPECT_TRUE(tooltip_manager
->IsVisible());
876 // Once it's visible, it keeps visibility and is pointing to the same
878 button_host
->MouseExitedButton(app_button
);
879 EXPECT_TRUE(tooltip_manager
->IsVisible());
880 EXPECT_EQ(app_button
, GetTooltipAnchorView());
882 // When entered to another item, it switches to the new item. There is no
883 // delay for the visibility.
884 button_host
->MouseEnteredButton(tab_button
);
885 EXPECT_TRUE(tooltip_manager
->IsVisible());
886 EXPECT_EQ(tab_button
, GetTooltipAnchorView());
888 button_host
->MouseExitedButton(tab_button
);
889 tooltip_manager
->Close();
891 // Next time: enter app_button -> move immediately to tab_button.
892 button_host
->MouseEnteredButton(app_button
);
893 button_host
->MouseExitedButton(app_button
);
894 button_host
->MouseEnteredButton(tab_button
);
895 EXPECT_FALSE(tooltip_manager
->IsVisible());
896 EXPECT_EQ(tab_button
, GetTooltipAnchorView());
899 TEST_F(LauncherViewTest
, ShouldHideTooltipTest
) {
900 LauncherID app_button_id
= AddAppShortcut();
901 LauncherID tab_button_id
= AddTabbedBrowser();
903 // The tooltip shouldn't hide if the mouse is on normal buttons.
904 for (int i
= 0; i
< test_api_
->GetButtonCount(); i
++) {
905 internal::LauncherButton
* button
= test_api_
->GetButton(i
);
909 EXPECT_FALSE(launcher_view_
->ShouldHideTooltip(
910 button
->GetMirroredBounds().CenterPoint()))
911 << "LauncherView tries to hide on button " << i
;
914 // The tooltip should not hide on the app-list button.
915 views::View
* app_list_button
= launcher_view_
->GetAppListButtonView();
916 EXPECT_FALSE(launcher_view_
->ShouldHideTooltip(
917 app_list_button
->GetMirroredBounds().CenterPoint()));
919 // The tooltip shouldn't hide if the mouse is in the gap between two buttons.
920 gfx::Rect app_button_rect
= GetButtonByID(app_button_id
)->GetMirroredBounds();
921 gfx::Rect tab_button_rect
= GetButtonByID(tab_button_id
)->GetMirroredBounds();
922 ASSERT_FALSE(app_button_rect
.Intersects(tab_button_rect
));
923 EXPECT_FALSE(launcher_view_
->ShouldHideTooltip(
924 gfx::UnionRects(app_button_rect
, tab_button_rect
).CenterPoint()));
926 // The tooltip should hide if it's outside of all buttons.
928 for (int i
= 0; i
< test_api_
->GetButtonCount(); i
++) {
929 internal::LauncherButton
* button
= test_api_
->GetButton(i
);
933 all_area
.Union(button
->GetMirroredBounds());
935 all_area
.Union(launcher_view_
->GetAppListButtonView()->GetMirroredBounds());
936 EXPECT_FALSE(launcher_view_
->ShouldHideTooltip(all_area
.origin()));
937 EXPECT_FALSE(launcher_view_
->ShouldHideTooltip(
938 gfx::Point(all_area
.right() - 1, all_area
.bottom() - 1)));
939 EXPECT_TRUE(launcher_view_
->ShouldHideTooltip(
940 gfx::Point(all_area
.right(), all_area
.y())));
941 EXPECT_TRUE(launcher_view_
->ShouldHideTooltip(
942 gfx::Point(all_area
.x() - 1, all_area
.y())));
943 EXPECT_TRUE(launcher_view_
->ShouldHideTooltip(
944 gfx::Point(all_area
.x(), all_area
.y() - 1)));
945 EXPECT_TRUE(launcher_view_
->ShouldHideTooltip(
946 gfx::Point(all_area
.x(), all_area
.bottom())));
949 TEST_F(LauncherViewTest
, ShouldHideTooltipWithAppListWindowTest
) {
950 Shell::GetInstance()->ToggleAppList(NULL
);
951 ASSERT_TRUE(Shell::GetInstance()->GetAppListWindow());
953 // The tooltip shouldn't hide if the mouse is on normal buttons.
954 for (int i
= 1; i
< test_api_
->GetButtonCount(); i
++) {
955 internal::LauncherButton
* button
= test_api_
->GetButton(i
);
959 EXPECT_FALSE(launcher_view_
->ShouldHideTooltip(
960 button
->GetMirroredBounds().CenterPoint()))
961 << "LauncherView tries to hide on button " << i
;
964 // The tooltip should hide on the app-list button.
965 views::View
* app_list_button
= launcher_view_
->GetAppListButtonView();
966 EXPECT_TRUE(launcher_view_
->ShouldHideTooltip(
967 app_list_button
->GetMirroredBounds().CenterPoint()));
970 // Test that by moving the mouse cursor off the button onto the bubble it closes
972 TEST_F(LauncherViewTest
, ShouldHideTooltipWhenHoveringOnTooltip
) {
973 internal::LauncherTooltipManager
* tooltip_manager
=
974 launcher_view_
->tooltip_manager();
975 tooltip_manager
->CreateZeroDelayTimerForTest();
976 aura::test::EventGenerator
generator(Shell::GetPrimaryRootWindow());
978 // Move the mouse off any item and check that no tooltip is shown.
979 generator
.MoveMouseTo(gfx::Point(0, 0));
980 EXPECT_FALSE(tooltip_manager
->IsVisible());
982 // Move the mouse over the button and check that it is visible.
983 views::View
* app_list_button
= launcher_view_
->GetAppListButtonView();
984 gfx::Rect bounds
= app_list_button
->GetBoundsInScreen();
985 generator
.MoveMouseTo(bounds
.CenterPoint());
986 // Wait for the timer to go off.
987 RunAllPendingInMessageLoop();
988 EXPECT_TRUE(tooltip_manager
->IsVisible());
990 // Move the mouse cursor slightly to the right of the item. The tooltip should
992 generator
.MoveMouseBy(-(bounds
.width() / 2 + 5), 0);
993 // Make sure there is no delayed close.
994 RunAllPendingInMessageLoop();
995 EXPECT_TRUE(tooltip_manager
->IsVisible());
997 // Move back - it should still stay open.
998 generator
.MoveMouseBy(bounds
.width() / 2 + 5, 0);
999 // Make sure there is no delayed close.
1000 RunAllPendingInMessageLoop();
1001 EXPECT_TRUE(tooltip_manager
->IsVisible());
1003 // Now move the mouse cursor slightly above the item - so that it is over the
1004 // tooltip bubble. Now it should disappear.
1005 generator
.MoveMouseBy(0, -(bounds
.height() / 2 + 5));
1006 // Wait until the delayed close kicked in.
1007 RunAllPendingInMessageLoop();
1008 EXPECT_FALSE(tooltip_manager
->IsVisible());
1011 // Resizing launcher view while an add animation without fade-in is running,
1012 // which happens when overflow happens. App list button should end up in its
1013 // new ideal bounds.
1014 TEST_F(LauncherViewTest
, ResizeDuringOverflowAddAnimation
) {
1015 // All buttons should be visible.
1016 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
1017 test_api_
->GetButtonCount());
1019 // Add buttons until overflow. Let the non-overflow add animations finish but
1020 // leave the last running.
1021 int items_added
= 0;
1022 AddTabbedBrowserNoWait();
1023 while (!test_api_
->IsOverflowButtonVisible()) {
1024 test_api_
->RunMessageLoopUntilAnimationsDone();
1025 AddTabbedBrowserNoWait();
1027 ASSERT_LT(items_added
, 10000);
1030 // Resize launcher view with that animation running and stay overflown.
1031 gfx::Rect bounds
= launcher_view_
->bounds();
1032 bounds
.set_width(bounds
.width() - kLauncherPreferredSize
);
1033 launcher_view_
->SetBoundsRect(bounds
);
1034 ASSERT_TRUE(test_api_
->IsOverflowButtonVisible());
1036 // Finish the animation.
1037 test_api_
->RunMessageLoopUntilAnimationsDone();
1039 // App list button should ends up in its new ideal bounds.
1040 const int app_list_button_index
= test_api_
->GetButtonCount() - 1;
1041 const gfx::Rect
& app_list_ideal_bounds
=
1042 test_api_
->GetIdealBoundsByIndex(app_list_button_index
);
1043 const gfx::Rect
& app_list_bounds
=
1044 test_api_
->GetBoundsByIndex(app_list_button_index
);
1045 EXPECT_EQ(app_list_bounds
, app_list_ideal_bounds
);
1048 // Check that the first item in the list follows Fitt's law by including the
1049 // first pixel and being therefore bigger then the others.
1050 TEST_F(LauncherViewTest
, CheckFittsLaw
) {
1051 // All buttons should be visible.
1052 ASSERT_EQ(test_api_
->GetLastVisibleIndex() + 1,
1053 test_api_
->GetButtonCount());
1054 gfx::Rect ideal_bounds_0
= test_api_
->GetIdealBoundsByIndex(0);
1055 gfx::Rect ideal_bounds_1
= test_api_
->GetIdealBoundsByIndex(1);
1056 EXPECT_GT(ideal_bounds_0
.width(), ideal_bounds_1
.width());
1059 INSTANTIATE_TEST_CASE_P(LtrRtl
, LauncherViewTextDirectionTest
, testing::Bool());