Commit dd378ab0 authored by Xiaoqian Dai's avatar Xiaoqian Dai Committed by Commit Bot

Move fling/swipe event handling logic to WindowResizer.

It's needed for crbug.com/874510.
Since we have different WindowResizer for laptop mode and tablet mode,
and they will have different behavior for fling/swipe events, we should
let WindowResizer to do the fling/swipe event handling. So move the
logic to WindowResizer.

No functionality is changed in this CL. I'll add special logic for
tablet mode window fling/swipe in a following-up CL.

Bug: 874510
Change-Id: I91b917e8ec947681db12d5be8b8c8b982299af9a
Reviewed-on: https://chromium-review.googlesource.com/c/1262457
Commit-Queue: Xiaoqian Dai <xdai@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597380}
parent 92f861e1
......@@ -43,6 +43,8 @@ void DefaultWindowResizer::RevertDrag() {
window_state_->SetRestoreBoundsInScreen(details().restore_bounds);
}
void DefaultWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {}
DefaultWindowResizer::DefaultWindowResizer(wm::WindowState* window_state)
: WindowResizer(window_state), did_move_or_resize_(false) {
DCHECK(details().is_resizable);
......
......@@ -33,6 +33,7 @@ class ASH_EXPORT DefaultWindowResizer : public WindowResizer {
void Drag(const gfx::Point& location, int event_flags) override;
void CompleteDrag() override;
void RevertDrag() override;
void FlingOrSwipe(ui::GestureEvent* event) override;
private:
explicit DefaultWindowResizer(wm::WindowState* window_state);
......
......@@ -70,52 +70,7 @@ void DragWindowResizer::Drag(const gfx::Point& location, int event_flags) {
void DragWindowResizer::CompleteDrag() {
next_window_resizer_->CompleteDrag();
GetTarget()->layer()->SetOpacity(details().initial_opacity);
drag_window_controller_.reset();
// TODO(malaykeshav) - This is temporary fix/workaround that keeps performance
// but may not give the best UI while dragging. See https://crbug/834114
RecursiveSchedulePainter(GetTarget()->layer());
// Check if the destination is another display.
gfx::Point last_mouse_location_in_screen = last_mouse_location_;
::wm::ConvertPointToScreen(GetTarget()->parent(),
&last_mouse_location_in_screen);
display::Screen* screen = display::Screen::GetScreen();
const display::Display dst_display =
screen->GetDisplayNearestPoint(last_mouse_location_in_screen);
if (dst_display.id() !=
screen->GetDisplayNearestWindow(GetTarget()->GetRootWindow()).id()) {
// Adjust the size and position so that it doesn't exceed the size of
// work area.
const gfx::Size& size = dst_display.work_area().size();
gfx::Rect bounds = GetTarget()->bounds();
if (bounds.width() > size.width()) {
int diff = bounds.width() - size.width();
bounds.set_x(bounds.x() + diff / 2);
bounds.set_width(size.width());
}
if (bounds.height() > size.height())
bounds.set_height(size.height());
gfx::Rect dst_bounds = bounds;
::wm::ConvertRectToScreen(GetTarget()->parent(), &dst_bounds);
// Adjust the position so that the cursor is on the window.
if (!dst_bounds.Contains(last_mouse_location_in_screen)) {
if (last_mouse_location_in_screen.x() < dst_bounds.x())
dst_bounds.set_x(last_mouse_location_in_screen.x());
else if (last_mouse_location_in_screen.x() > dst_bounds.right())
dst_bounds.set_x(last_mouse_location_in_screen.x() -
dst_bounds.width());
}
ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(dst_display.bounds(),
&dst_bounds);
GetTarget()->SetBoundsInScreen(dst_bounds, dst_display);
}
EndDragImpl();
}
void DragWindowResizer::RevertDrag() {
......@@ -125,6 +80,11 @@ void DragWindowResizer::RevertDrag() {
GetTarget()->layer()->SetOpacity(details().initial_opacity);
}
void DragWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {
next_window_resizer_->FlingOrSwipe(event);
EndDragImpl();
}
DragWindowResizer::DragWindowResizer(
std::unique_ptr<WindowResizer> next_window_resizer,
wm::WindowState* window_state)
......@@ -175,4 +135,52 @@ bool DragWindowResizer::ShouldAllowMouseWarp() {
wm::IsWindowUserPositionable(GetTarget());
}
void DragWindowResizer::EndDragImpl() {
GetTarget()->layer()->SetOpacity(details().initial_opacity);
drag_window_controller_.reset();
// TODO(malaykeshav) - This is temporary fix/workaround that keeps performance
// but may not give the best UI while dragging. See https://crbug/834114
RecursiveSchedulePainter(GetTarget()->layer());
// Check if the destination is another display.
gfx::Point last_mouse_location_in_screen = last_mouse_location_;
::wm::ConvertPointToScreen(GetTarget()->parent(),
&last_mouse_location_in_screen);
display::Screen* screen = display::Screen::GetScreen();
const display::Display dst_display =
screen->GetDisplayNearestPoint(last_mouse_location_in_screen);
if (dst_display.id() !=
screen->GetDisplayNearestWindow(GetTarget()->GetRootWindow()).id()) {
// Adjust the size and position so that it doesn't exceed the size of
// work area.
const gfx::Size& size = dst_display.work_area().size();
gfx::Rect bounds = GetTarget()->bounds();
if (bounds.width() > size.width()) {
int diff = bounds.width() - size.width();
bounds.set_x(bounds.x() + diff / 2);
bounds.set_width(size.width());
}
if (bounds.height() > size.height())
bounds.set_height(size.height());
gfx::Rect dst_bounds = bounds;
::wm::ConvertRectToScreen(GetTarget()->parent(), &dst_bounds);
// Adjust the position so that the cursor is on the window.
if (!dst_bounds.Contains(last_mouse_location_in_screen)) {
if (last_mouse_location_in_screen.x() < dst_bounds.x())
dst_bounds.set_x(last_mouse_location_in_screen.x());
else if (last_mouse_location_in_screen.x() > dst_bounds.right())
dst_bounds.set_x(last_mouse_location_in_screen.x() -
dst_bounds.width());
}
ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(dst_display.bounds(),
&dst_bounds);
GetTarget()->SetBoundsInScreen(dst_bounds, dst_display);
}
}
} // namespace ash
......@@ -33,6 +33,7 @@ class ASH_EXPORT DragWindowResizer : public WindowResizer {
void Drag(const gfx::Point& location, int event_flags) override;
void CompleteDrag() override;
void RevertDrag() override;
void FlingOrSwipe(ui::GestureEvent* event) override;
WindowResizer* next_window_resizer_for_testing() {
return next_window_resizer_.get();
......@@ -50,6 +51,10 @@ class ASH_EXPORT DragWindowResizer : public WindowResizer {
// Returns true if we should allow the mouse pointer to warp.
bool ShouldAllowMouseWarp();
// Do the real work of ending a drag. If the drag ends in a different display,
// move the dragged window to that display.
void EndDragImpl();
std::unique_ptr<WindowResizer> next_window_resizer_;
// Shows a semi-transparent image of the window being dragged.
......
......@@ -78,4 +78,12 @@ void TabletModeBrowserWindowDragController::RevertDrag() {
previous_location_in_screen_);
}
void TabletModeBrowserWindowDragController::FlingOrSwipe(
ui::GestureEvent* event) {
// TODO(xdai): Add special logic for fling events.
drag_delegate_->EndWindowDrag(
wm::WmToplevelWindowEventHandler::DragResult::SUCCESS,
previous_location_in_screen_);
}
} // namespace ash
......@@ -34,6 +34,7 @@ class TabletModeBrowserWindowDragController : public WindowResizer {
void Drag(const gfx::Point& location_in_parent, int event_flags) override;
void CompleteDrag() override;
void RevertDrag() override;
void FlingOrSwipe(ui::GestureEvent* event) override;
TabletModeWindowDragDelegate* drag_delegate_for_testing() {
return drag_delegate_.get();
......
......@@ -21,6 +21,10 @@ namespace gfx {
class Rect;
}
namespace ui {
class GestureEvent;
}
namespace ash {
// WindowResizer is used by ToplevelWindowEventFilter to handle dragging, moving
......@@ -58,6 +62,9 @@ class ASH_EXPORT WindowResizer {
// Reverts the drag.
virtual void RevertDrag() = 0;
// Flings or Swipes to end the drag.
virtual void FlingOrSwipe(ui::GestureEvent* event) = 0;
// Returns the target window the resizer was created for.
aura::Window* GetTarget() const;
......
......@@ -19,11 +19,6 @@
#include "ui/base/hit_test.h"
#include "ui/events/event.h"
namespace {
const double kMinHorizVelocityForWindowSwipe = 1100;
const double kMinVertVelocityForWindowMinimize = 1000;
}
namespace ash {
namespace wm {
......@@ -331,64 +326,9 @@ void WmToplevelWindowEventHandler::OnGestureEvent(ui::GestureEvent* event,
event->StopPropagation();
return;
case ui::ET_SCROLL_FLING_START:
CompleteDrag(DragResult::SUCCESS);
// TODO(xdai): We'll do special handling in tablet mode later.
if (Shell::Get()
->tablet_mode_controller()
->IsTabletModeWindowManagerEnabled()) {
return;
}
// TODO(pkotwicz): Fix tests which inadvertently start flings and check
// window_resizer_->IsMove() instead of the hittest component at |event|'s
// location.
if (GetNonClientComponent(target, event->location()) != HTCAPTION ||
!GetWindowState(target)->IsNormalOrSnapped()) {
return;
}
if (event->details().velocity_y() > kMinVertVelocityForWindowMinimize) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::MINIMIZED);
} else if (event->details().velocity_y() <
-kMinVertVelocityForWindowMinimize) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::MAXIMIZED);
} else if (event->details().velocity_x() >
kMinHorizVelocityForWindowSwipe) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::RIGHT_SNAPPED);
} else if (event->details().velocity_x() <
-kMinHorizVelocityForWindowSwipe) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::LEFT_SNAPPED);
}
event->StopPropagation();
return;
FALLTHROUGH;
case ui::ET_GESTURE_SWIPE:
DCHECK_GT(event->details().touch_points(), 0);
if (event->details().touch_points() == 1)
return;
if (!GetWindowState(target)->IsNormalOrSnapped())
return;
CompleteDrag(DragResult::SUCCESS);
if (event->details().swipe_down()) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::MINIMIZED);
} else if (event->details().swipe_up()) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::MAXIMIZED);
} else if (event->details().swipe_right()) {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::RIGHT_SNAPPED);
} else {
SetWindowStateTypeFromGesture(target,
mojom::WindowStateType::LEFT_SNAPPED);
}
event->StopPropagation();
HandleFlingOrSwipe(event);
return;
default:
return;
......@@ -409,7 +349,6 @@ bool WmToplevelWindowEventHandler::AttemptToStartDrag(
}
end_closure_ = std::move(end_closure);
pre_drag_window_bounds_ = window->bounds();
in_gesture_drag_ = (source == ::wm::WINDOW_MOVE_SOURCE_TOUCH);
return true;
}
......@@ -554,44 +493,17 @@ void WmToplevelWindowEventHandler::HandleCaptureLost(ui::LocatedEvent* event) {
}
}
void WmToplevelWindowEventHandler::SetWindowStateTypeFromGesture(
aura::Window* window,
mojom::WindowStateType new_state_type) {
wm::WindowState* window_state = GetWindowState(window);
// TODO(oshima): Move extra logic (set_unminimize_to_restore_bounds,
// SetRestoreBoundsInParent) that modifies the window state
// into WindowState.
switch (new_state_type) {
case mojom::WindowStateType::MINIMIZED:
if (window_state->CanMinimize()) {
window_state->Minimize();
window_state->set_unminimize_to_restore_bounds(true);
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
}
break;
case mojom::WindowStateType::MAXIMIZED:
if (window_state->CanMaximize()) {
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
window_state->Maximize();
}
break;
case mojom::WindowStateType::LEFT_SNAPPED:
if (window_state->CanSnap()) {
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
window_state->OnWMEvent(&event);
}
break;
case mojom::WindowStateType::RIGHT_SNAPPED:
if (window_state->CanSnap()) {
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
const wm::WMEvent event(wm::WM_EVENT_SNAP_RIGHT);
window_state->OnWMEvent(&event);
}
break;
default:
NOTREACHED();
}
void WmToplevelWindowEventHandler::HandleFlingOrSwipe(ui::GestureEvent* event) {
UpdateGestureTarget(nullptr);
if (!window_resizer_)
return;
std::unique_ptr<ScopedWindowResizer> resizer(std::move(window_resizer_));
resizer->resizer()->FlingOrSwipe(event);
first_finger_hittest_ = HTNOWHERE;
in_gesture_drag_ = false;
if (end_closure_)
std::move(end_closure_).Run(DragResult::SUCCESS);
}
void WmToplevelWindowEventHandler::ResizerWindowDestroyed() {
......
......@@ -111,10 +111,8 @@ class ASH_EXPORT WmToplevelWindowEventHandler
// Called when mouse capture is lost.
void HandleCaptureLost(ui::LocatedEvent* event);
// Sets |window|'s state type to |new_state_type|. Called after the drag has
// been completed for fling gestures.
void SetWindowStateTypeFromGesture(aura::Window* window,
mojom::WindowStateType new_state_type);
// Handles the gesture fling or swipe event.
void HandleFlingOrSwipe(ui::GestureEvent* event);
// Invoked from ScopedWindowResizer if the window is destroyed.
void ResizerWindowDestroyed();
......@@ -137,11 +135,6 @@ class ASH_EXPORT WmToplevelWindowEventHandler
// screen.
gfx::Point first_finger_touch_point_;
// The window bounds when the drag was started. When a window is minimized,
// maximized or snapped via a swipe/fling gesture, the restore bounds should
// be set to the bounds of the window when the drag was started.
gfx::Rect pre_drag_window_bounds_;
// Is a window move/resize in progress because of gesture events?
bool in_gesture_drag_ = false;
......
......@@ -21,6 +21,7 @@
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_positioning_utils.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "ash/wm/workspace/phantom_window_controller.h"
#include "ash/wm/workspace/two_step_edge_cycler.h"
......@@ -41,6 +42,9 @@
namespace {
constexpr double kMinHorizVelocityForWindowSwipe = 1100;
constexpr double kMinVertVelocityForWindowMinimize = 1000;
// Returns true if |window| can be dragged from the top of the screen in tablet
// mode.
bool CanDragInTabletMode(aura::Window* window, int window_component) {
......@@ -540,6 +544,67 @@ void WorkspaceWindowResizer::RevertDrag() {
}
}
void WorkspaceWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {
if (event->type() != ui::ET_SCROLL_FLING_START &&
event->type() != ui::ET_GESTURE_SWIPE) {
return;
}
if (event->type() == ui::ET_SCROLL_FLING_START) {
CompleteDrag();
// TODO(pkotwicz): Fix tests which inadvertently start flings and check
// window_resizer_->IsMove() instead of the hittest component at |event|'s
// location.
if (wm::GetNonClientComponent(GetTarget(), event->location()) !=
HTCAPTION ||
!wm::GetWindowState(GetTarget())->IsNormalOrSnapped()) {
return;
}
if (event->details().velocity_y() > kMinVertVelocityForWindowMinimize) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::MINIMIZED);
} else if (event->details().velocity_y() <
-kMinVertVelocityForWindowMinimize) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::MAXIMIZED);
} else if (event->details().velocity_x() >
kMinHorizVelocityForWindowSwipe) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::RIGHT_SNAPPED);
} else if (event->details().velocity_x() <
-kMinHorizVelocityForWindowSwipe) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::LEFT_SNAPPED);
}
} else {
DCHECK_EQ(event->type(), ui::ET_GESTURE_SWIPE);
DCHECK_GT(event->details().touch_points(), 0);
if (event->details().touch_points() == 1)
return;
if (!wm::GetWindowState(GetTarget())->IsNormalOrSnapped())
return;
CompleteDrag();
if (event->details().swipe_down()) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::MINIMIZED);
} else if (event->details().swipe_up()) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::MAXIMIZED);
} else if (event->details().swipe_right()) {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::RIGHT_SNAPPED);
} else {
SetWindowStateTypeFromGesture(GetTarget(),
mojom::WindowStateType::LEFT_SNAPPED);
}
}
event->StopPropagation();
}
WorkspaceWindowResizer::WorkspaceWindowResizer(
wm::WindowState* window_state,
const std::vector<aura::Window*>& attached_windows)
......@@ -590,6 +655,8 @@ WorkspaceWindowResizer::WorkspaceWindowResizer(
}
instance = this;
pre_drag_window_bounds_ = window_state->window()->bounds();
window_state->OnDragStarted(details().window_component);
}
......@@ -1043,4 +1110,44 @@ bool WorkspaceWindowResizer::AreBoundsValidSnappedBounds(
return bounds_in_parent == snapped_bounds;
}
void WorkspaceWindowResizer::SetWindowStateTypeFromGesture(
aura::Window* window,
mojom::WindowStateType new_state_type) {
wm::WindowState* window_state = wm::GetWindowState(window);
// TODO(oshima): Move extra logic (set_unminimize_to_restore_bounds,
// SetRestoreBoundsInParent) that modifies the window state
// into WindowState.
switch (new_state_type) {
case mojom::WindowStateType::MINIMIZED:
if (window_state->CanMinimize()) {
window_state->Minimize();
window_state->set_unminimize_to_restore_bounds(true);
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
}
break;
case mojom::WindowStateType::MAXIMIZED:
if (window_state->CanMaximize()) {
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
window_state->Maximize();
}
break;
case mojom::WindowStateType::LEFT_SNAPPED:
if (window_state->CanSnap()) {
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
window_state->OnWMEvent(&event);
}
break;
case mojom::WindowStateType::RIGHT_SNAPPED:
if (window_state->CanSnap()) {
window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
const wm::WMEvent event(wm::WM_EVENT_SNAP_RIGHT);
window_state->OnWMEvent(&event);
}
break;
default:
NOTREACHED();
}
}
} // namespace ash
......@@ -60,6 +60,7 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
void Drag(const gfx::Point& location_in_parent, int event_flags) override;
void CompleteDrag() override;
void RevertDrag() override;
void FlingOrSwipe(ui::GestureEvent* event) override;
private:
friend class WorkspaceWindowResizerTest;
......@@ -154,6 +155,11 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
bool AreBoundsValidSnappedBounds(mojom::WindowStateType snapped_type,
const gfx::Rect& bounds_in_parent) const;
// Sets |window|'s state type to |new_state_type|. Called after the drag has
// been completed for fling/swipe gestures.
void SetWindowStateTypeFromGesture(aura::Window* window,
mojom::WindowStateType new_state_type);
wm::WindowState* window_state() { return window_state_; }
const std::vector<aura::Window*> attached_windows_;
......@@ -208,6 +214,11 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
// should attach.
MatchedEdge magnetism_edge_;
// The window bounds when the drag was started. When a window is minimized,
// maximized or snapped via a swipe/fling gesture, the restore bounds should
// be set to the bounds of the window when the drag was started.
gfx::Rect pre_drag_window_bounds_;
// Used to determine if this has been deleted during a drag such as when a tab
// gets dragged into another browser window.
base::WeakPtrFactory<WorkspaceWindowResizer> weak_ptr_factory_;
......
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