| 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 |