Change behaviour of the Alt-] and Alt-[ keys so that it cycles through SnapLeft/SnapR...
[chromium-blink-merge.git] / ash / wm / maximize_mode / maximize_mode_window_state.cc
blob46345badd014c393c274adb7da74aefa0959e8c3
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.
5 #include "ash/wm/maximize_mode/maximize_mode_window_state.h"
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/maximize_mode/maximize_mode_window_manager.h"
13 #include "ash/wm/window_animations.h"
14 #include "ash/wm/window_properties.h"
15 #include "ash/wm/window_state_delegate.h"
16 #include "ash/wm/window_state_util.h"
17 #include "ash/wm/window_util.h"
18 #include "ash/wm/wm_event.h"
19 #include "ash/wm/workspace/workspace_window_resizer.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_delegate.h"
23 #include "ui/compositor/layer.h"
24 #include "ui/gfx/display.h"
25 #include "ui/gfx/rect.h"
26 #include "ui/views/view_constants_aura.h"
27 #include "ui/views/widget/widget.h"
29 namespace ash {
30 namespace {
32 // Returns the biggest possible size for a window which is about to be
33 // maximized.
34 gfx::Size GetMaximumSizeOfWindow(wm::WindowState* window_state) {
35 DCHECK(window_state->CanMaximize() || window_state->CanResize());
37 gfx::Size workspace_size = ScreenUtil::GetMaximizedWindowBoundsInParent(
38 window_state->window()).size();
40 aura::WindowDelegate* delegate = window_state->window()->delegate();
41 if (!delegate)
42 return workspace_size;
44 gfx::Size size = delegate->GetMaximumSize();
45 if (size.IsEmpty())
46 return workspace_size;
48 size.SetToMin(workspace_size);
49 return size;
52 // Returns the centered bounds of the given bounds in the work area.
53 gfx::Rect GetCenteredBounds(const gfx::Rect& bounds_in_parent,
54 wm::WindowState* state_object) {
55 gfx::Rect work_area_in_parent =
56 ScreenUtil::GetDisplayWorkAreaBoundsInParent(state_object->window());
57 work_area_in_parent.ClampToCenteredSize(bounds_in_parent.size());
58 return work_area_in_parent;
61 // Returns the maximized/full screen and/or centered bounds of a window.
62 gfx::Rect GetBoundsInMaximizedMode(wm::WindowState* state_object) {
63 if (state_object->IsFullscreen())
64 return ScreenUtil::GetDisplayBoundsInParent(state_object->window());
66 gfx::Rect bounds_in_parent;
67 // Make the window as big as possible.
68 if (state_object->CanMaximize() || state_object->CanResize()) {
69 bounds_in_parent.set_size(GetMaximumSizeOfWindow(state_object));
70 } else {
71 // We prefer the user given window dimensions over the current windows
72 // dimensions since they are likely to be the result from some other state
73 // object logic.
74 if (state_object->HasRestoreBounds())
75 bounds_in_parent = state_object->GetRestoreBoundsInParent();
76 else
77 bounds_in_parent = state_object->window()->bounds();
79 return GetCenteredBounds(bounds_in_parent, state_object);
82 } // namespace
84 // static
85 void MaximizeModeWindowState::UpdateWindowPosition(
86 wm::WindowState* window_state, bool animated) {
87 gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
89 if (bounds_in_parent == window_state->window()->bounds())
90 return;
92 if (animated)
93 window_state->SetBoundsDirect(bounds_in_parent);
94 else
95 window_state->SetBoundsDirectAnimated(bounds_in_parent);
98 MaximizeModeWindowState::MaximizeModeWindowState(
99 aura::Window* window, MaximizeModeWindowManager* creator)
100 : window_(window),
101 creator_(creator),
102 current_state_type_(wm::GetWindowState(window)->GetStateType()),
103 defer_bounds_updates_(false) {
104 old_state_.reset(
105 wm::GetWindowState(window)->SetStateObject(
106 scoped_ptr<State>(this).Pass()).release());
109 MaximizeModeWindowState::~MaximizeModeWindowState() {
110 creator_->WindowStateDestroyed(window_);
113 void MaximizeModeWindowState::LeaveMaximizeMode(wm::WindowState* window_state) {
114 // Note: When we return we will destroy ourselves with the |our_reference|.
115 scoped_ptr<wm::WindowState::State> our_reference =
116 window_state->SetStateObject(old_state_.Pass());
119 void MaximizeModeWindowState::SetDeferBoundsUpdates(bool defer_bounds_updates) {
120 if (defer_bounds_updates_ == defer_bounds_updates)
121 return;
123 defer_bounds_updates_ = defer_bounds_updates;
124 if (!defer_bounds_updates_)
125 UpdateBounds(wm::GetWindowState(window_), true);
128 void MaximizeModeWindowState::OnWMEvent(wm::WindowState* window_state,
129 const wm::WMEvent* event) {
130 switch (event->type()) {
131 case wm::WM_EVENT_TOGGLE_FULLSCREEN:
132 ToggleFullScreen(window_state, window_state->delegate());
133 break;
134 case wm::WM_EVENT_FULLSCREEN:
135 UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_FULLSCREEN, true);
136 break;
137 case wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
138 case wm::WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
139 case wm::WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
140 case wm::WM_EVENT_TOGGLE_MAXIMIZE:
141 case wm::WM_EVENT_CYCLE_SNAP_DOCK_LEFT:
142 case wm::WM_EVENT_CYCLE_SNAP_DOCK_RIGHT:
143 case wm::WM_EVENT_CENTER:
144 case wm::WM_EVENT_SNAP_LEFT:
145 case wm::WM_EVENT_SNAP_RIGHT:
146 case wm::WM_EVENT_NORMAL:
147 case wm::WM_EVENT_MAXIMIZE:
148 case wm::WM_EVENT_DOCK:
149 UpdateWindow(window_state,
150 GetMaximizedOrCenteredWindowType(window_state),
151 true);
152 return;
153 case wm::WM_EVENT_MINIMIZE:
154 UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_MINIMIZED, true);
155 return;
156 case wm::WM_EVENT_SHOW_INACTIVE:
157 return;
158 case wm::WM_EVENT_SET_BOUNDS:
159 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MAXIMIZED) {
160 // Having a maximized window, it could have been created with an empty
161 // size and the caller should get his size upon leaving the maximized
162 // mode. As such we set the restore bounds to the requested bounds.
163 gfx::Rect bounds_in_parent =
164 (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds();
165 if (!bounds_in_parent.IsEmpty())
166 window_state->SetRestoreBoundsInParent(bounds_in_parent);
167 } else if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED &&
168 current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
169 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) {
170 // In all other cases (except for minimized windows) we respect the
171 // requested bounds and center it to a fully visible area on the screen.
172 gfx::Rect bounds_in_parent =
173 (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds();
174 bounds_in_parent = GetCenteredBounds(bounds_in_parent, window_state);
175 if (bounds_in_parent != window_state->window()->bounds()) {
176 if (window_state->window()->IsVisible())
177 window_state->SetBoundsDirectAnimated(bounds_in_parent);
178 else
179 window_state->SetBoundsDirect(bounds_in_parent);
182 break;
183 case wm::WM_EVENT_ADDED_TO_WORKSPACE:
184 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
185 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN &&
186 current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) {
187 wm::WindowStateType new_state =
188 GetMaximizedOrCenteredWindowType(window_state);
189 UpdateWindow(window_state, new_state, true);
191 break;
192 case wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED:
193 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED)
194 UpdateBounds(window_state, true);
195 break;
196 case wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED:
197 // Don't animate on a screen rotation - just snap to new size.
198 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED)
199 UpdateBounds(window_state, false);
200 break;
204 wm::WindowStateType MaximizeModeWindowState::GetType() const {
205 return current_state_type_;
208 void MaximizeModeWindowState::AttachState(
209 wm::WindowState* window_state,
210 wm::WindowState::State* previous_state) {
211 current_state_type_ = previous_state->GetType();
213 views::Widget* widget =
214 views::Widget::GetWidgetForNativeWindow(window_state->window());
215 if (widget) {
216 gfx::Rect bounds = widget->GetRestoredBounds();
217 if (!bounds.IsEmpty()) {
218 // We do not want to do a session restore to our window states. Therefore
219 // we tell the window to use the current default states instead.
220 window_state->window()->SetProperty(ash::kRestoreShowStateOverrideKey,
221 window_state->GetShowState());
222 window_state->window()->SetProperty(ash::kRestoreBoundsOverrideKey,
223 new gfx::Rect(widget->GetRestoredBounds()));
227 // Initialize the state to a good preset.
228 if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
229 current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED &&
230 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) {
231 UpdateWindow(window_state,
232 GetMaximizedOrCenteredWindowType(window_state),
233 true);
236 window_state->set_can_be_dragged(false);
239 void MaximizeModeWindowState::DetachState(wm::WindowState* window_state) {
240 // From now on, we can use the default session restore mechanism again.
241 window_state->window()->ClearProperty(ash::kRestoreBoundsOverrideKey);
242 window_state->set_can_be_dragged(true);
245 void MaximizeModeWindowState::UpdateWindow(wm::WindowState* window_state,
246 wm::WindowStateType target_state,
247 bool animated) {
248 DCHECK(target_state == wm::WINDOW_STATE_TYPE_MINIMIZED ||
249 target_state == wm::WINDOW_STATE_TYPE_MAXIMIZED ||
250 (target_state == wm::WINDOW_STATE_TYPE_NORMAL &&
251 !window_state->CanMaximize()) ||
252 target_state == wm::WINDOW_STATE_TYPE_FULLSCREEN);
254 if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) {
255 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED)
256 return;
258 current_state_type_ = target_state;
259 ::wm::SetWindowVisibilityAnimationType(
260 window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
261 window_state->window()->Hide();
262 if (window_state->IsActive())
263 window_state->Deactivate();
264 return;
267 if (current_state_type_ == target_state) {
268 // If the state type did not change, update it accordingly.
269 UpdateBounds(window_state, animated);
270 return;
273 const wm::WindowStateType old_state_type = current_state_type_;
274 current_state_type_ = target_state;
275 window_state->UpdateWindowShowStateFromStateType();
276 window_state->NotifyPreStateTypeChange(old_state_type);
277 UpdateBounds(window_state, animated);
278 window_state->NotifyPostStateTypeChange(old_state_type);
280 if ((window_state->window()->TargetVisibility() ||
281 old_state_type == wm::WINDOW_STATE_TYPE_MINIMIZED) &&
282 !window_state->window()->layer()->visible()) {
283 // The layer may be hidden if the window was previously minimized. Make
284 // sure it's visible.
285 window_state->window()->Show();
289 wm::WindowStateType MaximizeModeWindowState::GetMaximizedOrCenteredWindowType(
290 wm::WindowState* window_state) {
291 return window_state->CanMaximize() ? wm::WINDOW_STATE_TYPE_MAXIMIZED :
292 wm::WINDOW_STATE_TYPE_NORMAL;
295 void MaximizeModeWindowState::UpdateBounds(wm::WindowState* window_state,
296 bool animated) {
297 if (defer_bounds_updates_)
298 return;
299 gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
300 // If we have a target bounds rectangle, we center it and set it
301 // accordingly.
302 if (!bounds_in_parent.IsEmpty() &&
303 bounds_in_parent != window_state->window()->bounds()) {
304 if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED ||
305 !window_state->window()->IsVisible() ||
306 !animated) {
307 window_state->SetBoundsDirect(bounds_in_parent);
308 } else {
309 // If we animate (to) maximized mode, we want to use the cross fade to
310 // avoid flashing.
311 if (window_state->IsMaximized())
312 window_state->SetBoundsDirectCrossFade(bounds_in_parent);
313 else
314 window_state->SetBoundsDirectAnimated(bounds_in_parent);
319 } // namespace ash