Split Screen mode implementation.

Implements Split Screen mode and window cycling behavior.
Resubmit of https://codereview.chromium.org/420603011/

TBR=oshima@chromium.org,mukai@chromium.org

BUG=383421

Review URL: https://codereview.chromium.org/461503002

Cr-Commit-Position: refs/heads/master@{#289064}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289064 0039d316-1c4b-4281-b951-d872f2087c98
parent 39b2ef6f
......@@ -69,10 +69,13 @@
'system/power_button_controller.h',
'system/public/system_ui.h',
'system/system_ui_impl.cc',
'wm/public/window_manager.h',
'wm/public/window_manager_observer.h',
'wm/bezel_controller.cc',
'wm/bezel_controller.h',
'wm/mru_window_tracker.cc',
'wm/mru_window_tracker.h',
'wm/public/window_list_provider.h',
'wm/public/window_manager.h',
'wm/public/window_manager_observer.h',
'wm/split_view_controller.cc',
'wm/split_view_controller.h',
'wm/title_drag_controller.cc',
......
......@@ -6,6 +6,10 @@
#include "ui/aura/window.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/display.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/screen.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace athena {
namespace {
......@@ -15,7 +19,7 @@ namespace {
// So setting this width fairly high for now.
const float kBezelWidth = 20.0f;
const float kScrollPositionNone = -100;
const float kScrollDeltaNone = 0;
bool ShouldProcessGesture(ui::EventType event_type) {
return event_type == ui::ET_GESTURE_SCROLL_UPDATE ||
......@@ -24,6 +28,24 @@ bool ShouldProcessGesture(ui::EventType event_type) {
event_type == ui::ET_GESTURE_END;
}
gfx::Display GetDisplay(aura::Window* window) {
gfx::Screen* screen = gfx::Screen::GetScreenFor(window);
return screen->GetDisplayNearestWindow(window);
}
float GetDistance(const gfx::PointF& location,
aura::Window* window,
BezelController::Bezel bezel) {
DCHECK(bezel == BezelController::BEZEL_LEFT ||
bezel == BezelController::BEZEL_RIGHT);
// Convert location from window coordinates to screen coordinates.
gfx::Point point_in_screen(gfx::ToRoundedPoint(location));
wm::ConvertPointToScreen(window, &point_in_screen);
return bezel == BezelController::BEZEL_LEFT
? point_in_screen.x()
: point_in_screen.x() - GetDisplay(window).bounds().width();
}
} // namespace
BezelController::BezelController(aura::Window* container)
......@@ -34,25 +56,22 @@ BezelController::BezelController(aura::Window* container)
left_right_delegate_(NULL) {
}
float BezelController::GetDistance(const gfx::PointF& position,
BezelController::Bezel bezel) {
DCHECK(bezel == BEZEL_LEFT || bezel == BEZEL_RIGHT);
return bezel == BEZEL_LEFT
? position.x()
: position.x() - container_->GetBoundsInScreen().width();
void BezelController::SetState(BezelController::State state) {
// Use SetState(State, float) if |state| is one of the BEZEL_SCROLLING states.
DCHECK_NE(state, BEZEL_SCROLLING_TWO_FINGERS);
DCHECK_NE(state, BEZEL_SCROLLING_ONE_FINGER);
SetState(state, kScrollDeltaNone);
}
void BezelController::SetState(BezelController::State state,
const gfx::PointF& scroll_position) {
float scroll_delta) {
if (!left_right_delegate_ || state == state_)
return;
if (state == BEZEL_SCROLLING_TWO_FINGERS) {
float delta = GetDistance(scroll_position, scroll_bezel_);
left_right_delegate_->ScrollBegin(scroll_bezel_, delta);
} else if (state_ == BEZEL_SCROLLING_TWO_FINGERS) {
if (state == BEZEL_SCROLLING_TWO_FINGERS)
left_right_delegate_->ScrollBegin(scroll_bezel_, scroll_delta);
else if (state_ == BEZEL_SCROLLING_TWO_FINGERS)
left_right_delegate_->ScrollEnd();
}
state_ = state;
if (state == NONE) {
scroll_bezel_ = BEZEL_NONE;
......@@ -62,10 +81,10 @@ void BezelController::SetState(BezelController::State state,
// Only implemented for LEFT and RIGHT bezels ATM.
BezelController::Bezel BezelController::GetBezel(const gfx::PointF& location) {
int screen_width = GetDisplay(container_).bounds().width();
if (location.x() < kBezelWidth) {
return BEZEL_LEFT;
} else if (location.x() >
container_->GetBoundsInScreen().width() - kBezelWidth) {
} else if (location.x() > screen_width - kBezelWidth) {
return BEZEL_RIGHT;
} else {
return BEZEL_NONE;
......@@ -73,7 +92,7 @@ BezelController::Bezel BezelController::GetBezel(const gfx::PointF& location) {
}
void BezelController::OnGestureEvent(ui::GestureEvent* event) {
// TODO (mfomitchev): Currently we aren't retargetting or consuming any of the
// TODO(mfomitchev): Currently we aren't retargetting or consuming any of the
// touch events. This means that content can prevent the generation of gesture
// events and two-finger scroll won't work. Possible solution to this problem
// is hosting our own gesture recognizer or retargetting touch events at the
......@@ -92,11 +111,15 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
const gfx::PointF& event_location = event->location_f();
const ui::GestureEventDetails& event_details = event->details();
int num_touch_points = event_details.touch_points();
float scroll_delta = kScrollDeltaNone;
if (scroll_bezel_ != BEZEL_NONE) {
aura::Window* target_window = static_cast<aura::Window*>(event->target());
scroll_delta = GetDistance(event_location, target_window, scroll_bezel_);
}
if (type == ui::ET_GESTURE_BEGIN) {
if (num_touch_points > 2) {
SetState(IGNORE_CURRENT_SCROLL,
gfx::Point(kScrollPositionNone, kScrollPositionNone));
SetState(IGNORE_CURRENT_SCROLL);
return;
}
BezelController::Bezel event_bezel = GetBezel(event->location_f());
......@@ -104,12 +127,10 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
case NONE:
scroll_bezel_ = event_bezel;
scroll_target_ = event->target();
if (event_bezel != BEZEL_LEFT && event_bezel != BEZEL_RIGHT) {
SetState(IGNORE_CURRENT_SCROLL,
gfx::Point(kScrollPositionNone, kScrollPositionNone));
} else {
SetState(BEZEL_GESTURE_STARTED, event_location);
}
if (event_bezel != BEZEL_LEFT && event_bezel != BEZEL_RIGHT)
SetState(IGNORE_CURRENT_SCROLL);
else
SetState(BEZEL_GESTURE_STARTED);
break;
case IGNORE_CURRENT_SCROLL:
break;
......@@ -120,12 +141,11 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
DCHECK_NE(scroll_bezel_, BEZEL_NONE);
if (event_bezel != scroll_bezel_) {
SetState(IGNORE_CURRENT_SCROLL,
gfx::Point(kScrollPositionNone, kScrollPositionNone));
SetState(IGNORE_CURRENT_SCROLL);
return;
}
if (state_ == BEZEL_SCROLLING_ONE_FINGER)
SetState(BEZEL_SCROLLING_TWO_FINGERS, event_location);
SetState(BEZEL_SCROLLING_TWO_FINGERS);
break;
case BEZEL_SCROLLING_TWO_FINGERS:
// Should've exited above
......@@ -138,10 +158,9 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
CHECK(scroll_target_);
if (num_touch_points == 1) {
SetState(NONE, gfx::Point(kScrollPositionNone, kScrollPositionNone));
SetState(NONE);
} else {
SetState(IGNORE_CURRENT_SCROLL,
gfx::Point(kScrollPositionNone, kScrollPositionNone));
SetState(IGNORE_CURRENT_SCROLL);
}
} else if (type == ui::ET_GESTURE_SCROLL_BEGIN) {
DCHECK(state_ == IGNORE_CURRENT_SCROLL || state_ == BEZEL_GESTURE_STARTED);
......@@ -149,19 +168,18 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
return;
if (num_touch_points == 1) {
SetState(BEZEL_SCROLLING_ONE_FINGER, event_location);
SetState(BEZEL_SCROLLING_ONE_FINGER, scroll_delta);
return;
}
DCHECK_EQ(num_touch_points, 2);
SetState(BEZEL_SCROLLING_TWO_FINGERS, event_location);
SetState(BEZEL_SCROLLING_TWO_FINGERS, scroll_delta);
if (left_right_delegate_->CanScroll())
event->SetHandled();
} else if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
if (state_ != BEZEL_SCROLLING_TWO_FINGERS)
return;
float scroll_delta = GetDistance(event_location, scroll_bezel_);
left_right_delegate_->ScrollUpdate(scroll_delta);
if (left_right_delegate_->CanScroll())
event->SetHandled();
......
......@@ -35,12 +35,15 @@ class BezelController : public ui::EventHandler {
virtual ~ScrollDelegate() {}
// Beginning of a bezel scroll gesture started from the |bezel|.
// |delta| is the difference between the x-coordinate of the current scroll
// position and the bezel. It will be zero or negative for the right bezel.
virtual void ScrollBegin(Bezel bezel, float delta) = 0;
// End of the current bezel scroll
virtual void ScrollEnd() = 0;
// Update of the scroll position for the currently active bezel scroll.
// |delta| has the same meaning as in ScrollBegin().
virtual void ScrollUpdate(float delta) = 0;
// Should return false if the delegate isn't going to react to the scroll
......@@ -67,11 +70,10 @@ class BezelController : public ui::EventHandler {
BEZEL_SCROLLING_TWO_FINGERS,
};
// Calculates the distance from |position| to the |bezel|.
float GetDistance(const gfx::PointF& position, Bezel bezel);
// |scroll_position| only needs to be passed in the scrolling state
void SetState(State state, const gfx::PointF& scroll_position);
void SetState(State state);
// |scroll_delta| only needs to be passed when |state| is one of the
// BEZEL_SROLLING states.
void SetState(State state, float scroll_delta);
// Returns the bezel corresponding to the |location| or BEZEL_NONE if the
// location is outside of the bezel area.
......
// 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/mru_window_tracker.h"
#include "ui/aura/window.h"
namespace athena {
MruWindowTracker::MruWindowTracker(aura::Window* container)
: container_(container) {
container->AddObserver(this);
}
MruWindowTracker::~MruWindowTracker() {
if (container_)
container_->RemoveObserver(this);
}
aura::Window::Windows MruWindowTracker::GetWindowList() const {
return aura::Window::Windows(mru_windows_.begin(), mru_windows_.end());
}
void MruWindowTracker::MoveToFront(aura::Window* window) {
DCHECK(window);
CHECK_EQ(container_, window->parent());
std::list<aura::Window*>::iterator it =
std::find(mru_windows_.begin(), mru_windows_.end(), window);
DCHECK(it != mru_windows_.end());
mru_windows_.erase(it);
mru_windows_.push_back(window);
}
// Overridden from WindowObserver:
void MruWindowTracker::OnWillRemoveWindow(aura::Window* window) {
std::list<aura::Window*>::iterator it =
std::find(mru_windows_.begin(), mru_windows_.end(), window);
if (it == mru_windows_.end()) {
// All normal windows should be tracked in mru_windows_
DCHECK_NE(window->type(), ui::wm::WINDOW_TYPE_NORMAL);
return;
}
mru_windows_.erase(it);
}
void MruWindowTracker::OnWindowAdded(aura::Window* new_window) {
// We are only interested in ordering normal windows.
if (new_window->type() == ui::wm::WINDOW_TYPE_NORMAL)
mru_windows_.push_back(new_window);
}
} // namespace athena
// 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.
#ifndef ATHENA_WM_MRU_WINDOW_TRACKER_H_
#define ATHENA_WM_MRU_WINDOW_TRACKER_H_
#include <list>
#include "athena/wm/public/window_list_provider.h"
#include "ui/aura/window_observer.h"
namespace aura {
class Window;
}
namespace athena {
// Maintains a most recently used list of windows. This is used for window
// cycling and overview mode.
class MruWindowTracker : public WindowListProvider,
public aura::WindowObserver {
public:
explicit MruWindowTracker(aura::Window* container);
virtual ~MruWindowTracker();
// Overridden from WindowListProvider
virtual aura::Window::Windows GetWindowList() const OVERRIDE;
// Updates the mru_windows_ list to move |window| to the front.
// |window| must be the child of |container_|.
virtual void MoveToFront(aura::Window* window) OVERRIDE;
private:
// Overridden from WindowObserver:
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE;
// List of windows that have been used in the container, sorted by most
// recently used.
std::list<aura::Window*> mru_windows_;
aura::Window* container_;
DISALLOW_COPY_AND_ASSIGN(MruWindowTracker);
};
} // namespace athena
#endif // ATHENA_WM_MRU_WINDOW_TRACKER_H_
// 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.
#ifndef ATHENA_WM_PUBLIC_WINDOW_LIST_PROVIDER_H_
#define ATHENA_WM_PUBLIC_WINDOW_LIST_PROVIDER_H_
#include "athena/athena_export.h"
#include "ui/aura/window.h"
namespace athena {
// Interface for an ordered list of aura::Window objects.
class ATHENA_EXPORT WindowListProvider {
public:
virtual ~WindowListProvider() {}
// Returns an ordered list of windows.
virtual aura::Window::Windows GetWindowList() const = 0;
// Moves the window to the front of the list.
virtual void MoveToFront(aura::Window* window) = 0;
};
} // namespace athena
#endif // ATHENA_WM_PUBLIC_WINDOW_LIST_PROVIDER_H_
......@@ -24,6 +24,8 @@ class ATHENA_EXPORT WindowManager {
virtual void ToggleOverview() = 0;
virtual bool IsOverviewModeActive() = 0;
virtual void AddObserver(WindowManagerObserver* observer) = 0;
virtual void RemoveObserver(WindowManagerObserver* observer) = 0;
};
......
......@@ -13,10 +13,10 @@ class ATHENA_EXPORT WindowManagerObserver {
public:
virtual ~WindowManagerObserver() {}
// Called when the overview mode is displayed.
// Called immediately before the overview mode is displayed.
virtual void OnOverviewModeEnter() = 0;
// Called when going out of overview mode.
// Called immediately after going out of the overview mode.
virtual void OnOverviewModeExit() = 0;
};
......
......@@ -4,29 +4,273 @@
#include "athena/wm/split_view_controller.h"
#include <cmath>
#include "athena/wm/public/window_list_provider.h"
#include "athena/wm/public/window_manager.h"
#include "base/bind.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
namespace athena {
namespace {
// An animation observer that runs a callback at the end of the animation, and
// destroys itself.
class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
public:
explicit CallbackAnimationObserver(const base::Closure& closure)
: closure_(closure) {}
virtual ~CallbackAnimationObserver() {}
private:
// Overridden from ui::ImplicitAnimationObserver:
virtual void OnImplicitAnimationsCompleted() OVERRIDE {
if (!closure_.is_null())
closure_.Run();
delete this;
}
SplitViewController::SplitViewController() {
const base::Closure closure_;
DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
};
} // namespace
SplitViewController::SplitViewController(
aura::Window* container,
WindowListProvider* window_list_provider,
WindowManager* window_manager)
: state_(INACTIVE),
container_(container),
window_manager_(window_manager),
window_list_provider_(window_list_provider),
current_activity_window_(NULL),
left_window_(NULL),
right_window_(NULL),
separator_position_(0),
weak_factory_(this) {
window_manager->AddObserver(this);
}
SplitViewController::~SplitViewController() {
window_manager_->RemoveObserver(this);
}
bool SplitViewController::IsSplitViewModeActive() const {
return state_ == ACTIVE;
}
void SplitViewController::UpdateLayout(bool animate) {
if (!left_window_)
return;
CHECK(right_window_);
gfx::Transform left_transform;
gfx::Transform right_transform;
int container_width = container_->GetBoundsInScreen().width();
if (state_ == ACTIVE) {
// This method should only be called once in ACTIVE state when
// the left and rightwindows are still full screen and need to be resized.
CHECK_EQ(left_window_->bounds().width(), container_width);
CHECK_EQ(right_window_->bounds().width(), container_width);
// Windows should be resized via an animation when entering the ACTIVE
// state.
CHECK(animate);
// We scale the windows here, but when the animation finishes, we reset
// the scaling and update the window bounds to the proper size - see
// OnAnimationCompleted().
left_transform.Scale(.5, 1);
right_transform.Scale(.5, 1);
right_transform.Translate(container_width, 0);
} else {
left_transform.Translate(separator_position_ - container_width, 0);
right_transform.Translate(separator_position_, 0);
}
left_window_->Show();
right_window_->Show();
SetWindowTransform(left_window_, left_transform, animate);
SetWindowTransform(right_window_, right_transform, animate);
}
void SplitViewController::SetWindowTransform(aura::Window* window,
const gfx::Transform& transform,
bool animate) {
if (animate) {
scoped_refptr<ui::LayerAnimator> animator = window->layer()->GetAnimator();
ui::ScopedLayerAnimationSettings settings(animator);
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.AddObserver(new CallbackAnimationObserver(
base::Bind(&SplitViewController::OnAnimationCompleted,
weak_factory_.GetWeakPtr(),
window)));
window->SetTransform(transform);
} else {
window->SetTransform(transform);
}
}
void SplitViewController::OnAnimationCompleted(aura::Window* window) {
DCHECK(window == left_window_ || window == right_window_);
if (state_ == ACTIVE) {
gfx::Rect window_bounds = gfx::Rect(container_->bounds().size());
int container_width = window_bounds.width();
window_bounds.set_width(container_width / 2);
window->SetTransform(gfx::Transform());
if (window == left_window_) {
left_window_->SetBounds(window_bounds);
} else {
window_bounds.set_x(container_width / 2);
right_window_->SetBounds(window_bounds);
}
} else {
int container_width = container_->bounds().width();
window->SetTransform(gfx::Transform());
if (window == left_window_) {
if (separator_position_ == 0)
left_window_->Hide();
if (state_ == INACTIVE)
left_window_ = NULL;
} else {
if (separator_position_ == container_width)
right_window_->Hide();
if (state_ == INACTIVE)
right_window_ = NULL;
}
}
}
void SplitViewController::UpdateSeparatorPositionFromScrollDelta(float delta) {
gfx::Screen* screen = gfx::Screen::GetScreenFor(container_);
const gfx::Rect& display_bounds =
screen->GetDisplayNearestWindow(container_).bounds();
gfx::Rect container_bounds = container_->GetBoundsInScreen();
separator_position_ =
delta > 0 ? ((int)delta) + display_bounds.x() - container_bounds.x()
: display_bounds.right() - container_bounds.x() + delta;
}
aura::Window* SplitViewController::GetCurrentActivityWindow() {
if (!current_activity_window_) {
aura::Window::Windows windows = window_list_provider_->GetWindowList();
if (windows.empty())
return NULL;
current_activity_window_ = windows.back();
}
return current_activity_window_;
}
///////////////////////////////////////////////////////////////////////////////
// Begin BezelController::ScrollDelegate overrides.
void SplitViewController::ScrollBegin(BezelController::Bezel bezel,
float delta) {
if (!CanScroll())
return;
state_ = SCROLLING;
aura::Window* current_window = GetCurrentActivityWindow();
CHECK(current_window);
aura::Window::Windows windows = window_list_provider_->GetWindowList();
CHECK(windows.size() >= 2);
aura::Window::Windows::const_iterator it =
std::find(windows.begin(), windows.end(), current_window);
CHECK(it != windows.end());
if (delta > 0) {
right_window_ = current_window;
// reverse iterator points to the position before normal iterator |it|
aura::Window::Windows::const_reverse_iterator rev_it(it);
// circle to end if needed.
left_window_ = rev_it == windows.rend() ? windows.back() : *(rev_it);
} else {
left_window_ = current_window;
++it;
// circle to front if needed.
right_window_ = it == windows.end() ? windows.front() : *it;
}
CHECK(left_window_);
CHECK(right_window_);
// TODO(oshima|mfomitchev): crbug.com/388362
// Until we are properly hiding off-screen windows in window manager:
// Loop through all windows and hide them
for (it = windows.begin(); it != windows.end(); ++it) {
if (*it != left_window_ && *it != right_window_)
(*it)->Hide();
}
UpdateSeparatorPositionFromScrollDelta(delta);
UpdateLayout(false);
}
// Max distance from the scroll end position to the middle of the screen where
// we would go into the split view mode.
const int kMaxDistanceFromMiddle = 120;
void SplitViewController::ScrollEnd() {
if (state_ != SCROLLING)
return;
int container_width = container_->GetBoundsInScreen().width();
if (std::abs(container_width / 2 - separator_position_) <=
kMaxDistanceFromMiddle) {
state_ = ACTIVE;
separator_position_ = container_width / 2;
} else if (separator_position_ < container_width / 2) {
separator_position_ = 0;
current_activity_window_ = right_window_;
state_ = INACTIVE;
} else {
separator_position_ = container_width;
current_activity_window_ = left_window_;
state_ = INACTIVE;
}
UpdateLayout(true);
}
void SplitViewController::ScrollUpdate(float delta) {
if (state_ != SCROLLING)
return;
UpdateSeparatorPositionFromScrollDelta(delta);
UpdateLayout(false);
}
bool SplitViewController::CanScroll() {
return false;
// TODO(mfomitchev): return false in vertical orientation, in full screen.
bool result = (!window_manager_->IsOverviewModeActive() &&
!IsSplitViewModeActive() &&
window_list_provider_->GetWindowList().size() >= 2);
return result;
}
///////////////////////////////////////////////////////////////////////////////
// WindowManagerObserver overrides
void SplitViewController::OnOverviewModeEnter() {
if (state_ == ACTIVE) {
CHECK(left_window_);
CHECK(right_window_);
window_list_provider_->MoveToFront(right_window_);
window_list_provider_->MoveToFront(left_window_);
// TODO(mfomitchev): This shouldn't be done here, but the overview mode's
// transition animation currently looks bad if the starting transform of
// any window is not gfx::Transform().
right_window_->SetTransform(gfx::Transform());
} else if (current_activity_window_) {
window_list_provider_->MoveToFront(current_activity_window_);
}
state_ = INACTIVE;
left_window_ = NULL;
right_window_ = NULL;
current_activity_window_ = NULL;
}
void SplitViewController::OnOverviewModeExit() {
}
} // namespace athena
......@@ -6,24 +6,99 @@
#define ATHENA_WM_SPLIT_VIEW_CONTROLLER_H_
#include "athena/wm/bezel_controller.h"
#include "athena/wm/public/window_manager_observer.h"
#include "athena/wm/window_overview_mode.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
namespace gfx {
class Transform;
}
namespace athena {
class WindowListProvider;
class WindowManager;
class WindowOverviewModeDelegate;
// Responsible for entering split view mode, exiting from split view mode, and
// laying out the windows in split view mode.
class SplitViewController : public BezelController::ScrollDelegate {
class SplitViewController : public BezelController::ScrollDelegate,
public WindowManagerObserver {
public:
SplitViewController();
SplitViewController(aura::Window* container,
WindowListProvider* window_list_provider,
WindowManager* window_manager);
virtual ~SplitViewController();
bool IsSplitViewModeActive() const;
private:
enum State {
// Split View mode is not active. |left_window_| and |right_window| are
// NULL.
INACTIVE,
// Two windows |left_window_| and |right_window| are shown side by side and
// there is a horizontal scroll in progress which is dragging the separator
// between the two windows.
SCROLLING,
// Split View mode is active with |left_window_| and |right_window| showing
// side by side each occupying half the screen. No scroll in progress.
ACTIVE
};
void UpdateLayout(bool animate);
void SetWindowTransform(aura::Window* left_window,
const gfx::Transform& transform,
bool animate);
void OnAnimationCompleted(aura::Window* window);
void UpdateSeparatorPositionFromScrollDelta(float delta);
// Returns the current activity shown to the user. Persists through the
// SCROLLING and ACTIVE states and gets updated if the current activity goes
// off screen when the user switches between activities.
aura::Window* GetCurrentActivityWindow();
// BezelController::ScrollDelegate overrides.
virtual void ScrollBegin(BezelController::Bezel bezel, float delta) OVERRIDE;
virtual void ScrollEnd() OVERRIDE;
virtual void ScrollUpdate(float delta) OVERRIDE;
virtual bool CanScroll() OVERRIDE;
// WindowManagerObserver
virtual void OnOverviewModeEnter() OVERRIDE;
virtual void OnOverviewModeExit() OVERRIDE;
State state_;
aura::Window* container_;
// Window Manager which owns this SplitViewController.
// Must be non NULL for the duration of the lifetime.
WindowManager* window_manager_;
// Provider of the list of windows to cycle through. Not owned.
WindowListProvider* window_list_provider_;
// Keeps track of the current activity shown as the user switches between
// activities. Persists through the SCROLLING and ACTIVE states. Gets reset
// to NULL when overview mode is activated.
aura::Window* current_activity_window_;
// Windows for the left and right activities shown in SCROLLING and ACTIVE
// states. In INACTIVE state these are NULL.
aura::Window* left_window_;
aura::Window* right_window_;
// Position of the separator between left_window_ and right_window_ in
// container_ coordinates (along the x axis).
int separator_position_;
base::WeakPtrFactory<SplitViewController> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SplitViewController);
};
......
......@@ -10,6 +10,7 @@
#include "athena/input/public/accelerator_manager.h"
#include "athena/screen/public/screen_manager.h"
#include "athena/wm/bezel_controller.h"
#include "athena/wm/mru_window_tracker.h"
#include "athena/wm/public/window_manager_observer.h"
#include "athena/wm/split_view_controller.h"
#include "athena/wm/title_drag_controller.h"
......@@ -39,6 +40,8 @@ class WindowManagerImpl : public WindowManager,
// WindowManager:
virtual void ToggleOverview() OVERRIDE;
virtual bool IsOverviewModeActive() OVERRIDE;
private:
enum Command {
COMMAND_TOGGLE_OVERVIEW,
......@@ -71,13 +74,16 @@ class WindowManagerImpl : public WindowManager,
virtual void OnTitleDragCompleted(aura::Window* window) OVERRIDE;
virtual void OnTitleDragCanceled(aura::Window* window) OVERRIDE;
// Should be declared first so that it is destoyed last.
ObserverList<WindowManagerObserver> observers_;
scoped_ptr<aura::Window> container_;
scoped_ptr<MruWindowTracker> mru_window_tracker_;
scoped_ptr<WindowOverviewMode> overview_;
scoped_ptr<BezelController> bezel_controller_;
scoped_ptr<SplitViewController> split_view_controller_;
scoped_ptr<wm::WMState> wm_state_;
scoped_ptr<TitleDragController> title_drag_controller_;
ObserverList<WindowManagerObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(WindowManagerImpl);
};
......@@ -109,8 +115,10 @@ WindowManagerImpl::WindowManagerImpl() {
container_.reset(ScreenManager::Get()->CreateDefaultContainer(params));
container_->SetLayoutManager(new AthenaContainerLayoutManager);
container_->AddObserver(this);
mru_window_tracker_.reset(new MruWindowTracker(container_.get()));
bezel_controller_.reset(new BezelController(container_.get()));
split_view_controller_.reset(new SplitViewController());
split_view_controller_.reset(new SplitViewController(
container_.get(), mru_window_tracker_.get(), this));
bezel_controller_->set_left_right_delegate(split_view_controller_.get());
container_->AddPreTargetHandler(bezel_controller_.get());
title_drag_controller_.reset(new TitleDragController(container_.get(), this));
......@@ -148,15 +156,26 @@ void WindowManagerImpl::ToggleOverview() {
SetInOverview(overview_.get() == NULL);
}
bool WindowManagerImpl::IsOverviewModeActive() {
return overview_;
}
void WindowManagerImpl::SetInOverview(bool active) {
bool in_overview = !!overview_;
if (active == in_overview)
return;
if (active) {
overview_ = WindowOverviewMode::Create(container_.get(), this);
FOR_EACH_OBSERVER(WindowManagerObserver, observers_,
OnOverviewModeEnter());
// Re-stack all windows in the order defined by mru_window_tracker_.
aura::Window::Windows window_list = mru_window_tracker_->GetWindowList();
aura::Window::Windows::iterator it;
for (it = window_list.begin(); it != window_list.end(); ++it)
container_->StackChildAtTop(*it);
overview_ = WindowOverviewMode::Create(container_.get(),
mru_window_tracker_.get(),
this);
} else {
overview_.reset();
FOR_EACH_OBSERVER(WindowManagerObserver, observers_,
......@@ -182,8 +201,7 @@ void WindowManagerImpl::RemoveObserver(WindowManagerObserver* observer) {
}
void WindowManagerImpl::OnSelectWindow(aura::Window* window) {
CHECK_EQ(container_.get(), window->parent());
container_->StackChildAtTop(window);
mru_window_tracker_->MoveToFront(window);
wm::ActivateWindow(window);
SetInOverview(false);
}
......
......@@ -9,6 +9,7 @@
#include <vector>
#include "athena/common/closure_animation_observer.h"
#include "athena/wm/public/window_list_provider.h"
#include "base/bind.h"
#include "base/macros.h"
#include "ui/aura/scoped_window_targeter.h"
......@@ -52,10 +53,6 @@ namespace athena {
namespace {
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,
......@@ -109,8 +106,10 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
public ui::CompositorAnimationObserver {
public:
WindowOverviewModeImpl(aura::Window* container,
const WindowListProvider* window_list_provider,
WindowOverviewModeDelegate* delegate)
: container_(container),
window_list_provider_(window_list_provider),
delegate_(delegate),
scoped_targeter_(new aura::ScopedWindowTargeter(
container,
......@@ -128,13 +127,10 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
virtual ~WindowOverviewModeImpl() {
container_->set_target_handler(container_->delegate());
RemoveAnimationObserver();
const aura::Window::Windows& windows = container_->children();
for (aura::Window::Windows::const_iterator iter = windows.begin();
iter != windows.end();
++iter) {
if ((*iter)->GetProperty(kWindowOverviewState))
RestoreWindowState(*iter);
}
aura::Window::Windows windows = window_list_provider_->GetWindowList();
if (windows.empty())
return;
std::for_each(windows.begin(), windows.end(), &RestoreWindowState);
}
private:
......@@ -142,10 +138,8 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
// positions. The transforms are set in the |kWindowOverviewState| property of
// the windows.
void ComputeTerminalStatesForAllWindows() {
const aura::Window::Windows& windows = container_->children();
size_t window_count = std::count_if(windows.begin(), windows.end(),
ShouldShowWindowInOverviewMode);
aura::Window::Windows windows = window_list_provider_->GetWindowList();
size_t window_count = windows.size();
size_t index = 0;
const gfx::Size container_size = container_->bounds().size();
......@@ -154,10 +148,8 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
iter != windows.rend();
++iter) {
++iter, ++index) {
aura::Window* window = (*iter);
if (!ShouldShowWindowInOverviewMode(window))
continue;
gfx::Transform top_transform;
int top = (window_count - index - 1) * kGapBetweenWindowsTop;
......@@ -177,27 +169,20 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
state->progress = 0.f;
state->shadow = CreateShadowForWindow(window);
window->SetProperty(kWindowOverviewState, state);
index++;
}
}
// Sets the initial position for the windows for the overview mode.
void SetInitialWindowStates() {
aura::Window::Windows windows = window_list_provider_->GetWindowList();
size_t window_count = windows.size();
// 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 = container_->children();
for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
iter != windows.rend();
++iter) {
aura::Window* window = (*iter);
if (!window->GetProperty(kWindowOverviewState))
continue;
for (size_t i = 0; i < window_count; ++i) {
float progress = 0.f;
if (index < arraysize(kInitialProgress))
progress = kInitialProgress[index];
aura::Window* window = windows[window_count - 1 - i];
if (i < arraysize(kInitialProgress))
progress = kInitialProgress[i];
scoped_refptr<ui::LayerAnimator> animator =
window->layer()->GetAnimator();
......@@ -218,14 +203,13 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250));
SetWindowProgress(window, progress);
}
index++;
}
}
scoped_ptr<wm::Shadow> CreateShadowForWindow(aura::Window* window) {
scoped_ptr<wm::Shadow> shadow(new wm::Shadow());
shadow->Init(wm::Shadow::STYLE_ACTIVE);
shadow->SetContentBounds(gfx::Rect(window->bounds().size()));
shadow->SetContentBounds(gfx::Rect(container_->bounds().size()));
shadow->layer()->SetVisible(true);
window->layer()->Add(shadow->layer());
return shadow.Pass();
......@@ -254,7 +238,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
void DoScroll(float delta_y) {
const float kEpsilon = 1e-3f;
float delta_y_p = std::abs(delta_y) / GetScrollableHeight();
const aura::Window::Windows& windows = container_->children();
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.
......@@ -263,8 +247,6 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
++iter) {
aura::Window* window = (*iter);
WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
if (!state)
continue;
if (state->progress > kEpsilon) {
// It is possible to scroll |window| up. Scroll it up, and update
// |delta_y_p| for the next window.
......@@ -276,14 +258,12 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
} else {
// Scroll down. Start with the bottom-most (i.e. front-most in terms of
// z-index) window, and try to scroll them down.
for (aura::Window::Windows::const_reverse_iterator iter =
windows.rbegin();
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 (!state)
continue;
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.
......@@ -499,6 +479,8 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
const float kMinOpacity = 0.2f;
aura::Window* container_;
// Provider of the stack of windows to show in the overview mode. Not owned.
const WindowListProvider* window_list_provider_;
WindowOverviewModeDelegate* delegate_;
scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_;
scoped_ptr<ui::FlingCurve> fling_;
......@@ -514,9 +496,10 @@ class WindowOverviewModeImpl : public WindowOverviewMode,
// static
scoped_ptr<WindowOverviewMode> WindowOverviewMode::Create(
aura::Window* container,
const WindowListProvider* window_list_provider,
WindowOverviewModeDelegate* delegate) {
return scoped_ptr<WindowOverviewMode>(
new WindowOverviewModeImpl(container, delegate));
new WindowOverviewModeImpl(container, window_list_provider, delegate));
}
} // namespace athena
......@@ -6,12 +6,10 @@
#define ATHENA_WM_WINDOW_OVERVIEW_MODE_H_
#include "base/memory/scoped_ptr.h"
namespace aura {
class Window;
}
#include "ui/aura/window.h"
namespace athena {
class WindowListProvider;
class WindowOverviewModeDelegate {
public:
......@@ -26,6 +24,7 @@ class WindowOverviewMode {
static scoped_ptr<WindowOverviewMode> Create(
aura::Window* container,
const WindowListProvider* window_list_provider,
WindowOverviewModeDelegate* delegate);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment