OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "athena/wm/window_overview_mode.h" | |
6 | |
7 #include <complex> | |
8 #include <vector> | |
9 | |
10 #include "athena/util/athena_constants.h" | |
11 #include "athena/wm/overview_toolbar.h" | |
12 #include "athena/wm/public/window_list_provider.h" | |
13 #include "athena/wm/public/window_list_provider_observer.h" | |
14 #include "athena/wm/split_view_controller.h" | |
15 #include "base/bind.h" | |
16 #include "base/memory/scoped_vector.h" | |
17 #include "ui/aura/scoped_window_targeter.h" | |
18 #include "ui/aura/window.h" | |
19 #include "ui/aura/window_delegate.h" | |
20 #include "ui/aura/window_property.h" | |
21 #include "ui/aura/window_targeter.h" | |
22 #include "ui/aura/window_tree_host.h" | |
23 #include "ui/compositor/closure_animation_observer.h" | |
24 #include "ui/compositor/compositor.h" | |
25 #include "ui/compositor/compositor_animation_observer.h" | |
26 #include "ui/compositor/scoped_layer_animation_settings.h" | |
27 #include "ui/events/event_handler.h" | |
28 #include "ui/events/gestures/fling_curve.h" | |
29 #include "ui/gfx/frame_time.h" | |
30 #include "ui/gfx/transform.h" | |
31 #include "ui/wm/core/shadow_types.h" | |
32 #include "ui/wm/core/window_animations.h" | |
33 #include "ui/wm/core/window_util.h" | |
34 #include "ui/wm/public/activation_client.h" | |
35 | |
36 namespace { | |
37 | |
38 struct WindowOverviewState { | |
39 // The current overview state of the window. 0.f means the window is at the | |
40 // topmost position. 1.f means the window is at the bottom-most position. | |
41 float progress; | |
42 | |
43 // The top-most and bottom-most vertical position of the window in overview | |
44 // mode. | |
45 float max_y; | |
46 float min_y; | |
47 | |
48 // |split| is set if this window is one of the two split windows in split-view | |
49 // mode. | |
50 bool split; | |
51 }; | |
52 | |
53 } // namespace | |
54 | |
55 DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState*); | |
56 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowOverviewState, | |
57 kWindowOverviewState, | |
58 nullptr); | |
59 | |
60 namespace athena { | |
61 | |
62 namespace { | |
63 | |
64 const float kOverviewDefaultScale = 0.75f; | |
65 | |
66 gfx::Transform GetTransformForSplitWindow(aura::Window* window, float scale) { | |
67 const float kScrollWindowPositionInOverview = 0.65f; | |
68 int x_translate = window->bounds().width() * (1 - scale) / 2; | |
69 gfx::Transform transform; | |
70 transform.Translate( | |
71 x_translate, window->bounds().height() * kScrollWindowPositionInOverview); | |
72 transform.Scale(scale, scale); | |
73 return transform; | |
74 } | |
75 | |
76 // Gets the transform for the window in its current state. | |
77 gfx::Transform GetTransformForState(aura::Window* window, | |
78 WindowOverviewState* state) { | |
79 if (state->split) | |
80 return GetTransformForSplitWindow(window, kOverviewDefaultScale); | |
81 | |
82 const float kProgressToStartShrinking = 0.07; | |
83 const float kOverviewScale = 0.75f; | |
84 float scale = kOverviewScale; | |
85 if (state->progress < kProgressToStartShrinking) { | |
86 const float kShrunkMinimumScale = 0.7f; | |
87 scale = gfx::Tween::FloatValueBetween( | |
88 state->progress / kProgressToStartShrinking, | |
89 kShrunkMinimumScale, | |
90 kOverviewScale); | |
91 } | |
92 int container_width = window->parent()->bounds().width(); | |
93 int window_width = window->bounds().width(); | |
94 int window_x = window->bounds().x(); | |
95 float x_translate = (container_width - (window_width * scale)) / 2 - window_x; | |
96 float y_translate = gfx::Tween::FloatValueBetween( | |
97 state->progress, state->min_y, state->max_y); | |
98 gfx::Transform transform; | |
99 transform.Translate(x_translate, y_translate); | |
100 transform.Scale(scale, scale); | |
101 return transform; | |
102 } | |
103 | |
104 // A utility class used to set the transform/opacity to the window and | |
105 // its transient children. | |
106 class TransientGroupSetter { | |
107 public: | |
108 explicit TransientGroupSetter(aura::Window* window) : window_(window) { | |
109 } | |
110 ~TransientGroupSetter() {} | |
111 | |
112 // Aborts all animations including its transient children. | |
113 void AbortAllAnimations() { | |
114 window_->layer()->GetAnimator()->AbortAllAnimations(); | |
115 for (aura::Window* transient_child : wm::GetTransientChildren(window_)) | |
116 transient_child->layer()->GetAnimator()->AbortAllAnimations(); | |
117 } | |
118 | |
119 // Applys transform to the window and its transient children. | |
120 // Transient children gets a tranfrorm with the offset relateive | |
121 // it its transient parent. | |
122 void SetTransform(const gfx::Transform& transform) { | |
123 window_->SetTransform(transform); | |
124 for (aura::Window* transient_child : wm::GetTransientChildren(window_)) { | |
125 gfx::Rect window_bounds = window_->bounds(); | |
126 gfx::Rect child_bounds = transient_child->bounds(); | |
127 gfx::Transform transient_window_transform(TranslateTransformOrigin( | |
128 child_bounds.origin() - window_bounds.origin(), transform)); | |
129 transient_child->SetTransform(transient_window_transform); | |
130 } | |
131 } | |
132 | |
133 // Sets the opacity to the window and its transient children. | |
134 void SetOpacity(float opacity) { | |
135 window_->layer()->SetOpacity(opacity); | |
136 for (aura::Window* transient_child : wm::GetTransientChildren(window_)) { | |
137 transient_child->layer()->SetOpacity(opacity); | |
138 } | |
139 } | |
140 | |
141 // Apply the transform with the overview scroll |progress|. | |
142 void SetWindowProgress(float progress) { | |
143 WindowOverviewState* state = window_->GetProperty(kWindowOverviewState); | |
144 state->progress = progress; | |
145 | |
146 SetTransform(GetTransformForState(window_, state)); | |
147 } | |
148 | |
149 private: | |
150 static gfx::Transform TranslateTransformOrigin( | |
151 const gfx::Vector2d& new_origin, | |
152 const gfx::Transform& transform) { | |
153 gfx::Transform result; | |
154 result.Translate(-new_origin.x(), -new_origin.y()); | |
155 result.PreconcatTransform(transform); | |
156 result.Translate(new_origin.x(), new_origin.y()); | |
157 return result; | |
158 } | |
159 | |
160 aura::Window* window_; | |
161 | |
162 DISALLOW_COPY_AND_ASSIGN(TransientGroupSetter); | |
163 }; | |
164 | |
165 // TransientGroupSetter with animation. | |
166 class AnimateTransientGroupSetter : public TransientGroupSetter { | |
167 public: | |
168 explicit AnimateTransientGroupSetter(aura::Window* window) | |
169 : TransientGroupSetter(window) { | |
170 animation_settings_.push_back(CreateScopedLayerAnimationSettings(window)); | |
171 for (aura::Window* transient_child : wm::GetTransientChildren(window)) { | |
172 animation_settings_.push_back( | |
173 CreateScopedLayerAnimationSettings(transient_child)); | |
174 } | |
175 } | |
176 ~AnimateTransientGroupSetter() {} | |
177 | |
178 ui::ScopedLayerAnimationSettings* GetMainWindowAnimationSettings() { | |
179 CHECK(animation_settings_.size()); | |
180 return animation_settings_[0]; | |
181 } | |
182 | |
183 private: | |
184 static ui::ScopedLayerAnimationSettings* CreateScopedLayerAnimationSettings( | |
185 aura::Window* window) { | |
186 const int kTransitionMs = 250; | |
187 | |
188 ui::ScopedLayerAnimationSettings* settings = | |
189 new ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()); | |
190 settings->SetPreemptionStrategy( | |
191 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
192 settings->SetTransitionDuration( | |
193 base::TimeDelta::FromMilliseconds(kTransitionMs)); | |
194 return settings; | |
195 } | |
196 | |
197 ScopedVector<ui::ScopedLayerAnimationSettings> animation_settings_; | |
198 DISALLOW_COPY_AND_ASSIGN(AnimateTransientGroupSetter); | |
199 }; | |
200 | |
201 void HideWindowIfNotVisible(aura::Window* window, | |
202 SplitViewController* split_view_controller) { | |
203 bool should_hide = true; | |
204 if (split_view_controller->IsSplitViewModeActive()) { | |
205 should_hide = window != split_view_controller->left_window() && | |
206 window != split_view_controller->right_window(); | |
207 } else { | |
208 aura::Window* active = aura::client::GetActivationClient( | |
209 window->GetRootWindow())->GetActiveWindow(); | |
210 should_hide = active != window && wm::GetTransientParent(active) != window; | |
211 } | |
212 if (should_hide) | |
213 window->Hide(); | |
214 } | |
215 | |
216 // Resets the overview-related state for |window|. | |
217 void RestoreWindowState(aura::Window* window, | |
218 SplitViewController* split_view_controller) { | |
219 window->ClearProperty(kWindowOverviewState); | |
220 | |
221 AnimateTransientGroupSetter setter(window); | |
222 | |
223 setter.GetMainWindowAnimationSettings()->AddObserver( | |
224 new ui::ClosureAnimationObserver( | |
225 base::Bind(&HideWindowIfNotVisible, window, split_view_controller))); | |
226 | |
227 setter.SetTransform(gfx::Transform()); | |
228 // Reset the window opacity in case the user is dragging a window. | |
229 setter.SetOpacity(1.0f); | |
230 | |
231 wm::SetShadowType(window, wm::SHADOW_TYPE_NONE); | |
232 } | |
233 | |
234 gfx::RectF GetTransformedBounds(aura::Window* window) { | |
235 gfx::Transform transform; | |
236 gfx::RectF bounds = window->bounds(); | |
237 transform.Translate(bounds.x(), bounds.y()); | |
238 transform.PreconcatTransform(window->layer()->transform()); | |
239 transform.Translate(-bounds.x(), -bounds.y()); | |
240 transform.TransformRect(&bounds); | |
241 return bounds; | |
242 } | |
243 | |
244 void TransformSplitWindowScale(aura::Window* window, float scale) { | |
245 gfx::Transform transform = window->layer()->GetTargetTransform(); | |
246 if (transform.Scale2d() == gfx::Vector2dF(scale, scale)) | |
247 return; | |
248 AnimateTransientGroupSetter setter(window); | |
249 setter.SetTransform(GetTransformForSplitWindow(window, scale)); | |
250 } | |
251 | |
252 void AnimateWindowTo(aura::Window* animate_window, | |
253 aura::Window* target_window) { | |
254 AnimateTransientGroupSetter setter(animate_window); | |
255 | |
256 WindowOverviewState* target_state = | |
257 target_window->GetProperty(kWindowOverviewState); | |
258 setter.SetWindowProgress(target_state->progress); | |
259 } | |
260 | |
261 // Always returns the same target. | |
262 class StaticWindowTargeter : public aura::WindowTargeter { | |
263 public: | |
264 explicit StaticWindowTargeter(aura::Window* target) : target_(target) {} | |
265 ~StaticWindowTargeter() override {} | |
266 | |
267 private: | |
268 // aura::WindowTargeter: | |
269 ui::EventTarget* FindTargetForEvent(ui::EventTarget* root, | |
270 ui::Event* event) override { | |
271 return target_; | |
272 } | |
273 | |
274 ui::EventTarget* FindTargetForLocatedEvent(ui::EventTarget* root, | |
275 ui::LocatedEvent* event) override { | |
276 return target_; | |
277 } | |
278 | |
279 aura::Window* target_; | |
280 DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter); | |
281 }; | |
282 | |
283 class WindowOverviewModeImpl : public WindowOverviewMode, | |
284 public ui::EventHandler, | |
285 public ui::CompositorAnimationObserver, | |
286 public WindowListProviderObserver { | |
287 public: | |
288 WindowOverviewModeImpl(aura::Window* container, | |
289 WindowListProvider* window_list_provider, | |
290 SplitViewController* split_view_controller, | |
291 WindowOverviewModeDelegate* delegate) | |
292 : container_(container), | |
293 window_list_provider_(window_list_provider), | |
294 split_view_controller_(split_view_controller), | |
295 delegate_(delegate), | |
296 scoped_targeter_(new aura::ScopedWindowTargeter( | |
297 container, | |
298 scoped_ptr<ui::EventTargeter>( | |
299 new StaticWindowTargeter(container)))), | |
300 dragged_window_(nullptr) { | |
301 CHECK(delegate_); | |
302 container_->set_target_handler(this); | |
303 | |
304 // Prepare the desired transforms for all the windows, and set the initial | |
305 // state on the windows. | |
306 ComputeTerminalStatesForAllWindows(); | |
307 SetInitialWindowStates(); | |
308 | |
309 window_list_provider_->AddObserver(this); | |
310 } | |
311 | |
312 ~WindowOverviewModeImpl() override { | |
313 window_list_provider_->RemoveObserver(this); | |
314 container_->set_target_handler(container_->delegate()); | |
315 RemoveAnimationObserver(); | |
316 const aura::Window::Windows& windows = | |
317 window_list_provider_->GetWindowList(); | |
318 if (windows.empty()) | |
319 return; | |
320 for (aura::Window* window : windows) | |
321 RestoreWindowState(window, split_view_controller_); | |
322 } | |
323 | |
324 private: | |
325 // Computes the transforms for all windows in both the topmost and bottom-most | |
326 // positions. The transforms are set in the |kWindowOverviewState| property of | |
327 // the windows. | |
328 void ComputeTerminalStatesForAllWindows() { | |
329 size_t index = 0; | |
330 | |
331 const aura::Window::Windows& windows = | |
332 window_list_provider_->GetWindowList(); | |
333 for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); | |
334 iter != windows.rend(); | |
335 ++iter, ++index) { | |
336 aura::Window* window = (*iter); | |
337 wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE); | |
338 | |
339 WindowOverviewState* state = new WindowOverviewState; | |
340 window->SetProperty(kWindowOverviewState, state); | |
341 if (split_view_controller_->IsSplitViewModeActive() && | |
342 (window == split_view_controller_->left_window() || | |
343 window == split_view_controller_->right_window())) { | |
344 // Do not let the left/right windows be scrolled. | |
345 gfx::Transform transform = | |
346 GetTransformForSplitWindow(window, kOverviewDefaultScale); | |
347 state->max_y = state->min_y = transform.To2dTranslation().y(); | |
348 state->split = true; | |
349 --index; | |
350 continue; | |
351 } | |
352 state->split = false; | |
353 UpdateTerminalStateForWindowAtIndex(window, index, windows.size()); | |
354 } | |
355 } | |
356 | |
357 // Computes the terminal states (i.e. the transforms for the top-most and | |
358 // bottom-most position in the stack) for |window|. |window_count| is the | |
359 // number of windows in the stack, and |index| is the position of the window | |
360 // in the stack (0 being the front-most window). | |
361 void UpdateTerminalStateForWindowAtIndex(aura::Window* window, | |
362 size_t index, | |
363 size_t window_count) { | |
364 const int kGapBetweenWindowsBottom = 10; | |
365 const int kGapBetweenWindowsTop = 5; | |
366 | |
367 int top = | |
368 (window_count - index - 1) * kGapBetweenWindowsTop + kSystemUIHeight; | |
369 int bottom = GetScrollableHeight() - (index * kGapBetweenWindowsBottom); | |
370 | |
371 WindowOverviewState* state = window->GetProperty(kWindowOverviewState); | |
372 CHECK(state); | |
373 if (state->split) | |
374 return; | |
375 state->min_y = top; | |
376 state->max_y = bottom - window->bounds().y(); | |
377 state->progress = 0.f; | |
378 } | |
379 | |
380 // Sets the initial position for the windows for the overview mode. | |
381 void SetInitialWindowStates() { | |
382 // The initial overview state of the topmost three windows. | |
383 const float kInitialProgress[] = { 0.5f, 0.05f, 0.01f }; | |
384 size_t index = 0; | |
385 const aura::Window::Windows& windows = | |
386 window_list_provider_->GetWindowList(); | |
387 for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); | |
388 iter != windows.rend(); | |
389 ++iter) { | |
390 float progress = 0.f; | |
391 aura::Window* window = *iter; | |
392 if (split_view_controller_->IsSplitViewModeActive() && | |
393 (window == split_view_controller_->left_window() || | |
394 window == split_view_controller_->right_window())) { | |
395 progress = 1; | |
396 } else { | |
397 if (index < arraysize(kInitialProgress)) | |
398 progress = kInitialProgress[index]; | |
399 ++index; | |
400 } | |
401 | |
402 TransientGroupSetter setter(window); | |
403 | |
404 // Unset any in-progress animation. | |
405 setter.AbortAllAnimations(); | |
406 | |
407 // Showing transient parent will show the transient children if any. | |
408 window->Show(); | |
409 | |
410 setter.SetTransform(gfx::Transform()); | |
411 // Setup the animation. | |
412 { | |
413 AnimateTransientGroupSetter setter(window); | |
414 setter.SetWindowProgress(progress); | |
415 } | |
416 } | |
417 } | |
418 | |
419 aura::Window* SelectWindowAt(ui::LocatedEvent* event) { | |
420 CHECK_EQ(container_, event->target()); | |
421 // Find the old targeter to find the target of the event. | |
422 ui::EventTarget* window = container_; | |
423 ui::EventTargeter* targeter = scoped_targeter_->old_targeter(); | |
424 while (!targeter && window->GetParentTarget()) { | |
425 window = window->GetParentTarget(); | |
426 targeter = window->GetEventTargeter(); | |
427 } | |
428 if (!targeter) | |
429 return nullptr; | |
430 aura::Window* target = static_cast<aura::Window*>( | |
431 targeter->FindTargetForLocatedEvent(container_, event)); | |
432 while (target && target->parent() != container_) | |
433 target = target->parent(); | |
434 aura::Window* transient_parent = | |
435 target ? wm::GetTransientParent(target) : nullptr; | |
436 return transient_parent ? transient_parent : target; | |
437 } | |
438 | |
439 // Scroll the window list by |delta_y| amount. |delta_y| is negative when | |
440 // scrolling up; and positive when scrolling down. | |
441 void DoScroll(float delta_y) { | |
442 const float kEpsilon = 1e-3f; | |
443 float delta_y_p = std::abs(delta_y) / GetScrollableHeight(); | |
444 const aura::Window::Windows& windows = | |
445 window_list_provider_->GetWindowList(); | |
446 if (delta_y < 0) { | |
447 // Scroll up. Start with the top-most (i.e. behind-most in terms of | |
448 // z-index) window, and try to scroll them up. | |
449 for (aura::Window::Windows::const_iterator iter = windows.begin(); | |
450 delta_y_p > kEpsilon && iter != windows.end(); | |
451 ++iter) { | |
452 aura::Window* window = (*iter); | |
453 WindowOverviewState* state = window->GetProperty(kWindowOverviewState); | |
454 if (state->progress > kEpsilon) { | |
455 // It is possible to scroll |window| up. Scroll it up, and update | |
456 // |delta_y_p| for the next window. | |
457 float apply = delta_y_p * state->progress; | |
458 TransientGroupSetter setter(window); | |
459 setter.SetWindowProgress(std::max(0.f, state->progress - apply * 3)); | |
460 delta_y_p -= apply; | |
461 } | |
462 } | |
463 } else { | |
464 // Scroll down. Start with the bottom-most (i.e. front-most in terms of | |
465 // z-index) window, and try to scroll them down. | |
466 aura::Window::Windows::const_reverse_iterator iter; | |
467 for (iter = windows.rbegin(); | |
468 delta_y_p > kEpsilon && iter != windows.rend(); | |
469 ++iter) { | |
470 aura::Window* window = (*iter); | |
471 WindowOverviewState* state = window->GetProperty(kWindowOverviewState); | |
472 if (1.f - state->progress > kEpsilon) { | |
473 // It is possible to scroll |window| down. Scroll it down, and update | |
474 // |delta_y_p| for the next window. | |
475 TransientGroupSetter setter(window); | |
476 setter.SetWindowProgress(std::min(1.f, state->progress + delta_y_p)); | |
477 delta_y_p /= 2.f; | |
478 } | |
479 } | |
480 } | |
481 } | |
482 | |
483 int GetScrollableHeight() const { | |
484 const float kScrollableFraction = 0.85f; | |
485 const float kScrollableFractionInSplit = 0.5f; | |
486 const float fraction = split_view_controller_->IsSplitViewModeActive() | |
487 ? kScrollableFractionInSplit | |
488 : kScrollableFraction; | |
489 return container_->bounds().height() * fraction; | |
490 } | |
491 | |
492 void CreateFlingerFor(const ui::GestureEvent& event) { | |
493 gfx::Vector2dF velocity(event.details().velocity_x(), | |
494 event.details().velocity_y()); | |
495 fling_.reset(new ui::FlingCurve(velocity, gfx::FrameTime::Now())); | |
496 } | |
497 | |
498 void AddAnimationObserver() { | |
499 ui::Compositor* compositor = container_->GetHost()->compositor(); | |
500 if (!compositor->HasAnimationObserver(this)) | |
501 compositor->AddAnimationObserver(this); | |
502 } | |
503 | |
504 void RemoveAnimationObserver() { | |
505 ui::Compositor* compositor = container_->GetHost()->compositor(); | |
506 if (compositor->HasAnimationObserver(this)) | |
507 compositor->RemoveAnimationObserver(this); | |
508 } | |
509 | |
510 aura::Window* GetSplitWindowDropTarget(const ui::GestureEvent& event) const { | |
511 if (!split_view_controller_->IsSplitViewModeActive()) | |
512 return nullptr; | |
513 CHECK(dragged_window_); | |
514 CHECK_NE(split_view_controller_->left_window(), dragged_window_); | |
515 CHECK_NE(split_view_controller_->right_window(), dragged_window_); | |
516 aura::Window* window = split_view_controller_->left_window(); | |
517 if (GetTransformedBounds(window).Contains(event.location())) | |
518 return window; | |
519 window = split_view_controller_->right_window(); | |
520 if (GetTransformedBounds(window).Contains(event.location())) | |
521 return window; | |
522 return nullptr; | |
523 } | |
524 | |
525 void DragWindow(const ui::GestureEvent& event) { | |
526 CHECK(dragged_window_); | |
527 CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type()); | |
528 CHECK(overview_toolbar_); | |
529 gfx::Vector2dF dragged_distance = | |
530 dragged_start_location_ - event.location(); | |
531 WindowOverviewState* dragged_state = | |
532 dragged_window_->GetProperty(kWindowOverviewState); | |
533 CHECK(dragged_state); | |
534 gfx::Transform transform = | |
535 GetTransformForState(dragged_window_, dragged_state); | |
536 transform.Translate(-dragged_distance.x(), 0); | |
537 TransientGroupSetter setter(dragged_window_); | |
538 setter.SetTransform(transform); | |
539 | |
540 // Update the toolbar. | |
541 const int kMinDistanceForActionButtons = 20; | |
542 if (fabs(dragged_distance.x()) > kMinDistanceForActionButtons) | |
543 overview_toolbar_->ShowActionButtons(); | |
544 else | |
545 overview_toolbar_->HideActionButtons(); | |
546 | |
547 // See if the touch-point is above one of the action-buttons. | |
548 OverviewToolbar::ActionType new_action = | |
549 overview_toolbar_->GetHighlightAction(event); | |
550 | |
551 // If the touch-point is not above any of the action buttons, then highlight | |
552 // the close-button by default, if the user has dragged enough to close the | |
553 // window. | |
554 if (new_action == OverviewToolbar::ACTION_TYPE_NONE) { | |
555 if (fabs(dragged_distance.x()) > kMinDistanceForDismissal) | |
556 new_action = OverviewToolbar::ACTION_TYPE_CLOSE; | |
557 else | |
558 new_action = OverviewToolbar::ACTION_TYPE_NONE; | |
559 } | |
560 OverviewToolbar::ActionType previous_action = | |
561 overview_toolbar_->current_action(); | |
562 overview_toolbar_->SetHighlightAction(new_action); | |
563 | |
564 aura::Window* split_drop = GetSplitWindowDropTarget(event); | |
565 | |
566 // If the user has selected to get into split-view mode, then show the | |
567 // window with full opacity. Otherwise, fade it out as it closes. Animate | |
568 // the opacity if transitioning to/from the split-view button. | |
569 bool animate_opacity = | |
570 (new_action != previous_action) && | |
571 ((new_action == OverviewToolbar::ACTION_TYPE_SPLIT) || | |
572 (previous_action == OverviewToolbar::ACTION_TYPE_SPLIT)); | |
573 float ratio = std::min( | |
574 1.f, std::abs(dragged_distance.x()) / kMinDistanceForDismissal); | |
575 float opacity = | |
576 (new_action == OverviewToolbar::ACTION_TYPE_SPLIT || split_drop) | |
577 ? 1 | |
578 : gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity); | |
579 if (animate_opacity) { | |
580 AnimateTransientGroupSetter setter(dragged_window_); | |
581 setter.SetOpacity(opacity); | |
582 } else { | |
583 TransientGroupSetter setter(dragged_window_); | |
584 setter.SetOpacity(opacity); | |
585 } | |
586 | |
587 if (split_view_controller_->IsSplitViewModeActive()) { | |
588 float scale = kOverviewDefaultScale; | |
589 if (split_drop == split_view_controller_->left_window()) | |
590 scale = kMaxScaleForSplitTarget; | |
591 TransformSplitWindowScale(split_view_controller_->left_window(), scale); | |
592 | |
593 scale = kOverviewDefaultScale; | |
594 if (split_drop == split_view_controller_->right_window()) | |
595 scale = kMaxScaleForSplitTarget; | |
596 TransformSplitWindowScale(split_view_controller_->right_window(), scale); | |
597 } | |
598 } | |
599 | |
600 bool ShouldCloseDragWindow(const ui::GestureEvent& event) const { | |
601 gfx::Vector2dF dragged_distance = | |
602 dragged_start_location_ - event.location(); | |
603 if (event.type() == ui::ET_GESTURE_SCROLL_END) | |
604 return std::abs(dragged_distance.x()) >= kMinDistanceForDismissal; | |
605 CHECK_EQ(ui::ET_SCROLL_FLING_START, event.type()); | |
606 const bool dragging_towards_right = dragged_distance.x() < 0; | |
607 const bool swipe_towards_right = event.details().velocity_x() > 0; | |
608 if (dragging_towards_right != swipe_towards_right) | |
609 return false; | |
610 const float kMinVelocityForDismissal = 500.f; | |
611 return std::abs(event.details().velocity_x()) > kMinVelocityForDismissal; | |
612 } | |
613 | |
614 void CloseDragWindow(const ui::GestureEvent& gesture) { | |
615 // Animate |dragged_window_| offscreen first, then destroy it. | |
616 { | |
617 AnimateTransientGroupSetter setter(dragged_window_); | |
618 | |
619 WindowOverviewState* dragged_state = | |
620 dragged_window_->GetProperty(kWindowOverviewState); | |
621 CHECK(dragged_state); | |
622 gfx::Transform transform = dragged_window_->layer()->transform(); | |
623 gfx::RectF transformed_bounds = dragged_window_->bounds(); | |
624 transform.TransformRect(&transformed_bounds); | |
625 float transform_x = 0.f; | |
626 if (gesture.location().x() > dragged_start_location_.x()) | |
627 transform_x = container_->bounds().right() - transformed_bounds.x(); | |
628 else | |
629 transform_x = -(transformed_bounds.x() + transformed_bounds.width()); | |
630 transform.Translate(transform_x / kOverviewDefaultScale, 0); | |
631 | |
632 setter.SetOpacity(kMinOpacity); | |
633 } | |
634 delete dragged_window_; | |
635 dragged_window_ = nullptr; | |
636 } | |
637 | |
638 void RestoreDragWindow() { | |
639 CHECK(dragged_window_); | |
640 WindowOverviewState* dragged_state = | |
641 dragged_window_->GetProperty(kWindowOverviewState); | |
642 CHECK(dragged_state); | |
643 | |
644 AnimateTransientGroupSetter setter(dragged_window_); | |
645 setter.SetTransform(GetTransformForState(dragged_window_, dragged_state)); | |
646 setter.SetOpacity(1.0f); | |
647 dragged_window_ = nullptr; | |
648 } | |
649 | |
650 void EndDragWindow(const ui::GestureEvent& gesture) { | |
651 CHECK(dragged_window_); | |
652 CHECK(overview_toolbar_); | |
653 OverviewToolbar::ActionType action = overview_toolbar_->current_action(); | |
654 overview_toolbar_.reset(); | |
655 if (action == OverviewToolbar::ACTION_TYPE_SPLIT) { | |
656 delegate_->OnSelectSplitViewWindow( | |
657 nullptr, dragged_window_, dragged_window_); | |
658 return; | |
659 } | |
660 | |
661 // If the window is dropped on one of the left/right windows in split-mode, | |
662 // then switch that window. | |
663 aura::Window* split_drop = GetSplitWindowDropTarget(gesture); | |
664 if (split_drop) { | |
665 aura::Window* left = split_view_controller_->left_window(); | |
666 aura::Window* right = split_view_controller_->right_window(); | |
667 if (left == split_drop) | |
668 left = dragged_window_; | |
669 else | |
670 right = dragged_window_; | |
671 delegate_->OnSelectSplitViewWindow(left, right, dragged_window_); | |
672 return; | |
673 } | |
674 | |
675 if (ShouldCloseDragWindow(gesture)) | |
676 CloseDragWindow(gesture); | |
677 else | |
678 RestoreDragWindow(); | |
679 } | |
680 | |
681 void SelectWindow(aura::Window* window) { | |
682 if (!split_view_controller_->IsSplitViewModeActive()) { | |
683 delegate_->OnSelectWindow(window); | |
684 } else { | |
685 // If the selected window is one of the left/right windows, then keep the | |
686 // current state. | |
687 if (window == split_view_controller_->left_window() || | |
688 window == split_view_controller_->right_window()) { | |
689 delegate_->OnSelectSplitViewWindow( | |
690 split_view_controller_->left_window(), | |
691 split_view_controller_->right_window(), | |
692 window); | |
693 } else { | |
694 delegate_->OnSelectWindow(window); | |
695 } | |
696 } | |
697 } | |
698 | |
699 // ui::EventHandler: | |
700 void OnMouseEvent(ui::MouseEvent* mouse) override { | |
701 if (mouse->type() == ui::ET_MOUSE_PRESSED) { | |
702 aura::Window* select = SelectWindowAt(mouse); | |
703 if (select) { | |
704 mouse->SetHandled(); | |
705 SelectWindow(select); | |
706 } | |
707 } else if (mouse->type() == ui::ET_MOUSEWHEEL) { | |
708 DoScroll(static_cast<ui::MouseWheelEvent*>(mouse)->y_offset()); | |
709 } | |
710 } | |
711 | |
712 void OnScrollEvent(ui::ScrollEvent* scroll) override { | |
713 if (scroll->type() == ui::ET_SCROLL) | |
714 DoScroll(scroll->y_offset()); | |
715 } | |
716 | |
717 void OnGestureEvent(ui::GestureEvent* gesture) override { | |
718 if (gesture->type() == ui::ET_GESTURE_TAP) { | |
719 aura::Window* select = SelectWindowAt(gesture); | |
720 if (select) { | |
721 gesture->SetHandled(); | |
722 SelectWindow(select); | |
723 } | |
724 } else if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) { | |
725 if (std::abs(gesture->details().scroll_x_hint()) > | |
726 std::abs(gesture->details().scroll_y_hint()) * 2) { | |
727 dragged_start_location_ = gesture->location(); | |
728 dragged_window_ = SelectWindowAt(gesture); | |
729 if (split_view_controller_->IsSplitViewModeActive() && | |
730 (dragged_window_ == split_view_controller_->left_window() || | |
731 dragged_window_ == split_view_controller_->right_window())) { | |
732 // TODO(sad): Allow closing the left/right window. Closing one of | |
733 // these windows will terminate the split-view mode. Until then, do | |
734 // not allow closing these (since otherwise it gets into an undefined | |
735 // state). | |
736 dragged_window_ = nullptr; | |
737 } | |
738 | |
739 if (dragged_window_) { | |
740 // Show the toolbar (for closing a window, or going into split-view | |
741 // mode). If already in split-view mode, then do not show the 'Split' | |
742 // option. | |
743 overview_toolbar_.reset(new OverviewToolbar(container_)); | |
744 if (!split_view_controller_->CanActivateSplitViewMode()) { | |
745 overview_toolbar_->DisableAction( | |
746 OverviewToolbar::ACTION_TYPE_SPLIT); | |
747 } | |
748 } | |
749 } | |
750 } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { | |
751 if (dragged_window_) | |
752 DragWindow(*gesture); | |
753 else | |
754 DoScroll(gesture->details().scroll_y()); | |
755 gesture->SetHandled(); | |
756 } else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { | |
757 if (dragged_window_) | |
758 EndDragWindow(*gesture); | |
759 gesture->SetHandled(); | |
760 } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { | |
761 if (dragged_window_) { | |
762 EndDragWindow(*gesture); | |
763 } else { | |
764 CreateFlingerFor(*gesture); | |
765 AddAnimationObserver(); | |
766 } | |
767 gesture->SetHandled(); | |
768 } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) { | |
769 if (fling_) { | |
770 fling_.reset(); | |
771 RemoveAnimationObserver(); | |
772 gesture->SetHandled(); | |
773 } | |
774 dragged_window_ = nullptr; | |
775 } | |
776 } | |
777 | |
778 // ui::CompositorAnimationObserver: | |
779 void OnAnimationStep(base::TimeTicks timestamp) override { | |
780 CHECK(fling_); | |
781 gfx::Vector2dF delta; | |
782 bool fling_active = fling_->ComputeScrollDeltaAtTime(timestamp, &delta); | |
783 | |
784 if (!delta.IsZero()) | |
785 DoScroll(delta.y()); | |
786 | |
787 if (!fling_active) { | |
788 fling_.reset(); | |
789 RemoveAnimationObserver(); | |
790 } | |
791 } | |
792 | |
793 // WindowListProviderObserver: | |
794 void OnWindowStackingChangedInList() override { | |
795 // Recompute the states of all windows. There isn't enough information at | |
796 // this point to do anything more clever. | |
797 ComputeTerminalStatesForAllWindows(); | |
798 SetInitialWindowStates(); | |
799 } | |
800 | |
801 void OnWindowAddedToList(aura::Window* removed_window) override {} | |
802 | |
803 void OnWindowRemovedFromList(aura::Window* removed_window, | |
804 int index) override { | |
805 const aura::Window::Windows& windows = | |
806 window_list_provider_->GetWindowList(); | |
807 if (windows.empty()) | |
808 return; | |
809 CHECK_LE(index, static_cast<int>(windows.size())); | |
810 if (index == 0) { | |
811 // The back-most window has been removed. Move all the remaining windows | |
812 // one step backwards. | |
813 for (int i = windows.size() - 1; i > 0; --i) { | |
814 UpdateTerminalStateForWindowAtIndex( | |
815 windows[i], windows.size() - 1 - i, windows.size()); | |
816 AnimateWindowTo(windows[i], windows[i - 1]); | |
817 } | |
818 UpdateTerminalStateForWindowAtIndex(windows.front(), | |
819 windows.size() - 1, | |
820 windows.size()); | |
821 AnimateWindowTo(windows.front(), removed_window); | |
822 } else { | |
823 // Move all windows behind the removed window one step forwards. | |
824 for (int i = 0; i < index - 1; ++i) { | |
825 UpdateTerminalStateForWindowAtIndex(windows[i], windows.size() - 1 - i, | |
826 windows.size()); | |
827 AnimateWindowTo(windows[i], windows[i + 1]); | |
828 } | |
829 UpdateTerminalStateForWindowAtIndex(windows[index - 1], | |
830 windows.size() - index, | |
831 windows.size()); | |
832 AnimateWindowTo(windows[index - 1], removed_window); | |
833 } | |
834 } | |
835 | |
836 const int kMinDistanceForDismissal = 300; | |
837 const float kMaxOpacity = 1.0f; | |
838 const float kMinOpacity = 0.2f; | |
839 const float kMaxScaleForSplitTarget = 0.9f; | |
840 | |
841 aura::Window* container_; | |
842 // Provider of the stack of windows to show in the overview mode. Not owned. | |
843 WindowListProvider* window_list_provider_; | |
844 SplitViewController* split_view_controller_; | |
845 | |
846 WindowOverviewModeDelegate* delegate_; | |
847 scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_; | |
848 scoped_ptr<ui::FlingCurve> fling_; | |
849 | |
850 aura::Window* dragged_window_; | |
851 gfx::Point dragged_start_location_; | |
852 scoped_ptr<OverviewToolbar> overview_toolbar_; | |
853 | |
854 DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl); | |
855 }; | |
856 | |
857 } // namespace | |
858 | |
859 // static | |
860 scoped_ptr<WindowOverviewMode> WindowOverviewMode::Create( | |
861 aura::Window* container, | |
862 WindowListProvider* window_list_provider, | |
863 SplitViewController* split_view_controller, | |
864 WindowOverviewModeDelegate* delegate) { | |
865 return scoped_ptr<WindowOverviewMode>( | |
866 new WindowOverviewModeImpl(container, window_list_provider, | |
867 split_view_controller, delegate)); | |
868 } | |
869 | |
870 } // namespace athena | |
OLD | NEW |