blob: a0301e101a224c4c14b0b2f33ee70e52335950a3 [file] [log] [blame]
// 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 = nullptr;
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<aura::Window*>(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<aura::Window*>(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