diff options
| author | mfomitchev@chromium.org <mfomitchev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-13 22:03:12 +0000 |
|---|---|---|
| committer | mfomitchev@chromium.org <mfomitchev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-13 22:04:34 +0000 |
| commit | 1f883b1b119ae23936923680a7dd5d0f537c870e (patch) | |
| tree | a3aebf66db04b6f1468b77c4a6fe6212ae94da62 /athena/wm | |
| parent | dd4a4f84e9d5dc227f78d36d2812b4a9938cab81 (diff) | |
| download | chromium_src-1f883b1b119ae23936923680a7dd5d0f537c870e.zip chromium_src-1f883b1b119ae23936923680a7dd5d0f537c870e.tar.gz chromium_src-1f883b1b119ae23936923680a7dd5d0f537c870e.tar.bz2 | |
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/468133002
Cr-Commit-Position: refs/heads/master@{#289392}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289392 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'athena/wm')
| -rw-r--r-- | athena/wm/bezel_controller.cc | 84 | ||||
| -rw-r--r-- | athena/wm/bezel_controller.h | 12 | ||||
| -rw-r--r-- | athena/wm/mru_window_tracker.cc | 53 | ||||
| -rw-r--r-- | athena/wm/mru_window_tracker.h | 49 | ||||
| -rw-r--r-- | athena/wm/public/window_list_provider.h | 27 | ||||
| -rw-r--r-- | athena/wm/public/window_manager.h | 2 | ||||
| -rw-r--r-- | athena/wm/public/window_manager_observer.h | 4 | ||||
| -rw-r--r-- | athena/wm/split_view_controller.cc | 248 | ||||
| -rw-r--r-- | athena/wm/split_view_controller.h | 79 | ||||
| -rw-r--r-- | athena/wm/window_manager_impl.cc | 25 | ||||
| -rw-r--r-- | athena/wm/window_overview_mode.cc | 63 | ||||
| -rw-r--r-- | athena/wm/window_overview_mode.h | 7 |
12 files changed, 561 insertions, 92 deletions
diff --git a/athena/wm/bezel_controller.cc b/athena/wm/bezel_controller.cc index fb62155..a4e8156 100644 --- a/athena/wm/bezel_controller.cc +++ b/athena/wm/bezel_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(); diff --git a/athena/wm/bezel_controller.h b/athena/wm/bezel_controller.h index 30fe974..7bec71a 100644 --- a/athena/wm/bezel_controller.h +++ b/athena/wm/bezel_controller.h @@ -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. diff --git a/athena/wm/mru_window_tracker.cc b/athena/wm/mru_window_tracker.cc new file mode 100644 index 0000000..b2cda0c --- /dev/null +++ b/athena/wm/mru_window_tracker.cc @@ -0,0 +1,53 @@ +// 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 diff --git a/athena/wm/mru_window_tracker.h b/athena/wm/mru_window_tracker.h new file mode 100644 index 0000000..0ec9d98 --- /dev/null +++ b/athena/wm/mru_window_tracker.h @@ -0,0 +1,49 @@ +// 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_ diff --git a/athena/wm/public/window_list_provider.h b/athena/wm/public/window_list_provider.h new file mode 100644 index 0000000..7ffadf4 --- /dev/null +++ b/athena/wm/public/window_list_provider.h @@ -0,0 +1,27 @@ +// 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_ diff --git a/athena/wm/public/window_manager.h b/athena/wm/public/window_manager.h index 23aab7f..04e2cce 100644 --- a/athena/wm/public/window_manager.h +++ b/athena/wm/public/window_manager.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; }; diff --git a/athena/wm/public/window_manager_observer.h b/athena/wm/public/window_manager_observer.h index e7f6bcf..216b58c 100644 --- a/athena/wm/public/window_manager_observer.h +++ b/athena/wm/public/window_manager_observer.h @@ -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; }; diff --git a/athena/wm/split_view_controller.cc b/athena/wm/split_view_controller.cc index 7da3675..595a61a 100644 --- a/athena/wm/split_view_controller.cc +++ b/athena/wm/split_view_controller.cc @@ -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 diff --git a/athena/wm/split_view_controller.h b/athena/wm/split_view_controller.h index e0652de..337b75b 100644 --- a/athena/wm/split_view_controller.h +++ b/athena/wm/split_view_controller.h @@ -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); }; diff --git a/athena/wm/window_manager_impl.cc b/athena/wm/window_manager_impl.cc index 560252a..37daf43 100644 --- a/athena/wm/window_manager_impl.cc +++ b/athena/wm/window_manager_impl.cc @@ -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" @@ -41,6 +42,8 @@ class WindowManagerImpl : public WindowManager, // WindowManager: virtual void ToggleOverview() OVERRIDE; + virtual bool IsOverviewModeActive() OVERRIDE; + private: enum Command { CMD_TOGGLE_OVERVIEW, @@ -74,6 +77,7 @@ class WindowManagerImpl : public WindowManager, virtual void OnTitleDragCanceled(aura::Window* window) OVERRIDE; 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_; @@ -112,8 +116,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)); @@ -127,6 +133,7 @@ WindowManagerImpl::WindowManagerImpl() { WindowManagerImpl::~WindowManagerImpl() { overview_.reset(); + mru_window_tracker_.reset(); if (container_) { container_->RemoveObserver(this); container_->RemovePreTargetHandler(bezel_controller_.get()); @@ -154,15 +161,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_, @@ -188,8 +206,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); } diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc index efad0b1..91ee8c9 100644 --- a/athena/wm/window_overview_mode.cc +++ b/athena/wm/window_overview_mode.cc @@ -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" @@ -50,10 +51,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, @@ -108,8 +105,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, @@ -127,13 +126,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: @@ -141,10 +137,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(); @@ -153,10 +147,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; @@ -176,27 +168,20 @@ class WindowOverviewModeImpl : public WindowOverviewMode, state->progress = 0.f; window->SetProperty(kWindowOverviewState, state); wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE); - - 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(); @@ -217,7 +202,6 @@ class WindowOverviewModeImpl : public WindowOverviewMode, settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250)); SetWindowProgress(window, progress); } - index++; } } @@ -244,7 +228,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. @@ -253,8 +237,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. @@ -266,14 +248,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. @@ -489,6 +469,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_; @@ -504,9 +486,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 diff --git a/athena/wm/window_overview_mode.h b/athena/wm/window_overview_mode.h index 46eb249..a95a0fb 100644 --- a/athena/wm/window_overview_mode.h +++ b/athena/wm/window_overview_mode.h @@ -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); }; |
