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