blob: ada7e36ebd7c15b64ba22e011de7e48df418d17b [file] [log] [blame]
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +00001// 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.
4
5#include "ash/wm/default_state.h"
6
7#include "ash/display/display_controller.h"
8#include "ash/screen_util.h"
9#include "ash/shell.h"
10#include "ash/shell_window_ids.h"
11#include "ash/wm/coordinate_conversion.h"
12#include "ash/wm/window_animations.h"
13#include "ash/wm/window_state.h"
14#include "ash/wm/window_state_delegate.h"
Bo Liu5c02ac12014-05-01 10:37:37 -070015#include "ash/wm/window_state_util.h"
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000016#include "ash/wm/window_util.h"
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000017#include "ash/wm/wm_event.h"
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000018#include "ash/wm/workspace/workspace_window_resizer.h"
19#include "ui/aura/client/aura_constants.h"
20#include "ui/aura/window.h"
21#include "ui/aura/window_delegate.h"
22#include "ui/gfx/display.h"
23#include "ui/gfx/rect.h"
24
25namespace ash {
26namespace wm {
27namespace {
28
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000029// This specifies how much percent (30%) of a window rect
30// must be visible when the window is added to the workspace.
31const float kMinimumPercentOnScreenArea = 0.3f;
32
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000033bool IsPanel(aura::Window* window) {
34 return window->parent() &&
Ben Murdochc5cede92014-04-10 11:22:14 +010035 window->parent()->id() == kShellWindowId_PanelContainer;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000036}
37
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000038void MoveToDisplayForRestore(WindowState* window_state) {
39 if (!window_state->HasRestoreBounds())
40 return;
41 const gfx::Rect& restore_bounds = window_state->GetRestoreBoundsInScreen();
42
43 // Move only if the restore bounds is outside of
44 // the display. There is no information about in which
45 // display it should be restored, so this is best guess.
46 // TODO(oshima): Restore information should contain the
47 // work area information like WindowResizer does for the
48 // last window location.
49 gfx::Rect display_area = Shell::GetScreen()->GetDisplayNearestWindow(
50 window_state->window()).bounds();
51
52 if (!display_area.Intersects(restore_bounds)) {
53 const gfx::Display& display =
54 Shell::GetScreen()->GetDisplayMatching(restore_bounds);
55 DisplayController* display_controller =
56 Shell::GetInstance()->display_controller();
57 aura::Window* new_root =
58 display_controller->GetRootWindowForDisplayId(display.id());
59 if (new_root != window_state->window()->GetRootWindow()) {
60 aura::Window* new_container =
61 Shell::GetContainer(new_root, window_state->window()->parent()->id());
62 new_container->AddChild(window_state->window());
63 }
64 }
65}
66
67} // namespace;
68
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000069DefaultState::DefaultState(WindowStateType initial_state_type)
70 : state_type_(initial_state_type) {}
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000071DefaultState::~DefaultState() {}
72
73void DefaultState::OnWMEvent(WindowState* window_state,
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000074 const WMEvent* event) {
75 if (ProcessWorkspaceEvents(window_state, event))
76 return;
77
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000078 if (ProcessCompoundEvents(window_state, event))
79 return;
80
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000081 WindowStateType next_state_type = WINDOW_STATE_TYPE_NORMAL;
82 switch (event->type()) {
83 case WM_EVENT_NORMAL:
84 next_state_type = WINDOW_STATE_TYPE_NORMAL;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000085 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000086 case WM_EVENT_MAXIMIZE:
87 next_state_type = WINDOW_STATE_TYPE_MAXIMIZED;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000088 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000089 case WM_EVENT_MINIMIZE:
90 next_state_type = WINDOW_STATE_TYPE_MINIMIZED;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000091 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000092 case WM_EVENT_FULLSCREEN:
93 next_state_type = WINDOW_STATE_TYPE_FULLSCREEN;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000094 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000095 case WM_EVENT_SNAP_LEFT:
96 next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +000097 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +000098 case WM_EVENT_SNAP_RIGHT:
99 next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000100 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000101 case WM_EVENT_SET_BOUNDS:
102 SetBounds(window_state, static_cast<const SetBoundsEvent*>(event));
103 return;
104 case WM_EVENT_SHOW_INACTIVE:
105 next_state_type = WINDOW_STATE_TYPE_INACTIVE;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000106 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000107 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
108 case WM_EVENT_TOGGLE_MAXIMIZE:
109 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
110 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
111 case WM_EVENT_TOGGLE_FULLSCREEN:
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000112 case WM_EVENT_CENTER:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000113 NOTREACHED() << "Compound event should not reach here:" << event;
114 return;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000115 case WM_EVENT_ADDED_TO_WORKSPACE:
116 case WM_EVENT_WORKAREA_BOUNDS_CHANGED:
117 case WM_EVENT_DISPLAY_BOUNDS_CHANGED:
118 NOTREACHED() << "Workspace event should not reach here:" << event;
119 return;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000120 }
121
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000122 WindowStateType current = window_state->GetStateType();
123
124 if (next_state_type == current && window_state->IsSnapped()) {
125 gfx::Rect snapped_bounds = event->type() == WM_EVENT_SNAP_LEFT ?
126 GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
127 GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
128 window_state->SetBoundsDirectAnimated(snapped_bounds);
129 return;
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000130 }
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000131
Ben Murdocheffb81e2014-03-31 11:51:25 +0100132 EnterToNextState(window_state, next_state_type);
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000133}
134
135WindowStateType DefaultState::GetType() const {
136 return state_type_;
137}
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000138
Ben Murdocheffb81e2014-03-31 11:51:25 +0100139void DefaultState::AttachState(
140 WindowState* window_state,
141 WindowState::State* state_in_previous_mode) {
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000142 DCHECK_EQ(stored_window_state_, window_state);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000143
Ben Murdocheffb81e2014-03-31 11:51:25 +0100144 ReenterToCurrentState(window_state, state_in_previous_mode);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000145
Ben Murdocheffb81e2014-03-31 11:51:25 +0100146 // If the display has changed while in the another mode,
147 // we need to let windows know the change.
148 gfx::Display current_display = Shell::GetScreen()->
149 GetDisplayNearestWindow(window_state->window());
150 if (stored_display_state_.bounds() != current_display.bounds()) {
151 const WMEvent event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000152 window_state->OnWMEvent(&event);
Ben Murdocheffb81e2014-03-31 11:51:25 +0100153 } else if (stored_display_state_.work_area() != current_display.work_area()) {
154 const WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED);
155 window_state->OnWMEvent(&event);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000156 }
157}
158
159void DefaultState::DetachState(WindowState* window_state) {
160 stored_window_state_ = window_state;
161 aura::Window* window = window_state->window();
162 stored_bounds_ = window->bounds();
163 stored_restore_bounds_ = window_state->HasRestoreBounds() ?
164 window_state->GetRestoreBoundsInParent() : gfx::Rect();
Ben Murdocheffb81e2014-03-31 11:51:25 +0100165 // Remember the display state so that in case of the display change
166 // while in the other mode, we can perform necessary action to
167 // restore the window state to the proper state for the current
168 // display.
169 stored_display_state_ = Shell::GetScreen()->
170 GetDisplayNearestWindow(window_state->window());
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000171}
172
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000173// static
174bool DefaultState::ProcessCompoundEvents(WindowState* window_state,
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000175 const WMEvent* event) {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000176 aura::Window* window = window_state->window();
177
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000178 switch (event->type()) {
179 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000180 if (window_state->IsFullscreen()) {
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000181 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
182 window_state->OnWMEvent(&event);
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000183 } else if (window_state->IsMaximized()) {
184 window_state->Restore();
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000185 } else if (window_state->IsNormalOrSnapped()) {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000186 if (window_state->CanMaximize())
187 window_state->Maximize();
188 }
189 return true;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000190 case WM_EVENT_TOGGLE_MAXIMIZE:
191 if (window_state->IsFullscreen()) {
192 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
193 window_state->OnWMEvent(&event);
194 } else if (window_state->IsMaximized()) {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000195 window_state->Restore();
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000196 } else if (window_state->CanMaximize()) {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000197 window_state->Maximize();
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000198 }
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000199 return true;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000200 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000201 gfx::Rect work_area =
202 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
203
204 // Maximize vertically if:
205 // - The window does not have a max height defined.
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000206 // - The window has the normal state type. Snapped windows are excluded
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000207 // because they are already maximized vertically and reverting to the
208 // restored bounds looks weird.
209 if (window->delegate()->GetMaximumSize().height() != 0 ||
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000210 !window_state->IsNormalStateType()) {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000211 return true;
212 }
213 if (window_state->HasRestoreBounds() &&
214 (window->bounds().height() == work_area.height() &&
215 window->bounds().y() == work_area.y())) {
216 window_state->SetAndClearRestoreBounds();
217 } else {
218 window_state->SaveCurrentBoundsForRestore();
219 window->SetBounds(gfx::Rect(window->bounds().x(),
220 work_area.y(),
221 window->bounds().width(),
222 work_area.height()));
223 }
224 return true;
225 }
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000226 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000227 // Maximize horizontally if:
228 // - The window does not have a max width defined.
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000229 // - The window is snapped or has the normal state type.
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000230 if (window->delegate()->GetMaximumSize().width() != 0)
231 return true;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000232 if (!window_state->IsNormalOrSnapped())
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000233 return true;
234 gfx::Rect work_area =
235 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000236 if (window_state->IsNormalStateType() &&
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000237 window_state->HasRestoreBounds() &&
238 (window->bounds().width() == work_area.width() &&
239 window->bounds().x() == work_area.x())) {
240 window_state->SetAndClearRestoreBounds();
241 } else {
242 gfx::Rect new_bounds(work_area.x(),
243 window->bounds().y(),
244 work_area.width(),
245 window->bounds().height());
246
247 gfx::Rect restore_bounds = window->bounds();
248 if (window_state->IsSnapped()) {
249 window_state->SetRestoreBoundsInParent(new_bounds);
250 window_state->Restore();
251
252 // The restore logic prevents a window from being restored to bounds
253 // which match the workspace bounds exactly so it is necessary to set
254 // the bounds again below.
255 }
256
257 window_state->SetRestoreBoundsInParent(restore_bounds);
258 window->SetBounds(new_bounds);
259 }
260 return true;
261 }
Bo Liu5c02ac12014-05-01 10:37:37 -0700262 case WM_EVENT_TOGGLE_FULLSCREEN:
263 ToggleFullScreen(window_state, window_state->delegate());
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000264 return true;
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000265 case WM_EVENT_CENTER:
266 CenterWindow(window_state);
267 return true;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000268 case WM_EVENT_NORMAL:
269 case WM_EVENT_MAXIMIZE:
270 case WM_EVENT_MINIMIZE:
271 case WM_EVENT_FULLSCREEN:
272 case WM_EVENT_SNAP_LEFT:
273 case WM_EVENT_SNAP_RIGHT:
274 case WM_EVENT_SET_BOUNDS:
275 case WM_EVENT_SHOW_INACTIVE:
276 break;
277 case WM_EVENT_ADDED_TO_WORKSPACE:
278 case WM_EVENT_WORKAREA_BOUNDS_CHANGED:
279 case WM_EVENT_DISPLAY_BOUNDS_CHANGED:
280 NOTREACHED() << "Workspace event should not reach here:" << event;
281 break;
282 }
283 return false;
284}
285
286bool DefaultState::ProcessWorkspaceEvents(WindowState* window_state,
287 const WMEvent* event) {
288 switch (event->type()) {
289 case WM_EVENT_ADDED_TO_WORKSPACE: {
290 // When a window is dragged and dropped onto a different
291 // root window, the bounds will be updated after they are added
292 // to the root window.
293 // If a window is opened as maximized or fullscreen, its bounds may be
294 // empty, so update the bounds now before checking empty.
295 if (window_state->is_dragged() ||
296 SetMaximizedOrFullscreenBounds(window_state)) {
297 return true;
298 }
299
300 aura::Window* window = window_state->window();
301 gfx::Rect bounds = window->bounds();
302
303 // Don't adjust window bounds if the bounds are empty as this
304 // happens when a new views::Widget is created.
305 if (bounds.IsEmpty())
306 return true;
307
308 // Use entire display instead of workarea because the workarea can
309 // be further shrunk by the docked area. The logic ensures 30%
310 // visibility which should be enough to see where the window gets
311 // moved.
312 gfx::Rect display_area = ScreenUtil::GetDisplayBoundsInParent(window);
313 int min_width = bounds.width() * kMinimumPercentOnScreenArea;
314 int min_height = bounds.height() * kMinimumPercentOnScreenArea;
315 AdjustBoundsToEnsureWindowVisibility(
316 display_area, min_width, min_height, &bounds);
317 window_state->AdjustSnappedBounds(&bounds);
318 if (window->bounds() != bounds)
319 window_state->SetBoundsConstrained(bounds);
320 return true;
321 }
322 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: {
323 if (window_state->is_dragged() ||
324 SetMaximizedOrFullscreenBounds(window_state)) {
325 return true;
326 }
327 gfx::Rect work_area_in_parent =
328 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
329 gfx::Rect bounds = window_state->window()->bounds();
330 // When display bounds has changed, make sure the entire window is fully
331 // visible.
332 bounds.AdjustToFit(work_area_in_parent);
333 window_state->AdjustSnappedBounds(&bounds);
334 if (window_state->window()->bounds() != bounds)
335 window_state->SetBoundsDirectAnimated(bounds);
336 return true;
337 }
338 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: {
339 if (window_state->is_dragged() ||
340 SetMaximizedOrFullscreenBounds(window_state)) {
341 return true;
342 }
343 gfx::Rect work_area_in_parent =
344 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
345 gfx::Rect bounds = window_state->window()->bounds();
346 AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, &bounds);
347 window_state->AdjustSnappedBounds(&bounds);
348 if (window_state->window()->bounds() != bounds)
349 window_state->SetBoundsDirectAnimated(bounds);
350 return true;
351 }
352 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
353 case WM_EVENT_TOGGLE_MAXIMIZE:
354 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
355 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
356 case WM_EVENT_TOGGLE_FULLSCREEN:
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000357 case WM_EVENT_CENTER:
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000358 case WM_EVENT_NORMAL:
359 case WM_EVENT_MAXIMIZE:
360 case WM_EVENT_MINIMIZE:
361 case WM_EVENT_FULLSCREEN:
362 case WM_EVENT_SNAP_LEFT:
363 case WM_EVENT_SNAP_RIGHT:
364 case WM_EVENT_SET_BOUNDS:
365 case WM_EVENT_SHOW_INACTIVE:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000366 break;
367 }
368 return false;
369}
370
371// static
Ben Murdocheffb81e2014-03-31 11:51:25 +0100372bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) {
373 DCHECK(!window_state->is_dragged());
374 if (window_state->IsMaximized()) {
375 window_state->SetBoundsDirect(
376 ScreenUtil::GetMaximizedWindowBoundsInParent(window_state->window()));
377 return true;
378 }
379 if (window_state->IsFullscreen()) {
380 window_state->SetBoundsDirect(
381 ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
382 return true;
383 }
384 return false;
385}
386
387// static
388void DefaultState::SetBounds(WindowState* window_state,
389 const SetBoundsEvent* event) {
390 if (window_state->is_dragged()) {
391 window_state->SetBoundsDirect(event->requested_bounds());
392 } else if (window_state->IsSnapped()) {
393 gfx::Rect work_area_in_parent =
394 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
395 gfx::Rect child_bounds(event->requested_bounds());
396 AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds);
397 window_state->AdjustSnappedBounds(&child_bounds);
398 window_state->SetBoundsDirect(child_bounds);
399 } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
400 window_state->SetBoundsConstrained(event->requested_bounds());
401 }
402}
403
404void DefaultState::EnterToNextState(WindowState* window_state,
405 WindowStateType next_state_type) {
406 // Do nothing if we're already in the same state.
407 if (state_type_ == next_state_type)
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000408 return;
409
Ben Murdocheffb81e2014-03-31 11:51:25 +0100410 WindowStateType previous_state_type = state_type_;
411 state_type_ = next_state_type;
412
413 window_state->UpdateWindowShowStateFromStateType();
414 window_state->NotifyPreStateTypeChange(previous_state_type);
415
416 // This Docked/Snapped hack is due to the issue that IsDocked returns
417 // true for dragging window. TODO(oshima): Make docked window a state
418 // and remove this hack.
419 if (window_state->window()->parent() &&
420 (window_state->IsSnapped() ||
421 (!window_state->IsDocked() && !IsPanel(window_state->window())))) {
422 if (!window_state->HasRestoreBounds() &&
423 (previous_state_type == WINDOW_STATE_TYPE_DEFAULT ||
424 previous_state_type == WINDOW_STATE_TYPE_NORMAL) &&
425 !window_state->IsMinimized() &&
426 !window_state->IsNormalStateType()) {
427 window_state->SaveCurrentBoundsForRestore();
428 }
429
430 // When restoring from a minimized state, we want to restore to the previous
431 // bounds. However, we want to maintain the restore bounds. (The restore
432 // bounds are set if a user maximized the window in one axis by double
433 // clicking the window border for example).
434 gfx::Rect restore_bounds_in_screen;
435 if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
436 window_state->IsNormalStateType() &&
437 window_state->HasRestoreBounds() &&
438 !window_state->unminimize_to_restore_bounds()) {
439 restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen();
440 window_state->SaveCurrentBoundsForRestore();
441 }
442
443 if (window_state->IsMaximizedOrFullscreen())
444 MoveToDisplayForRestore(window_state);
445
446 UpdateBoundsFromState(window_state, previous_state_type);
447
448 // Normal state should have no restore bounds unless it's
449 // unminimzied.
450 if (!restore_bounds_in_screen.IsEmpty())
451 window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen);
452 else if (window_state->IsNormalStateType())
453 window_state->ClearRestoreBounds();
454 }
455 window_state->NotifyPostStateTypeChange(previous_state_type);
456}
457
458void DefaultState::ReenterToCurrentState(
459 WindowState* window_state,
460 WindowState::State* state_in_previous_mode) {
461 WindowStateType previous_state_type = state_in_previous_mode->GetType();
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100462 if (previous_state_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) {
463 // A state change should not move a window out of full screen since full
464 // screen is a "special mode" the user wanted to be in and should be
465 // respected as such.
466 state_type_ = wm::WINDOW_STATE_TYPE_FULLSCREEN;
467 }
Ben Murdocheffb81e2014-03-31 11:51:25 +0100468 window_state->UpdateWindowShowStateFromStateType();
469 window_state->NotifyPreStateTypeChange(previous_state_type);
470
Ben Murdoch0529e5d2014-04-24 10:50:13 +0100471 if ((state_type_ == wm::WINDOW_STATE_TYPE_NORMAL ||
472 state_type_ == wm::WINDOW_STATE_TYPE_DEFAULT) &&
473 !stored_bounds_.IsEmpty()) {
Ben Murdocheffb81e2014-03-31 11:51:25 +0100474 // Use the restore mechanism to set the bounds for
475 // the window in normal state. This also covers unminimize case.
476 window_state->SetRestoreBoundsInParent(stored_bounds_);
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000477 }
478
Ben Murdocheffb81e2014-03-31 11:51:25 +0100479 UpdateBoundsFromState(window_state, state_in_previous_mode->GetType());
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000480
Ben Murdocheffb81e2014-03-31 11:51:25 +0100481 // Then restore the restore bounds to their previous value.
482 if (!stored_restore_bounds_.IsEmpty())
483 window_state->SetRestoreBoundsInParent(stored_restore_bounds_);
484 else
485 window_state->ClearRestoreBounds();
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000486
Ben Murdocheffb81e2014-03-31 11:51:25 +0100487 window_state->NotifyPostStateTypeChange(previous_state_type);
488}
489
490void DefaultState::UpdateBoundsFromState(WindowState* window_state,
491 WindowStateType previous_state_type) {
492 aura::Window* window = window_state->window();
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000493 gfx::Rect bounds_in_parent;
Ben Murdocheffb81e2014-03-31 11:51:25 +0100494 switch (state_type_) {
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000495 case WINDOW_STATE_TYPE_LEFT_SNAPPED:
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000496 case WINDOW_STATE_TYPE_RIGHT_SNAPPED:
Ben Murdocheffb81e2014-03-31 11:51:25 +0100497 bounds_in_parent = state_type_ == WINDOW_STATE_TYPE_LEFT_SNAPPED ?
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000498 GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) :
499 GetDefaultRightSnappedWindowBoundsInParent(window_state->window());
500 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000501 case WINDOW_STATE_TYPE_DEFAULT:
502 case WINDOW_STATE_TYPE_NORMAL: {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000503 gfx::Rect work_area_in_parent =
504 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_state->window());
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000505 if (window_state->HasRestoreBounds())
506 bounds_in_parent = window_state->GetRestoreBoundsInParent();
507 else
508 bounds_in_parent = window->bounds();
509 // Make sure that part of the window is always visible.
510 AdjustBoundsToEnsureMinimumWindowVisibility(
511 work_area_in_parent, &bounds_in_parent);
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000512 break;
513 }
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000514 case WINDOW_STATE_TYPE_MAXIMIZED:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000515 bounds_in_parent = ScreenUtil::GetMaximizedWindowBoundsInParent(window);
516 break;
517
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000518 case WINDOW_STATE_TYPE_FULLSCREEN:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000519 bounds_in_parent = ScreenUtil::GetDisplayBoundsInParent(window);
520 break;
521
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000522 case WINDOW_STATE_TYPE_MINIMIZED:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000523 break;
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000524 case WINDOW_STATE_TYPE_INACTIVE:
525 case WINDOW_STATE_TYPE_DETACHED:
526 case WINDOW_STATE_TYPE_END:
527 case WINDOW_STATE_TYPE_AUTO_POSITIONED:
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000528 return;
529 }
530
Ben Murdocheffb81e2014-03-31 11:51:25 +0100531 if (state_type_ != WINDOW_STATE_TYPE_MINIMIZED) {
532 if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED ||
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000533 window_state->IsFullscreen()) {
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000534 window_state->SetBoundsDirect(bounds_in_parent);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000535 } else if (window_state->IsMaximized() ||
Ben Murdocheffb81e2014-03-31 11:51:25 +0100536 IsMaximizedOrFullscreenWindowStateType(previous_state_type)) {
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000537 window_state->SetBoundsDirectCrossFade(bounds_in_parent);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000538 } else if (window_state->is_dragged()) {
539 // SetBoundsDirectAnimated does not work when the window gets reparented.
540 // TODO(oshima): Consider fixing it and reenable the animation.
541 window_state->SetBoundsDirect(bounds_in_parent);
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000542 } else {
543 window_state->SetBoundsDirectAnimated(bounds_in_parent);
544 }
545 }
546
547 if (window_state->IsMinimized()) {
548 // Save the previous show state so that we can correctly restore it.
549 window_state->window()->SetProperty(aura::client::kRestoreShowStateKey,
Ben Murdocheffb81e2014-03-31 11:51:25 +0100550 ToWindowShowState(previous_state_type));
Torne (Richard Coles)a1401312014-03-18 10:20:56 +0000551 ::wm::SetWindowVisibilityAnimationType(
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000552 window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
553
554 // Hide the window.
555 window_state->window()->Hide();
556 // Activate another window.
557 if (window_state->IsActive())
558 window_state->Deactivate();
559 } else if ((window_state->window()->TargetVisibility() ||
Ben Murdocheffb81e2014-03-31 11:51:25 +0100560 previous_state_type == WINDOW_STATE_TYPE_MINIMIZED) &&
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000561 !window_state->window()->layer()->visible()) {
562 // The layer may be hidden if the window was previously minimized. Make
563 // sure it's visible.
564 window_state->window()->Show();
Ben Murdocheffb81e2014-03-31 11:51:25 +0100565 if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED &&
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000566 !window_state->IsMaximizedOrFullscreen()) {
567 window_state->set_unminimize_to_restore_bounds(false);
568 }
569 }
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000570}
571
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000572// static
573void DefaultState::CenterWindow(WindowState* window_state) {
574 if (!window_state->IsNormalOrSnapped())
575 return;
576 aura::Window* window = window_state->window();
577 if (window_state->IsSnapped()) {
578 gfx::Rect center_in_screen =
579 Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
580 gfx::Size size = window_state->HasRestoreBounds() ?
581 window_state->GetRestoreBoundsInScreen().size() :
582 window->bounds().size();
583 center_in_screen.ClampToCenteredSize(size);
584 window_state->SetRestoreBoundsInScreen(center_in_screen);
585 window_state->Restore();
586 } else {
587 gfx::Rect center_in_parent =
588 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window);
589 center_in_parent.ClampToCenteredSize(window->bounds().size());
590 window_state->SetBoundsDirectAnimated(center_in_parent);
591 }
Ben Murdocheffb81e2014-03-31 11:51:25 +0100592 // Centering window is treated as if a user moved and resized the window.
593 window_state->set_bounds_changed_by_user(true);
Torne (Richard Coles)23730a62014-03-21 14:25:57 +0000594}
595
Torne (Richard Coles)5d1f7b12014-02-21 12:16:55 +0000596} // namespace wm
597} // namespace ash