blob: 6a3dad7ac420c7b47d75dd9cb5da980f2ecd15db [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/shelf_layout_manager.h"
#include "ash/accelerators/accelerator_controller.h"
#include "ash/accelerators/accelerator_table.h"
#include "ash/ash_switches.h"
#include "ash/display/display_manager.h"
#include "ash/focus_cycler.h"
#include "ash/launcher/launcher.h"
#include "ash/root_window_controller.h"
#include "ash/screen_ash.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/shell_window_ids.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_item.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/window_util.h"
#include "base/command_line.h"
#include "base/utf_string_conversions.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/window.h"
#include "ui/base/animation/animation_container_element.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace internal {
namespace {
void StepWidgetLayerAnimatorToEnd(views::Widget* widget) {
ui::AnimationContainerElement* element =
static_cast<ui::AnimationContainerElement*>(
widget->GetNativeView()->layer()->GetAnimator());
element->Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
}
ShelfLayoutManager* GetShelfLayoutManager() {
aura::Window* window = Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_LauncherContainer);
return static_cast<ShelfLayoutManager*>(window->layout_manager());
}
class ShelfLayoutObserverTest : public ShelfLayoutManager::Observer {
public:
ShelfLayoutObserverTest()
: changed_auto_hide_state_(false) {
}
virtual ~ShelfLayoutObserverTest() {}
bool changed_auto_hide_state() const { return changed_auto_hide_state_; }
private:
virtual void OnAutoHideStateChanged(
ShelfAutoHideState new_state) OVERRIDE {
changed_auto_hide_state_ = true;
}
bool changed_auto_hide_state_;
DISALLOW_COPY_AND_ASSIGN(ShelfLayoutObserverTest);
};
// Trivial item implementation that tracks its views for testing.
class TestItem : public SystemTrayItem {
public:
TestItem()
: SystemTrayItem(Shell::GetInstance()->system_tray()),
tray_view_(NULL),
default_view_(NULL),
detailed_view_(NULL),
notification_view_(NULL) {}
virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
tray_view_ = new views::View;
// Add a label so it has non-zero width.
tray_view_->SetLayoutManager(new views::FillLayout);
tray_view_->AddChildView(new views::Label(UTF8ToUTF16("Tray")));
return tray_view_;
}
virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
default_view_ = new views::View;
default_view_->SetLayoutManager(new views::FillLayout);
default_view_->AddChildView(new views::Label(UTF8ToUTF16("Default")));
return default_view_;
}
virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
detailed_view_ = new views::View;
detailed_view_->SetLayoutManager(new views::FillLayout);
detailed_view_->AddChildView(new views::Label(UTF8ToUTF16("Detailed")));
return detailed_view_;
}
virtual views::View* CreateNotificationView(
user::LoginStatus status) OVERRIDE {
notification_view_ = new views::View;
return notification_view_;
}
virtual void DestroyTrayView() OVERRIDE {
tray_view_ = NULL;
}
virtual void DestroyDefaultView() OVERRIDE {
default_view_ = NULL;
}
virtual void DestroyDetailedView() OVERRIDE {
detailed_view_ = NULL;
}
virtual void DestroyNotificationView() OVERRIDE {
notification_view_ = NULL;
}
virtual void UpdateAfterLoginStatusChange(
user::LoginStatus status) OVERRIDE {}
views::View* tray_view() const { return tray_view_; }
views::View* default_view() const { return default_view_; }
views::View* detailed_view() const { return detailed_view_; }
views::View* notification_view() const { return notification_view_; }
private:
views::View* tray_view_;
views::View* default_view_;
views::View* detailed_view_;
views::View* notification_view_;
DISALLOW_COPY_AND_ASSIGN(TestItem);
};
} // namespace
class ShelfLayoutManagerTest : public ash::test::AshTestBase {
public:
ShelfLayoutManagerTest() {}
ShelfLayoutManager* shelf_layout_manager() {
return Shell::GetPrimaryRootWindowController()->shelf();
}
void SetState(ShelfLayoutManager* shelf,
ShelfVisibilityState state) {
shelf->SetState(state);
}
void UpdateAutoHideStateNow() {
GetShelfLayoutManager()->UpdateAutoHideStateNow();
}
aura::Window* CreateTestWindow() {
aura::Window* window = new aura::Window(NULL);
window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
window->SetType(aura::client::WINDOW_TYPE_NORMAL);
window->Init(ui::LAYER_TEXTURED);
SetDefaultParentByPrimaryRootWindow(window);
return window;
}
// Overridden from AshTestBase:
virtual void SetUp() OVERRIDE {
CommandLine::ForCurrentProcess()->AppendSwitch(
ash::switches::kAshEnableTrayDragging);
test::AshTestBase::SetUp();
}
private:
DISALLOW_COPY_AND_ASSIGN(ShelfLayoutManagerTest);
};
// Fails on Mac only. Need to be implemented. http://crbug.com/111279.
#if defined(OS_MACOSX)
#define MAYBE_SetVisible DISABLED_SetVisible
#else
#define MAYBE_SetVisible SetVisible
#endif
// Makes sure SetVisible updates work area and widget appropriately.
TEST_F(ShelfLayoutManagerTest, MAYBE_SetVisible) {
ShelfLayoutManager* shelf = GetShelfLayoutManager();
// Force an initial layout.
shelf->LayoutShelf();
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
gfx::Rect status_bounds(
shelf->status_area_widget()->GetWindowBoundsInScreen());
gfx::Rect launcher_bounds(
shelf->launcher_widget()->GetWindowBoundsInScreen());
int shelf_height = shelf->GetIdealBounds().height();
const gfx::Display& display = Shell::GetInstance()->display_manager()->
GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
ASSERT_NE(-1, display.id());
// Bottom inset should be the max of widget heights.
EXPECT_EQ(shelf_height,
display.bounds().bottom() - display.work_area().bottom());
// Hide the shelf.
SetState(shelf, SHELF_HIDDEN);
// Run the animation to completion.
StepWidgetLayerAnimatorToEnd(shelf->launcher_widget());
StepWidgetLayerAnimatorToEnd(shelf->status_area_widget());
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
EXPECT_EQ(0,
display.bounds().bottom() - display.work_area().bottom());
// Make sure the bounds of the two widgets changed.
EXPECT_GE(shelf->launcher_widget()->GetNativeView()->bounds().y(),
Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom());
EXPECT_GE(shelf->status_area_widget()->GetNativeView()->bounds().y(),
Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom());
// And show it again.
SetState(shelf, SHELF_VISIBLE);
// Run the animation to completion.
StepWidgetLayerAnimatorToEnd(shelf->launcher_widget());
StepWidgetLayerAnimatorToEnd(shelf->status_area_widget());
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
EXPECT_EQ(shelf_height,
display.bounds().bottom() - display.work_area().bottom());
// Make sure the bounds of the two widgets changed.
launcher_bounds = shelf->launcher_widget()->GetNativeView()->bounds();
int bottom =
Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom() - shelf_height;
EXPECT_EQ(launcher_bounds.y(),
bottom + (shelf->GetIdealBounds().height() -
launcher_bounds.height()) / 2);
status_bounds = shelf->status_area_widget()->GetNativeView()->bounds();
EXPECT_EQ(status_bounds.y(),
bottom + shelf_height - status_bounds.height());
}
// Makes sure LayoutShelf invoked while animating cleans things up.
TEST_F(ShelfLayoutManagerTest, LayoutShelfWhileAnimating) {
ShelfLayoutManager* shelf = GetShelfLayoutManager();
// Force an initial layout.
shelf->LayoutShelf();
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
const gfx::Display& display = Shell::GetInstance()->display_manager()->
GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
// Hide the shelf.
SetState(shelf, SHELF_HIDDEN);
shelf->LayoutShelf();
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
EXPECT_EQ(0, display.bounds().bottom() - display.work_area().bottom());
// Make sure the bounds of the two widgets changed.
EXPECT_GE(shelf->launcher_widget()->GetNativeView()->bounds().y(),
Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom());
EXPECT_GE(shelf->status_area_widget()->GetNativeView()->bounds().y(),
Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom());
}
// Makes sure the launcher is initially sized correctly.
TEST_F(ShelfLayoutManagerTest, LauncherInitiallySized) {
Launcher* launcher = Launcher::ForPrimaryDisplay();
ASSERT_TRUE(launcher);
ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
ASSERT_TRUE(shelf_layout_manager);
ASSERT_TRUE(shelf_layout_manager->status_area_widget());
int status_width = shelf_layout_manager->status_area_widget()->
GetWindowBoundsInScreen().width();
// Test only makes sense if the status is > 0, which is better be.
EXPECT_GT(status_width, 0);
EXPECT_EQ(status_width, launcher->status_size().width());
}
// Makes sure the launcher is sized when the status area changes size.
TEST_F(ShelfLayoutManagerTest, LauncherUpdatedWhenStatusAreaChangesSize) {
Launcher* launcher = Launcher::ForPrimaryDisplay();
ASSERT_TRUE(launcher);
ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager();
ASSERT_TRUE(shelf_layout_manager);
ASSERT_TRUE(shelf_layout_manager->status_area_widget());
shelf_layout_manager->status_area_widget()->SetBounds(
gfx::Rect(0, 0, 200, 200));
EXPECT_EQ(200, launcher->status_size().width());
}
// Verifies when the shell is deleted with a full screen window we don't
// crash. This test is here as originally the crash was in ShelfLayoutManager.
TEST_F(ShelfLayoutManagerTest, DontReferenceLauncherAfterDeletion) {
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
// Widget is now owned by the parent window.
widget->Init(params);
widget->SetFullscreen(true);
}
// Various assertions around auto-hide.
TEST_F(ShelfLayoutManagerTest, AutoHide) {
aura::RootWindow* root = Shell::GetPrimaryRootWindow();
aura::test::EventGenerator generator(root, root);
generator.MoveMouseTo(0, 0);
ShelfLayoutManager* shelf = GetShelfLayoutManager();
shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
// Widget is now owned by the parent window.
widget->Init(params);
widget->Maximize();
widget->Show();
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
// LayoutShelf() forces the animation to completion, at which point the
// launcher should go off the screen.
shelf->LayoutShelf();
EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize,
shelf->launcher_widget()->GetWindowBoundsInScreen().y());
EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize,
Shell::GetScreen()->GetDisplayNearestWindow(
root).work_area().bottom());
// Move the mouse to the bottom of the screen.
generator.MoveMouseTo(0, root->bounds().bottom() - 1);
// Shelf should be shown again (but it shouldn't have changed the work area).
SetState(shelf, SHELF_AUTO_HIDE);
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
shelf->LayoutShelf();
EXPECT_EQ(root->bounds().bottom() - shelf->GetIdealBounds().height(),
shelf->launcher_widget()->GetWindowBoundsInScreen().y());
EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize,
Shell::GetScreen()->GetDisplayNearestWindow(
root).work_area().bottom());
// Move mouse back up.
generator.MoveMouseTo(0, 0);
SetState(shelf, SHELF_AUTO_HIDE);
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
shelf->LayoutShelf();
EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize,
shelf->launcher_widget()->GetWindowBoundsInScreen().y());
// Drag mouse to bottom of screen.
generator.PressLeftButton();
generator.MoveMouseTo(0, root->bounds().bottom() - 1);
UpdateAutoHideStateNow();
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
generator.ReleaseLeftButton();
generator.MoveMouseTo(1, root->bounds().bottom() - 1);
UpdateAutoHideStateNow();
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
generator.PressLeftButton();
generator.MoveMouseTo(1, root->bounds().bottom() - 1);
UpdateAutoHideStateNow();
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
}
// Assertions around the lock screen showing.
TEST_F(ShelfLayoutManagerTest, VisibleWhenLockScreenShowing) {
// Since ShelfLayoutManager queries for mouse location, move the mouse so
// it isn't over the shelf.
aura::test::EventGenerator generator(
Shell::GetPrimaryRootWindow(), gfx::Point());
generator.MoveMouseTo(0, 0);
ShelfLayoutManager* shelf = GetShelfLayoutManager();
shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
// Widget is now owned by the parent window.
widget->Init(params);
widget->Maximize();
widget->Show();
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
aura::RootWindow* root = Shell::GetPrimaryRootWindow();
// LayoutShelf() forces the animation to completion, at which point the
// launcher should go off the screen.
shelf->LayoutShelf();
EXPECT_EQ(root->bounds().bottom() - ShelfLayoutManager::kAutoHideSize,
shelf->launcher_widget()->GetWindowBoundsInScreen().y());
aura::Window* lock_container = Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_LockScreenContainer);
views::Widget* lock_widget = new views::Widget;
views::Widget::InitParams lock_params(
views::Widget::InitParams::TYPE_WINDOW);
lock_params.bounds = gfx::Rect(0, 0, 200, 200);
lock_params.parent = lock_container;
// Widget is now owned by the parent window.
lock_widget->Init(lock_params);
lock_widget->Maximize();
lock_widget->Show();
// Lock the screen.
Shell::GetInstance()->delegate()->LockScreen();
shelf->UpdateVisibilityState();
// Showing a widget in the lock screen should force the shelf to be visibile.
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
Shell::GetInstance()->delegate()->UnlockScreen();
shelf->UpdateVisibilityState();
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
}
// Assertions around SetAutoHideBehavior.
TEST_F(ShelfLayoutManagerTest, SetAutoHideBehavior) {
// Since ShelfLayoutManager queries for mouse location, move the mouse so
// it isn't over the shelf.
aura::test::EventGenerator generator(
Shell::GetPrimaryRootWindow(), gfx::Point());
generator.MoveMouseTo(0, 0);
ShelfLayoutManager* shelf = GetShelfLayoutManager();
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
// Widget is now owned by the parent window.
widget->Init(params);
widget->Show();
aura::Window* window = widget->GetNativeWindow();
gfx::Rect display_bounds(
Shell::GetScreen()->GetDisplayNearestWindow(window).bounds());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
widget->Maximize();
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow(
window).work_area().bottom(),
widget->GetWorkAreaBoundsInScreen().bottom());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow(
window).work_area().bottom(),
widget->GetWorkAreaBoundsInScreen().bottom());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow(
window).work_area().bottom(),
widget->GetWorkAreaBoundsInScreen().bottom());
}
// Verifies the shelf is visible when status/launcher is focused.
TEST_F(ShelfLayoutManagerTest, VisibleWhenStatusOrLauncherFocused) {
// Since ShelfLayoutManager queries for mouse location, move the mouse so
// it isn't over the shelf.
aura::test::EventGenerator generator(
Shell::GetPrimaryRootWindow(), gfx::Point());
generator.MoveMouseTo(0, 0);
ShelfLayoutManager* shelf = GetShelfLayoutManager();
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
// Widget is now owned by the parent window.
widget->Init(params);
widget->Show();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
// Focus the launcher. Have to go through the focus cycler as normal focus
// requests to it do nothing.
shelf->launcher()->GetFocusCycler()->RotateFocus(FocusCycler::FORWARD);
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
widget->Activate();
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
// Trying to activate the status should fail, since we only allow activating
// it when the user is using the keyboard (i.e. through FocusCycler).
shelf->status_area_widget()->Activate();
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
shelf->launcher()->GetFocusCycler()->RotateFocus(FocusCycler::FORWARD);
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
}
// Makes sure shelf will be visible when app list opens as shelf is in
// SHELF_VISIBLE state,and toggling app list won't change shelf
// visibility state.
TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfVisibleState) {
Shell* shell = Shell::GetInstance();
internal::RootWindowController* controller =
Shell::GetPrimaryRootWindowController();
ShelfLayoutManager* shelf = shelf_layout_manager();
shelf->LayoutShelf();
controller->SetShelfAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
// Create a normal unmaximized windowm shelf should be visible.
aura::Window* window = CreateTestWindow();
window->SetBounds(gfx::Rect(0, 0, 100, 100));
window->Show();
EXPECT_FALSE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
// Toggle app list to show, and the shelf stays visible.
shell->ToggleAppList();
EXPECT_TRUE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
// Toggle app list to hide, and the shelf stays visible.
shell->ToggleAppList();
EXPECT_FALSE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
}
// Makes sure shelf will be shown with SHELF_AUTO_HIDE_SHOWN state
// when app list opens as shelf is in SHELF_AUTO_HIDE state, and
// toggling app list won't change shelf visibility state.
TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfAutoHideState) {
Shell* shell = Shell::GetInstance();
ShelfLayoutManager* shelf = shelf_layout_manager();
internal::RootWindowController* controller =
Shell::GetPrimaryRootWindowController();
shelf->LayoutShelf();
controller->SetShelfAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
// Create a window and show it in maximized state.
aura::Window* window = CreateTestWindow();
window->SetBounds(gfx::Rect(0, 0, 100, 100));
window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
window->Show();
wm::ActivateWindow(window);
EXPECT_FALSE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
// Toggle app list to show.
shell->ToggleAppList();
// The shelf's auto hide state won't be changed until the timer fires, so
// calling shell->UpdateShelfVisibility() is kind of manually helping it to
// update the state.
shell->UpdateShelfVisibility();
EXPECT_TRUE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
// Toggle app list to hide.
shell->ToggleAppList();
EXPECT_FALSE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
}
// Makes sure shelf will be hidden when app list opens as shelf is in HIDDEN
// state, and toggling app list won't change shelf visibility state.
TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfHiddenState) {
Shell* shell = Shell::GetInstance();
ShelfLayoutManager* shelf = shelf_layout_manager();
// For shelf to be visible, app list is not open in initial state.
shelf->LayoutShelf();
// Create a window and make it full screen.
aura::Window* window = CreateTestWindow();
window->SetBounds(gfx::Rect(0, 0, 100, 100));
window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
window->Show();
wm::ActivateWindow(window);
// App list and shelf is not shown.
EXPECT_FALSE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
// Toggle app list to show.
shell->ToggleAppList();
EXPECT_TRUE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
// Toggle app list to hide.
shell->ToggleAppList();
EXPECT_FALSE(shell->GetAppListTargetVisibility());
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
}
// Tests SHELF_ALIGNMENT_LEFT and SHELF_ALIGNMENT_RIGHT.
TEST_F(ShelfLayoutManagerTest, SetAlignment) {
ShelfLayoutManager* shelf = shelf_layout_manager();
// Force an initial layout.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
shelf->LayoutShelf();
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
gfx::Rect launcher_bounds(
shelf->launcher_widget()->GetWindowBoundsInScreen());
const internal::DisplayManager* manager =
Shell::GetInstance()->display_manager();
gfx::Display display =
manager->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
ASSERT_NE(-1, display.id());
EXPECT_EQ(shelf->GetIdealBounds().width(),
display.GetWorkAreaInsets().left());
EXPECT_GE(
launcher_bounds.width(),
shelf->launcher_widget()->GetContentsView()->GetPreferredSize().width());
EXPECT_EQ(SHELF_ALIGNMENT_LEFT,
Shell::GetInstance()->system_tray()->shelf_alignment());
StatusAreaWidget* status_area_widget = shelf->status_area_widget();
gfx::Rect status_bounds(status_area_widget->GetWindowBoundsInScreen());
EXPECT_GE(status_bounds.width(),
status_area_widget->GetContentsView()->GetPreferredSize().width());
EXPECT_EQ(shelf->GetIdealBounds().width(),
display.GetWorkAreaInsets().left());
EXPECT_EQ(0, display.GetWorkAreaInsets().top());
EXPECT_EQ(0, display.GetWorkAreaInsets().bottom());
EXPECT_EQ(0, display.GetWorkAreaInsets().right());
EXPECT_EQ(display.bounds().x(), launcher_bounds.x());
EXPECT_EQ(display.bounds().y(), launcher_bounds.y());
EXPECT_EQ(display.bounds().height(), launcher_bounds.height());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
display = manager->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
EXPECT_EQ(ShelfLayoutManager::kAutoHideSize,
display.GetWorkAreaInsets().left());
EXPECT_EQ(ShelfLayoutManager::kAutoHideSize, display.work_area().x());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
display = manager->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
launcher_bounds = shelf->launcher_widget()->GetWindowBoundsInScreen();
display = manager->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
ASSERT_NE(-1, display.id());
EXPECT_EQ(shelf->GetIdealBounds().width(),
display.GetWorkAreaInsets().right());
EXPECT_GE(launcher_bounds.width(),
shelf->launcher_widget()->GetContentsView()->GetPreferredSize().width());
EXPECT_EQ(SHELF_ALIGNMENT_RIGHT,
Shell::GetInstance()->system_tray()->shelf_alignment());
status_bounds = gfx::Rect(status_area_widget->GetWindowBoundsInScreen());
EXPECT_GE(status_bounds.width(),
status_area_widget->GetContentsView()->GetPreferredSize().width());
EXPECT_EQ(shelf->GetIdealBounds().width(),
display.GetWorkAreaInsets().right());
EXPECT_EQ(0, display.GetWorkAreaInsets().top());
EXPECT_EQ(0, display.GetWorkAreaInsets().bottom());
EXPECT_EQ(0, display.GetWorkAreaInsets().left());
EXPECT_EQ(display.work_area().right(), launcher_bounds.x());
EXPECT_EQ(display.bounds().y(), launcher_bounds.y());
EXPECT_EQ(display.bounds().height(), launcher_bounds.height());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
display = manager->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow());
EXPECT_EQ(ShelfLayoutManager::kAutoHideSize,
display.GetWorkAreaInsets().right());
EXPECT_EQ(ShelfLayoutManager::kAutoHideSize,
display.bounds().right() - display.work_area().right());
}
TEST_F(ShelfLayoutManagerTest, GestureDrag) {
ShelfLayoutManager* shelf = GetShelfLayoutManager();
internal::RootWindowController* controller =
Shell::GetPrimaryRootWindowController();
controller->SetShelfAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
shelf->LayoutShelf();
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
widget->Init(params);
widget->Show();
widget->Maximize();
aura::Window* window = widget->GetNativeWindow();
gfx::Rect shelf_shown = shelf->launcher_widget()->GetWindowBoundsInScreen();
gfx::Rect bounds_shelf = window->bounds();
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
// Swipe up on the shelf. This should not change any state.
gfx::Point start =
shelf->launcher_widget()->GetWindowBoundsInScreen().CenterPoint();
gfx::Point end(start.x(), start.y() + 100);
// Swipe down on the shelf to hide it.
end.set_y(start.y() + 100);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
EXPECT_NE(bounds_shelf.ToString(), window->bounds().ToString());
EXPECT_NE(shelf_shown.ToString(),
shelf->launcher_widget()->GetWindowBoundsInScreen().ToString());
gfx::Rect bounds_noshelf = window->bounds();
gfx::Rect shelf_hidden = shelf->launcher_widget()->GetWindowBoundsInScreen();
// Swipe up to show the shelf.
generator.GestureScrollSequence(end, start,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
EXPECT_EQ(bounds_shelf.ToString(), window->bounds().ToString());
EXPECT_EQ(shelf_shown.ToString(),
shelf->launcher_widget()->GetWindowBoundsInScreen().ToString());
// Swipe up again. The shelf should hide.
end.set_y(start.y() - 100);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
EXPECT_EQ(shelf_hidden.ToString(),
shelf->launcher_widget()->GetWindowBoundsInScreen().ToString());
// Swipe up yet again to show it.
end.set_y(start.y() + 100);
generator.GestureScrollSequence(end, start,
base::TimeDelta::FromMilliseconds(10), 1);
// Swipe down very little. It shouldn't change any state.
end.set_y(start.y() + shelf_shown.height() * 3 / 10);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(100), 1);
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf->auto_hide_behavior());
EXPECT_EQ(bounds_shelf.ToString(), window->bounds().ToString());
EXPECT_EQ(shelf_shown.ToString(),
shelf->launcher_widget()->GetWindowBoundsInScreen().ToString());
// Swipe down again to hide.
end.set_y(start.y() + 100);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
EXPECT_EQ(bounds_noshelf.ToString(), window->bounds().ToString());
EXPECT_EQ(shelf_hidden.ToString(),
shelf->launcher_widget()->GetWindowBoundsInScreen().ToString());
// Make the window fullscreen.
widget->SetFullscreen(true);
gfx::Rect bounds_fullscreen = window->bounds();
EXPECT_TRUE(widget->IsFullscreen());
EXPECT_NE(bounds_noshelf.ToString(), bounds_fullscreen.ToString());
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
// Swipe-up. This should not change anything.
generator.GestureScrollSequence(end, start,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior());
EXPECT_EQ(bounds_fullscreen.ToString(), window->bounds().ToString());
}
TEST_F(ShelfLayoutManagerTest, GestureRevealsTrayBubble) {
ShelfLayoutManager* shelf = GetShelfLayoutManager();
shelf->LayoutShelf();
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
SystemTray* tray = Shell::GetInstance()->system_tray();
// First, make sure the shelf is visible.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
EXPECT_FALSE(tray->HasSystemBubble());
// Now, drag up on the tray to show the bubble.
gfx::Point start =
shelf->status_area_widget()->GetWindowBoundsInScreen().CenterPoint();
gfx::Point end(start.x(), start.y() - 100);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_TRUE(tray->HasSystemBubble());
tray->CloseBubbleForTest();
RunAllPendingInMessageLoop();
EXPECT_FALSE(tray->HasSystemBubble());
// Drag again, but only a small amount, and slowly. The bubble should not be
// visible.
end.set_y(start.y() - 30);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(500), 100);
EXPECT_FALSE(tray->HasSystemBubble());
// Now, hide the shelf.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
// Start a drag from the bezel, and drag up to show both the shelf and the
// tray bubble.
start.set_y(start.y() + 100);
end.set_y(start.y() - 400);
generator.GestureScrollSequence(start, end,
base::TimeDelta::FromMilliseconds(10), 1);
EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
EXPECT_TRUE(tray->HasSystemBubble());
}
TEST_F(ShelfLayoutManagerTest, ShelfFlickerOnTrayActivation) {
ShelfLayoutManager* shelf = GetShelfLayoutManager();
// Turn on auto-hide for the shelf.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
// Show the status menu. That should make the shelf visible again.
Shell::GetInstance()->accelerator_controller()->PerformAction(
SHOW_SYSTEM_TRAY_BUBBLE, ui::Accelerator());
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
EXPECT_TRUE(Shell::GetInstance()->system_tray()->HasSystemBubble());
// Now activate the tray (using the keyboard, instead of using the mouse to
// make sure the mouse does not alter the auto-hide state in the shelf).
// This should not trigger any auto-hide state change in the shelf.
ShelfLayoutObserverTest observer;
shelf->AddObserver(&observer);
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
generator.PressKey(ui::VKEY_SPACE, 0);
generator.ReleaseKey(ui::VKEY_SPACE, 0);
EXPECT_TRUE(Shell::GetInstance()->system_tray()->HasSystemBubble());
EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
EXPECT_FALSE(observer.changed_auto_hide_state());
shelf->RemoveObserver(&observer);
}
TEST_F(ShelfLayoutManagerTest, WorkAreaChangeWorkspace) {
// Make sure the shelf is always visible.
ShelfLayoutManager* shelf = GetShelfLayoutManager();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
shelf->LayoutShelf();
views::Widget* widget_one = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
widget_one->Init(params);
widget_one->Show();
widget_one->Maximize();
views::Widget* widget_two = new views::Widget;
widget_two->Init(params);
widget_two->Show();
widget_two->Maximize();
widget_two->Activate();
// Both windows are maximized. They should be of the same size.
EXPECT_EQ(widget_one->GetNativeWindow()->bounds().ToString(),
widget_two->GetNativeWindow()->bounds().ToString());
// Now hide the shelf.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
// The active maximized window will get resized to the new work area. However,
// the inactive window should not get resized.
EXPECT_NE(widget_one->GetNativeWindow()->bounds().ToString(),
widget_two->GetNativeWindow()->bounds().ToString());
// Activate the first window. Now, both windows should be of the same size
// again.
widget_one->Activate();
EXPECT_EQ(widget_one->GetNativeWindow()->bounds().ToString(),
widget_two->GetNativeWindow()->bounds().ToString());
// Now show the shelf.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
// The active maximized window will get resized to the new work area. However,
// the inactive window should not get resized.
EXPECT_NE(widget_one->GetNativeWindow()->bounds().ToString(),
widget_two->GetNativeWindow()->bounds().ToString());
// Activate the first window. Now, both windows should be of the same size
// again.
widget_two->Activate();
EXPECT_EQ(widget_one->GetNativeWindow()->bounds().ToString(),
widget_two->GetNativeWindow()->bounds().ToString());
}
// Confirm that the shelf is dimmed only when content is maximized and
// shelf is not autohidden.
TEST_F(ShelfLayoutManagerTest, Dimming) {
shelf_layout_manager()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
scoped_ptr<aura::Window> w1(CreateTestWindow());
w1->Show();
wm::ActivateWindow(w1.get());
// Normal window doesn't dim shelf.
w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
Launcher* launcher = Launcher::ForPrimaryDisplay();
EXPECT_FALSE(launcher->GetDimsShelf());
// Maximized window does.
w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
EXPECT_TRUE(launcher->GetDimsShelf());
// Change back to normal stops dimming.
w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
EXPECT_FALSE(launcher->GetDimsShelf());
// Changing back to maximized dims again.
w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
EXPECT_TRUE(launcher->GetDimsShelf());
// Changing shelf to autohide stops dimming.
shelf_layout_manager()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_FALSE(launcher->GetDimsShelf());
}
// Make sure that the shelf will not hide if the mouse is between a bubble and
// the shelf.
TEST_F(ShelfLayoutManagerTest, BubbleEnlargesShelfMouseHitArea) {
ShelfLayoutManager* shelf = GetShelfLayoutManager();
StatusAreaWidget* status_area_widget =
Shell::GetPrimaryRootWindowController()->status_area_widget();
SystemTray* tray = Shell::GetInstance()->system_tray();
shelf->LayoutShelf();
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
// Make two iterations - first without a message bubble which should make
// the shelf disappear and then with a message bubble which should keep it
// visible.
for (int i = 0; i < 2; i++) {
// Make sure the shelf is visible and position the mouse over it. Then
// allow auto hide.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
EXPECT_FALSE(status_area_widget->IsMessageBubbleShown());
gfx::Point center =
status_area_widget->GetWindowBoundsInScreen().CenterPoint();
generator.MoveMouseTo(center.x(), center.y());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_TRUE(shelf->IsVisible());
if (!i) {
// In our first iteration we make sure there is no bubble.
tray->CloseBubbleForTest();
EXPECT_FALSE(status_area_widget->IsMessageBubbleShown());
} else {
// In our second iteration we show a bubble.
TestItem *item = new TestItem;
tray->AddTrayItem(item);
tray->ShowNotificationView(item);
EXPECT_TRUE(status_area_widget->IsMessageBubbleShown());
}
// Move the pointer over the edge of the shelf.
generator.MoveMouseTo(
center.x(), status_area_widget->GetWindowBoundsInScreen().y() - 8);
shelf->UpdateVisibilityState();
if (i) {
EXPECT_TRUE(shelf->IsVisible());
EXPECT_TRUE(status_area_widget->IsMessageBubbleShown());
} else {
EXPECT_FALSE(shelf->IsVisible());
EXPECT_FALSE(status_area_widget->IsMessageBubbleShown());
}
}
}
} // namespace internal
} // namespace ash