blob: 6a3a55f50d787c868633e0df24a575adadf21a90 [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/window_overview_mode.h"
#include <complex>
#include <vector>
#include "athena/wm/overview_toolbar.h"
#include "athena/wm/public/window_list_provider.h"
#include "athena/wm/public/window_list_provider_observer.h"
#include "athena/wm/split_view_controller.h"
#include "base/bind.h"
#include "base/memory/scoped_vector.h"
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_property.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/closure_animation_observer.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/compositor_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event_handler.h"
#include "ui/events/gestures/fling_curve.h"
#include "ui/gfx/frame_time.h"
#include "ui/gfx/transform.h"
#include "ui/wm/core/shadow_types.h"
#include "ui/wm/core/window_animations.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
namespace {
struct WindowOverviewState {
// The current overview state of the window. 0.f means the window is at the
// topmost position. 1.f means the window is at the bottom-most position.
float progress;
// The top-most and bottom-most vertical position of the window in overview
// mode.
float max_y;
float min_y;
// |split| is set if this window is one of the two split windows in split-view
// mode.
bool split;
};
} // namespace
DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState*);
DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowOverviewState,
kWindowOverviewState,
nullptr);
namespace athena {
namespace {
const float kOverviewDefaultScale = 0.75f;
gfx::Transform GetTransformForSplitWindow(aura::Window* window, float scale) {
const float kScrollWindowPositionInOverview = 0.65f;
int x_translate = window->bounds().width() * (1 - scale) / 2;
gfx::Transform transform;
transform.Translate(
x_translate, window->bounds().height() * kScrollWindowPositionInOverview);
transform.Scale(scale, scale);
return transform;
}
// Gets the transform for the window in its current state.
gfx::Transform GetTransformForState(aura::Window* window,
WindowOverviewState* state) {
if (state->split)
return GetTransformForSplitWindow(window, kOverviewDefaultScale);
const float kProgressToStartShrinking = 0.07;
const float kOverviewScale = 0.75f;
float scale = kOverviewScale;
if (state->progress < kProgressToStartShrinking) {
const float kShrunkMinimumScale = 0.7f;
scale = gfx::Tween::FloatValueBetween(
state->progress / kProgressToStartShrinking,
kShrunkMinimumScale,
kOverviewScale);
}
int container_width = window->parent()->bounds().width();
int window_width = window->bounds().width();
int window_x = window->bounds().x();
float x_translate = (container_width - (window_width * scale)) / 2 - window_x;
float y_translate = gfx::Tween::FloatValueBetween(
state->progress, state->min_y, state->max_y);
gfx::Transform transform;
transform.Translate(x_translate, y_translate);
transform.Scale(scale, scale);
return transform;
}
// A utility class used to set the transform/opacity to the window and
// its transient children.
class TransientGroupSetter {
public:
explicit TransientGroupSetter(aura::Window* window) : window_(window) {
}
~TransientGroupSetter() {}
// Aborts all animations including its transient children.
void AbortAllAnimations() {
window_->layer()->GetAnimator()->AbortAllAnimations();
for (aura::Window* transient_child : wm::GetTransientChildren(window_))
transient_child->layer()->GetAnimator()->AbortAllAnimations();
}
// Applys transform to the window and its transient children.
// Transient children gets a tranfrorm with the offset relateive
// it its transient parent.
void SetTransform(const gfx::Transform& transform) {
window_->SetTransform(transform);
for (aura::Window* transient_child : wm::GetTransientChildren(window_)) {
gfx::Rect window_bounds = window_->bounds();
gfx::Rect child_bounds = transient_child->bounds();
gfx::Transform transient_window_transform(TranslateTransformOrigin(
child_bounds.origin() - window_bounds.origin(), transform));
transient_child->SetTransform(transient_window_transform);
}
}
// Sets the opacity to the window and its transient children.
void SetOpacity(float opacity) {
window_->layer()->SetOpacity(opacity);
for (aura::Window* transient_child : wm::GetTransientChildren(window_)) {
transient_child->layer()->SetOpacity(opacity);
}
}
// Apply the transform with the overview scroll |progress|.
void SetWindowProgress(float progress) {
WindowOverviewState* state = window_->GetProperty(kWindowOverviewState);
state->progress = progress;
SetTransform(GetTransformForState(window_, state));
}
private:
static gfx::Transform TranslateTransformOrigin(
const gfx::Vector2d& new_origin,
const gfx::Transform& transform) {
gfx::Transform result;
result.Translate(-new_origin.x(), -new_origin.y());
result.PreconcatTransform(transform);
result.Translate(new_origin.x(), new_origin.y());
return result;
}
aura::Window* window_;
DISALLOW_COPY_AND_ASSIGN(TransientGroupSetter);
};
// TransientGroupSetter with animation.
class AnimateTransientGroupSetter : public TransientGroupSetter {
public:
explicit AnimateTransientGroupSetter(aura::Window* window)
: TransientGroupSetter(window) {
animation_settings_.push_back(CreateScopedLayerAnimationSettings(window));
for (aura::Window* transient_child : wm::GetTransientChildren(window)) {
animation_settings_.push_back(
CreateScopedLayerAnimationSettings(transient_child));
}
}
~AnimateTransientGroupSetter() {}
ui::ScopedLayerAnimationSettings* GetMainWindowAnimationSettings() {
CHECK(animation_settings_.size());
return animation_settings_[0];
}
private:
static ui::ScopedLayerAnimationSettings* CreateScopedLayerAnimationSettings(
aura::Window* window) {
const int kTransitionMs = 250;
ui::ScopedLayerAnimationSettings* settings =
new ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator());
settings->SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kTransitionMs));
return settings;
}
ScopedVector<ui::ScopedLayerAnimationSettings> animation_settings_;
DISALLOW_COPY_AND_ASSIGN(AnimateTransientGroupSetter);
};
void HideWindowIfNotVisible(aura::Window* window,
SplitViewController* split_view_controller) {
bool should_hide = true;
if (split_view_controller->IsSplitViewModeActive()) {
should_hide = window != split_view_controller->left_window() &&
window != split_view_controller->right_window();
} else {
aura::Window* active = aura::client::GetActivationClient(
window->GetRootWindow())->GetActiveWindow();
should_hide = active != window && wm::GetTransientParent(active) != window;
}
if (should_hide)
window->Hide();
}
// Resets the overview-related state for |window|.
void RestoreWindowState(aura::Window* window,
SplitViewController* split_view_controller) {
window->ClearProperty(kWindowOverviewState);
AnimateTransientGroupSetter setter(window);
setter.GetMainWindowAnimationSettings()->AddObserver(
new ui::ClosureAnimationObserver(
base::Bind(&HideWindowIfNotVisible, window, split_view_controller)));
setter.SetTransform(gfx::Transform());
// Reset the window opacity in case the user is dragging a window.
setter.SetOpacity(1.0f);
wm::SetShadowType(window, wm::SHADOW_TYPE_NONE);
}
gfx::RectF GetTransformedBounds(aura::Window* window) {
gfx::Transform transform;
gfx::RectF bounds = window->bounds();
transform.Translate(bounds.x(), bounds.y());
transform.PreconcatTransform(window->layer()->transform());
transform.Translate(-bounds.x(), -bounds.y());
transform.TransformRect(&bounds);
return bounds;
}
void TransformSplitWindowScale(aura::Window* window, float scale) {
gfx::Transform transform = window->layer()->GetTargetTransform();
if (transform.Scale2d() == gfx::Vector2dF(scale, scale))
return;
AnimateTransientGroupSetter setter(window);
setter.SetTransform(GetTransformForSplitWindow(window, scale));
}
void AnimateWindowTo(aura::Window* animate_window,
aura::Window* target_window) {
AnimateTransientGroupSetter setter(animate_window);
WindowOverviewState* target_state =
target_window->GetProperty(kWindowOverviewState);
setter.SetWindowProgress(target_state->progress);
}
// Always returns the same target.
class StaticWindowTargeter : public aura::WindowTargeter {
public:
explicit StaticWindowTargeter(aura::Window* target) : target_(target) {}
~StaticWindowTargeter() override {}
private:
// aura::WindowTargeter:
ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
ui::Event* event) override {
return target_;
}
ui::EventTarget* FindTargetForLocatedEvent(ui::EventTarget* root,
ui::LocatedEvent* event) override {
return target_;
}
aura::Window* target_;
DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter);
};
class WindowOverviewModeImpl : public WindowOverviewMode,
public ui::EventHandler,
public ui::CompositorAnimationObserver,
public WindowListProviderObserver {
public:
WindowOverviewModeImpl(aura::Window* container,
WindowListProvider* window_list_provider,
SplitViewController* split_view_controller,
WindowOverviewModeDelegate* delegate)
: container_(container),
window_list_provider_(window_list_provider),
split_view_controller_(split_view_controller),
delegate_(delegate),
scoped_targeter_(new aura::ScopedWindowTargeter(
container,
scoped_ptr<ui::EventTargeter>(
new StaticWindowTargeter(container)))),
dragged_window_(nullptr) {
CHECK(delegate_);
container_->set_target_handler(this);
// Prepare the desired transforms for all the windows, and set the initial
// state on the windows.
ComputeTerminalStatesForAllWindows();
SetInitialWindowStates();
window_list_provider_->AddObserver(this);
}
~WindowOverviewModeImpl() override {
window_list_provider_->RemoveObserver(this);
container_->set_target_handler(container_->delegate());
RemoveAnimationObserver();
const aura::Window::Windows& windows =
window_list_provider_->GetWindowList();
if (windows.empty())
return;
for (aura::Window* window : windows)
RestoreWindowState(window, split_view_controller_);
}
private:
// Computes the transforms for all windows in both the topmost and bottom-most
// positions. The transforms are set in the |kWindowOverviewState| property of
// the windows.
void ComputeTerminalStatesForAllWindows() {
size_t index = 0;
const aura::Window::Windows& windows =
window_list_provider_->GetWindowList();
for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
iter != windows.rend();
++iter, ++index) {
aura::Window* window = (*iter);
wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE);
WindowOverviewState* state = new WindowOverviewState;
window->SetProperty(kWindowOverviewState, state);
if (split_view_controller_->IsSplitViewModeActive() &&
(window == split_view_controller_->left_window() ||
window == split_view_controller_->right_window())) {
// Do not let the left/right windows be scrolled.
gfx::Transform transform =
GetTransformForSplitWindow(window, kOverviewDefaultScale);
state->max_y = state->min_y = transform.To2dTranslation().y();
state->split = true;
--index;
continue;
}
state->split = false;
UpdateTerminalStateForWindowAtIndex(window, index, windows.size());
}
}
// Computes the terminal states (i.e. the transforms for the top-most and
// bottom-most position in the stack) for |window|. |window_count| is the
// number of windows in the stack, and |index| is the position of the window
// in the stack (0 being the front-most window).
void UpdateTerminalStateForWindowAtIndex(aura::Window* window,
size_t index,
size_t window_count) {
const int kGapBetweenWindowsBottom = 10;
const int kGapBetweenWindowsTop = 5;
int top = (window_count - index - 1) * kGapBetweenWindowsTop;
int bottom = GetScrollableHeight() - (index * kGapBetweenWindowsBottom);
WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
CHECK(state);
if (state->split)
return;
state->min_y = top;
state->max_y = bottom - window->bounds().y();
state->progress = 0.f;
}
// Sets the initial position for the windows for the overview mode.
void SetInitialWindowStates() {
// The initial overview state of the topmost three windows.
const float kInitialProgress[] = { 0.5f, 0.05f, 0.01f };
size_t index = 0;
const aura::Window::Windows& windows =
window_list_provider_->GetWindowList();
for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
iter != windows.rend();
++iter) {
float progress = 0.f;
aura::Window* window = *iter;
if (split_view_controller_->IsSplitViewModeActive() &&
(window == split_view_controller_->left_window() ||
window == split_view_controller_->right_window())) {
progress = 1;
} else {
if (index < arraysize(kInitialProgress))
progress = kInitialProgress[index];
++index;
}
TransientGroupSetter setter(window);
// Unset any in-progress animation.
setter.AbortAllAnimations();
// Showing transient parent will show the transient children if any.
window->Show();
setter.SetTransform(gfx::Transform());
// Setup the animation.
{
AnimateTransientGroupSetter setter(window);
setter.SetWindowProgress(progress);
}
}
}
aura::Window* SelectWindowAt(ui::LocatedEvent* event) {
CHECK_EQ(container_, event->target());
// Find the old targeter to find the target of the event.
ui::EventTarget* window = container_;
ui::EventTargeter* targeter = scoped_targeter_->old_targeter();
while (!targeter && window->GetParentTarget()) {
window = window->GetParentTarget();
targeter = window->GetEventTargeter();
}
if (!targeter)
return nullptr;
aura::Window* target = static_cast<aura::Window*>(
targeter->FindTargetForLocatedEvent(container_, event));
while (target && target->parent() != container_)
target = target->parent();
aura::Window* transient_parent =
target ? wm::GetTransientParent(target) : nullptr;
return transient_parent ? transient_parent : target;
}
// Scroll the window list by |delta_y| amount. |delta_y| is negative when
// scrolling up; and positive when scrolling down.
void DoScroll(float delta_y) {
const float kEpsilon = 1e-3f;
float delta_y_p = std::abs(delta_y) / GetScrollableHeight();
const aura::Window::Windows& windows =
window_list_provider_->GetWindowList();
if (delta_y < 0) {
// Scroll up. Start with the top-most (i.e. behind-most in terms of
// z-index) window, and try to scroll them up.
for (aura::Window::Windows::const_iterator iter = windows.begin();
delta_y_p > kEpsilon && iter != windows.end();
++iter) {
aura::Window* window = (*iter);
WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
if (state->progress > kEpsilon) {
// It is possible to scroll |window| up. Scroll it up, and update
// |delta_y_p| for the next window.
float apply = delta_y_p * state->progress;
TransientGroupSetter setter(window);
setter.SetWindowProgress(std::max(0.f, state->progress - apply * 3));
delta_y_p -= apply;
}
}
} else {
// Scroll down. Start with the bottom-most (i.e. front-most in terms of
// z-index) window, and try to scroll them down.
aura::Window::Windows::const_reverse_iterator iter;
for (iter = windows.rbegin();
delta_y_p > kEpsilon && iter != windows.rend();
++iter) {
aura::Window* window = (*iter);
WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
if (1.f - state->progress > kEpsilon) {
// It is possible to scroll |window| down. Scroll it down, and update
// |delta_y_p| for the next window.
TransientGroupSetter setter(window);
setter.SetWindowProgress(std::min(1.f, state->progress + delta_y_p));
delta_y_p /= 2.f;
}
}
}
}
int GetScrollableHeight() const {
const float kScrollableFraction = 0.85f;
const float kScrollableFractionInSplit = 0.5f;
const float fraction = split_view_controller_->IsSplitViewModeActive()
? kScrollableFractionInSplit
: kScrollableFraction;
return container_->bounds().height() * fraction;
}
void CreateFlingerFor(const ui::GestureEvent& event) {
gfx::Vector2dF velocity(event.details().velocity_x(),
event.details().velocity_y());
fling_.reset(new ui::FlingCurve(velocity, gfx::FrameTime::Now()));
}
void AddAnimationObserver() {
ui::Compositor* compositor = container_->GetHost()->compositor();
if (!compositor->HasAnimationObserver(this))
compositor->AddAnimationObserver(this);
}
void RemoveAnimationObserver() {
ui::Compositor* compositor = container_->GetHost()->compositor();
if (compositor->HasAnimationObserver(this))
compositor->RemoveAnimationObserver(this);
}
aura::Window* GetSplitWindowDropTarget(const ui::GestureEvent& event) const {
if (!split_view_controller_->IsSplitViewModeActive())
return nullptr;
CHECK(dragged_window_);
CHECK_NE(split_view_controller_->left_window(), dragged_window_);
CHECK_NE(split_view_controller_->right_window(), dragged_window_);
aura::Window* window = split_view_controller_->left_window();
if (GetTransformedBounds(window).Contains(event.location()))
return window;
window = split_view_controller_->right_window();
if (GetTransformedBounds(window).Contains(event.location()))
return window;
return nullptr;
}
void DragWindow(const ui::GestureEvent& event) {
CHECK(dragged_window_);
CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type());
CHECK(overview_toolbar_);
gfx::Vector2dF dragged_distance =
dragged_start_location_ - event.location();
WindowOverviewState* dragged_state =
dragged_window_->GetProperty(kWindowOverviewState);
CHECK(dragged_state);
gfx::Transform transform =
GetTransformForState(dragged_window_, dragged_state);
transform.Translate(-dragged_distance.x(), 0);
TransientGroupSetter setter(dragged_window_);
setter.SetTransform(transform);
// Update the toolbar.
const int kMinDistanceForActionButtons = 20;
if (fabs(dragged_distance.x()) > kMinDistanceForActionButtons)
overview_toolbar_->ShowActionButtons();
else
overview_toolbar_->HideActionButtons();
// See if the touch-point is above one of the action-buttons.
OverviewToolbar::ActionType new_action =
overview_toolbar_->GetHighlightAction(event);
// If the touch-point is not above any of the action buttons, then highlight
// the close-button by default, if the user has dragged enough to close the
// window.
if (new_action == OverviewToolbar::ACTION_TYPE_NONE) {
if (fabs(dragged_distance.x()) > kMinDistanceForDismissal)
new_action = OverviewToolbar::ACTION_TYPE_CLOSE;
else
new_action = OverviewToolbar::ACTION_TYPE_NONE;
}
OverviewToolbar::ActionType previous_action =
overview_toolbar_->current_action();
overview_toolbar_->SetHighlightAction(new_action);
aura::Window* split_drop = GetSplitWindowDropTarget(event);
// If the user has selected to get into split-view mode, then show the
// window with full opacity. Otherwise, fade it out as it closes. Animate
// the opacity if transitioning to/from the split-view button.
bool animate_opacity =
(new_action != previous_action) &&
((new_action == OverviewToolbar::ACTION_TYPE_SPLIT) ||
(previous_action == OverviewToolbar::ACTION_TYPE_SPLIT));
float ratio = std::min(
1.f, std::abs(dragged_distance.x()) / kMinDistanceForDismissal);
float opacity =
(new_action == OverviewToolbar::ACTION_TYPE_SPLIT || split_drop)
? 1
: gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity);
if (animate_opacity) {
AnimateTransientGroupSetter setter(dragged_window_);
setter.SetOpacity(opacity);
} else {
TransientGroupSetter setter(dragged_window_);
setter.SetOpacity(opacity);
}
if (split_view_controller_->IsSplitViewModeActive()) {
float scale = kOverviewDefaultScale;
if (split_drop == split_view_controller_->left_window())
scale = kMaxScaleForSplitTarget;
TransformSplitWindowScale(split_view_controller_->left_window(), scale);
scale = kOverviewDefaultScale;
if (split_drop == split_view_controller_->right_window())
scale = kMaxScaleForSplitTarget;
TransformSplitWindowScale(split_view_controller_->right_window(), scale);
}
}
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.
{
AnimateTransientGroupSetter setter(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());
transform.Translate(transform_x / kOverviewDefaultScale, 0);
setter.SetOpacity(kMinOpacity);
}
delete dragged_window_;
dragged_window_ = nullptr;
}
void RestoreDragWindow() {
CHECK(dragged_window_);
WindowOverviewState* dragged_state =
dragged_window_->GetProperty(kWindowOverviewState);
CHECK(dragged_state);
AnimateTransientGroupSetter setter(dragged_window_);
setter.SetTransform(GetTransformForState(dragged_window_, dragged_state));
setter.SetOpacity(1.0f);
dragged_window_ = nullptr;
}
void EndDragWindow(const ui::GestureEvent& gesture) {
CHECK(dragged_window_);
CHECK(overview_toolbar_);
OverviewToolbar::ActionType action = overview_toolbar_->current_action();
overview_toolbar_.reset();
if (action == OverviewToolbar::ACTION_TYPE_SPLIT) {
delegate_->OnSelectSplitViewWindow(
nullptr, dragged_window_, dragged_window_);
return;
}
// If the window is dropped on one of the left/right windows in split-mode,
// then switch that window.
aura::Window* split_drop = GetSplitWindowDropTarget(gesture);
if (split_drop) {
aura::Window* left = split_view_controller_->left_window();
aura::Window* right = split_view_controller_->right_window();
if (left == split_drop)
left = dragged_window_;
else
right = dragged_window_;
delegate_->OnSelectSplitViewWindow(left, right, dragged_window_);
return;
}
if (ShouldCloseDragWindow(gesture))
CloseDragWindow(gesture);
else
RestoreDragWindow();
}
void SelectWindow(aura::Window* window) {
if (!split_view_controller_->IsSplitViewModeActive()) {
delegate_->OnSelectWindow(window);
} else {
// If the selected window is one of the left/right windows, then keep the
// current state.
if (window == split_view_controller_->left_window() ||
window == split_view_controller_->right_window()) {
delegate_->OnSelectSplitViewWindow(
split_view_controller_->left_window(),
split_view_controller_->right_window(),
window);
} else {
delegate_->OnSelectWindow(window);
}
}
}
// ui::EventHandler:
void OnMouseEvent(ui::MouseEvent* mouse) override {
if (mouse->type() == ui::ET_MOUSE_PRESSED) {
aura::Window* select = SelectWindowAt(mouse);
if (select) {
mouse->SetHandled();
SelectWindow(select);
}
} else if (mouse->type() == ui::ET_MOUSEWHEEL) {
DoScroll(static_cast<ui::MouseWheelEvent*>(mouse)->y_offset());
}
}
void OnScrollEvent(ui::ScrollEvent* scroll) override {
if (scroll->type() == ui::ET_SCROLL)
DoScroll(scroll->y_offset());
}
void OnGestureEvent(ui::GestureEvent* gesture) override {
if (gesture->type() == ui::ET_GESTURE_TAP) {
aura::Window* select = SelectWindowAt(gesture);
if (select) {
gesture->SetHandled();
SelectWindow(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);
if (split_view_controller_->IsSplitViewModeActive() &&
(dragged_window_ == split_view_controller_->left_window() ||
dragged_window_ == split_view_controller_->right_window())) {
// TODO(sad): Allow closing the left/right window. Closing one of
// these windows will terminate the split-view mode. Until then, do
// not allow closing these (since otherwise it gets into an undefined
// state).
dragged_window_ = nullptr;
}
if (dragged_window_) {
// Show the toolbar (for closing a window, or going into split-view
// mode). If already in split-view mode, then do not show the 'Split'
// option.
overview_toolbar_.reset(new OverviewToolbar(container_));
if (!split_view_controller_->CanActivateSplitViewMode()) {
overview_toolbar_->DisableAction(
OverviewToolbar::ACTION_TYPE_SPLIT);
}
}
}
} else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
if (dragged_window_)
DragWindow(*gesture);
else
DoScroll(gesture->details().scroll_y());
gesture->SetHandled();
} else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) {
if (dragged_window_)
EndDragWindow(*gesture);
gesture->SetHandled();
} else if (gesture->type() == ui::ET_SCROLL_FLING_START) {
if (dragged_window_) {
EndDragWindow(*gesture);
} else {
CreateFlingerFor(*gesture);
AddAnimationObserver();
}
gesture->SetHandled();
} else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) {
if (fling_) {
fling_.reset();
RemoveAnimationObserver();
gesture->SetHandled();
}
dragged_window_ = nullptr;
}
}
// ui::CompositorAnimationObserver:
void OnAnimationStep(base::TimeTicks timestamp) override {
CHECK(fling_);
gfx::Vector2dF delta;
bool fling_active = fling_->ComputeScrollDeltaAtTime(timestamp, &delta);
if (!delta.IsZero())
DoScroll(delta.y());
if (!fling_active) {
fling_.reset();
RemoveAnimationObserver();
}
}
// WindowListProviderObserver:
void OnWindowStackingChangedInList() override {
// Recompute the states of all windows. There isn't enough information at
// this point to do anything more clever.
ComputeTerminalStatesForAllWindows();
SetInitialWindowStates();
}
void OnWindowAddedToList(aura::Window* removed_window) override {}
void OnWindowRemovedFromList(aura::Window* removed_window,
int index) override {
const aura::Window::Windows& windows =
window_list_provider_->GetWindowList();
if (windows.empty())
return;
CHECK_LE(index, static_cast<int>(windows.size()));
if (index == 0) {
// The back-most window has been removed. Move all the remaining windows
// one step backwards.
for (int i = windows.size() - 1; i > 0; --i) {
UpdateTerminalStateForWindowAtIndex(
windows[i], windows.size() - 1 - i, windows.size());
AnimateWindowTo(windows[i], windows[i - 1]);
}
UpdateTerminalStateForWindowAtIndex(windows.front(),
windows.size() - 1,
windows.size());
AnimateWindowTo(windows.front(), removed_window);
} else {
// Move all windows behind the removed window one step forwards.
for (int i = 0; i < index - 1; ++i) {
UpdateTerminalStateForWindowAtIndex(windows[i], windows.size() - 1 - i,
windows.size());
AnimateWindowTo(windows[i], windows[i + 1]);
}
UpdateTerminalStateForWindowAtIndex(windows[index - 1],
windows.size() - index,
windows.size());
AnimateWindowTo(windows[index - 1], removed_window);
}
}
const int kMinDistanceForDismissal = 300;
const float kMaxOpacity = 1.0f;
const float kMinOpacity = 0.2f;
const float kMaxScaleForSplitTarget = 0.9f;
aura::Window* container_;
// Provider of the stack of windows to show in the overview mode. Not owned.
WindowListProvider* window_list_provider_;
SplitViewController* split_view_controller_;
WindowOverviewModeDelegate* delegate_;
scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_;
scoped_ptr<ui::FlingCurve> fling_;
aura::Window* dragged_window_;
gfx::Point dragged_start_location_;
scoped_ptr<OverviewToolbar> overview_toolbar_;
DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl);
};
} // namespace
// static
scoped_ptr<WindowOverviewMode> WindowOverviewMode::Create(
aura::Window* container,
WindowListProvider* window_list_provider,
SplitViewController* split_view_controller,
WindowOverviewModeDelegate* delegate) {
return scoped_ptr<WindowOverviewMode>(
new WindowOverviewModeImpl(container, window_list_provider,
split_view_controller, delegate));
}
} // namespace athena