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"
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"
25 // Returns a target transform which is suitable for animating a windows's
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()));
38 SplitViewController::SplitViewController(
39 aura::Window
* container
,
40 WindowListProvider
* window_list_provider
)
42 container_(container
),
43 window_list_provider_(window_list_provider
),
46 separator_position_(0),
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
)
64 if (right_window_
== left
)
65 right_window_
= right
;
70 right
= right_window_
;
73 if (!left
&& iter
!= windows
.rend()) {
76 if (left
== right
&& iter
!= windows
.rend()) {
82 if (!right
&& iter
!= windows
.rend()) {
85 if (right
== left
&& iter
!= windows
.rend()) {
92 if (right_window_
!= right
) {
93 right_window_
= right
;
94 container_
->StackChildAtTop(right_window_
);
96 if (left_window_
!= left
) {
98 container_
->StackChildAtTop(left_window_
);
103 void SplitViewController::ReplaceWindow(aura::Window
* window
,
104 aura::Window
* replace_with
) {
105 CHECK(IsSplitViewModeActive());
107 CHECK(window
== left_window_
|| window
== right_window_
);
108 CHECK(replace_with
!= left_window_
&& replace_with
!= right_window_
);
110 aura::Window::Windows windows
= window_list_provider_
->GetWindowList();
111 DCHECK(std::find(windows
.begin(), windows
.end(), replace_with
) !=
115 replace_with
->SetBounds(window
->bounds());
116 replace_with
->SetTransform(gfx::Transform());
117 if (window
== left_window_
)
118 left_window_
= replace_with
;
120 right_window_
= replace_with
;
121 wm::ActivateWindow(replace_with
);
122 window
->SetTransform(gfx::Transform());
125 void SplitViewController::DeactivateSplitMode() {
126 CHECK_NE(SCROLLING
, state_
);
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();
142 container_width
/ 2, 0, container_width
/ 2, work_area
.height());
145 void SplitViewController::UpdateLayout(bool animate
) {
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
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());
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
,
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(),
185 window
->SetTransform(transform
);
187 window
->SetTransform(transform
);
191 void SplitViewController::OnAnimationCompleted(aura::Window
* window
) {
192 // Animation can be cancelled when deactivated.
193 if (left_window_
== NULL
)
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());
201 right_window_
->SetBounds(GetRightTargetBounds());
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
)
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
,
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
));
245 right_window_
= current_window
;
246 left_window_
= *(iter
+ 1);
248 left_window_
= current_window
;
249 right_window_
= *(iter
+ 1);
253 CHECK(right_window_
);
255 UpdateSeparatorPositionFromScrollDelta(delta
);
259 void SplitViewController::ScrollEnd() {
260 if (state_
!= SCROLLING
)
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
) {
270 separator_position_
= container_width
/ 2;
271 } else if (separator_position_
< container_width
/ 2) {
272 separator_position_
= 0;
274 wm::ActivateWindow(right_window_
);
276 separator_position_
= container_width
;
278 wm::ActivateWindow(left_window_
);
283 void SplitViewController::ScrollUpdate(float delta
) {
284 if (state_
!= SCROLLING
)
286 UpdateSeparatorPositionFromScrollDelta(delta
);
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);
297 } // namespace athena