Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(303)

Side by Side Diff: ash/common/wm/wm_toplevel_window_event_handler.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ash/common/wm/wm_toplevel_window_event_handler.h ('k') | ash/common/wm/wm_types.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "ash/common/wm/wm_toplevel_window_event_handler.h"
6
7 #include "ash/common/wm/window_resizer.h"
8 #include "ash/common/wm/window_state.h"
9 #include "ash/common/wm/window_state_observer.h"
10 #include "ash/common/wm/wm_event.h"
11 #include "ash/common/wm_shell.h"
12 #include "ash/common/wm_window.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_observer.h"
15 #include "ui/base/hit_test.h"
16 #include "ui/events/event.h"
17
18 namespace {
19 const double kMinHorizVelocityForWindowSwipe = 1100;
20 const double kMinVertVelocityForWindowMinimize = 1000;
21 }
22
23 namespace ash {
24 namespace wm {
25
26 namespace {
27
28 // Returns whether |window| can be moved via a two finger drag given
29 // the hittest results of the two fingers.
30 bool CanStartTwoFingerMove(WmWindow* window,
31 int window_component1,
32 int window_component2) {
33 // We allow moving a window via two fingers when the hittest components are
34 // HTCLIENT. This is done so that a window can be dragged via two fingers when
35 // the tab strip is full and hitting the caption area is difficult. We check
36 // the window type and the state type so that we do not steal touches from the
37 // web contents.
38 if (!window->GetWindowState()->IsNormalOrSnapped() ||
39 window->GetType() != ui::wm::WINDOW_TYPE_NORMAL) {
40 return false;
41 }
42 int component1_behavior =
43 WindowResizer::GetBoundsChangeForWindowComponent(window_component1);
44 int component2_behavior =
45 WindowResizer::GetBoundsChangeForWindowComponent(window_component2);
46 return (component1_behavior & WindowResizer::kBoundsChange_Resizes) == 0 &&
47 (component2_behavior & WindowResizer::kBoundsChange_Resizes) == 0;
48 }
49
50 // Returns whether |window| can be moved or resized via one finger given
51 // |window_component|.
52 bool CanStartOneFingerDrag(int window_component) {
53 return WindowResizer::GetBoundsChangeForWindowComponent(window_component) !=
54 0;
55 }
56
57 // Returns the window component containing |event|'s location.
58 int GetWindowComponent(WmWindow* window, const ui::LocatedEvent& event) {
59 return window->GetNonClientComponent(event.location());
60 }
61
62 } // namespace
63
64 // ScopedWindowResizer ---------------------------------------------------------
65
66 // Wraps a WindowResizer and installs an observer on its target window. When
67 // the window is destroyed ResizerWindowDestroyed() is invoked back on the
68 // WmToplevelWindowEventHandler to clean up.
69 class WmToplevelWindowEventHandler::ScopedWindowResizer
70 : public aura::WindowObserver,
71 public wm::WindowStateObserver {
72 public:
73 ScopedWindowResizer(WmToplevelWindowEventHandler* handler,
74 std::unique_ptr<WindowResizer> resizer);
75 ~ScopedWindowResizer() override;
76
77 // Returns true if the drag moves the window and does not resize.
78 bool IsMove() const;
79
80 WindowResizer* resizer() { return resizer_.get(); }
81
82 // WindowObserver overrides:
83 void OnWindowDestroying(aura::Window* window) override;
84
85 // WindowStateObserver overrides:
86 void OnPreWindowStateTypeChange(wm::WindowState* window_state,
87 wm::WindowStateType type) override;
88
89 private:
90 WmToplevelWindowEventHandler* handler_;
91 std::unique_ptr<WindowResizer> resizer_;
92
93 // Whether ScopedWindowResizer grabbed capture.
94 bool grabbed_capture_;
95
96 DISALLOW_COPY_AND_ASSIGN(ScopedWindowResizer);
97 };
98
99 WmToplevelWindowEventHandler::ScopedWindowResizer::ScopedWindowResizer(
100 WmToplevelWindowEventHandler* handler,
101 std::unique_ptr<WindowResizer> resizer)
102 : handler_(handler), resizer_(std::move(resizer)), grabbed_capture_(false) {
103 WmWindow* target = resizer_->GetTarget();
104 target->aura_window()->AddObserver(this);
105 target->GetWindowState()->AddObserver(this);
106
107 if (!target->HasCapture()) {
108 grabbed_capture_ = true;
109 target->SetCapture();
110 }
111 }
112
113 WmToplevelWindowEventHandler::ScopedWindowResizer::~ScopedWindowResizer() {
114 WmWindow* target = resizer_->GetTarget();
115 target->aura_window()->RemoveObserver(this);
116 target->GetWindowState()->RemoveObserver(this);
117 if (grabbed_capture_)
118 target->ReleaseCapture();
119 }
120
121 bool WmToplevelWindowEventHandler::ScopedWindowResizer::IsMove() const {
122 return resizer_->details().bounds_change ==
123 WindowResizer::kBoundsChange_Repositions;
124 }
125
126 void WmToplevelWindowEventHandler::ScopedWindowResizer::
127 OnPreWindowStateTypeChange(wm::WindowState* window_state,
128 wm::WindowStateType old) {
129 handler_->CompleteDrag(DragResult::SUCCESS);
130 }
131
132 void WmToplevelWindowEventHandler::ScopedWindowResizer::OnWindowDestroying(
133 aura::Window* window) {
134 DCHECK_EQ(resizer_->GetTarget(), WmWindow::Get(window));
135 handler_->ResizerWindowDestroyed();
136 }
137
138 // WmToplevelWindowEventHandler
139 // --------------------------------------------------
140
141 WmToplevelWindowEventHandler::WmToplevelWindowEventHandler(WmShell* shell)
142 : shell_(shell), first_finger_hittest_(HTNOWHERE) {
143 shell_->AddDisplayObserver(this);
144 }
145
146 WmToplevelWindowEventHandler::~WmToplevelWindowEventHandler() {
147 shell_->RemoveDisplayObserver(this);
148 }
149
150 void WmToplevelWindowEventHandler::OnKeyEvent(ui::KeyEvent* event) {
151 if (window_resizer_.get() && event->type() == ui::ET_KEY_PRESSED &&
152 event->key_code() == ui::VKEY_ESCAPE) {
153 CompleteDrag(DragResult::REVERT);
154 }
155 }
156
157 void WmToplevelWindowEventHandler::OnMouseEvent(ui::MouseEvent* event,
158 WmWindow* target) {
159 if (event->handled())
160 return;
161 if ((event->flags() &
162 (ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON)) != 0)
163 return;
164
165 if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED) {
166 // Capture is grabbed when both gesture and mouse drags start. Handle
167 // capture loss regardless of which type of drag is in progress.
168 HandleCaptureLost(event);
169 return;
170 }
171
172 if (in_gesture_drag_)
173 return;
174
175 switch (event->type()) {
176 case ui::ET_MOUSE_PRESSED:
177 HandleMousePressed(target, event);
178 break;
179 case ui::ET_MOUSE_DRAGGED:
180 HandleDrag(target, event);
181 break;
182 case ui::ET_MOUSE_RELEASED:
183 HandleMouseReleased(target, event);
184 break;
185 case ui::ET_MOUSE_MOVED:
186 HandleMouseMoved(target, event);
187 break;
188 case ui::ET_MOUSE_EXITED:
189 HandleMouseExited(target, event);
190 break;
191 default:
192 break;
193 }
194 }
195
196 void WmToplevelWindowEventHandler::OnGestureEvent(ui::GestureEvent* event,
197 WmWindow* target) {
198 if (event->handled())
199 return;
200 if (!target->HasNonClientArea())
201 return;
202
203 if (window_resizer_.get() && !in_gesture_drag_)
204 return;
205
206 if (window_resizer_.get() &&
207 window_resizer_->resizer()->GetTarget() != target) {
208 return;
209 }
210
211 if (event->details().touch_points() > 2) {
212 if (CompleteDrag(DragResult::SUCCESS))
213 event->StopPropagation();
214 return;
215 }
216
217 switch (event->type()) {
218 case ui::ET_GESTURE_TAP_DOWN: {
219 int component = GetWindowComponent(target, *event);
220 if (!(WindowResizer::GetBoundsChangeForWindowComponent(component) &
221 WindowResizer::kBoundsChange_Resizes))
222 return;
223 target->ShowResizeShadow(component);
224 return;
225 }
226 case ui::ET_GESTURE_END: {
227 target->HideResizeShadow();
228
229 if (window_resizer_.get() &&
230 (event->details().touch_points() == 1 ||
231 !CanStartOneFingerDrag(first_finger_hittest_))) {
232 CompleteDrag(DragResult::SUCCESS);
233 event->StopPropagation();
234 }
235 return;
236 }
237 case ui::ET_GESTURE_BEGIN: {
238 if (event->details().touch_points() == 1) {
239 first_finger_hittest_ = GetWindowComponent(target, *event);
240 } else if (window_resizer_.get()) {
241 if (!window_resizer_->IsMove()) {
242 // The transition from resizing with one finger to resizing with two
243 // fingers causes unintended resizing because the location of
244 // ET_GESTURE_SCROLL_UPDATE jumps from the position of the first
245 // finger to the position in the middle of the two fingers. For this
246 // reason two finger resizing is not supported.
247 CompleteDrag(DragResult::SUCCESS);
248 event->StopPropagation();
249 }
250 } else {
251 int second_finger_hittest = GetWindowComponent(target, *event);
252 if (CanStartTwoFingerMove(target, first_finger_hittest_,
253 second_finger_hittest)) {
254 gfx::Point location_in_parent =
255 event->details().bounding_box().CenterPoint();
256 AttemptToStartDrag(target, location_in_parent, HTCAPTION,
257 aura::client::WINDOW_MOVE_SOURCE_TOUCH,
258 EndClosure());
259 event->StopPropagation();
260 }
261 }
262 return;
263 }
264 case ui::ET_GESTURE_SCROLL_BEGIN: {
265 // The one finger drag is not started in ET_GESTURE_BEGIN to avoid the
266 // window jumping upon initiating a two finger drag. When a one finger
267 // drag is converted to a two finger drag, a jump occurs because the
268 // location of the ET_GESTURE_SCROLL_UPDATE event switches from the single
269 // finger's position to the position in the middle of the two fingers.
270 if (window_resizer_.get())
271 return;
272 int component = GetWindowComponent(target, *event);
273 if (!CanStartOneFingerDrag(component))
274 return;
275 gfx::Point location_in_parent(
276 target->ConvertPointToTarget(target->GetParent(), event->location()));
277 AttemptToStartDrag(target, location_in_parent, component,
278 aura::client::WINDOW_MOVE_SOURCE_TOUCH, EndClosure());
279 event->StopPropagation();
280 return;
281 }
282 default:
283 break;
284 }
285
286 if (!window_resizer_.get())
287 return;
288
289 switch (event->type()) {
290 case ui::ET_GESTURE_SCROLL_UPDATE:
291 HandleDrag(target, event);
292 event->StopPropagation();
293 return;
294 case ui::ET_GESTURE_SCROLL_END:
295 // We must complete the drag here instead of as a result of ET_GESTURE_END
296 // because otherwise the drag will be reverted when EndMoveLoop() is
297 // called.
298 // TODO(pkotwicz): Pass drag completion status to
299 // WindowMoveClient::EndMoveLoop().
300 CompleteDrag(DragResult::SUCCESS);
301 event->StopPropagation();
302 return;
303 case ui::ET_SCROLL_FLING_START:
304 CompleteDrag(DragResult::SUCCESS);
305
306 // TODO(pkotwicz): Fix tests which inadvertantly start flings and check
307 // window_resizer_->IsMove() instead of the hittest component at |event|'s
308 // location.
309 if (GetWindowComponent(target, *event) != HTCAPTION ||
310 !target->GetWindowState()->IsNormalOrSnapped()) {
311 return;
312 }
313
314 if (event->details().velocity_y() > kMinVertVelocityForWindowMinimize) {
315 SetWindowStateTypeFromGesture(target, wm::WINDOW_STATE_TYPE_MINIMIZED);
316 } else if (event->details().velocity_y() <
317 -kMinVertVelocityForWindowMinimize) {
318 SetWindowStateTypeFromGesture(target, wm::WINDOW_STATE_TYPE_MAXIMIZED);
319 } else if (event->details().velocity_x() >
320 kMinHorizVelocityForWindowSwipe) {
321 SetWindowStateTypeFromGesture(target,
322 wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED);
323 } else if (event->details().velocity_x() <
324 -kMinHorizVelocityForWindowSwipe) {
325 SetWindowStateTypeFromGesture(target,
326 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED);
327 }
328 event->StopPropagation();
329 return;
330 case ui::ET_GESTURE_SWIPE:
331 DCHECK_GT(event->details().touch_points(), 0);
332 if (event->details().touch_points() == 1)
333 return;
334 if (!target->GetWindowState()->IsNormalOrSnapped())
335 return;
336
337 CompleteDrag(DragResult::SUCCESS);
338
339 if (event->details().swipe_down()) {
340 SetWindowStateTypeFromGesture(target, wm::WINDOW_STATE_TYPE_MINIMIZED);
341 } else if (event->details().swipe_up()) {
342 SetWindowStateTypeFromGesture(target, wm::WINDOW_STATE_TYPE_MAXIMIZED);
343 } else if (event->details().swipe_right()) {
344 SetWindowStateTypeFromGesture(target,
345 wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED);
346 } else {
347 SetWindowStateTypeFromGesture(target,
348 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED);
349 }
350 event->StopPropagation();
351 return;
352 default:
353 return;
354 }
355 }
356
357 bool WmToplevelWindowEventHandler::AttemptToStartDrag(
358 WmWindow* window,
359 const gfx::Point& point_in_parent,
360 int window_component,
361 aura::client::WindowMoveSource source,
362 const EndClosure& end_closure) {
363 if (window_resizer_.get())
364 return false;
365 std::unique_ptr<WindowResizer> resizer(
366 CreateWindowResizer(window, point_in_parent, window_component, source));
367 if (!resizer)
368 return false;
369
370 end_closure_ = end_closure;
371 window_resizer_.reset(new ScopedWindowResizer(this, std::move(resizer)));
372
373 pre_drag_window_bounds_ = window->GetBounds();
374 in_gesture_drag_ = (source == aura::client::WINDOW_MOVE_SOURCE_TOUCH);
375 return true;
376 }
377
378 void WmToplevelWindowEventHandler::RevertDrag() {
379 CompleteDrag(DragResult::REVERT);
380 }
381
382 bool WmToplevelWindowEventHandler::CompleteDrag(DragResult result) {
383 if (!window_resizer_)
384 return false;
385
386 std::unique_ptr<ScopedWindowResizer> resizer(std::move(window_resizer_));
387 switch (result) {
388 case DragResult::SUCCESS:
389 resizer->resizer()->CompleteDrag();
390 break;
391 case DragResult::REVERT:
392 resizer->resizer()->RevertDrag();
393 break;
394 case DragResult::WINDOW_DESTROYED:
395 // We explicitly do not invoke RevertDrag() since that may do things to
396 // the window that was destroyed.
397 break;
398 }
399
400 first_finger_hittest_ = HTNOWHERE;
401 in_gesture_drag_ = false;
402 if (!end_closure_.is_null()) {
403 // Clear local state in case running the closure deletes us.
404 EndClosure end_closure = end_closure_;
405 end_closure_.Reset();
406 end_closure.Run(result);
407 }
408 return true;
409 }
410
411 void WmToplevelWindowEventHandler::HandleMousePressed(WmWindow* target,
412 ui::MouseEvent* event) {
413 if (event->phase() != ui::EP_PRETARGET || !target->HasNonClientArea())
414 return;
415
416 // We also update the current window component here because for the
417 // mouse-drag-release-press case, where the mouse is released and
418 // pressed without mouse move event.
419 int component = GetWindowComponent(target, *event);
420 if ((event->flags() & (ui::EF_IS_DOUBLE_CLICK | ui::EF_IS_TRIPLE_CLICK)) ==
421 0 &&
422 WindowResizer::GetBoundsChangeForWindowComponent(component)) {
423 gfx::Point location_in_parent(
424 target->ConvertPointToTarget(target->GetParent(), event->location()));
425 AttemptToStartDrag(target, location_in_parent, component,
426 aura::client::WINDOW_MOVE_SOURCE_MOUSE, EndClosure());
427 // Set as handled so that other event handlers do no act upon the event
428 // but still receive it so that they receive both parts of each pressed/
429 // released pair.
430 event->SetHandled();
431 } else {
432 CompleteDrag(DragResult::SUCCESS);
433 }
434 }
435
436 void WmToplevelWindowEventHandler::HandleMouseReleased(WmWindow* target,
437 ui::MouseEvent* event) {
438 if (event->phase() == ui::EP_PRETARGET)
439 CompleteDrag(DragResult::SUCCESS);
440 }
441
442 void WmToplevelWindowEventHandler::HandleDrag(WmWindow* target,
443 ui::LocatedEvent* event) {
444 // This function only be triggered to move window
445 // by mouse drag or touch move event.
446 DCHECK(event->type() == ui::ET_MOUSE_DRAGGED ||
447 event->type() == ui::ET_TOUCH_MOVED ||
448 event->type() == ui::ET_GESTURE_SCROLL_UPDATE);
449
450 // Drag actions are performed pre-target handling to prevent spurious mouse
451 // moves from the move/size operation from being sent to the target.
452 if (event->phase() != ui::EP_PRETARGET)
453 return;
454
455 if (!window_resizer_)
456 return;
457 window_resizer_->resizer()->Drag(
458 target->ConvertPointToTarget(target->GetParent(), event->location()),
459 event->flags());
460 event->StopPropagation();
461 }
462
463 void WmToplevelWindowEventHandler::HandleMouseMoved(WmWindow* target,
464 ui::LocatedEvent* event) {
465 // Shadow effects are applied after target handling. Note that we don't
466 // respect ER_HANDLED here right now since we have not had a reason to allow
467 // the target to cancel shadow rendering.
468 if (event->phase() != ui::EP_POSTTARGET || !target->HasNonClientArea())
469 return;
470
471 // TODO(jamescook): Move the resize cursor update code into here from
472 // CompoundEventFilter?
473 if (event->flags() & ui::EF_IS_NON_CLIENT) {
474 int component = target->GetNonClientComponent(event->location());
475 target->ShowResizeShadow(component);
476 } else {
477 target->HideResizeShadow();
478 }
479 }
480
481 void WmToplevelWindowEventHandler::HandleMouseExited(WmWindow* target,
482 ui::LocatedEvent* event) {
483 // Shadow effects are applied after target handling. Note that we don't
484 // respect ER_HANDLED here right now since we have not had a reason to allow
485 // the target to cancel shadow rendering.
486 if (event->phase() != ui::EP_POSTTARGET)
487 return;
488
489 target->HideResizeShadow();
490 }
491
492 void WmToplevelWindowEventHandler::HandleCaptureLost(ui::LocatedEvent* event) {
493 if (event->phase() == ui::EP_PRETARGET) {
494 // We complete the drag instead of reverting it, as reverting it will result
495 // in a weird behavior when a dragged tab produces a modal dialog while the
496 // drag is in progress. crbug.com/558201.
497 CompleteDrag(DragResult::SUCCESS);
498 }
499 }
500
501 void WmToplevelWindowEventHandler::SetWindowStateTypeFromGesture(
502 WmWindow* window,
503 wm::WindowStateType new_state_type) {
504 wm::WindowState* window_state = window->GetWindowState();
505 // TODO(oshima): Move extra logic (set_unminimize_to_restore_bounds,
506 // SetRestoreBoundsInParent) that modifies the window state
507 // into WindowState.
508 switch (new_state_type) {
509 case wm::WINDOW_STATE_TYPE_MINIMIZED:
510 if (window_state->CanMinimize()) {
511 window_state->Minimize();
512 window_state->set_unminimize_to_restore_bounds(true);
513 window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
514 }
515 break;
516 case wm::WINDOW_STATE_TYPE_MAXIMIZED:
517 if (window_state->CanMaximize()) {
518 window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
519 window_state->Maximize();
520 }
521 break;
522 case wm::WINDOW_STATE_TYPE_LEFT_SNAPPED:
523 if (window_state->CanSnap()) {
524 window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
525 const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
526 window_state->OnWMEvent(&event);
527 }
528 break;
529 case wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED:
530 if (window_state->CanSnap()) {
531 window_state->SetRestoreBoundsInParent(pre_drag_window_bounds_);
532 const wm::WMEvent event(wm::WM_EVENT_SNAP_RIGHT);
533 window_state->OnWMEvent(&event);
534 }
535 break;
536 default:
537 NOTREACHED();
538 }
539 }
540
541 void WmToplevelWindowEventHandler::ResizerWindowDestroyed() {
542 CompleteDrag(DragResult::WINDOW_DESTROYED);
543 }
544
545 void WmToplevelWindowEventHandler::OnDisplayConfigurationChanging() {
546 CompleteDrag(DragResult::REVERT);
547 }
548
549 } // namespace wm
550 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/wm/wm_toplevel_window_event_handler.h ('k') | ash/common/wm/wm_types.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698