1 // Copyright 2013 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/window_state.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/screen_util.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/wm/default_state.h"
12 #include "ash/wm/window_animations.h"
13 #include "ash/wm/window_properties.h"
14 #include "ash/wm/window_state_delegate.h"
15 #include "ash/wm/window_state_observer.h"
16 #include "ash/wm/window_util.h"
17 #include "ash/wm/wm_event.h"
18 #include "base/auto_reset.h"
19 #include "base/command_line.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/layout_manager.h"
22 #include "ui/aura/window.h"
23 #include "ui/aura/window_delegate.h"
24 #include "ui/compositor/layer_tree_owner.h"
25 #include "ui/compositor/scoped_layer_animation_settings.h"
26 #include "ui/gfx/display.h"
27 #include "ui/gfx/screen.h"
28 #include "ui/wm/core/window_util.h"
29
30 namespace ash {
31 namespace wm {
32
33 namespace {
34
35 // A tentative class to set the bounds on the window.
36 // TODO(oshima): Once all logic is cleaned up, move this to the real layout
37 // manager with proper friendship.
38 class BoundsSetter : public aura::LayoutManager {
39 public:
BoundsSetter()40 BoundsSetter() {}
~BoundsSetter()41 virtual ~BoundsSetter() {}
42
43 // aura::LayoutManager overrides:
OnWindowResized()44 virtual void OnWindowResized() OVERRIDE {}
OnWindowAddedToLayout(aura::Window * child)45 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {}
OnWillRemoveWindowFromLayout(aura::Window * child)46 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
OnWindowRemovedFromLayout(aura::Window * child)47 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
OnChildWindowVisibilityChanged(aura::Window * child,bool visible)48 virtual void OnChildWindowVisibilityChanged(
49 aura::Window* child, bool visible) OVERRIDE {}
SetChildBounds(aura::Window * child,const gfx::Rect & requested_bounds)50 virtual void SetChildBounds(
51 aura::Window* child, const gfx::Rect& requested_bounds) OVERRIDE {}
52
SetBounds(aura::Window * window,const gfx::Rect & bounds)53 void SetBounds(aura::Window* window, const gfx::Rect& bounds) {
54 SetChildBoundsDirect(window, bounds);
55 }
56
57 private:
58 DISALLOW_COPY_AND_ASSIGN(BoundsSetter);
59 };
60
WMEventTypeFromShowState(ui::WindowShowState requested_show_state)61 WMEventType WMEventTypeFromShowState(ui::WindowShowState requested_show_state) {
62 switch (requested_show_state) {
63 case ui::SHOW_STATE_DEFAULT:
64 case ui::SHOW_STATE_NORMAL:
65 return WM_EVENT_NORMAL;
66 case ui::SHOW_STATE_MINIMIZED:
67 return WM_EVENT_MINIMIZE;
68 case ui::SHOW_STATE_MAXIMIZED:
69 return WM_EVENT_MAXIMIZE;
70 case ui::SHOW_STATE_FULLSCREEN:
71 return WM_EVENT_FULLSCREEN;
72 case ui::SHOW_STATE_INACTIVE:
73 return WM_EVENT_SHOW_INACTIVE;
74 case ui::SHOW_STATE_END:
75 NOTREACHED() << "No WMEvent defined for the show state:"
76 << requested_show_state;
77 }
78 return WM_EVENT_NORMAL;
79 }
80
81 } // namespace
82
~WindowState()83 WindowState::~WindowState() {
84 // WindowState is registered as an owned property of |window_|, and window
85 // unregisters all of its observers in its d'tor before destroying its
86 // properties. As a result, window_->RemoveObserver() doesn't need to (and
87 // shouldn't) be called here.
88 }
89
HasDelegate() const90 bool WindowState::HasDelegate() const {
91 return delegate_;
92 }
93
SetDelegate(scoped_ptr<WindowStateDelegate> delegate)94 void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) {
95 DCHECK(!delegate_.get());
96 delegate_ = delegate.Pass();
97 }
98
GetStateType() const99 WindowStateType WindowState::GetStateType() const {
100 return current_state_->GetType();
101 }
102
IsMinimized() const103 bool WindowState::IsMinimized() const {
104 return GetStateType() == WINDOW_STATE_TYPE_MINIMIZED;
105 }
106
IsMaximized() const107 bool WindowState::IsMaximized() const {
108 return GetStateType() == WINDOW_STATE_TYPE_MAXIMIZED;
109 }
110
IsFullscreen() const111 bool WindowState::IsFullscreen() const {
112 return GetStateType() == WINDOW_STATE_TYPE_FULLSCREEN;
113 }
114
IsMaximizedOrFullscreen() const115 bool WindowState::IsMaximizedOrFullscreen() const {
116 return GetStateType() == WINDOW_STATE_TYPE_FULLSCREEN ||
117 GetStateType() == WINDOW_STATE_TYPE_MAXIMIZED;
118 }
119
IsSnapped() const120 bool WindowState::IsSnapped() const {
121 return GetStateType() == WINDOW_STATE_TYPE_LEFT_SNAPPED ||
122 GetStateType() == WINDOW_STATE_TYPE_RIGHT_SNAPPED;
123 }
124
IsNormalStateType() const125 bool WindowState::IsNormalStateType() const {
126 return GetStateType() == WINDOW_STATE_TYPE_NORMAL ||
127 GetStateType() == WINDOW_STATE_TYPE_DEFAULT;
128 }
129
IsNormalOrSnapped() const130 bool WindowState::IsNormalOrSnapped() const {
131 return IsNormalStateType() || IsSnapped();
132 }
133
IsActive() const134 bool WindowState::IsActive() const {
135 return IsActiveWindow(window_);
136 }
137
IsDocked() const138 bool WindowState::IsDocked() const {
139 return window_->parent() &&
140 window_->parent()->id() == kShellWindowId_DockedContainer;
141 }
142
CanMaximize() const143 bool WindowState::CanMaximize() const {
144 // Window must have the kCanMaximizeKey and have no maximum width or height.
145 if (!window()->GetProperty(aura::client::kCanMaximizeKey))
146 return false;
147
148 if (!window()->delegate())
149 return true;
150
151 gfx::Size max_size = window_->delegate()->GetMaximumSize();
152 return !max_size.width() && !max_size.height();
153 }
154
CanMinimize() const155 bool WindowState::CanMinimize() const {
156 RootWindowController* controller = RootWindowController::ForWindow(window_);
157 if (!controller)
158 return false;
159 aura::Window* lockscreen =
160 controller->GetContainer(kShellWindowId_LockScreenContainersContainer);
161 if (lockscreen->Contains(window_))
162 return false;
163
164 return true;
165 }
166
CanResize() const167 bool WindowState::CanResize() const {
168 return window_->GetProperty(aura::client::kCanResizeKey);
169 }
170
CanActivate() const171 bool WindowState::CanActivate() const {
172 return ::wm::CanActivateWindow(window_);
173 }
174
CanSnap() const175 bool WindowState::CanSnap() const {
176 if (!CanResize() || window_->type() == ui::wm::WINDOW_TYPE_PANEL ||
177 ::wm::GetTransientParent(window_))
178 return false;
179 // If a window cannot be maximized, assume it cannot snap either.
180 // TODO(oshima): We should probably snap if the maximum size is greater than
181 // the snapped size.
182 return CanMaximize();
183 }
184
HasRestoreBounds() const185 bool WindowState::HasRestoreBounds() const {
186 return window_->GetProperty(aura::client::kRestoreBoundsKey) != NULL;
187 }
188
Maximize()189 void WindowState::Maximize() {
190 window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
191 }
192
Minimize()193 void WindowState::Minimize() {
194 window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
195 }
196
Unminimize()197 void WindowState::Unminimize() {
198 window_->SetProperty(
199 aura::client::kShowStateKey,
200 window_->GetProperty(aura::client::kRestoreShowStateKey));
201 window_->ClearProperty(aura::client::kRestoreShowStateKey);
202 }
203
Activate()204 void WindowState::Activate() {
205 ActivateWindow(window_);
206 }
207
Deactivate()208 void WindowState::Deactivate() {
209 DeactivateWindow(window_);
210 }
211
Restore()212 void WindowState::Restore() {
213 if (!IsNormalStateType()) {
214 const WMEvent event(WM_EVENT_NORMAL);
215 OnWMEvent(&event);
216 }
217 }
218
OnWMEvent(const WMEvent * event)219 void WindowState::OnWMEvent(const WMEvent* event) {
220 current_state_->OnWMEvent(this, event);
221 }
222
SaveCurrentBoundsForRestore()223 void WindowState::SaveCurrentBoundsForRestore() {
224 gfx::Rect bounds_in_screen =
225 ScreenUtil::ConvertRectToScreen(window_->parent(),
226 window_->bounds());
227 SetRestoreBoundsInScreen(bounds_in_screen);
228 }
229
GetRestoreBoundsInScreen() const230 gfx::Rect WindowState::GetRestoreBoundsInScreen() const {
231 return *window_->GetProperty(aura::client::kRestoreBoundsKey);
232 }
233
GetRestoreBoundsInParent() const234 gfx::Rect WindowState::GetRestoreBoundsInParent() const {
235 return ScreenUtil::ConvertRectFromScreen(window_->parent(),
236 GetRestoreBoundsInScreen());
237 }
238
SetRestoreBoundsInScreen(const gfx::Rect & bounds)239 void WindowState::SetRestoreBoundsInScreen(const gfx::Rect& bounds) {
240 window_->SetProperty(aura::client::kRestoreBoundsKey, new gfx::Rect(bounds));
241 }
242
SetRestoreBoundsInParent(const gfx::Rect & bounds)243 void WindowState::SetRestoreBoundsInParent(const gfx::Rect& bounds) {
244 SetRestoreBoundsInScreen(
245 ScreenUtil::ConvertRectToScreen(window_->parent(), bounds));
246 }
247
ClearRestoreBounds()248 void WindowState::ClearRestoreBounds() {
249 window_->ClearProperty(aura::client::kRestoreBoundsKey);
250 }
251
SetStateObject(scoped_ptr<WindowState::State> new_state)252 scoped_ptr<WindowState::State> WindowState::SetStateObject(
253 scoped_ptr<WindowState::State> new_state) {
254 current_state_->DetachState(this);
255 scoped_ptr<WindowState::State> old_object = current_state_.Pass();
256 current_state_ = new_state.Pass();
257 current_state_->AttachState(this, old_object.get());
258 return old_object.Pass();
259 }
260
SetPreAutoManageWindowBounds(const gfx::Rect & bounds)261 void WindowState::SetPreAutoManageWindowBounds(
262 const gfx::Rect& bounds) {
263 pre_auto_manage_window_bounds_.reset(new gfx::Rect(bounds));
264 }
265
AddObserver(WindowStateObserver * observer)266 void WindowState::AddObserver(WindowStateObserver* observer) {
267 observer_list_.AddObserver(observer);
268 }
269
RemoveObserver(WindowStateObserver * observer)270 void WindowState::RemoveObserver(WindowStateObserver* observer) {
271 observer_list_.RemoveObserver(observer);
272 }
273
set_bounds_changed_by_user(bool bounds_changed_by_user)274 void WindowState::set_bounds_changed_by_user(bool bounds_changed_by_user) {
275 bounds_changed_by_user_ = bounds_changed_by_user;
276 if (bounds_changed_by_user)
277 pre_auto_manage_window_bounds_.reset();
278 }
279
CreateDragDetails(aura::Window * window,const gfx::Point & point_in_parent,int window_component,aura::client::WindowMoveSource source)280 void WindowState::CreateDragDetails(aura::Window* window,
281 const gfx::Point& point_in_parent,
282 int window_component,
283 aura::client::WindowMoveSource source) {
284 drag_details_.reset(
285 new DragDetails(window, point_in_parent, window_component, source));
286 }
287
DeleteDragDetails()288 void WindowState::DeleteDragDetails() {
289 drag_details_.reset();
290 }
291
SetAndClearRestoreBounds()292 void WindowState::SetAndClearRestoreBounds() {
293 DCHECK(HasRestoreBounds());
294 SetBoundsInScreen(GetRestoreBoundsInScreen());
295 ClearRestoreBounds();
296 }
297
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)298 void WindowState::OnWindowPropertyChanged(aura::Window* window,
299 const void* key,
300 intptr_t old) {
301 DCHECK_EQ(window, window_);
302 if (key == aura::client::kShowStateKey && !ignore_property_change_) {
303 WMEvent event(WMEventTypeFromShowState(GetShowState()));
304 OnWMEvent(&event);
305 }
306 }
307
WindowState(aura::Window * window)308 WindowState::WindowState(aura::Window* window)
309 : window_(window),
310 window_position_managed_(false),
311 bounds_changed_by_user_(false),
312 panel_attached_(true),
313 ignored_by_shelf_(false),
314 can_consume_system_keys_(false),
315 top_row_keys_are_function_keys_(false),
316 unminimize_to_restore_bounds_(false),
317 in_immersive_fullscreen_(false),
318 hide_shelf_when_fullscreen_(true),
319 minimum_visibility_(false),
320 can_be_dragged_(true),
321 ignore_property_change_(false),
322 current_state_(new DefaultState(ToWindowStateType(GetShowState()))) {
323 window_->AddObserver(this);
324 }
325
GetShowState() const326 ui::WindowShowState WindowState::GetShowState() const {
327 return window_->GetProperty(aura::client::kShowStateKey);
328 }
329
SetBoundsInScreen(const gfx::Rect & bounds_in_screen)330 void WindowState::SetBoundsInScreen(
331 const gfx::Rect& bounds_in_screen) {
332 gfx::Rect bounds_in_parent =
333 ScreenUtil::ConvertRectFromScreen(window_->parent(),
334 bounds_in_screen);
335 window_->SetBounds(bounds_in_parent);
336 }
337
AdjustSnappedBounds(gfx::Rect * bounds)338 void WindowState::AdjustSnappedBounds(gfx::Rect* bounds) {
339 if (is_dragged() || !IsSnapped())
340 return;
341 gfx::Rect maximized_bounds = ScreenUtil::GetMaximizedWindowBoundsInParent(
342 window_);
343 if (GetStateType() == WINDOW_STATE_TYPE_LEFT_SNAPPED)
344 bounds->set_x(maximized_bounds.x());
345 else if (GetStateType() == WINDOW_STATE_TYPE_RIGHT_SNAPPED)
346 bounds->set_x(maximized_bounds.right() - bounds->width());
347 bounds->set_y(maximized_bounds.y());
348 bounds->set_height(maximized_bounds.height());
349 }
350
UpdateWindowShowStateFromStateType()351 void WindowState::UpdateWindowShowStateFromStateType() {
352 ui::WindowShowState new_window_state =
353 ToWindowShowState(current_state_->GetType());
354 if (new_window_state != GetShowState()) {
355 base::AutoReset<bool> resetter(&ignore_property_change_, true);
356 window_->SetProperty(aura::client::kShowStateKey, new_window_state);
357 }
358 }
359
NotifyPreStateTypeChange(WindowStateType old_window_state_type)360 void WindowState::NotifyPreStateTypeChange(
361 WindowStateType old_window_state_type) {
362 FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
363 OnPreWindowStateTypeChange(this, old_window_state_type));
364 }
365
NotifyPostStateTypeChange(WindowStateType old_window_state_type)366 void WindowState::NotifyPostStateTypeChange(
367 WindowStateType old_window_state_type) {
368 FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
369 OnPostWindowStateTypeChange(this, old_window_state_type));
370 }
371
SetBoundsDirect(const gfx::Rect & bounds)372 void WindowState::SetBoundsDirect(const gfx::Rect& bounds) {
373 gfx::Rect actual_new_bounds(bounds);
374 // Ensure we don't go smaller than our minimum bounds in "normal" window
375 // modes
376 if (window_->delegate() && !IsMaximized() && !IsFullscreen()) {
377 // Get the minimum usable size of the minimum size and the screen size.
378 gfx::Size min_size = window_->delegate()->GetMinimumSize();
379 min_size.SetToMin(gfx::Screen::GetScreenFor(
380 window_)->GetDisplayNearestWindow(window_).work_area().size());
381
382 actual_new_bounds.set_width(
383 std::max(min_size.width(), actual_new_bounds.width()));
384 actual_new_bounds.set_height(
385 std::max(min_size.height(), actual_new_bounds.height()));
386 }
387 BoundsSetter().SetBounds(window_, actual_new_bounds);
388 SnapWindowToPixelBoundary(window_);
389 }
390
SetBoundsConstrained(const gfx::Rect & bounds)391 void WindowState::SetBoundsConstrained(const gfx::Rect& bounds) {
392 gfx::Rect work_area_in_parent =
393 ScreenUtil::GetDisplayWorkAreaBoundsInParent(window_);
394 gfx::Rect child_bounds(bounds);
395 AdjustBoundsSmallerThan(work_area_in_parent.size(), &child_bounds);
396 SetBoundsDirect(child_bounds);
397 }
398
SetBoundsDirectAnimated(const gfx::Rect & bounds)399 void WindowState::SetBoundsDirectAnimated(const gfx::Rect& bounds) {
400 const int kBoundsChangeSlideDurationMs = 120;
401
402 ui::Layer* layer = window_->layer();
403 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
404 slide_settings.SetPreemptionStrategy(
405 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
406 slide_settings.SetTransitionDuration(
407 base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs));
408 SetBoundsDirect(bounds);
409 }
410
SetBoundsDirectCrossFade(const gfx::Rect & new_bounds)411 void WindowState::SetBoundsDirectCrossFade(const gfx::Rect& new_bounds) {
412 // Some test results in invoking CrossFadeToBounds when window is not visible.
413 // No animation is necessary in that case, thus just change the bounds and
414 // quit.
415 if (!window_->TargetVisibility()) {
416 SetBoundsConstrained(new_bounds);
417 return;
418 }
419
420 const gfx::Rect old_bounds = window_->bounds();
421
422 // Create fresh layers for the window and all its children to paint into.
423 // Takes ownership of the old layer and all its children, which will be
424 // cleaned up after the animation completes.
425 // Specify |set_bounds| to true here to keep the old bounds in the child
426 // windows of |window|.
427 scoped_ptr<ui::LayerTreeOwner> old_layer_owner =
428 ::wm::RecreateLayers(window_);
429 ui::Layer* old_layer = old_layer_owner->root();
430 DCHECK(old_layer);
431 ui::Layer* new_layer = window_->layer();
432
433 // Resize the window to the new size, which will force a layout and paint.
434 SetBoundsDirect(new_bounds);
435
436 // Ensure the higher-resolution layer is on top.
437 bool old_on_top = (old_bounds.width() > new_bounds.width());
438 if (old_on_top)
439 old_layer->parent()->StackBelow(new_layer, old_layer);
440 else
441 old_layer->parent()->StackAbove(new_layer, old_layer);
442
443 CrossFadeAnimation(window_, old_layer_owner.Pass(), gfx::Tween::EASE_OUT);
444 }
445
GetActiveWindowState()446 WindowState* GetActiveWindowState() {
447 aura::Window* active = GetActiveWindow();
448 return active ? GetWindowState(active) : NULL;
449 }
450
GetWindowState(aura::Window * window)451 WindowState* GetWindowState(aura::Window* window) {
452 if (!window)
453 return NULL;
454 WindowState* settings = window->GetProperty(kWindowStateKey);
455 if(!settings) {
456 settings = new WindowState(window);
457 window->SetProperty(kWindowStateKey, settings);
458 }
459 return settings;
460 }
461
GetWindowState(const aura::Window * window)462 const WindowState* GetWindowState(const aura::Window* window) {
463 return GetWindowState(const_cast<aura::Window*>(window));
464 }
465
466 } // namespace wm
467 } // namespace ash
468