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/wm/toplevel_window_event_handler.h"
8 #include "ash/wm/property_util.h"
9 #include "ash/wm/resize_shadow_controller.h"
10 #include "ash/wm/window_properties.h"
11 #include "ash/wm/window_resizer.h"
12 #include "ash/wm/window_util.h"
13 #include "ash/wm/workspace/snap_sizer.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "ui/aura/client/aura_constants.h"
17 #include "ui/aura/client/cursor_client.h"
18 #include "ui/aura/env.h"
19 #include "ui/aura/root_window.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_delegate.h"
22 #include "ui/aura/window_observer.h"
23 #include "ui/base/cursor/cursor.h"
24 #include "ui/base/events/event.h"
25 #include "ui/base/events/event_utils.h"
26 #include "ui/base/gestures/gesture_recognizer.h"
27 #include "ui/base/hit_test.h"
28 #include "ui/base/ui_base_types.h"
29 #include "ui/compositor/layer.h"
30 #include "ui/compositor/scoped_layer_animation_settings.h"
31 #include "ui/gfx/screen.h"
34 const double kMinHorizVelocityForWindowSwipe
= 1100;
35 const double kMinVertVelocityForWindowMinimize
= 1000;
42 gfx::Point
ConvertPointToParent(aura::Window
* window
,
43 const gfx::Point
& point
) {
44 gfx::Point
result(point
);
45 aura::Window::ConvertPointToTarget(window
, window
->parent(), &result
);
51 // ScopedWindowResizer ---------------------------------------------------------
53 // Wraps a WindowResizer and installs an observer on its target window. When
54 // the window is destroyed ResizerWindowDestroyed() is invoked back on the
55 // ToplevelWindowEventHandler to clean up.
56 class ToplevelWindowEventHandler::ScopedWindowResizer
57 : public aura::WindowObserver
{
59 ScopedWindowResizer(ToplevelWindowEventHandler
* handler
,
60 WindowResizer
* resizer
);
61 virtual ~ScopedWindowResizer();
63 WindowResizer
* resizer() { return resizer_
.get(); }
65 // WindowObserver overrides:
66 virtual void OnWindowHierarchyChanging(
67 const HierarchyChangeParams
& params
) OVERRIDE
;
68 virtual void OnWindowPropertyChanged(aura::Window
* window
,
70 intptr_t old
) OVERRIDE
;
71 virtual void OnWindowDestroying(aura::Window
* window
) OVERRIDE
;
74 void AddHandlers(aura::Window
* container
);
75 void RemoveHandlers();
77 ToplevelWindowEventHandler
* handler_
;
78 scoped_ptr
<WindowResizer
> resizer_
;
80 // If not NULL, this is an additional container that the dragged window has
81 // moved to which ScopedWindowResizer has temporarily added observers on.
82 aura::Window
* target_container_
;
84 DISALLOW_COPY_AND_ASSIGN(ScopedWindowResizer
);
87 ToplevelWindowEventHandler::ScopedWindowResizer::ScopedWindowResizer(
88 ToplevelWindowEventHandler
* handler
,
89 WindowResizer
* resizer
)
92 target_container_(NULL
) {
94 resizer_
->GetTarget()->AddObserver(this);
97 ToplevelWindowEventHandler::ScopedWindowResizer::~ScopedWindowResizer() {
100 resizer_
->GetTarget()->RemoveObserver(this);
103 void ToplevelWindowEventHandler::ScopedWindowResizer::OnWindowHierarchyChanging(
104 const HierarchyChangeParams
& params
) {
105 if (params
.receiver
!= resizer_
->GetTarget())
108 if (params
.receiver
->GetProperty(internal::kContinueDragAfterReparent
)) {
109 params
.receiver
->SetProperty(internal::kContinueDragAfterReparent
, false);
110 AddHandlers(params
.new_parent
);
112 handler_
->CompleteDrag(DRAG_COMPLETE
, 0);
116 void ToplevelWindowEventHandler::ScopedWindowResizer::OnWindowPropertyChanged(
117 aura::Window
* window
,
120 if (key
== aura::client::kShowStateKey
&& !wm::IsWindowNormal(window
))
121 handler_
->CompleteDrag(DRAG_COMPLETE
, 0);
124 void ToplevelWindowEventHandler::ScopedWindowResizer::OnWindowDestroying(
125 aura::Window
* window
) {
126 DCHECK(resizer_
.get());
127 DCHECK_EQ(resizer_
->GetTarget(), window
);
128 handler_
->ResizerWindowDestroyed();
131 void ToplevelWindowEventHandler::ScopedWindowResizer::AddHandlers(
132 aura::Window
* container
) {
134 if (!handler_
->owner()->Contains(container
)) {
135 container
->AddPreTargetHandler(handler_
);
136 container
->AddPostTargetHandler(handler_
);
137 target_container_
= container
;
141 void ToplevelWindowEventHandler::ScopedWindowResizer::RemoveHandlers() {
142 if (target_container_
) {
143 target_container_
->RemovePreTargetHandler(handler_
);
144 target_container_
->RemovePostTargetHandler(handler_
);
145 target_container_
= NULL
;
150 // ToplevelWindowEventHandler --------------------------------------------------
152 ToplevelWindowEventHandler::ToplevelWindowEventHandler(aura::Window
* owner
)
154 in_move_loop_(false),
155 move_cancelled_(false),
156 in_gesture_drag_(false),
158 aura::client::SetWindowMoveClient(owner
, this);
159 Shell::GetInstance()->display_controller()->AddObserver(this);
160 owner
->AddPreTargetHandler(this);
161 owner
->AddPostTargetHandler(this);
164 ToplevelWindowEventHandler::~ToplevelWindowEventHandler() {
165 Shell::GetInstance()->display_controller()->RemoveObserver(this);
170 void ToplevelWindowEventHandler::OnKeyEvent(ui::KeyEvent
* event
) {
171 if (window_resizer_
.get() && event
->type() == ui::ET_KEY_PRESSED
&&
172 event
->key_code() == ui::VKEY_ESCAPE
) {
173 CompleteDrag(DRAG_REVERT
, event
->flags());
177 void ToplevelWindowEventHandler::OnMouseEvent(
178 ui::MouseEvent
* event
) {
179 if ((event
->flags() &
180 (ui::EF_MIDDLE_MOUSE_BUTTON
| ui::EF_RIGHT_MOUSE_BUTTON
)) != 0)
183 if (in_gesture_drag_
)
186 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
187 switch (event
->type()) {
188 case ui::ET_MOUSE_PRESSED
:
189 HandleMousePressed(target
, event
);
191 case ui::ET_MOUSE_DRAGGED
:
192 HandleDrag(target
, event
);
194 case ui::ET_MOUSE_CAPTURE_CHANGED
:
195 case ui::ET_MOUSE_RELEASED
:
196 HandleMouseReleased(target
, event
);
198 case ui::ET_MOUSE_MOVED
:
199 HandleMouseMoved(target
, event
);
201 case ui::ET_MOUSE_EXITED
:
202 HandleMouseExited(target
, event
);
209 void ToplevelWindowEventHandler::OnGestureEvent(ui::GestureEvent
* event
) {
210 aura::Window
* target
= static_cast<aura::Window
*>(event
->target());
211 if (!target
->delegate())
214 if (in_move_loop_
&& !in_gesture_drag_
)
217 switch (event
->type()) {
218 case ui::ET_GESTURE_TAP_DOWN
: {
220 target
->delegate()->GetNonClientComponent(event
->location());
221 if (!(WindowResizer::GetBoundsChangeForWindowComponent(component
) &
222 WindowResizer::kBoundsChange_Resizes
))
224 internal::ResizeShadowController
* controller
=
225 Shell::GetInstance()->resize_shadow_controller();
227 controller
->ShowShadow(target
, component
);
230 case ui::ET_GESTURE_END
: {
231 internal::ResizeShadowController
* controller
=
232 Shell::GetInstance()->resize_shadow_controller();
234 controller
->HideShadow(target
);
237 case ui::ET_GESTURE_SCROLL_BEGIN
: {
238 if (in_gesture_drag_
)
241 target
->delegate()->GetNonClientComponent(event
->location());
242 if (WindowResizer::GetBoundsChangeForWindowComponent(component
) == 0) {
243 window_resizer_
.reset();
246 in_gesture_drag_
= true;
247 pre_drag_window_bounds_
= target
->bounds();
248 gfx::Point
location_in_parent(
249 ConvertPointToParent(target
, event
->location()));
250 CreateScopedWindowResizer(target
, location_in_parent
, component
,
251 aura::client::WINDOW_MOVE_SOURCE_TOUCH
);
254 case ui::ET_GESTURE_SCROLL_UPDATE
: {
255 if (!in_gesture_drag_
)
257 if (window_resizer_
.get() &&
258 window_resizer_
->resizer()->GetTarget() != target
) {
261 HandleDrag(target
, event
);
264 case ui::ET_GESTURE_SCROLL_END
:
265 case ui::ET_SCROLL_FLING_START
: {
266 if (!in_gesture_drag_
)
268 if (window_resizer_
.get() &&
269 window_resizer_
->resizer()->GetTarget() != target
) {
273 CompleteDrag(DRAG_COMPLETE
, event
->flags());
276 in_move_loop_
= false;
278 in_gesture_drag_
= false;
280 if (event
->type() == ui::ET_GESTURE_SCROLL_END
) {
281 event
->StopPropagation();
286 target
->delegate()->GetNonClientComponent(event
->location());
287 if (WindowResizer::GetBoundsChangeForWindowComponent(component
) == 0)
289 if (!wm::IsWindowNormal(target
))
292 if (fabs(event
->details().velocity_y()) >
293 kMinVertVelocityForWindowMinimize
) {
294 // Minimize/maximize.
295 if (event
->details().velocity_y() > 0 &&
296 wm::CanMinimizeWindow(target
)) {
297 wm::MinimizeWindow(target
);
298 SetWindowAlwaysRestoresToRestoreBounds(target
, true);
299 SetRestoreBoundsInParent(target
, pre_drag_window_bounds_
);
300 } else if (wm::CanMaximizeWindow(target
)) {
301 SetRestoreBoundsInParent(target
, pre_drag_window_bounds_
);
302 wm::MaximizeWindow(target
);
304 } else if (wm::CanSnapWindow(target
) &&
305 fabs(event
->details().velocity_x()) >
306 kMinHorizVelocityForWindowSwipe
) {
308 ui::ScopedLayerAnimationSettings
scoped_setter(
309 target
->layer()->GetAnimator());
310 scoped_setter
.SetPreemptionStrategy(
311 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS
);
312 internal::SnapSizer::SnapWindow(target
,
313 event
->details().velocity_x() < 0 ?
314 internal::SnapSizer::LEFT_EDGE
: internal::SnapSizer::RIGHT_EDGE
);
322 event
->StopPropagation();
325 aura::client::WindowMoveResult
ToplevelWindowEventHandler::RunMoveLoop(
326 aura::Window
* source
,
327 const gfx::Vector2d
& drag_offset
,
328 aura::client::WindowMoveSource move_source
) {
329 DCHECK(!in_move_loop_
); // Can only handle one nested loop at a time.
330 in_move_loop_
= true;
331 move_cancelled_
= false;
332 aura::RootWindow
* root_window
= source
->GetRootWindow();
334 gfx::Point drag_location
;
335 if (move_source
== aura::client::WINDOW_MOVE_SOURCE_TOUCH
&&
336 aura::Env::GetInstance()->is_touch_down()) {
337 in_gesture_drag_
= true;
338 bool has_point
= root_window
->gesture_recognizer()->
339 GetLastTouchPointForTarget(source
, &drag_location
);
342 drag_location
= root_window
->GetLastMouseLocationInRoot();
343 aura::Window::ConvertPointToTarget(
344 root_window
, source
->parent(), &drag_location
);
346 // Set the cursor before calling CreateScopedWindowResizer(), as that will
347 // eventually call LockCursor() and prevent the cursor from changing.
348 aura::client::CursorClient
* cursor_client
=
349 aura::client::GetCursorClient(root_window
);
351 cursor_client
->SetCursor(ui::kCursorPointer
);
352 CreateScopedWindowResizer(source
, drag_location
, HTCAPTION
, move_source
);
353 bool destroyed
= false;
354 destroyed_
= &destroyed
;
355 #if !defined(OS_MACOSX)
356 base::MessageLoopForUI
* loop
= base::MessageLoopForUI::current();
357 base::MessageLoop::ScopedNestableTaskAllower
allow_nested(loop
);
358 base::RunLoop
run_loop(aura::Env::GetInstance()->GetDispatcher());
359 quit_closure_
= run_loop
.QuitClosure();
361 #endif // !defined(OS_MACOSX)
363 return aura::client::MOVE_CANCELED
;
365 in_gesture_drag_
= in_move_loop_
= false;
366 return move_cancelled_
? aura::client::MOVE_CANCELED
:
367 aura::client::MOVE_SUCCESSFUL
;
370 void ToplevelWindowEventHandler::EndMoveLoop() {
374 in_move_loop_
= false;
375 if (window_resizer_
) {
376 window_resizer_
->resizer()->RevertDrag();
377 window_resizer_
.reset();
382 void ToplevelWindowEventHandler::OnDisplayConfigurationChanging() {
384 move_cancelled_
= true;
386 } else if (window_resizer_
) {
387 window_resizer_
->resizer()->RevertDrag();
388 window_resizer_
.reset();
392 void ToplevelWindowEventHandler::CreateScopedWindowResizer(
393 aura::Window
* window
,
394 const gfx::Point
& point_in_parent
,
395 int window_component
,
396 aura::client::WindowMoveSource source
) {
397 window_resizer_
.reset();
398 WindowResizer
* resizer
=
399 CreateWindowResizer(window
, point_in_parent
, window_component
,
402 window_resizer_
.reset(new ScopedWindowResizer(this, resizer
));
405 void ToplevelWindowEventHandler::CompleteDrag(DragCompletionStatus status
,
407 scoped_ptr
<ScopedWindowResizer
> resizer(window_resizer_
.release());
409 if (status
== DRAG_COMPLETE
)
410 resizer
->resizer()->CompleteDrag(event_flags
);
412 resizer
->resizer()->RevertDrag();
416 void ToplevelWindowEventHandler::HandleMousePressed(
417 aura::Window
* target
,
418 ui::MouseEvent
* event
) {
419 // Move/size operations are initiated post-target handling to give the target
420 // an opportunity to cancel this default behavior by returning ER_HANDLED.
421 if (ui::EventCanceledDefaultHandling(*event
))
424 // We also update the current window component here because for the
425 // mouse-drag-release-press case, where the mouse is released and
426 // pressed without mouse move event.
428 target
->delegate()->GetNonClientComponent(event
->location());
429 if ((event
->flags() &
430 (ui::EF_IS_DOUBLE_CLICK
| ui::EF_IS_TRIPLE_CLICK
)) == 0 &&
431 WindowResizer::GetBoundsChangeForWindowComponent(component
)) {
432 gfx::Point
location_in_parent(
433 ConvertPointToParent(target
, event
->location()));
434 CreateScopedWindowResizer(target
, location_in_parent
, component
,
435 aura::client::WINDOW_MOVE_SOURCE_MOUSE
);
437 window_resizer_
.reset();
439 if (WindowResizer::GetBoundsChangeForWindowComponent(component
) != 0)
440 event
->StopPropagation();
443 void ToplevelWindowEventHandler::HandleMouseReleased(
444 aura::Window
* target
,
445 ui::MouseEvent
* event
) {
446 if (event
->phase() != ui::EP_PRETARGET
)
449 CompleteDrag(event
->type() == ui::ET_MOUSE_RELEASED
?
450 DRAG_COMPLETE
: DRAG_REVERT
,
454 in_move_loop_
= false;
456 // Completing the drag may result in hiding the window. If this happens
457 // return true so no other handlers/observers see the event. Otherwise
458 // they see the event on a hidden window.
459 if (event
->type() == ui::ET_MOUSE_CAPTURE_CHANGED
&&
460 !target
->IsVisible()) {
461 event
->StopPropagation();
465 void ToplevelWindowEventHandler::HandleDrag(
466 aura::Window
* target
,
467 ui::LocatedEvent
* event
) {
468 // This function only be triggered to move window
469 // by mouse drag or touch move event.
470 DCHECK(event
->type() == ui::ET_MOUSE_DRAGGED
||
471 event
->type() == ui::ET_TOUCH_MOVED
||
472 event
->type() == ui::ET_GESTURE_SCROLL_UPDATE
);
474 // Drag actions are performed pre-target handling to prevent spurious mouse
475 // moves from the move/size operation from being sent to the target.
476 if (event
->phase() != ui::EP_PRETARGET
)
479 if (!window_resizer_
)
481 window_resizer_
->resizer()->Drag(
482 ConvertPointToParent(target
, event
->location()), event
->flags());
483 event
->StopPropagation();
486 void ToplevelWindowEventHandler::HandleMouseMoved(
487 aura::Window
* target
,
488 ui::LocatedEvent
* event
) {
489 // Shadow effects are applied after target handling. Note that we don't
490 // respect ER_HANDLED here right now since we have not had a reason to allow
491 // the target to cancel shadow rendering.
492 if (event
->phase() != ui::EP_POSTTARGET
)
495 // TODO(jamescook): Move the resize cursor update code into here from
496 // CompoundEventFilter?
497 internal::ResizeShadowController
* controller
=
498 Shell::GetInstance()->resize_shadow_controller();
500 if (event
->flags() & ui::EF_IS_NON_CLIENT
) {
502 target
->delegate()->GetNonClientComponent(event
->location());
503 controller
->ShowShadow(target
, component
);
505 controller
->HideShadow(target
);
510 void ToplevelWindowEventHandler::HandleMouseExited(
511 aura::Window
* target
,
512 ui::LocatedEvent
* event
) {
513 // Shadow effects are applied after target handling. Note that we don't
514 // respect ER_HANDLED here right now since we have not had a reason to allow
515 // the target to cancel shadow rendering.
516 if (event
->phase() != ui::EP_POSTTARGET
)
519 internal::ResizeShadowController
* controller
=
520 Shell::GetInstance()->resize_shadow_controller();
522 controller
->HideShadow(target
);
525 void ToplevelWindowEventHandler::ResizerWindowDestroyed() {
526 // We explicitly don't invoke RevertDrag() since that may do things to window.
527 // Instead we destroy the resizer.
528 window_resizer_
.reset();
530 // End the move loop. This does nothing if we're not in a move loop.