Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / athena / wm / split_view_controller.cc
blobe36b7a94e05623cbabd35e45564bdb0f691ae6e1
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "athena/wm/split_view_controller.h"
7 #include <cmath>
9 #include "athena/wm/public/window_list_provider.h"
10 #include "athena/wm/public/window_manager.h"
11 #include "base/bind.h"
12 #include "ui/aura/window.h"
13 #include "ui/compositor/closure_animation_observer.h"
14 #include "ui/compositor/layer_animation_observer.h"
15 #include "ui/compositor/scoped_layer_animation_settings.h"
16 #include "ui/events/event_handler.h"
17 #include "ui/gfx/display.h"
18 #include "ui/gfx/screen.h"
19 #include "ui/wm/core/window_util.h"
21 namespace athena {
23 namespace {
25 // Returns a target transform which is suitable for animating a windows's
26 // bounds.
27 gfx::Transform GetTargetTransformForBoundsAnimation(const gfx::Rect& from,
28 const gfx::Rect& to) {
29 gfx::Transform transform;
30 transform.Translate(to.x() - from.x(), to.y() - from.y());
31 transform.Scale(to.width() / static_cast<float>(from.width()),
32 to.height() / static_cast<float>(from.height()));
33 return transform;
36 } // namespace
38 SplitViewController::SplitViewController(
39 aura::Window* container,
40 WindowListProvider* window_list_provider)
41 : state_(INACTIVE),
42 container_(container),
43 window_list_provider_(window_list_provider),
44 left_window_(NULL),
45 right_window_(NULL),
46 separator_position_(0),
47 weak_factory_(this) {
50 SplitViewController::~SplitViewController() {
53 bool SplitViewController::IsSplitViewModeActive() const {
54 return state_ == ACTIVE;
57 void SplitViewController::ActivateSplitMode(aura::Window* left,
58 aura::Window* right) {
59 aura::Window::Windows windows = window_list_provider_->GetWindowList();
60 aura::Window::Windows::reverse_iterator iter = windows.rbegin();
61 if (state_ == ACTIVE) {
62 if (left_window_ == right)
63 left_window_ = left;
64 if (right_window_ == left)
65 right_window_ = right;
67 if (!left)
68 left = left_window_;
69 if (!right)
70 right = right_window_;
73 if (!left && iter != windows.rend()) {
74 left = *iter;
75 iter++;
76 if (left == right && iter != windows.rend()) {
77 left = *iter;
78 iter++;
82 if (!right && iter != windows.rend()) {
83 right = *iter;
84 iter++;
85 if (right == left && iter != windows.rend()) {
86 right = *iter;
87 iter++;
91 state_ = ACTIVE;
92 if (right_window_ != right) {
93 right_window_ = right;
94 container_->StackChildAtTop(right_window_);
96 if (left_window_ != left) {
97 left_window_ = left;
98 container_->StackChildAtTop(left_window_);
100 UpdateLayout(true);
103 void SplitViewController::ReplaceWindow(aura::Window* window,
104 aura::Window* replace_with) {
105 CHECK(IsSplitViewModeActive());
106 CHECK(replace_with);
107 CHECK(window == left_window_ || window == right_window_);
108 CHECK(replace_with != left_window_ && replace_with != right_window_);
109 #if !defined(NDEBUG)
110 aura::Window::Windows windows = window_list_provider_->GetWindowList();
111 DCHECK(std::find(windows.begin(), windows.end(), replace_with) !=
112 windows.end());
113 #endif
115 replace_with->SetBounds(window->bounds());
116 replace_with->SetTransform(gfx::Transform());
117 if (window == left_window_)
118 left_window_ = replace_with;
119 else
120 right_window_ = replace_with;
121 wm::ActivateWindow(replace_with);
122 window->SetTransform(gfx::Transform());
125 void SplitViewController::DeactivateSplitMode() {
126 CHECK_NE(SCROLLING, state_);
127 state_ = INACTIVE;
128 left_window_ = right_window_ = NULL;
131 gfx::Rect SplitViewController::GetLeftTargetBounds() {
132 gfx::Rect work_area =
133 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area();
134 return gfx::Rect(0, 0, container_->bounds().width() / 2, work_area.height());
137 gfx::Rect SplitViewController::GetRightTargetBounds() {
138 gfx::Rect work_area =
139 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area();
140 int container_width = container_->bounds().width();
141 return gfx::Rect(
142 container_width / 2, 0, container_width / 2, work_area.height());
145 void SplitViewController::UpdateLayout(bool animate) {
146 if (!left_window_)
147 return;
148 CHECK(right_window_);
149 gfx::Transform left_transform;
150 gfx::Transform right_transform;
151 int container_width = container_->GetBoundsInScreen().width();
152 if (state_ == ACTIVE) {
153 // Windows should be resized via an animation when entering the ACTIVE
154 // state.
155 CHECK(animate);
156 // We scale the windows here, but when the animation finishes, we reset
157 // the scaling and update the window bounds to the proper size - see
158 // OnAnimationCompleted().
159 left_transform = GetTargetTransformForBoundsAnimation(
160 left_window_->bounds(), GetLeftTargetBounds());
161 right_transform = GetTargetTransformForBoundsAnimation(
162 right_window_->bounds(), GetRightTargetBounds());
163 } else {
164 left_transform.Translate(separator_position_ - container_width, 0);
165 right_transform.Translate(separator_position_, 0);
167 left_window_->Show();
168 right_window_->Show();
169 SetWindowTransform(left_window_, left_transform, animate);
170 SetWindowTransform(right_window_, right_transform, animate);
173 void SplitViewController::SetWindowTransform(aura::Window* window,
174 const gfx::Transform& transform,
175 bool animate) {
176 if (animate) {
177 scoped_refptr<ui::LayerAnimator> animator = window->layer()->GetAnimator();
178 ui::ScopedLayerAnimationSettings settings(animator);
179 settings.SetPreemptionStrategy(
180 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
181 settings.AddObserver(new ui::ClosureAnimationObserver(
182 base::Bind(&SplitViewController::OnAnimationCompleted,
183 weak_factory_.GetWeakPtr(),
184 window)));
185 window->SetTransform(transform);
186 } else {
187 window->SetTransform(transform);
191 void SplitViewController::OnAnimationCompleted(aura::Window* window) {
192 // Animation can be cancelled when deactivated.
193 if (left_window_ == NULL)
194 return;
195 DCHECK(window == left_window_ || window == right_window_);
196 if (state_ == ACTIVE) {
197 window->SetTransform(gfx::Transform());
198 if (window == left_window_)
199 left_window_->SetBounds(GetLeftTargetBounds());
200 else
201 right_window_->SetBounds(GetRightTargetBounds());
202 } else {
203 int container_width = container_->bounds().width();
204 window->SetTransform(gfx::Transform());
205 if (window == left_window_) {
206 if (separator_position_ == 0)
207 left_window_->Hide();
208 if (state_ == INACTIVE)
209 left_window_ = NULL;
210 } else {
211 if (separator_position_ == container_width)
212 right_window_->Hide();
213 if (state_ == INACTIVE)
214 right_window_ = NULL;
219 void SplitViewController::UpdateSeparatorPositionFromScrollDelta(float delta) {
220 gfx::Screen* screen = gfx::Screen::GetScreenFor(container_);
221 const gfx::Rect& display_bounds =
222 screen->GetDisplayNearestWindow(container_).bounds();
223 gfx::Rect container_bounds = container_->GetBoundsInScreen();
224 separator_position_ =
225 delta > 0 ? ((int)delta) + display_bounds.x() - container_bounds.x()
226 : display_bounds.right() - container_bounds.x() + delta;
229 ///////////////////////////////////////////////////////////////////////////////
230 // BezelController::ScrollDelegate:
232 void SplitViewController::ScrollBegin(BezelController::Bezel bezel,
233 float delta) {
234 if (!CanScroll())
235 return;
236 state_ = SCROLLING;
238 aura::Window::Windows windows = window_list_provider_->GetWindowList();
239 CHECK(windows.size() >= 2);
240 aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
241 aura::Window* current_window = *(iter);
242 CHECK(wm::IsActiveWindow(current_window));
244 if (delta > 0) {
245 right_window_ = current_window;
246 left_window_ = *(iter + 1);
247 } else {
248 left_window_ = current_window;
249 right_window_ = *(iter + 1);
252 CHECK(left_window_);
253 CHECK(right_window_);
255 UpdateSeparatorPositionFromScrollDelta(delta);
256 UpdateLayout(false);
259 void SplitViewController::ScrollEnd() {
260 if (state_ != SCROLLING)
261 return;
263 // Max distance from the scroll end position to the middle of the screen where
264 // we would go into the split view mode.
265 const int kMaxDistanceFromMiddle = 120;
266 int container_width = container_->GetBoundsInScreen().width();
267 if (std::abs(container_width / 2 - separator_position_) <=
268 kMaxDistanceFromMiddle) {
269 state_ = ACTIVE;
270 separator_position_ = container_width / 2;
271 } else if (separator_position_ < container_width / 2) {
272 separator_position_ = 0;
273 state_ = INACTIVE;
274 wm::ActivateWindow(right_window_);
275 } else {
276 separator_position_ = container_width;
277 state_ = INACTIVE;
278 wm::ActivateWindow(left_window_);
280 UpdateLayout(true);
283 void SplitViewController::ScrollUpdate(float delta) {
284 if (state_ != SCROLLING)
285 return;
286 UpdateSeparatorPositionFromScrollDelta(delta);
287 UpdateLayout(false);
290 bool SplitViewController::CanScroll() {
291 // TODO(mfomitchev): return false in vertical orientation, in full screen.
292 bool result = (!IsSplitViewModeActive() &&
293 window_list_provider_->GetWindowList().size() >= 2);
294 return result;
297 } // namespace athena