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

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

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

Powered by Google App Engine
This is Rietveld 408576698