// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "athena/wm/title_drag_controller.h" #include "base/bind.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/base/hit_test.h" #include "ui/compositor/closure_animation_observer.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/wm/core/shadow.h" #include "ui/wm/core/window_util.h" namespace { // The minimum amount to drag to confirm a window switch at the end of the // non-fling gesture. const int kMinDragDistanceForSwitch = 300; // The minimum velocity to confirm a window switch for a fling (only applicable // if the amount dragged was not sufficient, i.e. smaller than // kMinDragDistanceForSwitch). const int kMinDragVelocityForSwitch = 5000; } namespace athena { TitleDragController::TitleDragController(aura::Window* container, TitleDragControllerDelegate* delegate) : container_(container), delegate_(delegate), weak_ptr_(this) { CHECK(container_); CHECK(delegate_); container_->AddPreTargetHandler(this); } TitleDragController::~TitleDragController() { container_->RemovePreTargetHandler(this); } void TitleDragController::EndTransition(aura::Window* window, bool complete) { ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); settings.AddObserver(new ui::ClosureAnimationObserver( base::Bind(&TitleDragController::OnTransitionEnd, weak_ptr_.GetWeakPtr(), window, complete))); gfx::Transform transform; transform.Translate(0, complete ? window->bounds().height() : 0); window->SetTransform(transform); } void TitleDragController::OnTransitionEnd(aura::Window* window, bool complete) { weak_ptr_.InvalidateWeakPtrs(); if (!tracker_.Contains(window)) window = NULL; shadow_.reset(); if (window) { window->SetTransform(gfx::Transform()); tracker_.Remove(window); } if (complete && window && wm::IsActiveWindow(window)) delegate_->OnTitleDragCompleted(window); else delegate_->OnTitleDragCanceled(window); } void TitleDragController::OnGestureEvent(ui::GestureEvent* gesture) { // Do not process any gesture events if an animation is still in progress from // a previous drag. if (weak_ptr_.HasWeakPtrs()) return; if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) { // It is possible to start a gesture sequence on a second window while a // drag is already in progress (e.g. the user starts interacting with the // window that is being revealed by the title-drag). Ignore these gesture // sequences. if (!tracker_.windows().empty()) return; aura::Window* window = static_cast(gesture->target()); if (!window || !window->delegate()) return; int component = window->delegate()->GetNonClientComponent(gesture->location()); if (component != HTCAPTION) return; if (!delegate_->GetWindowBehind(window)) return; tracker_.Add(window); drag_start_location_ = gesture->root_location(); return; } // If this gesture is for a different window, then ignore. aura::Window* window = static_cast(gesture->target()); if (!tracker_.Contains(window)) return; if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) { delegate_->OnTitleDragStarted(window); shadow_.reset(new wm::Shadow()); shadow_->Init(wm::Shadow::STYLE_ACTIVE); shadow_->SetContentBounds(gfx::Rect(window->bounds().size())); window->layer()->Add(shadow_->layer()); gesture->SetHandled(); return; } if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { gfx::Vector2dF distance = gesture->root_location() - drag_start_location_; gfx::Transform transform; transform.Translate(0, std::max(0.f, distance.y())); window->SetTransform(transform); gesture->SetHandled(); return; } if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { gfx::Vector2dF distance = gesture->root_location() - drag_start_location_; EndTransition(window, distance.y() >= kMinDragDistanceForSwitch); gesture->SetHandled(); return; } if (gesture->type() == ui::ET_SCROLL_FLING_START) { gfx::Vector2dF distance = gesture->root_location() - drag_start_location_; bool swipe_downwards = gesture->details().velocity_y() > 0; EndTransition( window, swipe_downwards && (distance.y() >= kMinDragDistanceForSwitch || gesture->details().velocity_y() >= kMinDragVelocityForSwitch)); gesture->SetHandled(); } } } // namespace athena