Index: athena/wm/window_overview_mode.cc |
diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc |
index a294cdec6e66df34328c89ec08afeb6d72ca16d6..e04858a3253a2d63354bfd9045cf3d50a8da6011 100644 |
--- a/athena/wm/window_overview_mode.cc |
+++ b/athena/wm/window_overview_mode.cc |
@@ -8,6 +8,7 @@ |
#include <functional> |
#include <vector> |
+#include "base/bind.h" |
#include "base/macros.h" |
#include "ui/aura/scoped_window_targeter.h" |
#include "ui/aura/window.h" |
@@ -40,6 +41,29 @@ struct WindowOverviewState { |
scoped_ptr<wm::Shadow> shadow; |
}; |
+// Runs a callback at the end of the animation. This observe also destroys |
+// itself afterwards. |
+class ClosureAnimationObserver : public ui::ImplicitAnimationObserver { |
+ public: |
+ explicit ClosureAnimationObserver(const base::Closure& closure) |
+ : closure_(closure) { |
+ DCHECK(!closure_.is_null()); |
+ } |
+ private: |
+ virtual ~ClosureAnimationObserver() { |
+ } |
+ |
+ // ui::ImplicitAnimationObserver: |
+ virtual void OnImplicitAnimationsCompleted() OVERRIDE { |
+ closure_.Run(); |
+ delete this; |
+ } |
+ |
+ const base::Closure closure_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ClosureAnimationObserver); |
+}; |
+ |
} // namespace |
DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState*) |
@@ -54,13 +78,18 @@ bool ShouldShowWindowInOverviewMode(aura::Window* window) { |
return window->type() == ui::wm::WINDOW_TYPE_NORMAL; |
} |
+// Gets the transform for the window in its current state. |
+gfx::Transform GetTransformForState(WindowOverviewState* state) { |
+ return gfx::Tween::TransformValueBetween(state->progress, |
+ state->top, |
+ state->bottom); |
+} |
+ |
// Sets the progress-state for the window in the overview mode. |
void SetWindowProgress(aura::Window* window, float progress) { |
WindowOverviewState* state = window->GetProperty(kWindowOverviewState); |
- gfx::Transform transform = |
- gfx::Tween::TransformValueBetween(progress, state->top, state->bottom); |
- window->SetTransform(transform); |
state->progress = progress; |
+ window->SetTransform(GetTransformForState(state)); |
} |
// Resets the overview-related state for |window|. |
@@ -108,7 +137,8 @@ class WindowOverviewModeImpl : public WindowOverviewMode, |
scoped_targeter_(new aura::ScopedWindowTargeter( |
container, |
scoped_ptr<ui::EventTargeter>( |
- new StaticWindowTargeter(container)))) { |
+ new StaticWindowTargeter(container)))), |
+ dragged_window_(NULL) { |
container_->set_target_handler(this); |
// Prepare the desired transforms for all the windows, and set the initial |
@@ -143,8 +173,6 @@ class WindowOverviewModeImpl : public WindowOverviewMode, |
const int kGapBetweenWindowsBottom = 10; |
const int kGapBetweenWindowsTop = 5; |
- const float kMinScale = 0.6f; |
- const float kMaxScale = 0.95f; |
for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); |
iter != windows.rend(); |
@@ -311,6 +339,101 @@ class WindowOverviewModeImpl : public WindowOverviewMode, |
compositor->RemoveAnimationObserver(this); |
} |
+ void DragWindow(const ui::GestureEvent& event) { |
+ CHECK(dragged_window_); |
+ CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type()); |
+ gfx::Vector2dF dragged_distance = |
+ dragged_start_location_ - event.location(); |
+ WindowOverviewState* dragged_state = |
+ dragged_window_->GetProperty(kWindowOverviewState); |
+ CHECK(dragged_state); |
+ gfx::Transform transform = GetTransformForState(dragged_state); |
+ transform.Translate(-dragged_distance.x(), 0); |
+ dragged_window_->SetTransform(transform); |
+ |
+ float ratio = std::min( |
+ 1.f, std::abs(dragged_distance.x()) / kMinDistanceForDismissal); |
+ float opacity = |
+ gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity); |
+ dragged_window_->layer()->SetOpacity(opacity); |
+ } |
+ |
+ bool ShouldCloseDragWindow(const ui::GestureEvent& event) const { |
+ gfx::Vector2dF dragged_distance = |
+ dragged_start_location_ - event.location(); |
+ if (event.type() == ui::ET_GESTURE_SCROLL_END) |
+ return std::abs(dragged_distance.x()) >= kMinDistanceForDismissal; |
+ CHECK_EQ(ui::ET_SCROLL_FLING_START, event.type()); |
+ const bool dragging_towards_right = dragged_distance.x() < 0; |
+ const bool swipe_towards_right = event.details().velocity_x() > 0; |
+ if (dragging_towards_right != swipe_towards_right) |
+ return false; |
+ const float kMinVelocityForDismissal = 500.f; |
+ return std::abs(event.details().velocity_x()) > kMinVelocityForDismissal; |
+ } |
+ |
+ void CloseDragWindow(const ui::GestureEvent& gesture) { |
+ // Animate |dragged_window_| offscreen first, then destroy it. |
+ ui::ScopedLayerAnimationSettings settings( |
+ dragged_window_->layer()->GetAnimator()); |
+ settings.SetPreemptionStrategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ settings.AddObserver(new ClosureAnimationObserver( |
+ base::Bind(&base::DeletePointer<aura::Window>, dragged_window_))); |
+ |
+ WindowOverviewState* dragged_state = |
+ dragged_window_->GetProperty(kWindowOverviewState); |
+ CHECK(dragged_state); |
+ gfx::Transform transform = dragged_window_->layer()->transform(); |
+ gfx::RectF transformed_bounds = dragged_window_->bounds(); |
+ transform.TransformRect(&transformed_bounds); |
+ float transform_x = 0.f; |
+ if (gesture.location().x() > dragged_start_location_.x()) |
+ transform_x = container_->bounds().right() - transformed_bounds.x(); |
+ else |
+ transform_x = -(transformed_bounds.x() + transformed_bounds.width()); |
+ float scale = gfx::Tween::FloatValueBetween( |
+ dragged_state->progress, kMinScale, kMaxScale); |
+ transform.Translate(transform_x / scale, 0); |
+ dragged_window_->SetTransform(transform); |
+ dragged_window_->layer()->SetOpacity(kMinOpacity); |
+ |
+ // Move the windows behind |dragged_window_| in the stack forward one step. |
+ const aura::Window::Windows& list = container_->children(); |
+ for (aura::Window::Windows::const_iterator iter = list.begin(); |
+ iter != list.end() && *iter != dragged_window_; |
+ ++iter) { |
+ aura::Window* window = *iter; |
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); |
+ settings.SetPreemptionStrategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ WindowOverviewState* state = window->GetProperty(kWindowOverviewState); |
+ |
+ aura::Window* next = *(iter + 1); |
+ WindowOverviewState* next_state = next->GetProperty(kWindowOverviewState); |
+ state->top = next_state->top; |
+ state->bottom = next_state->bottom; |
+ SetWindowProgress(window, next_state->progress); |
+ } |
+ |
+ dragged_window_ = NULL; |
+ } |
+ |
+ void RestoreDragWindow() { |
+ CHECK(dragged_window_); |
+ WindowOverviewState* dragged_state = |
+ dragged_window_->GetProperty(kWindowOverviewState); |
+ CHECK(dragged_state); |
+ |
+ ui::ScopedLayerAnimationSettings settings( |
+ dragged_window_->layer()->GetAnimator()); |
+ settings.SetPreemptionStrategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ dragged_window_->SetTransform(GetTransformForState(dragged_state)); |
+ dragged_window_->layer()->SetOpacity(1.f); |
+ dragged_window_ = NULL; |
+ } |
+ |
// ui::EventHandler: |
virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE { |
if (mouse->type() == ui::ET_MOUSE_PRESSED) { |
@@ -336,17 +459,44 @@ class WindowOverviewModeImpl : public WindowOverviewMode, |
gesture->SetHandled(); |
delegate_->OnSelectWindow(select); |
} |
+ } else if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) { |
+ if (std::abs(gesture->details().scroll_x_hint()) > |
+ std::abs(gesture->details().scroll_y_hint()) * 2) { |
+ dragged_start_location_ = gesture->location(); |
+ dragged_window_ = SelectWindowAt(gesture); |
+ } |
} else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { |
- DoScroll(gesture->details().scroll_y()); |
+ if (dragged_window_) |
+ DragWindow(*gesture); |
+ else |
+ DoScroll(gesture->details().scroll_y()); |
gesture->SetHandled(); |
- } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { |
- CreateFlingerFor(*gesture); |
- AddAnimationObserver(); |
+ } else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { |
+ if (dragged_window_) { |
+ if (ShouldCloseDragWindow(*gesture)) |
+ CloseDragWindow(*gesture); |
+ else |
+ RestoreDragWindow(); |
+ } |
gesture->SetHandled(); |
- } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN && fling_) { |
- fling_.reset(); |
- RemoveAnimationObserver(); |
+ } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { |
+ if (dragged_window_) { |
+ if (ShouldCloseDragWindow(*gesture)) |
+ CloseDragWindow(*gesture); |
+ else |
+ RestoreDragWindow(); |
+ } else { |
+ CreateFlingerFor(*gesture); |
+ AddAnimationObserver(); |
+ } |
gesture->SetHandled(); |
+ } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) { |
+ if (fling_) { |
+ fling_.reset(); |
+ RemoveAnimationObserver(); |
+ gesture->SetHandled(); |
+ } |
+ dragged_window_ = NULL; |
} |
} |
@@ -364,11 +514,20 @@ class WindowOverviewModeImpl : public WindowOverviewMode, |
} |
} |
+ const int kMinDistanceForDismissal = 300; |
+ const float kMinScale = 0.6f; |
+ const float kMaxScale = 0.95f; |
+ const float kMaxOpacity = 1.0f; |
+ const float kMinOpacity = 0.2f; |
+ |
aura::Window* container_; |
WindowOverviewModeDelegate* delegate_; |
scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_; |
scoped_ptr<ui::FlingCurve> fling_; |
+ aura::Window* dragged_window_; |
+ gfx::Point dragged_start_location_; |
+ |
DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl); |
}; |