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

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

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