OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ash/wm/common/default_state.h" | |
6 | |
7 #include "ash/wm/common/dock/docked_window_layout_manager.h" | |
8 #include "ash/wm/common/window_animation_types.h" | |
9 #include "ash/wm/common/window_parenting_utils.h" | |
10 #include "ash/wm/common/window_positioning_utils.h" | |
11 #include "ash/wm/common/window_state.h" | |
12 #include "ash/wm/common/window_state_delegate.h" | |
13 #include "ash/wm/common/window_state_util.h" | |
14 #include "ash/wm/common/wm_event.h" | |
15 #include "ash/wm/common/wm_globals.h" | |
16 #include "ash/wm/common/wm_root_window_controller.h" | |
17 #include "ash/wm/common/wm_screen_util.h" | |
18 #include "ash/wm/common/wm_shell_window_ids.h" | |
19 #include "ash/wm/common/wm_window.h" | |
20 #include "ui/display/display.h" | |
21 #include "ui/display/screen.h" | |
22 | |
23 namespace ash { | |
24 namespace wm { | |
25 namespace { | |
26 | |
27 // This specifies how much percent (30%) of a window rect | |
28 // must be visible when the window is added to the workspace. | |
29 const float kMinimumPercentOnScreenArea = 0.3f; | |
30 | |
31 // When a window that has restore bounds at least as large as a work area is | |
32 // unmaximized, inset the bounds slightly so that they are not exactly the same. | |
33 // This makes it easier to resize the window. | |
34 const int kMaximizedWindowInset = 10; // DIPs. | |
35 | |
36 bool IsMinimizedWindowState(const WindowStateType state_type) { | |
37 return state_type == WINDOW_STATE_TYPE_MINIMIZED || | |
38 state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED; | |
39 } | |
40 | |
41 void MoveToDisplayForRestore(WindowState* window_state) { | |
42 if (!window_state->HasRestoreBounds()) | |
43 return; | |
44 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen(); | |
45 | |
46 // Move only if the restore bounds is outside of | |
47 // the display. There is no information about in which | |
48 // display it should be restored, so this is best guess. | |
49 // TODO(oshima): Restore information should contain the | |
50 // work area information like WindowResizer does for the | |
51 // last window location. | |
52 gfx::Rect display_area = | |
53 window_state->window()->GetDisplayNearestWindow().bounds(); | |
54 | |
55 if (!display_area.Intersects(restore_bounds)) { | |
56 const display::Display& display = | |
57 display::Screen::GetScreen()->GetDisplayMatching(restore_bounds); | |
58 WmGlobals* globals = window_state->window()->GetGlobals(); | |
59 WmWindow* new_root = globals->GetRootWindowForDisplayId(display.id()); | |
60 if (new_root != window_state->window()->GetRootWindow()) { | |
61 WmWindow* new_container = new_root->GetChildByShellWindowId( | |
62 window_state->window()->GetParent()->GetShellWindowId()); | |
63 new_container->AddChild(window_state->window()); | |
64 } | |
65 } | |
66 } | |
67 | |
68 DockedWindowLayoutManager* GetDockedWindowLayoutManager(WmGlobals* globals) { | |
69 return DockedWindowLayoutManager::Get(globals->GetActiveWindow()); | |
70 } | |
71 | |
72 class ScopedPreferredAlignmentResetter { | |
73 public: | |
74 ScopedPreferredAlignmentResetter(DockedAlignment dock_alignment, | |
75 DockedWindowLayoutManager* dock_layout) | |
76 : docked_window_layout_manager_(dock_layout) { | |
77 docked_window_layout_manager_->set_preferred_alignment(dock_alignment); | |
78 } | |
79 ~ScopedPreferredAlignmentResetter() { | |
80 docked_window_layout_manager_->set_preferred_alignment( | |
81 DOCKED_ALIGNMENT_NONE); | |
82 } | |
83 | |
84 private: | |
85 DockedWindowLayoutManager* docked_window_layout_manager_; | |
86 | |
87 DISALLOW_COPY_AND_ASSIGN(ScopedPreferredAlignmentResetter); | |
88 }; | |
89 | |
90 class ScopedDockedLayoutEventSourceResetter { | |
91 public: | |
92 ScopedDockedLayoutEventSourceResetter(DockedWindowLayoutManager* dock_layout) | |
93 : docked_window_layout_manager_(dock_layout) { | |
94 docked_window_layout_manager_->set_event_source( | |
95 DOCKED_ACTION_SOURCE_KEYBOARD); | |
96 } | |
97 ~ScopedDockedLayoutEventSourceResetter() { | |
98 docked_window_layout_manager_->set_event_source( | |
99 DOCKED_ACTION_SOURCE_UNKNOWN); | |
100 } | |
101 | |
102 private: | |
103 DockedWindowLayoutManager* docked_window_layout_manager_; | |
104 | |
105 DISALLOW_COPY_AND_ASSIGN(ScopedDockedLayoutEventSourceResetter); | |
106 }; | |
107 | |
108 void CycleSnapDock(WindowState* window_state, WMEventType event) { | |
109 DockedWindowLayoutManager* dock_layout = | |
110 GetDockedWindowLayoutManager(window_state->window()->GetGlobals()); | |
111 wm::WindowStateType desired_snap_state = | |
112 event == WM_EVENT_CYCLE_SNAP_DOCK_LEFT | |
113 ? wm::WINDOW_STATE_TYPE_LEFT_SNAPPED | |
114 : wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED; | |
115 DockedAlignment desired_dock_alignment = | |
116 event == WM_EVENT_CYCLE_SNAP_DOCK_LEFT ? DOCKED_ALIGNMENT_LEFT | |
117 : DOCKED_ALIGNMENT_RIGHT; | |
118 DockedAlignment current_dock_alignment = | |
119 dock_layout ? dock_layout->CalculateAlignment() : DOCKED_ALIGNMENT_NONE; | |
120 | |
121 if (!window_state->IsDocked() || | |
122 (current_dock_alignment != DOCKED_ALIGNMENT_NONE && | |
123 current_dock_alignment != desired_dock_alignment)) { | |
124 if (window_state->CanSnap() && | |
125 window_state->GetStateType() != desired_snap_state && | |
126 window_state->window()->GetType() != ui::wm::WINDOW_TYPE_PANEL) { | |
127 const wm::WMEvent event(desired_snap_state == | |
128 wm::WINDOW_STATE_TYPE_LEFT_SNAPPED | |
129 ? wm::WM_EVENT_SNAP_LEFT | |
130 : wm::WM_EVENT_SNAP_RIGHT); | |
131 window_state->OnWMEvent(&event); | |
132 return; | |
133 } | |
134 | |
135 if (dock_layout && | |
136 dock_layout->CanDockWindow(window_state->window(), | |
137 desired_dock_alignment)) { | |
138 if (window_state->IsDocked()) { | |
139 dock_layout->MaybeSetDesiredDockedAlignment(desired_dock_alignment); | |
140 return; | |
141 } | |
142 | |
143 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout); | |
144 ScopedPreferredAlignmentResetter alignmentResetter(desired_dock_alignment, | |
145 dock_layout); | |
146 const wm::WMEvent event(wm::WM_EVENT_DOCK); | |
147 window_state->OnWMEvent(&event); | |
148 return; | |
149 } | |
150 } | |
151 | |
152 if (window_state->IsDocked() || window_state->IsSnapped()) { | |
153 ScopedDockedLayoutEventSourceResetter event_source_resetter(dock_layout); | |
154 window_state->Restore(); | |
155 return; | |
156 } | |
157 window_state->window()->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE); | |
158 } | |
159 | |
160 } // namespace | |
161 | |
162 DefaultState::DefaultState(WindowStateType initial_state_type) | |
163 : state_type_(initial_state_type), stored_window_state_(nullptr) {} | |
164 DefaultState::~DefaultState() {} | |
165 | |
166 void DefaultState::OnWMEvent(WindowState* window_state, const WMEvent* event) { | |
167 if (ProcessWorkspaceEvents(window_state, event)) | |
168 return; | |
169 | |
170 if (ProcessCompoundEvents(window_state, event)) | |
171 return; | |
172 | |
173 WindowStateType current_state_type = window_state->GetStateType(); | |
174 WindowStateType next_state_type = WINDOW_STATE_TYPE_NORMAL; | |
175 switch (event->type()) { | |
176 case WM_EVENT_NORMAL: | |
177 next_state_type = current_state_type == WINDOW_STATE_TYPE_DOCKED_MINIMIZED | |
178 ? WINDOW_STATE_TYPE_DOCKED | |
179 : WINDOW_STATE_TYPE_NORMAL; | |
180 break; | |
181 case WM_EVENT_MAXIMIZE: | |
182 next_state_type = WINDOW_STATE_TYPE_MAXIMIZED; | |
183 break; | |
184 case WM_EVENT_MINIMIZE: | |
185 next_state_type = current_state_type == WINDOW_STATE_TYPE_DOCKED | |
186 ? WINDOW_STATE_TYPE_DOCKED_MINIMIZED | |
187 : WINDOW_STATE_TYPE_MINIMIZED; | |
188 break; | |
189 case WM_EVENT_FULLSCREEN: | |
190 next_state_type = WINDOW_STATE_TYPE_FULLSCREEN; | |
191 break; | |
192 case WM_EVENT_SNAP_LEFT: | |
193 next_state_type = WINDOW_STATE_TYPE_LEFT_SNAPPED; | |
194 break; | |
195 case WM_EVENT_SNAP_RIGHT: | |
196 next_state_type = WINDOW_STATE_TYPE_RIGHT_SNAPPED; | |
197 break; | |
198 case WM_EVENT_DOCK: | |
199 next_state_type = WINDOW_STATE_TYPE_DOCKED; | |
200 break; | |
201 case WM_EVENT_SET_BOUNDS: | |
202 SetBounds(window_state, static_cast<const SetBoundsEvent*>(event)); | |
203 return; | |
204 case WM_EVENT_SHOW_INACTIVE: | |
205 next_state_type = WINDOW_STATE_TYPE_INACTIVE; | |
206 break; | |
207 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
208 case WM_EVENT_TOGGLE_MAXIMIZE: | |
209 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: | |
210 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: | |
211 case WM_EVENT_TOGGLE_FULLSCREEN: | |
212 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
213 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
214 case WM_EVENT_CENTER: | |
215 NOTREACHED() << "Compound event should not reach here:" << event; | |
216 return; | |
217 case WM_EVENT_ADDED_TO_WORKSPACE: | |
218 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: | |
219 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: | |
220 NOTREACHED() << "Workspace event should not reach here:" << event; | |
221 return; | |
222 } | |
223 | |
224 if (next_state_type == current_state_type && window_state->IsSnapped()) { | |
225 gfx::Rect snapped_bounds = | |
226 event->type() == WM_EVENT_SNAP_LEFT | |
227 ? GetDefaultLeftSnappedWindowBoundsInParent(window_state->window()) | |
228 : GetDefaultRightSnappedWindowBoundsInParent( | |
229 window_state->window()); | |
230 window_state->SetBoundsDirectAnimated(snapped_bounds); | |
231 return; | |
232 } | |
233 | |
234 EnterToNextState(window_state, next_state_type); | |
235 } | |
236 | |
237 WindowStateType DefaultState::GetType() const { | |
238 return state_type_; | |
239 } | |
240 | |
241 void DefaultState::AttachState(WindowState* window_state, | |
242 WindowState::State* state_in_previous_mode) { | |
243 DCHECK_EQ(stored_window_state_, window_state); | |
244 | |
245 ReenterToCurrentState(window_state, state_in_previous_mode); | |
246 | |
247 // If the display has changed while in the another mode, | |
248 // we need to let windows know the change. | |
249 display::Display current_display = | |
250 window_state->window()->GetDisplayNearestWindow(); | |
251 if (stored_display_state_.bounds() != current_display.bounds()) { | |
252 const WMEvent event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED); | |
253 window_state->OnWMEvent(&event); | |
254 } else if (stored_display_state_.work_area() != current_display.work_area()) { | |
255 const WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); | |
256 window_state->OnWMEvent(&event); | |
257 } | |
258 } | |
259 | |
260 void DefaultState::DetachState(WindowState* window_state) { | |
261 stored_window_state_ = window_state; | |
262 stored_bounds_ = window_state->window()->GetBounds(); | |
263 stored_restore_bounds_ = window_state->HasRestoreBounds() | |
264 ? window_state->GetRestoreBoundsInParent() | |
265 : gfx::Rect(); | |
266 // Remember the display state so that in case of the display change | |
267 // while in the other mode, we can perform necessary action to | |
268 // restore the window state to the proper state for the current | |
269 // display. | |
270 stored_display_state_ = window_state->window()->GetDisplayNearestWindow(); | |
271 } | |
272 | |
273 // static | |
274 bool DefaultState::ProcessCompoundEvents(WindowState* window_state, | |
275 const WMEvent* event) { | |
276 WmWindow* window = window_state->window(); | |
277 | |
278 switch (event->type()) { | |
279 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
280 if (window_state->IsFullscreen()) { | |
281 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
282 window_state->OnWMEvent(&event); | |
283 } else if (window_state->IsMaximized()) { | |
284 window_state->Restore(); | |
285 } else if (window_state->IsNormalOrSnapped()) { | |
286 if (window_state->CanMaximize()) | |
287 window_state->Maximize(); | |
288 } | |
289 return true; | |
290 case WM_EVENT_TOGGLE_MAXIMIZE: | |
291 if (window_state->IsFullscreen()) { | |
292 const wm::WMEvent event(wm::WM_EVENT_TOGGLE_FULLSCREEN); | |
293 window_state->OnWMEvent(&event); | |
294 } else if (window_state->IsMaximized()) { | |
295 window_state->Restore(); | |
296 } else if (window_state->CanMaximize()) { | |
297 window_state->Maximize(); | |
298 } | |
299 return true; | |
300 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: { | |
301 gfx::Rect work_area = GetDisplayWorkAreaBoundsInParent(window); | |
302 | |
303 // Maximize vertically if: | |
304 // - The window does not have a max height defined. | |
305 // - The window has the normal state type. Snapped windows are excluded | |
306 // because they are already maximized vertically and reverting to the | |
307 // restored bounds looks weird. | |
308 if (window->GetMaximumSize().height() != 0 || | |
309 !window_state->IsNormalStateType()) { | |
310 return true; | |
311 } | |
312 if (window_state->HasRestoreBounds() && | |
313 (window->GetBounds().height() == work_area.height() && | |
314 window->GetBounds().y() == work_area.y())) { | |
315 window_state->SetAndClearRestoreBounds(); | |
316 } else { | |
317 window_state->SaveCurrentBoundsForRestore(); | |
318 window->SetBounds(gfx::Rect(window->GetBounds().x(), work_area.y(), | |
319 window->GetBounds().width(), | |
320 work_area.height())); | |
321 } | |
322 return true; | |
323 } | |
324 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: { | |
325 // Maximize horizontally if: | |
326 // - The window does not have a max width defined. | |
327 // - The window is snapped or has the normal state type. | |
328 if (window->GetMaximumSize().width() != 0) | |
329 return true; | |
330 if (!window_state->IsNormalOrSnapped()) | |
331 return true; | |
332 gfx::Rect work_area = GetDisplayWorkAreaBoundsInParent(window); | |
333 if (window_state->IsNormalStateType() && | |
334 window_state->HasRestoreBounds() && | |
335 (window->GetBounds().width() == work_area.width() && | |
336 window->GetBounds().x() == work_area.x())) { | |
337 window_state->SetAndClearRestoreBounds(); | |
338 } else { | |
339 gfx::Rect new_bounds(work_area.x(), window->GetBounds().y(), | |
340 work_area.width(), window->GetBounds().height()); | |
341 | |
342 gfx::Rect restore_bounds = window->GetBounds(); | |
343 if (window_state->IsSnapped()) { | |
344 window_state->SetRestoreBoundsInParent(new_bounds); | |
345 window_state->Restore(); | |
346 | |
347 // The restore logic prevents a window from being restored to bounds | |
348 // which match the workspace bounds exactly so it is necessary to set | |
349 // the bounds again below. | |
350 } | |
351 | |
352 window_state->SetRestoreBoundsInParent(restore_bounds); | |
353 window->SetBounds(new_bounds); | |
354 } | |
355 return true; | |
356 } | |
357 case WM_EVENT_TOGGLE_FULLSCREEN: | |
358 ToggleFullScreen(window_state, window_state->delegate()); | |
359 return true; | |
360 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
361 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
362 CycleSnapDock(window_state, event->type()); | |
363 return true; | |
364 case WM_EVENT_CENTER: | |
365 CenterWindow(window_state); | |
366 return true; | |
367 case WM_EVENT_NORMAL: | |
368 case WM_EVENT_MAXIMIZE: | |
369 case WM_EVENT_MINIMIZE: | |
370 case WM_EVENT_FULLSCREEN: | |
371 case WM_EVENT_SNAP_LEFT: | |
372 case WM_EVENT_SNAP_RIGHT: | |
373 case WM_EVENT_SET_BOUNDS: | |
374 case WM_EVENT_SHOW_INACTIVE: | |
375 case WM_EVENT_DOCK: | |
376 break; | |
377 case WM_EVENT_ADDED_TO_WORKSPACE: | |
378 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: | |
379 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: | |
380 NOTREACHED() << "Workspace event should not reach here:" << event; | |
381 break; | |
382 } | |
383 return false; | |
384 } | |
385 | |
386 bool DefaultState::ProcessWorkspaceEvents(WindowState* window_state, | |
387 const WMEvent* event) { | |
388 switch (event->type()) { | |
389 case WM_EVENT_ADDED_TO_WORKSPACE: { | |
390 // When a window is dragged and dropped onto a different | |
391 // root window, the bounds will be updated after they are added | |
392 // to the root window. | |
393 // If a window is opened as maximized or fullscreen, its bounds may be | |
394 // empty, so update the bounds now before checking empty. | |
395 if (window_state->is_dragged() || | |
396 SetMaximizedOrFullscreenBounds(window_state)) { | |
397 return true; | |
398 } | |
399 | |
400 WmWindow* window = window_state->window(); | |
401 gfx::Rect bounds = window->GetBounds(); | |
402 | |
403 // Don't adjust window bounds if the bounds are empty as this | |
404 // happens when a new views::Widget is created. | |
405 if (bounds.IsEmpty()) | |
406 return true; | |
407 | |
408 // Only windows of type WINDOW_TYPE_NORMAL or WINDOW_TYPE_PANEL need to be | |
409 // adjusted to have minimum visibility, because they are positioned by the | |
410 // user and user should always be able to interact with them. Other | |
411 // windows are positioned programmatically. | |
412 if (!window_state->IsUserPositionable()) | |
413 return true; | |
414 | |
415 // Use entire display instead of workarea because the workarea can | |
416 // be further shrunk by the docked area. The logic ensures 30% | |
417 // visibility which should be enough to see where the window gets | |
418 // moved. | |
419 gfx::Rect display_area = GetDisplayBoundsInParent(window); | |
420 int min_width = bounds.width() * wm::kMinimumPercentOnScreenArea; | |
421 int min_height = bounds.height() * wm::kMinimumPercentOnScreenArea; | |
422 wm::AdjustBoundsToEnsureWindowVisibility(display_area, min_width, | |
423 min_height, &bounds); | |
424 window_state->AdjustSnappedBounds(&bounds); | |
425 if (window->GetBounds() != bounds) | |
426 window_state->SetBoundsConstrained(bounds); | |
427 return true; | |
428 } | |
429 case WM_EVENT_DISPLAY_BOUNDS_CHANGED: { | |
430 if (window_state->is_dragged() || | |
431 SetMaximizedOrFullscreenBounds(window_state)) { | |
432 return true; | |
433 } | |
434 gfx::Rect work_area_in_parent = | |
435 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
436 gfx::Rect bounds = window_state->window()->GetBounds(); | |
437 // When display bounds has changed, make sure the entire window is fully | |
438 // visible. | |
439 bounds.AdjustToFit(work_area_in_parent); | |
440 window_state->AdjustSnappedBounds(&bounds); | |
441 if (window_state->window()->GetBounds() != bounds) | |
442 window_state->SetBoundsDirectAnimated(bounds); | |
443 return true; | |
444 } | |
445 case WM_EVENT_WORKAREA_BOUNDS_CHANGED: { | |
446 // Don't resize the maximized window when the desktop is covered | |
447 // by fullscreen window. crbug.com/504299. | |
448 bool in_fullscreen = | |
449 window_state->window() | |
450 ->GetRootWindowController() | |
451 ->GetWorkspaceWindowState() == WORKSPACE_WINDOW_STATE_FULL_SCREEN; | |
452 if (in_fullscreen && window_state->IsMaximized()) | |
453 return true; | |
454 | |
455 if (window_state->is_dragged() || | |
456 SetMaximizedOrFullscreenBounds(window_state)) { | |
457 return true; | |
458 } | |
459 gfx::Rect work_area_in_parent = | |
460 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
461 gfx::Rect bounds = window_state->window()->GetBounds(); | |
462 wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, | |
463 &bounds); | |
464 window_state->AdjustSnappedBounds(&bounds); | |
465 if (window_state->window()->GetBounds() != bounds) | |
466 window_state->SetBoundsDirectAnimated(bounds); | |
467 return true; | |
468 } | |
469 case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION: | |
470 case WM_EVENT_TOGGLE_MAXIMIZE: | |
471 case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE: | |
472 case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE: | |
473 case WM_EVENT_TOGGLE_FULLSCREEN: | |
474 case WM_EVENT_CYCLE_SNAP_DOCK_LEFT: | |
475 case WM_EVENT_CYCLE_SNAP_DOCK_RIGHT: | |
476 case WM_EVENT_CENTER: | |
477 case WM_EVENT_NORMAL: | |
478 case WM_EVENT_MAXIMIZE: | |
479 case WM_EVENT_MINIMIZE: | |
480 case WM_EVENT_FULLSCREEN: | |
481 case WM_EVENT_SNAP_LEFT: | |
482 case WM_EVENT_SNAP_RIGHT: | |
483 case WM_EVENT_SET_BOUNDS: | |
484 case WM_EVENT_SHOW_INACTIVE: | |
485 case WM_EVENT_DOCK: | |
486 break; | |
487 } | |
488 return false; | |
489 } | |
490 | |
491 // static | |
492 bool DefaultState::SetMaximizedOrFullscreenBounds(WindowState* window_state) { | |
493 DCHECK(!window_state->is_dragged()); | |
494 if (window_state->IsMaximized()) { | |
495 window_state->SetBoundsDirect( | |
496 GetMaximizedWindowBoundsInParent(window_state->window())); | |
497 return true; | |
498 } | |
499 if (window_state->IsFullscreen()) { | |
500 window_state->SetBoundsDirect( | |
501 GetDisplayBoundsInParent(window_state->window())); | |
502 return true; | |
503 } | |
504 return false; | |
505 } | |
506 | |
507 // static | |
508 void DefaultState::SetBounds(WindowState* window_state, | |
509 const SetBoundsEvent* event) { | |
510 if (window_state->is_dragged()) { | |
511 // TODO(oshima|varkha): This may be no longer needed, as the dragging | |
512 // happens in docked window container. crbug.com/485612. | |
513 window_state->SetBoundsDirect(event->requested_bounds()); | |
514 } else if (window_state->IsSnapped()) { | |
515 gfx::Rect work_area_in_parent = | |
516 GetDisplayWorkAreaBoundsInParent(window_state->window()); | |
517 gfx::Rect child_bounds(event->requested_bounds()); | |
518 wm::AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds); | |
519 window_state->AdjustSnappedBounds(&child_bounds); | |
520 window_state->SetBoundsDirect(child_bounds); | |
521 } else if (!SetMaximizedOrFullscreenBounds(window_state)) { | |
522 window_state->SetBoundsConstrained(event->requested_bounds()); | |
523 } | |
524 } | |
525 | |
526 void DefaultState::EnterToNextState(WindowState* window_state, | |
527 WindowStateType next_state_type) { | |
528 // Do nothing if we're already in the same state. | |
529 if (state_type_ == next_state_type) | |
530 return; | |
531 | |
532 WindowStateType previous_state_type = state_type_; | |
533 state_type_ = next_state_type; | |
534 | |
535 window_state->UpdateWindowShowStateFromStateType(); | |
536 window_state->NotifyPreStateTypeChange(previous_state_type); | |
537 | |
538 if (window_state->window()->GetParent()) { | |
539 if (!window_state->HasRestoreBounds() && | |
540 (previous_state_type == WINDOW_STATE_TYPE_DEFAULT || | |
541 previous_state_type == WINDOW_STATE_TYPE_NORMAL) && | |
542 !window_state->IsMinimized() && !window_state->IsNormalStateType()) { | |
543 window_state->SaveCurrentBoundsForRestore(); | |
544 } | |
545 | |
546 // When restoring from a minimized state, we want to restore to the | |
547 // previous bounds. However, we want to maintain the restore bounds. | |
548 // (The restore bounds are set if a user maximized the window in one | |
549 // axis by double clicking the window border for example). | |
550 gfx::Rect restore_bounds_in_screen; | |
551 if (previous_state_type == WINDOW_STATE_TYPE_MINIMIZED && | |
552 window_state->IsNormalStateType() && window_state->HasRestoreBounds() && | |
553 !window_state->unminimize_to_restore_bounds()) { | |
554 restore_bounds_in_screen = window_state->GetRestoreBoundsInScreen(); | |
555 window_state->SaveCurrentBoundsForRestore(); | |
556 } | |
557 | |
558 if (window_state->IsMaximizedOrFullscreen()) | |
559 MoveToDisplayForRestore(window_state); | |
560 | |
561 UpdateBoundsFromState(window_state, previous_state_type); | |
562 | |
563 // Normal state should have no restore bounds unless it's | |
564 // unminimized. | |
565 if (!restore_bounds_in_screen.IsEmpty()) | |
566 window_state->SetRestoreBoundsInScreen(restore_bounds_in_screen); | |
567 else if (window_state->IsNormalStateType()) | |
568 window_state->ClearRestoreBounds(); | |
569 } | |
570 window_state->NotifyPostStateTypeChange(previous_state_type); | |
571 } | |
572 | |
573 void DefaultState::ReenterToCurrentState( | |
574 WindowState* window_state, | |
575 WindowState::State* state_in_previous_mode) { | |
576 WindowStateType previous_state_type = state_in_previous_mode->GetType(); | |
577 | |
578 // A state change should not move a window into or out of full screen since | |
579 // full screen is a "special mode" the user wanted to be in and should be | |
580 // respected as such. | |
581 if (previous_state_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) | |
582 state_type_ = wm::WINDOW_STATE_TYPE_FULLSCREEN; | |
583 else if (state_type_ == wm::WINDOW_STATE_TYPE_FULLSCREEN) | |
584 state_type_ = previous_state_type; | |
585 | |
586 window_state->UpdateWindowShowStateFromStateType(); | |
587 window_state->NotifyPreStateTypeChange(previous_state_type); | |
588 | |
589 if ((state_type_ == wm::WINDOW_STATE_TYPE_NORMAL || | |
590 state_type_ == wm::WINDOW_STATE_TYPE_DEFAULT) && | |
591 !stored_bounds_.IsEmpty()) { | |
592 // Use the restore mechanism to set the bounds for | |
593 // the window in normal state. This also covers unminimize case. | |
594 window_state->SetRestoreBoundsInParent(stored_bounds_); | |
595 } | |
596 | |
597 UpdateBoundsFromState(window_state, state_in_previous_mode->GetType()); | |
598 | |
599 // Then restore the restore bounds to their previous value. | |
600 if (!stored_restore_bounds_.IsEmpty()) | |
601 window_state->SetRestoreBoundsInParent(stored_restore_bounds_); | |
602 else | |
603 window_state->ClearRestoreBounds(); | |
604 | |
605 window_state->NotifyPostStateTypeChange(previous_state_type); | |
606 } | |
607 | |
608 void DefaultState::UpdateBoundsFromState(WindowState* window_state, | |
609 WindowStateType previous_state_type) { | |
610 WmWindow* window = window_state->window(); | |
611 gfx::Rect bounds_in_parent; | |
612 switch (state_type_) { | |
613 case WINDOW_STATE_TYPE_LEFT_SNAPPED: | |
614 case WINDOW_STATE_TYPE_RIGHT_SNAPPED: | |
615 bounds_in_parent = | |
616 state_type_ == WINDOW_STATE_TYPE_LEFT_SNAPPED | |
617 ? GetDefaultLeftSnappedWindowBoundsInParent(window) | |
618 : GetDefaultRightSnappedWindowBoundsInParent(window); | |
619 break; | |
620 case WINDOW_STATE_TYPE_DOCKED: { | |
621 if (window->GetParent()->GetShellWindowId() != | |
622 kShellWindowId_DockedContainer) { | |
623 WmWindow* docked_container = | |
624 window->GetRootWindow()->GetChildByShellWindowId( | |
625 kShellWindowId_DockedContainer); | |
626 ReparentChildWithTransientChildren(window, window->GetParent(), | |
627 docked_container); | |
628 } | |
629 // Return early because we don't want to update the bounds of the | |
630 // window below; as the bounds are managed by the dock layout. | |
631 return; | |
632 } | |
633 case WINDOW_STATE_TYPE_DEFAULT: | |
634 case WINDOW_STATE_TYPE_NORMAL: { | |
635 gfx::Rect work_area_in_parent = GetDisplayWorkAreaBoundsInParent(window); | |
636 if (window_state->HasRestoreBounds()) { | |
637 bounds_in_parent = window_state->GetRestoreBoundsInParent(); | |
638 // Check if the |window|'s restored size is bigger than the working area | |
639 // This may happen if a window was resized to maximized bounds or if the | |
640 // display resolution changed while the window was maximized. | |
641 if (previous_state_type == WINDOW_STATE_TYPE_MAXIMIZED && | |
642 bounds_in_parent.width() >= work_area_in_parent.width() && | |
643 bounds_in_parent.height() >= work_area_in_parent.height()) { | |
644 bounds_in_parent = work_area_in_parent; | |
645 bounds_in_parent.Inset(kMaximizedWindowInset, kMaximizedWindowInset, | |
646 kMaximizedWindowInset, kMaximizedWindowInset); | |
647 } | |
648 } else { | |
649 bounds_in_parent = window->GetBounds(); | |
650 } | |
651 // Make sure that part of the window is always visible. | |
652 wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_parent, | |
653 &bounds_in_parent); | |
654 break; | |
655 } | |
656 case WINDOW_STATE_TYPE_MAXIMIZED: | |
657 bounds_in_parent = GetMaximizedWindowBoundsInParent(window); | |
658 break; | |
659 | |
660 case WINDOW_STATE_TYPE_FULLSCREEN: | |
661 bounds_in_parent = GetDisplayBoundsInParent(window); | |
662 break; | |
663 | |
664 case WINDOW_STATE_TYPE_DOCKED_MINIMIZED: | |
665 case WINDOW_STATE_TYPE_MINIMIZED: | |
666 break; | |
667 case WINDOW_STATE_TYPE_INACTIVE: | |
668 case WINDOW_STATE_TYPE_END: | |
669 case WINDOW_STATE_TYPE_AUTO_POSITIONED: | |
670 return; | |
671 } | |
672 | |
673 if (!window_state->IsMinimized()) { | |
674 if (IsMinimizedWindowState(previous_state_type) || | |
675 window_state->IsFullscreen()) { | |
676 window_state->SetBoundsDirect(bounds_in_parent); | |
677 } else if (window_state->IsMaximized() || | |
678 IsMaximizedOrFullscreenWindowStateType(previous_state_type)) { | |
679 window_state->SetBoundsDirectCrossFade(bounds_in_parent); | |
680 } else if (window_state->is_dragged()) { | |
681 // SetBoundsDirectAnimated does not work when the window gets reparented. | |
682 // TODO(oshima): Consider fixing it and reenable the animation. | |
683 window_state->SetBoundsDirect(bounds_in_parent); | |
684 } else { | |
685 window_state->SetBoundsDirectAnimated(bounds_in_parent); | |
686 } | |
687 } | |
688 | |
689 if (window_state->IsMinimized()) { | |
690 // Save the previous show state so that we can correctly restore it. | |
691 window->SetRestoreShowState(ToWindowShowState(previous_state_type)); | |
692 window->SetVisibilityAnimationType( | |
693 WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE); | |
694 | |
695 // Hide the window. | |
696 window->Hide(); | |
697 // Activate another window. | |
698 if (window_state->IsActive()) | |
699 window_state->Deactivate(); | |
700 } else if ((window->GetTargetVisibility() || | |
701 IsMinimizedWindowState(previous_state_type)) && | |
702 !window->GetLayer()->visible()) { | |
703 // The layer may be hidden if the window was previously minimized. Make | |
704 // sure it's visible. | |
705 window->Show(); | |
706 if (IsMinimizedWindowState(previous_state_type) && | |
707 !window_state->IsMaximizedOrFullscreen()) { | |
708 window_state->set_unminimize_to_restore_bounds(false); | |
709 } | |
710 } | |
711 } | |
712 | |
713 // static | |
714 void DefaultState::CenterWindow(WindowState* window_state) { | |
715 if (!window_state->IsNormalOrSnapped()) | |
716 return; | |
717 WmWindow* window = window_state->window(); | |
718 if (window_state->IsSnapped()) { | |
719 gfx::Rect center_in_screen = window->GetDisplayNearestWindow().work_area(); | |
720 gfx::Size size = window_state->HasRestoreBounds() | |
721 ? window_state->GetRestoreBoundsInScreen().size() | |
722 : window->GetBounds().size(); | |
723 center_in_screen.ClampToCenteredSize(size); | |
724 window_state->SetRestoreBoundsInScreen(center_in_screen); | |
725 window_state->Restore(); | |
726 } else { | |
727 gfx::Rect center_in_parent = GetDisplayWorkAreaBoundsInParent(window); | |
728 center_in_parent.ClampToCenteredSize(window->GetBounds().size()); | |
729 window_state->SetBoundsDirectAnimated(center_in_parent); | |
730 } | |
731 // Centering window is treated as if a user moved and resized the window. | |
732 window_state->set_bounds_changed_by_user(true); | |
733 } | |
734 | |
735 } // namespace wm | |
736 } // namespace ash | |
OLD | NEW |