blob: 75c0f50604b8c143d04a4ede8fe21e1b17ddeb1e [file] [log] [blame]
// Copyright 2013 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/caption_buttons/alternate_frame_size_button.h"
#include "ash/ash_switches.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/caption_buttons/frame_caption_button.h"
#include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
#include "ash/wm/window_state.h"
#include "ash/wm/workspace/snap_sizer.h"
#include "base/command_line.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/window.h"
#include "ui/events/gestures/gesture_configuration.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
namespace test {
namespace {
class TestWidgetDelegate : public views::WidgetDelegateView {
public:
TestWidgetDelegate() {}
virtual ~TestWidgetDelegate() {}
// Overridden from views::WidgetDelegate:
virtual views::View* GetContentsView() OVERRIDE {
return this;
}
virtual bool CanResize() const OVERRIDE {
return true;
}
virtual bool CanMaximize() const OVERRIDE {
return true;
}
ash::FrameCaptionButtonContainerView* caption_button_container() {
return caption_button_container_;
}
private:
// Overridden from views::View:
virtual void Layout() OVERRIDE {
caption_button_container_->Layout();
// Right align the caption button container.
gfx::Size preferred_size = caption_button_container_->GetPreferredSize();
caption_button_container_->SetBounds(width() - preferred_size.width(), 0,
preferred_size.width(), preferred_size.height());
}
virtual void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) OVERRIDE {
if (details.is_add && details.child == this) {
caption_button_container_ = new FrameCaptionButtonContainerView(
GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
AddChildView(caption_button_container_);
}
}
// Not owned.
ash::FrameCaptionButtonContainerView* caption_button_container_;
DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
};
} // namespace
class AlternateFrameSizeButtonTest : public AshTestBase {
public:
AlternateFrameSizeButtonTest() {}
virtual ~AlternateFrameSizeButtonTest() {}
// Returns the center point of |view| in screen coordinates.
gfx::Point CenterPointInScreen(views::View* view) {
return view->GetBoundsInScreen().CenterPoint();
}
// Returns true if the window is snapped to |edge|.
bool IsSnapped(internal::SnapSizer::Edge edge) const {
ash::wm::WindowShowType show_type = window_state()->window_show_type();
if (edge == internal::SnapSizer::LEFT_EDGE)
return show_type == ash::wm::SHOW_TYPE_LEFT_SNAPPED;
else
return show_type == ash::wm::SHOW_TYPE_RIGHT_SNAPPED;
}
// Returns true if all three buttons are in the normal state.
bool AllButtonsInNormalState() const {
return minimize_button_->state() == views::Button::STATE_NORMAL &&
size_button_->state() == views::Button::STATE_NORMAL &&
close_button_->state() == views::Button::STATE_NORMAL;
}
// Creates a widget with |delegate|. The returned widget takes ownership of
// |delegate|.
views::Widget* CreateWidget(views::WidgetDelegate* delegate) {
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.context = CurrentContext();
params.delegate = delegate;
params.bounds = gfx::Rect(10, 10, 100, 100);
widget->Init(params);
widget->Show();
return widget;
}
// AshTestBase overrides:
virtual void SetUp() OVERRIDE {
AshTestBase::SetUp();
CommandLine* command_line = CommandLine::ForCurrentProcess();
command_line->AppendSwitch(
switches::kAshEnableAlternateFrameCaptionButtonStyle);
CHECK(!command_line->HasSwitch(switches::kAshMultipleSnapWindowWidths));
TestWidgetDelegate* delegate = new TestWidgetDelegate();
window_state_ = ash::wm::GetWindowState(
CreateWidget(delegate)->GetNativeWindow());
FrameCaptionButtonContainerView::TestApi test(
delegate->caption_button_container());
minimize_button_ = test.minimize_button();
size_button_ = test.size_button();
static_cast<AlternateFrameSizeButton*>(
size_button_)->set_delay_to_set_buttons_to_snap_mode(0);
close_button_ = test.close_button();
}
ash::wm::WindowState* window_state() { return window_state_; }
const ash::wm::WindowState* window_state() const { return window_state_; }
FrameCaptionButton* minimize_button() { return minimize_button_; }
FrameCaptionButton* size_button() { return size_button_; }
FrameCaptionButton* close_button() { return close_button_; }
private:
// Not owned.
ash::wm::WindowState* window_state_;
FrameCaptionButton* minimize_button_;
FrameCaptionButton* size_button_;
FrameCaptionButton* close_button_;
DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButtonTest);
};
// Tests that pressing the left mouse button or tapping down on the size button
// puts the button into the pressed state.
TEST_F(AlternateFrameSizeButtonTest, PressedState) {
aura::test::EventGenerator& generator = GetEventGenerator();
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressLeftButton();
EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
generator.ReleaseLeftButton();
RunAllPendingInMessageLoop();
EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressTouchId(3);
EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
generator.ReleaseTouchId(3);
RunAllPendingInMessageLoop();
EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
}
// Tests that clicking on the size button toggles between the maximized and
// normal state.
TEST_F(AlternateFrameSizeButtonTest, ClickSizeButtonTogglesMaximize) {
EXPECT_FALSE(window_state()->IsMaximized());
aura::test::EventGenerator& generator = GetEventGenerator();
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.ClickLeftButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(window_state()->IsMaximized());
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.ClickLeftButton();
RunAllPendingInMessageLoop();
EXPECT_FALSE(window_state()->IsMaximized());
generator.GestureTapAt(CenterPointInScreen(size_button()));
RunAllPendingInMessageLoop();
EXPECT_TRUE(window_state()->IsMaximized());
generator.GestureTapAt(CenterPointInScreen(size_button()));
RunAllPendingInMessageLoop();
EXPECT_FALSE(window_state()->IsMaximized());
}
// Test that clicking + dragging to a button adjacent to the size button snaps
// the window left or right.
TEST_F(AlternateFrameSizeButtonTest, ButtonDrag) {
EXPECT_TRUE(window_state()->IsNormalShowState());
EXPECT_FALSE(window_state()->IsSnapped());
// 1) Test by dragging the mouse.
// Snap right.
aura::test::EventGenerator& generator = GetEventGenerator();
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressLeftButton();
generator.MoveMouseTo(CenterPointInScreen(close_button()));
generator.ReleaseLeftButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
// Snap left.
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressLeftButton();
generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
generator.ReleaseLeftButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
// 2) Test with scroll gestures.
// Snap right.
generator.GestureScrollSequence(
CenterPointInScreen(size_button()),
CenterPointInScreen(close_button()),
base::TimeDelta::FromMilliseconds(100),
3);
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
// Snap left.
generator.GestureScrollSequence(
CenterPointInScreen(size_button()),
CenterPointInScreen(minimize_button()),
base::TimeDelta::FromMilliseconds(100),
3);
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
// 3) Test with tap gestures.
const int touch_default_radius =
ui::GestureConfiguration::default_radius();
ui::GestureConfiguration::set_default_radius(0);
// Snap right.
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
// Snap left.
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
ui::GestureConfiguration::set_default_radius(touch_default_radius);
}
// Test that clicking, dragging, and overshooting the minimize button a bit
// horizontally still snaps the window left.
TEST_F(AlternateFrameSizeButtonTest, SnapLeftOvershootMinimize) {
EXPECT_TRUE(window_state()->IsNormalShowState());
EXPECT_FALSE(window_state()->IsSnapped());
aura::test::EventGenerator& generator = GetEventGenerator();
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressLeftButton();
// Move to the minimize button.
generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
// Overshoot the minimize button.
generator.MoveMouseBy(-minimize_button()->width(), 0);
generator.ReleaseLeftButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
}
// Test that right clicking the size button has no effect.
TEST_F(AlternateFrameSizeButtonTest, RightMouseButton) {
EXPECT_TRUE(window_state()->IsNormalShowState());
EXPECT_FALSE(window_state()->IsSnapped());
aura::test::EventGenerator& generator = GetEventGenerator();
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressRightButton();
generator.ReleaseRightButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(window_state()->IsNormalShowState());
EXPECT_FALSE(window_state()->IsSnapped());
}
// Test that upon releasing the mouse button after having pressed the size
// button
// - The state of all the caption buttons is reset.
// - The icon displayed by all of the caption buttons is reset.
TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) {
EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
EXPECT_TRUE(AllButtonsInNormalState());
// Pressing the size button should result in the size button being pressed and
// the minimize and close button icons changing.
aura::test::EventGenerator& generator = GetEventGenerator();
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressLeftButton();
EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
// Dragging the mouse over the minimize button should press the minimize
// button and the minimize and close button icons should stay changed.
generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
EXPECT_EQ(views::Button::STATE_PRESSED, minimize_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
// Release the mouse, snapping the window left.
generator.ReleaseLeftButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
// None of the buttons should stay pressed and the buttons should have their
// regular icons.
EXPECT_TRUE(AllButtonsInNormalState());
EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
// Repeat test but release button where it does not affect the window's state
// because the code path is different.
generator.MoveMouseTo(CenterPointInScreen(size_button()));
generator.PressLeftButton();
EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
const gfx::Rect& kWorkAreaBoundsInScreen =
ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
// None of the buttons should be pressed because we are really far away from
// any of the caption buttons. The minimize and close button icons should
// be changed because the mouse is pressed.
EXPECT_TRUE(AllButtonsInNormalState());
EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
// Release the mouse. The window should stay snapped left.
generator.ReleaseLeftButton();
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
// The buttons should stay unpressed and the buttons should now have their
// regular icons.
EXPECT_TRUE(AllButtonsInNormalState());
EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
}
} // namespace test
} // namespace ash