Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(378)

Side by Side Diff: ash/wm/splitview/split_view_controller.cc

Issue 2918403006: CrOS Tablet Window management - Split Screen part I (Closed)
Patch Set: Add unittests. Will split the CL into two CLs. Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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.
4
5 #include "ash/wm/splitview/split_view_controller.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/public/cpp/shell_window_ids.h"
9 #include "ash/screen_util.h"
10 #include "ash/shell.h"
11 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
12 #include "ash/wm/mru_window_tracker.h"
13 #include "ash/wm/window_state.h"
14 #include "ash/wm/window_util.h"
15 #include "ash/wm/wm_event.h"
16 #include "base/command_line.h"
17 #include "ui/aura/window.h"
18 #include "ui/wm/core/coordinate_conversion.h"
19 #include "ui/wm/public/activation_client.h"
20
21 namespace ash {
22
23 namespace {
24
25 // Returns true if |window| can be activated and snapped.
26 bool CanSnap(aura::Window* window) {
27 if (!wm::CanActivateWindow(window))
28 return false;
29 return wm::GetWindowState(window)->CanSnap();
30 }
31
32 } // namespace
33
34 SplitViewController::SplitViewController() {
35 Shell::Get()->AddShellObserver(this);
36 Shell::Get()->activation_client()->AddObserver(this);
37 }
38
39 SplitViewController::~SplitViewController() {
40 Shell::Get()->RemoveShellObserver(this);
41 Shell::Get()->activation_client()->RemoveObserver(this);
42 EndSplitView();
43 }
44
45 // static
46 bool SplitViewController::ShouldAllowSplitView() {
47 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
48 switches::kAshEnableTabletSplitView)) {
49 return false;
50 }
51 if (!Shell::Get()
52 ->maximize_mode_controller()
53 ->IsMaximizeModeWindowManagerEnabled()) {
54 return false;
55 }
56 return true;
57 }
58
59 bool SplitViewController::IsSplitViewModeActive() const {
60 return state_ != NOSNAP;
61 }
62
63 void SplitViewController::SetLeftWindow(aura::Window* left_window) {
64 if (!left_window)
65 return;
66
67 if (state_ == NOSNAP)
68 Shell::Get()->NotifySplitViewModeStarting();
69
70 if (right_window_ == left_window) {
71 StopObserving(right_window_);
72 right_window_ = nullptr;
73 }
74 if (left_window_ != left_window) {
75 StopObserving(left_window_);
76 left_window_ = nullptr;
77 left_window_ = left_window;
78 left_window_->AddObserver(this);
79 wm::GetWindowState(left_window_)->AddObserver(this);
80 }
81
82 State previous_state = state_;
83 switch (state_) {
84 case NOSNAP:
85 default_snap_position_ = LEFT;
86 state_ = LEFT_SNAPPED;
87 break;
88 case LEFT_SNAPPED:
89 state_ = LEFT_SNAPPED;
90 break;
91 case RIGHT_SNAPPED:
92 case BOTH_SNAPPED:
93 state_ = BOTH_SNAPPED;
94 break;
95 }
96
97 const wm::WMEvent event(wm::WM_EVENT_SNAP_LEFT);
98 wm::GetWindowState(left_window_)->OnWMEvent(&event);
99 wm::ActivateWindow(left_window_);
100
101 NotifySplitViewStateChanged(previous_state, state_);
102 }
103
104 void SplitViewController::SetRightWindow(aura::Window* right_window) {
105 if (!right_window)
106 return;
107
108 if (state_ == NOSNAP)
109 Shell::Get()->NotifySplitViewModeStarting();
110
111 if (left_window_ == right_window) {
112 StopObserving(left_window_);
113 left_window_ = nullptr;
114 }
115 if (right_window_ != right_window) {
116 StopObserving(right_window_);
117 right_window_ = nullptr;
118 right_window_ = right_window;
119 right_window_->AddObserver(this);
120 wm::GetWindowState(right_window_)->AddObserver(this);
121 }
122
123 State previous_state = state_;
124 switch (state_) {
125 case NOSNAP:
126 default_snap_position_ = RIGHT;
127 state_ = RIGHT_SNAPPED;
128 break;
129 case RIGHT_SNAPPED:
130 state_ = RIGHT_SNAPPED;
131 break;
132 case LEFT_SNAPPED:
133 case BOTH_SNAPPED:
134 state_ = BOTH_SNAPPED;
135 break;
136 }
137
138 const wm::WMEvent event(wm::WM_EVENT_SNAP_RIGHT);
139 wm::GetWindowState(right_window_)->OnWMEvent(&event);
140 wm::ActivateWindow(right_window_);
141
142 NotifySplitViewStateChanged(previous_state, state_);
143 }
144
145 aura::Window* SplitViewController::GetDefaultSnappedWindow() {
146 if (default_snap_position_ == LEFT)
147 return left_window_;
148 if (default_snap_position_ == RIGHT)
149 return right_window_;
150 return nullptr;
151 }
152
153 gfx::Rect SplitViewController::GetSnappedWindowBoundsInParent(
154 aura::Window* window,
155 State snap_state) {
156 if (snap_state == LEFT_SNAPPED)
157 return GetLeftWindowBoundsInParent(window);
158 else if (snap_state == RIGHT_SNAPPED)
159 return GetRightWindowBoundsInParent(window);
160
161 NOTREACHED();
162 return gfx::Rect();
163 }
164
165 gfx::Rect SplitViewController::GetSnappedWindowBoundsInScreen(
166 aura::Window* window,
167 State snap_state) {
168 if (snap_state == LEFT_SNAPPED)
169 return GetLeftWindowBoundsInScreen(window);
170 else if (snap_state == RIGHT_SNAPPED)
171 return GetRightWindowBoundsInScreen(window);
172
173 NOTREACHED();
174 return gfx::Rect();
175 }
176
177 gfx::Rect SplitViewController::GetDisplayWorkAreaBoundsInParent(
178 aura::Window* window) {
179 aura::Window* root_window = window->GetRootWindow();
180 gfx::Rect bounds_in_parent = ScreenUtil::GetDisplayWorkAreaBoundsInParent(
181 root_window->GetChildById(kShellWindowId_DefaultContainer));
182 return bounds_in_parent;
183 }
184
185 gfx::Rect SplitViewController::GetDisplayWorkAreaBoundsInScreen(
186 aura::Window* window) {
187 gfx::Rect bounds = GetDisplayWorkAreaBoundsInParent(window);
188 ::wm::ConvertRectToScreen(window->GetRootWindow(), &bounds);
189 return bounds;
190 }
191
192 void SplitViewController::AddObserver(Observer* observer) {
193 observers_.AddObserver(observer);
194 }
195
196 void SplitViewController::RemoveObserver(Observer* observer) {
197 observers_.RemoveObserver(observer);
198 }
199
200 void SplitViewController::OnWindowDestroying(aura::Window* window) {
201 // If one of the snapped window gets closed, end the split view mode. The
202 // behavior might change in the future.
203 DCHECK(window == left_window_ || window == right_window_);
204 EndSplitView();
205 }
206
207 void SplitViewController::OnPostWindowStateTypeChange(
208 ash::wm::WindowState* window_state,
209 ash::wm::WindowStateType old_type) {
210 if (window_state->IsFullscreen() || window_state->IsMinimized() ||
211 window_state->IsMaximized()) {
212 // TODO(xdai): Decide what to do if one of the snapped windows gets
213 // minimized /maximized / full-screened. For now we end the split view mode
214 // for simplicity.
215 EndSplitView();
216 }
217 }
218
219 void SplitViewController::OnWindowActivated(ActivationReason reason,
220 aura::Window* gained_active,
221 aura::Window* lost_active) {
222 if (!IsSplitViewModeActive())
223 return;
224
225 // If |gained_active| was activated as a side effect of a window disposition
226 // change, do nothing. For example, when a snapped window is closed, another
227 // window will be activated before OnWindowDestroying() is called. We should
228 // not try to snap another window in this case.
229 if (reason == ActivationReason::WINDOW_DISPOSITION_CHANGED)
230 return;
231
232 // Only snap window that can be snapped but hasn't been snapped.
233 if (!gained_active || gained_active == left_window_ ||
234 gained_active == right_window_ || !CanSnap(gained_active)) {
235 return;
236 }
237
238 // Only window in MRU list can be snapped.
239 aura::Window::Windows windows =
240 Shell::Get()->mru_window_tracker()->BuildMruWindowList();
241 if (std::find(windows.begin(), windows.end(), gained_active) == windows.end())
242 return;
243
244 // Snap the window on the non-default side of the screen if split view mode
245 // is active.
246 if (default_snap_position_ == LEFT)
247 SetRightWindow(gained_active);
248 else
249 SetLeftWindow(gained_active);
250 }
251
252 void SplitViewController::OnOverviewModeStarting() {
253 // If split view mode is active, reset |state_| to make it be able to select
254 // another window from overview window grid.
255 if (IsSplitViewModeActive()) {
256 DCHECK(state_ == BOTH_SNAPPED);
257 if (default_snap_position_ == LEFT) {
258 StopObserving(right_window_);
259 state_ = LEFT_SNAPPED;
260 } else {
261 StopObserving(left_window_);
262 state_ = RIGHT_SNAPPED;
263 }
264 NotifySplitViewStateChanged(BOTH_SNAPPED, state_);
265 }
266 }
267
268 void SplitViewController::OnOverviewModeEnded() {
269 // If split view mode is active but only has one snapped window, use the MRU
270 // window list to auto select another window to snap.
271 if (IsSplitViewModeActive() && state_ != BOTH_SNAPPED) {
272 aura::Window::Windows windows =
273 Shell::Get()->mru_window_tracker()->BuildMruWindowList();
274 auto end = std::remove_if(windows.begin(), windows.end(),
275 std::not1(std::ptr_fun(&CanSnap)));
276 windows.resize(end - windows.begin());
277 for (auto* window : windows) {
278 if (window != GetDefaultSnappedWindow()) {
279 if (default_snap_position_ == LEFT)
280 SetRightWindow(window);
281 else
282 SetLeftWindow(window);
283 break;
284 }
285 }
286 }
287 }
288
289 void SplitViewController::EndSplitView() {
290 StopObserving(left_window_);
291 StopObserving(right_window_);
292 left_window_ = nullptr;
293 right_window_ = nullptr;
294 default_snap_position_ = LEFT;
295 separator_position_ = -1;
296
297 State previous_state = state_;
298 state_ = NOSNAP;
299 NotifySplitViewStateChanged(previous_state, state_);
300
301 Shell::Get()->NotifySplitViewModeEnded();
302 }
303
304 void SplitViewController::StopObserving(aura::Window* window) {
305 if (window) {
306 window->RemoveObserver(this);
307 if (wm::GetWindowState(window))
308 wm::GetWindowState(window)->RemoveObserver(this);
309 }
310 }
311
312 void SplitViewController::NotifySplitViewStateChanged(State previous_state,
313 State state) {
314 if (previous_state == state)
315 return;
316 for (Observer& observer : observers_)
317 observer.OnSplitViewStateChanged(previous_state, state);
318 }
319
320 gfx::Rect SplitViewController::GetLeftWindowBoundsInParent(
321 aura::Window* window) {
322 gfx::Rect bounds_in_parent = GetDisplayWorkAreaBoundsInParent(window);
323 if (separator_position_ < 0)
324 separator_position_ = bounds_in_parent.width() * 0.5f;
325 gfx::Rect bounds(bounds_in_parent.x(), bounds_in_parent.y(),
326 separator_position_, bounds_in_parent.height());
327 return bounds;
328 }
329
330 gfx::Rect SplitViewController::GetRightWindowBoundsInParent(
331 aura::Window* window) {
332 gfx::Rect bounds_in_parent = GetDisplayWorkAreaBoundsInParent(window);
333 if (separator_position_ < 0)
334 separator_position_ = bounds_in_parent.width() * 0.5f;
335 gfx::Rect bounds(bounds_in_parent.x() + separator_position_,
336 bounds_in_parent.y(),
337 bounds_in_parent.width() - separator_position_,
338 bounds_in_parent.height());
339 return bounds;
340 }
341
342 gfx::Rect SplitViewController::GetLeftWindowBoundsInScreen(
343 aura::Window* window) {
344 gfx::Rect bounds = GetLeftWindowBoundsInParent(window);
345 ::wm::ConvertRectToScreen(window->GetRootWindow(), &bounds);
346 return bounds;
347 }
348
349 gfx::Rect SplitViewController::GetRightWindowBoundsInScreen(
350 aura::Window* window) {
351 gfx::Rect bounds = GetRightWindowBoundsInParent(window);
352 ::wm::ConvertRectToScreen(window->GetRootWindow(), &bounds);
353 return bounds;
354 }
355
356 } // namespace ash
OLDNEW
« no previous file with comments | « ash/wm/splitview/split_view_controller.h ('k') | ash/wm/splitview/split_view_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698