Commit 59556033 authored by pkotwicz@chromium.org's avatar pkotwicz@chromium.org

This CL keeps the alternate style size button pressed during a drag when:

- the user has dragged to the snap left / snap right buttons
- the user has dragged back to the size button

BUG=344411
TEST=AlternateFrameSizeButtonTest.*

Review URL: https://codereview.chromium.org/168943006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@252859 0039d316-1c4b-4281-b951-d872f2087c98
parent 68e7cb52
...@@ -21,10 +21,22 @@ namespace { ...@@ -21,10 +21,22 @@ namespace {
// right. // right.
const int kSetButtonsToSnapModeDelayMs = 150; const int kSetButtonsToSnapModeDelayMs = 150;
// The amount that a user can overshoot the snap left / snap right button and // The amount that a user can overshoot one of the caption buttons while in
// keep the snap left / snap right button pressed. // "snap mode" and keep the button hovered/pressed.
const int kPressedHitBoundsExpandX = 200; const int kMaxOvershootX = 200;
const int kPressedHitBoundsExpandY = 50; const int kMaxOvershootY = 50;
// Returns true if a mouse drag while in "snap mode" at |location_in_screen|
// would hover/press |button| or keep it hovered/pressed.
bool HitTestButton(const ash::FrameCaptionButton* button,
const gfx::Point& location_in_screen) {
gfx::Rect expanded_bounds_in_screen = button->GetBoundsInScreen();
if (button->state() == views::Button::STATE_HOVERED ||
button->state() == views::Button::STATE_PRESSED) {
expanded_bounds_in_screen.Inset(-kMaxOvershootX, -kMaxOvershootY);
}
return expanded_bounds_in_screen.Contains(location_in_screen);
}
} // namespace } // namespace
...@@ -59,8 +71,12 @@ bool AlternateFrameSizeButton::OnMousePressed(const ui::MouseEvent& event) { ...@@ -59,8 +71,12 @@ bool AlternateFrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
} }
bool AlternateFrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) { bool AlternateFrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
UpdatePressedButton(event); UpdateSnapType(event);
FrameCaptionButton::OnMouseDragged(event); // By default a FrameCaptionButton reverts to STATE_NORMAL once the mouse
// leaves its bounds. Skip FrameCaptionButton's handling when
// |in_snap_mode_| == true because we want different behavior.
if (!in_snap_mode_)
FrameCaptionButton::OnMouseDragged(event);
return true; return true;
} }
...@@ -74,6 +90,12 @@ void AlternateFrameSizeButton::OnMouseCaptureLost() { ...@@ -74,6 +90,12 @@ void AlternateFrameSizeButton::OnMouseCaptureLost() {
FrameCaptionButton::OnMouseCaptureLost(); FrameCaptionButton::OnMouseCaptureLost();
} }
void AlternateFrameSizeButton::OnMouseMoved(const ui::MouseEvent& event) {
// Ignore any synthetic mouse moves during a drag.
if (!in_snap_mode_)
FrameCaptionButton::OnMouseMoved(event);
}
void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) { void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
if (event->details().touch_points() > 1) { if (event->details().touch_points() > 1) {
SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES); SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
...@@ -89,7 +111,7 @@ void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) { ...@@ -89,7 +111,7 @@ void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
UpdatePressedButton(*event); UpdateSnapType(*event);
event->SetHandled(); event->SetHandled();
return; return;
} }
...@@ -134,8 +156,7 @@ void AlternateFrameSizeButton::SetButtonsToSnapMode() { ...@@ -134,8 +156,7 @@ void AlternateFrameSizeButton::SetButtonsToSnapMode() {
AlternateFrameSizeButtonDelegate::ANIMATE_YES); AlternateFrameSizeButtonDelegate::ANIMATE_YES);
} }
void AlternateFrameSizeButton::UpdatePressedButton( void AlternateFrameSizeButton::UpdateSnapType(const ui::LocatedEvent& event) {
const ui::LocatedEvent& event) {
if (!in_snap_mode_) { if (!in_snap_mode_) {
// Set the buttons adjacent to the size button to snap left and right early // Set the buttons adjacent to the size button to snap left and right early
// if the user drags past the drag threshold. // if the user drags past the drag threshold.
...@@ -153,16 +174,16 @@ void AlternateFrameSizeButton::UpdatePressedButton( ...@@ -153,16 +174,16 @@ void AlternateFrameSizeButton::UpdatePressedButton(
gfx::Point event_location_in_screen(event.location()); gfx::Point event_location_in_screen(event.location());
views::View::ConvertPointToScreen(this, &event_location_in_screen); views::View::ConvertPointToScreen(this, &event_location_in_screen);
const FrameCaptionButton* to_hover =
GetButtonToHover(event_location_in_screen);
bool press_size_button =
to_hover || HitTestButton(this, event_location_in_screen);
delegate_->SetHoveredAndPressedButtons(
to_hover, press_size_button ? this : NULL);
gfx::Insets pressed_button_hittest_insets(-kPressedHitBoundsExpandY,
-kPressedHitBoundsExpandX,
-kPressedHitBoundsExpandY,
-kPressedHitBoundsExpandX);
const FrameCaptionButton* pressed_button = delegate_->PressButtonAt(
event_location_in_screen, pressed_button_hittest_insets);
snap_type_ = SNAP_NONE; snap_type_ = SNAP_NONE;
if (pressed_button) { if (to_hover) {
switch (pressed_button->icon()) { switch (to_hover->icon()) {
case CAPTION_BUTTON_ICON_LEFT_SNAPPED: case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
snap_type_ = SNAP_LEFT; snap_type_ = SNAP_LEFT;
break; break;
...@@ -170,8 +191,6 @@ void AlternateFrameSizeButton::UpdatePressedButton( ...@@ -170,8 +191,6 @@ void AlternateFrameSizeButton::UpdatePressedButton(
snap_type_ = SNAP_RIGHT; snap_type_ = SNAP_RIGHT;
break; break;
case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE: case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
// snap_type_ = SNAP_NONE
break;
case CAPTION_BUTTON_ICON_MINIMIZE: case CAPTION_BUTTON_ICON_MINIMIZE:
case CAPTION_BUTTON_ICON_CLOSE: case CAPTION_BUTTON_ICON_CLOSE:
case CAPTION_BUTTON_ICON_COUNT: case CAPTION_BUTTON_ICON_COUNT:
...@@ -200,10 +219,22 @@ void AlternateFrameSizeButton::UpdatePressedButton( ...@@ -200,10 +219,22 @@ void AlternateFrameSizeButton::UpdatePressedButton(
} }
} }
const FrameCaptionButton* AlternateFrameSizeButton::GetButtonToHover(
const gfx::Point& event_location_in_screen) const {
const FrameCaptionButton* closest_button = delegate_->GetButtonClosestTo(
event_location_in_screen);
if ((closest_button->icon() == CAPTION_BUTTON_ICON_LEFT_SNAPPED ||
closest_button->icon() == CAPTION_BUTTON_ICON_RIGHT_SNAPPED) &&
HitTestButton(closest_button, event_location_in_screen)) {
return closest_button;
}
return NULL;
}
bool AlternateFrameSizeButton::CommitSnap(const ui::LocatedEvent& event) { bool AlternateFrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
// The position of |event| may be different than the position of the previous // The position of |event| may be different than the position of the previous
// event. // event.
UpdatePressedButton(event); UpdateSnapType(event);
if (in_snap_mode_ && if (in_snap_mode_ &&
(snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) { (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
......
...@@ -26,12 +26,11 @@ class PhantomWindowController; ...@@ -26,12 +26,11 @@ class PhantomWindowController;
// When the mouse is pressed over the size button or the size button is touched: // When the mouse is pressed over the size button or the size button is touched:
// - The minimize and close buttons are set to snap left and snap right // - The minimize and close buttons are set to snap left and snap right
// respectively. // respectively.
// - The pressed button is updated during the drag to reflect the button // - The size button stays pressed while the mouse is over the buttons to snap
// underneath the mouse cursor. (The size button is potentially unpressed). // left and to snap right. The button underneath the mouse is hovered.
// When the drag terminates, the action for the pressed button is executed. // When the drag terminates, the action for the button underneath the mouse
// For the sake of simplicity, the size button is the event handler for a click // is executed. For the sake of simplicity, the size button is the event
// starting on the size button and the entire drag (including when the size // handler for a click starting on the size button and the entire drag.
// button is unpressed).
class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton { class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton {
public: public:
AlternateFrameSizeButton(views::ButtonListener* listener, AlternateFrameSizeButton(views::ButtonListener* listener,
...@@ -45,6 +44,7 @@ class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton { ...@@ -45,6 +44,7 @@ class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton {
virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
virtual void OnMouseCaptureLost() OVERRIDE; virtual void OnMouseCaptureLost() OVERRIDE;
virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
void set_delay_to_set_buttons_to_snap_mode(int delay_ms) { void set_delay_to_set_buttons_to_snap_mode(int delay_ms) {
...@@ -58,8 +58,14 @@ class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton { ...@@ -58,8 +58,14 @@ class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton {
// Sets the buttons adjacent to the size button to snap left and right. // Sets the buttons adjacent to the size button to snap left and right.
void SetButtonsToSnapMode(); void SetButtonsToSnapMode();
// Updates the pressed button based on |event_location|. // Updates |snap_type_|, whether the size button is pressed and whether any
void UpdatePressedButton(const ui::LocatedEvent& event); // other buttons are hovered.
void UpdateSnapType(const ui::LocatedEvent& event);
// Returns the button which should be hovered (if any) while in "snap mode"
// for |event_location_in_screen|.
const FrameCaptionButton* GetButtonToHover(
const gfx::Point& event_location_in_screen) const;
// Snaps |frame_| according to |snap_type_|. Returns true if |frame_| was // Snaps |frame_| according to |snap_type_|. Returns true if |frame_| was
// snapped. // snapped.
...@@ -92,8 +98,9 @@ class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton { ...@@ -92,8 +98,9 @@ class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton {
// right. // right.
bool in_snap_mode_; bool in_snap_mode_;
// The action of the currently pressed button. If |snap_type_| == SNAP_NONE, // The action to execute when the drag/click is ended. If
// the size button's default action is run when clicked. // |snap_type_| == SNAP_NONE, the size button's default action is run when the
// drag/click is ended.
SnapType snap_type_; SnapType snap_type_;
// Displays a preview of how the window's bounds will change as a result of // Displays a preview of how the window's bounds will change as a result of
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
namespace gfx { namespace gfx {
class Insets; class Insets;
class Point; class Point;
class Vector2d;
} }
namespace ash { namespace ash {
...@@ -38,15 +39,15 @@ class ASH_EXPORT AlternateFrameSizeButtonDelegate { ...@@ -38,15 +39,15 @@ class ASH_EXPORT AlternateFrameSizeButtonDelegate {
CaptionButtonIcon right_button_action, CaptionButtonIcon right_button_action,
Animate animate) = 0; Animate animate) = 0;
// Presses the button at |position_in_screen| and unpresses any other pressed // Returns the button closest to |position_in_screen|.
// caption buttons. virtual const FrameCaptionButton* GetButtonClosestTo(
// |pressed_button_hittest_insets| indicates how much the hittest insets for const gfx::Point& position_in_screen) const = 0;
// the currently pressed button should be expanded if no button was found at
// |position_in_screen| using the normal button hittest insets. // Sets |to_hover| and |to_pressed| to STATE_HOVERED and STATE_PRESSED
// Returns the button which was pressed. // respectively. All other buttons are to set to STATE_NORMAL.
virtual const FrameCaptionButton* PressButtonAt( virtual void SetHoveredAndPressedButtons(
const gfx::Point& position_in_screen, const FrameCaptionButton* to_hover,
const gfx::Insets& pressed_button_hittest_insets) const = 0; const FrameCaptionButton* to_press) = 0;
protected: protected:
virtual ~AlternateFrameSizeButtonDelegate() {} virtual ~AlternateFrameSizeButtonDelegate() {}
......
...@@ -322,11 +322,11 @@ TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) { ...@@ -322,11 +322,11 @@ TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) {
EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
// Dragging the mouse over the minimize button should press the minimize // Dragging the mouse over the minimize button should hover the minimize
// button and the minimize and close button icons should stay changed. // button and the minimize and close button icons should stay changed.
generator.MoveMouseTo(CenterPointInScreen(minimize_button())); generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
EXPECT_EQ(views::Button::STATE_PRESSED, minimize_button()->state()); EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state()); EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, close_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_LEFT_SNAPPED, minimize_button()->icon());
EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
...@@ -375,5 +375,43 @@ TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) { ...@@ -375,5 +375,43 @@ TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) {
EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
} }
// Test that the size button is pressed whenever the snap left/right buttons
// are hovered.
TEST_F(AlternateFrameSizeButtonTest, SizeButtonPressedWhenSnapButtonHovered) {
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 (snap left button) should hover
// the minimize button and keep the size button pressed.
generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
// Moving the mouse far away from the caption buttons and then moving it over
// the close button (snap right button) should hover the close button and
// keep the size button pressed.
const gfx::Rect& kWorkAreaBoundsInScreen =
ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
EXPECT_TRUE(AllButtonsInNormalState());
generator.MoveMouseTo(CenterPointInScreen(close_button()));
EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
EXPECT_EQ(views::Button::STATE_HOVERED, close_button()->state());
}
} // namespace test } // namespace test
} // namespace ash } // namespace ash
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "ash/wm/caption_buttons/frame_caption_button_container_view.h" #include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
#include <cmath>
#include "ash/ash_switches.h" #include "ash/ash_switches.h"
#include "ash/metrics/user_metrics_recorder.h" #include "ash/metrics/user_metrics_recorder.h"
#include "ash/shell.h" #include "ash/shell.h"
...@@ -262,43 +264,51 @@ void FrameCaptionButtonContainerView::SetButtonIcons( ...@@ -262,43 +264,51 @@ void FrameCaptionButtonContainerView::SetButtonIcons(
SetButtonIcon(close_button_, close_button_icon, animate); SetButtonIcon(close_button_, close_button_icon, animate);
} }
const FrameCaptionButton* const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo(
FrameCaptionButtonContainerView::PressButtonAt( const gfx::Point& position_in_screen) const {
const gfx::Point& position_in_screen, // Since the buttons all have the same size, the closest button is the button
const gfx::Insets& pressed_hittest_outer_insets) const { // with the center point closest to |position_in_screen|.
DCHECK(switches::UseAlternateFrameCaptionButtonStyle()); // TODO(pkotwicz): Make the caption buttons not overlap.
gfx::Point position(position_in_screen); gfx::Point position(position_in_screen);
views::View::ConvertPointFromScreen(this, &position); views::View::ConvertPointFromScreen(this, &position);
FrameCaptionButton* buttons[] = { FrameCaptionButton* buttons[] = {
close_button_, size_button_, minimize_button_ minimize_button_, size_button_, close_button_
}; };
FrameCaptionButton* pressed_button = NULL; int min_squared_distance = INT_MAX;
FrameCaptionButton* closest_button = NULL;
for (size_t i = 0; i < arraysize(buttons); ++i) { for (size_t i = 0; i < arraysize(buttons); ++i) {
FrameCaptionButton* button = buttons[i]; FrameCaptionButton* button = buttons[i];
if (!button->visible()) if (!button->visible())
continue; continue;
if (button->state() == views::Button::STATE_PRESSED) { gfx::Point center_point = button->bounds().CenterPoint();
gfx::Rect expanded_bounds = button->bounds(); int squared_distance = static_cast<int>(
expanded_bounds.Inset(pressed_hittest_outer_insets); pow(static_cast<double>(position.x() - center_point.x()), 2) +
if (expanded_bounds.Contains(position)) { pow(static_cast<double>(position.y() - center_point.y()), 2));
pressed_button = button; if (squared_distance < min_squared_distance) {
// Do not break in order to give preference to buttons which are min_squared_distance = squared_distance;
// closer to |position_in_screen| than the currently pressed button. closest_button = button;
// TODO(pkotwicz): Make the caption buttons not overlap.
}
} else if (ConvertPointToViewAndHitTest(this, button, position)) {
pressed_button = button;
break;
} }
} }
return closest_button;
}
void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
const FrameCaptionButton* to_hover,
const FrameCaptionButton* to_press) {
FrameCaptionButton* buttons[] = {
minimize_button_, size_button_, close_button_
};
for (size_t i = 0; i < arraysize(buttons); ++i) { for (size_t i = 0; i < arraysize(buttons); ++i) {
buttons[i]->SetState(buttons[i] == pressed_button ? FrameCaptionButton* button = buttons[i];
views::Button::STATE_PRESSED : views::Button::STATE_NORMAL); views::Button::ButtonState new_state = views::Button::STATE_NORMAL;
if (button == to_hover)
new_state = views::Button::STATE_HOVERED;
else if (button == to_press)
new_state = views::Button::STATE_PRESSED;
button->SetState(new_state);
} }
return pressed_button;
} }
FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
......
...@@ -124,9 +124,11 @@ class ASH_EXPORT FrameCaptionButtonContainerView ...@@ -124,9 +124,11 @@ class ASH_EXPORT FrameCaptionButtonContainerView
virtual void SetButtonIcons(CaptionButtonIcon minimize_button_icon, virtual void SetButtonIcons(CaptionButtonIcon minimize_button_icon,
CaptionButtonIcon close_button_icon, CaptionButtonIcon close_button_icon,
Animate animate) OVERRIDE; Animate animate) OVERRIDE;
virtual const FrameCaptionButton* PressButtonAt( virtual const FrameCaptionButton* GetButtonClosestTo(
const gfx::Point& position_in_screen, const gfx::Point& position_in_screen) const OVERRIDE;
const gfx::Insets& pressed_hittest_outer_insets) const OVERRIDE; virtual void SetHoveredAndPressedButtons(
const FrameCaptionButton* to_hover,
const FrameCaptionButton* to_press) OVERRIDE;
// The widget that the buttons act on. // The widget that the buttons act on.
views::Widget* frame_; views::Widget* frame_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment