[Smart Lock] Update some more UI strings.
[chromium-blink-merge.git] / athena / wm / window_overview_mode.cc
blob2f39c415528ee94094b724ecd2e718cf31858261
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.
5 #include "athena/wm/window_overview_mode.h"
7 #include <complex>
8 #include <vector>
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"
36 namespace {
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;
43 // The top-most and bottom-most vertical position of the window in overview
44 // mode.
45 float max_y;
46 float min_y;
48 // |split| is set if this window is one of the two split windows in split-view
49 // mode.
50 bool split;
53 } // namespace
55 DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState*);
56 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowOverviewState,
57 kWindowOverviewState,
58 nullptr);
60 namespace athena {
62 namespace {
64 const float kOverviewDefaultScale = 0.75f;
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;
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);
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);
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;
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) {
110 ~TransientGroupSetter() {}
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();
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);
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);
141 // Apply the transform with the overview scroll |progress|.
142 void SetWindowProgress(float progress) {
143 WindowOverviewState* state = window_->GetProperty(kWindowOverviewState);
144 state->progress = progress;
146 SetTransform(GetTransformForState(window_, state));
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;
160 aura::Window* window_;
162 DISALLOW_COPY_AND_ASSIGN(TransientGroupSetter);
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));
176 ~AnimateTransientGroupSetter() {}
178 ui::ScopedLayerAnimationSettings* GetMainWindowAnimationSettings() {
179 CHECK(animation_settings_.size());
180 return animation_settings_[0];
183 private:
184 static ui::ScopedLayerAnimationSettings* CreateScopedLayerAnimationSettings(
185 aura::Window* window) {
186 const int kTransitionMs = 250;
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;
197 ScopedVector<ui::ScopedLayerAnimationSettings> animation_settings_;
198 DISALLOW_COPY_AND_ASSIGN(AnimateTransientGroupSetter);
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;
212 if (should_hide)
213 window->Hide();
216 // Resets the overview-related state for |window|.
217 void RestoreWindowState(aura::Window* window,
218 SplitViewController* split_view_controller) {
219 window->ClearProperty(kWindowOverviewState);
221 AnimateTransientGroupSetter setter(window);
223 setter.GetMainWindowAnimationSettings()->AddObserver(
224 new ui::ClosureAnimationObserver(
225 base::Bind(&HideWindowIfNotVisible, window, split_view_controller)));
227 setter.SetTransform(gfx::Transform());
228 // Reset the window opacity in case the user is dragging a window.
229 setter.SetOpacity(1.0f);
231 wm::SetShadowType(window, wm::SHADOW_TYPE_NONE);
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;
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));
252 void AnimateWindowTo(aura::Window* animate_window,
253 aura::Window* target_window) {
254 AnimateTransientGroupSetter setter(animate_window);
256 WindowOverviewState* target_state =
257 target_window->GetProperty(kWindowOverviewState);
258 setter.SetWindowProgress(target_state->progress);
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 {}
267 private:
268 // aura::WindowTargeter:
269 ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
270 ui::Event* event) override {
271 return target_;
274 ui::EventTarget* FindTargetForLocatedEvent(ui::EventTarget* root,
275 ui::LocatedEvent* event) override {
276 return target_;
279 aura::Window* target_;
280 DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter);
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);
304 // Prepare the desired transforms for all the windows, and set the initial
305 // state on the windows.
306 ComputeTerminalStatesForAllWindows();
307 SetInitialWindowStates();
309 window_list_provider_->AddObserver(this);
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_);
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;
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);
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;
352 state->split = false;
353 UpdateTerminalStateForWindowAtIndex(window, index, windows.size());
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;
367 int top =
368 (window_count - index - 1) * kGapBetweenWindowsTop + kSystemUIHeight;
369 int bottom = GetScrollableHeight() - (index * kGapBetweenWindowsBottom);
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;
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;
402 TransientGroupSetter setter(window);
404 // Unset any in-progress animation.
405 setter.AbortAllAnimations();
407 // Showing transient parent will show the transient children if any.
408 window->Show();
410 setter.SetTransform(gfx::Transform());
411 // Setup the animation.
413 AnimateTransientGroupSetter setter(window);
414 setter.SetWindowProgress(progress);
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();
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;
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;
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;
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;
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()));
498 void AddAnimationObserver() {
499 ui::Compositor* compositor = container_->GetHost()->compositor();
500 if (!compositor->HasAnimationObserver(this))
501 compositor->AddAnimationObserver(this);
504 void RemoveAnimationObserver() {
505 ui::Compositor* compositor = container_->GetHost()->compositor();
506 if (compositor->HasAnimationObserver(this))
507 compositor->RemoveAnimationObserver(this);
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;
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);
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();
547 // See if the touch-point is above one of the action-buttons.
548 OverviewToolbar::ActionType new_action =
549 overview_toolbar_->GetHighlightAction(event);
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;
560 OverviewToolbar::ActionType previous_action =
561 overview_toolbar_->current_action();
562 overview_toolbar_->SetHighlightAction(new_action);
564 aura::Window* split_drop = GetSplitWindowDropTarget(event);
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)
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);
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);
593 scale = kOverviewDefaultScale;
594 if (split_drop == split_view_controller_->right_window())
595 scale = kMaxScaleForSplitTarget;
596 TransformSplitWindowScale(split_view_controller_->right_window(), scale);
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;
614 void CloseDragWindow(const ui::GestureEvent& gesture) {
615 // Animate |dragged_window_| offscreen first, then destroy it.
617 AnimateTransientGroupSetter setter(dragged_window_);
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);
632 setter.SetOpacity(kMinOpacity);
634 delete dragged_window_;
635 dragged_window_ = nullptr;
638 void RestoreDragWindow() {
639 CHECK(dragged_window_);
640 WindowOverviewState* dragged_state =
641 dragged_window_->GetProperty(kWindowOverviewState);
642 CHECK(dragged_state);
644 AnimateTransientGroupSetter setter(dragged_window_);
645 setter.SetTransform(GetTransformForState(dragged_window_, dragged_state));
646 setter.SetOpacity(1.0f);
647 dragged_window_ = nullptr;
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;
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;
675 if (ShouldCloseDragWindow(gesture))
676 CloseDragWindow(gesture);
677 else
678 RestoreDragWindow();
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);
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);
707 } else if (mouse->type() == ui::ET_MOUSEWHEEL) {
708 DoScroll(static_cast<ui::MouseWheelEvent*>(mouse)->y_offset());
712 void OnScrollEvent(ui::ScrollEvent* scroll) override {
713 if (scroll->type() == ui::ET_SCROLL)
714 DoScroll(scroll->y_offset());
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);
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;
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);
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();
767 gesture->SetHandled();
768 } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) {
769 if (fling_) {
770 fling_.reset();
771 RemoveAnimationObserver();
772 gesture->SetHandled();
774 dragged_window_ = nullptr;
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);
784 if (!delta.IsZero())
785 DoScroll(delta.y());
787 if (!fling_active) {
788 fling_.reset();
789 RemoveAnimationObserver();
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();
801 void OnWindowAddedToList(aura::Window* removed_window) override {}
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]);
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]);
829 UpdateTerminalStateForWindowAtIndex(windows[index - 1],
830 windows.size() - index,
831 windows.size());
832 AnimateWindowTo(windows[index - 1], removed_window);
836 const int kMinDistanceForDismissal = 300;
837 const float kMaxOpacity = 1.0f;
838 const float kMinOpacity = 0.2f;
839 const float kMaxScaleForSplitTarget = 0.9f;
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_;
846 WindowOverviewModeDelegate* delegate_;
847 scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_;
848 scoped_ptr<ui::FlingCurve> fling_;
850 aura::Window* dragged_window_;
851 gfx::Point dragged_start_location_;
852 scoped_ptr<OverviewToolbar> overview_toolbar_;
854 DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl);
857 } // namespace
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));
870 } // namespace athena