window_state.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_properties.h"
13#include "ash/wm/window_state_delegate.h"
14#include "ash/wm/window_state_observer.h"
15#include "ash/wm/window_util.h"
16#include "ash/wm/wm_types.h"
17#include "base/auto_reset.h"
18#include "base/command_line.h"
19#include "ui/aura/client/aura_constants.h"
20#include "ui/aura/layout_manager.h"
21#include "ui/aura/window.h"
22#include "ui/aura/window_delegate.h"
23#include "ui/compositor/scoped_layer_animation_settings.h"
24#include "ui/gfx/display.h"
25#include "ui/views/corewm/window_util.h"
26
27namespace ash {
28namespace wm {
29
30namespace {
31
32// A tentative class to set the bounds on the window.
33// TODO(oshima): Once all logic is cleaned up, move this to the real layout
34// manager with proper friendship.
35class BoundsSetter : public aura::LayoutManager {
36 public:
37  BoundsSetter() {}
38  virtual ~BoundsSetter() {}
39
40  // aura::LayoutManager overrides:
41  virtual void OnWindowResized() OVERRIDE {}
42  virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {}
43  virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
44  virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
45  virtual void OnChildWindowVisibilityChanged(
46      aura::Window* child, bool visible) OVERRIDE {}
47  virtual void SetChildBounds(
48      aura::Window* child, const gfx::Rect& requested_bounds) OVERRIDE {}
49
50  void SetBounds(aura::Window* window, const gfx::Rect& bounds) {
51    SetChildBoundsDirect(window, bounds);
52  }
53
54 private:
55  DISALLOW_COPY_AND_ASSIGN(BoundsSetter);
56};
57
58WMEvent WMEventFromShowState(ui::WindowShowState requested_show_state) {
59  switch (requested_show_state) {
60    case ui::SHOW_STATE_DEFAULT:
61    case ui::SHOW_STATE_NORMAL:
62      return NORMAL;
63    case ui::SHOW_STATE_MINIMIZED:
64      return MINIMIZE;
65    case ui::SHOW_STATE_MAXIMIZED:
66      return MAXIMIZE;
67    case ui::SHOW_STATE_FULLSCREEN:
68      return FULLSCREEN;
69    case ui::SHOW_STATE_INACTIVE:
70      return SHOW_INACTIVE;
71    case ui::SHOW_STATE_DETACHED:
72    case ui::SHOW_STATE_END:
73      NOTREACHED() << "No WMEvent defined for the show type:"
74                   << requested_show_state;
75  }
76  return NORMAL;
77}
78
79}  // namespace
80
81WindowState::WindowState(aura::Window* window)
82    : window_(window),
83      window_position_managed_(false),
84      bounds_changed_by_user_(false),
85      panel_attached_(true),
86      continue_drag_after_reparent_(false),
87      ignored_by_shelf_(false),
88      can_consume_system_keys_(false),
89      top_row_keys_are_function_keys_(false),
90      unminimize_to_restore_bounds_(false),
91      hide_shelf_when_fullscreen_(true),
92      animate_to_fullscreen_(true),
93      minimum_visibility_(false),
94      ignore_property_change_(false),
95      window_show_type_(ToWindowShowType(GetShowState())),
96      current_state_(new DefaultState) {
97  window_->AddObserver(this);
98#if defined(OS_CHROMEOS)
99  // NOTE(pkotwicz): Animating to immersive fullscreen does not look good. When
100  // switches::UseImmersiveFullscreenForAllWindows() returns true, most windows
101  // can be put into immersive fullscreen. It is not worth the added complexity
102  // to only animate to fullscreen if the window is put into immersive
103  // fullscreen.
104  animate_to_fullscreen_ = !switches::UseImmersiveFullscreenForAllWindows();
105#endif
106}
107
108WindowState::~WindowState() {
109}
110
111bool WindowState::HasDelegate() const {
112  return delegate_;
113}
114
115void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) {
116  DCHECK(!delegate_.get());
117  delegate_ = delegate.Pass();
118}
119
120ui::WindowShowState WindowState::GetShowState() const {
121  return window_->GetProperty(aura::client::kShowStateKey);
122}
123
124bool WindowState::IsMinimized() const {
125  return GetShowState() == ui::SHOW_STATE_MINIMIZED;
126}
127
128bool WindowState::IsMaximized() const {
129  return GetShowState() == ui::SHOW_STATE_MAXIMIZED;
130}
131
132bool WindowState::IsFullscreen() const {
133  return GetShowState() == ui::SHOW_STATE_FULLSCREEN;
134}
135
136bool WindowState::IsMaximizedOrFullscreen() const {
137  ui::WindowShowState show_state(GetShowState());
138  return show_state == ui::SHOW_STATE_FULLSCREEN ||
139      show_state == ui::SHOW_STATE_MAXIMIZED;
140}
141
142bool WindowState::IsNormalShowState() const {
143  ui::WindowShowState state = GetShowState();
144  return state == ui::SHOW_STATE_NORMAL || state == ui::SHOW_STATE_DEFAULT;
145}
146
147bool WindowState::IsNormalShowType() const {
148  return window_show_type_ == SHOW_TYPE_NORMAL ||
149      window_show_type_ == SHOW_TYPE_DEFAULT;
150}
151
152bool WindowState::IsActive() const {
153  return IsActiveWindow(window_);
154}
155
156bool WindowState::IsDocked() const {
157  return window_->parent() &&
158      window_->parent()->id() == internal::kShellWindowId_DockedContainer;
159}
160
161bool WindowState::IsSnapped() const {
162  return window_show_type_ == SHOW_TYPE_LEFT_SNAPPED ||
163      window_show_type_ == SHOW_TYPE_RIGHT_SNAPPED;
164}
165
166bool WindowState::CanMaximize() const {
167  return window_->GetProperty(aura::client::kCanMaximizeKey);
168}
169
170bool WindowState::CanMinimize() const {
171  internal::RootWindowController* controller =
172      internal::RootWindowController::ForWindow(window_);
173  if (!controller)
174    return false;
175  aura::Window* lockscreen = controller->GetContainer(
176      internal::kShellWindowId_LockScreenContainersContainer);
177  if (lockscreen->Contains(window_))
178    return false;
179
180  return true;
181}
182
183bool WindowState::CanResize() const {
184  return window_->GetProperty(aura::client::kCanResizeKey);
185}
186
187bool WindowState::CanActivate() const {
188  return views::corewm::CanActivateWindow(window_);
189}
190
191bool WindowState::CanSnap() const {
192  if (!CanResize() || window_->type() == ui::wm::WINDOW_TYPE_PANEL ||
193      views::corewm::GetTransientParent(window_))
194    return false;
195  // If a window has a maximum size defined, snapping may make it too big.
196  return window_->delegate() ? window_->delegate()->GetMaximumSize().IsEmpty() :
197                              true;
198}
199
200bool WindowState::HasRestoreBounds() const {
201  return window_->GetProperty(aura::client::kRestoreBoundsKey) != NULL;
202}
203
204void WindowState::Maximize() {
205  window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
206}
207
208void WindowState::SnapLeft(const gfx::Rect& bounds) {
209  SnapWindow(SHOW_TYPE_LEFT_SNAPPED, bounds);
210}
211
212void WindowState::SnapRight(const gfx::Rect& bounds) {
213  SnapWindow(SHOW_TYPE_RIGHT_SNAPPED, bounds);
214}
215
216void WindowState::Minimize() {
217  window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
218}
219
220void WindowState::Unminimize() {
221  window_->SetProperty(
222      aura::client::kShowStateKey,
223      window_->GetProperty(aura::client::kRestoreShowStateKey));
224  window_->ClearProperty(aura::client::kRestoreShowStateKey);
225}
226
227void WindowState::Activate() {
228  ActivateWindow(window_);
229}
230
231void WindowState::Deactivate() {
232  DeactivateWindow(window_);
233}
234
235void WindowState::Restore() {
236  if (!IsNormalShowType())
237    OnWMEvent(NORMAL);
238}
239
240void WindowState::ToggleFullscreen() {
241  OnWMEvent(TOGGLE_FULLSCREEN);
242}
243
244void WindowState::OnWMEvent(WMEvent event) {
245  current_state_->OnWMEvent(this, event);
246}
247
248void WindowState::SetBoundsInScreen(
249    const gfx::Rect& bounds_in_screen) {
250  gfx::Rect bounds_in_parent =
251      ScreenUtil::ConvertRectFromScreen(window_->parent(),
252                                       bounds_in_screen);
253  window_->SetBounds(bounds_in_parent);
254}
255
256void WindowState::SaveCurrentBoundsForRestore() {
257  gfx::Rect bounds_in_screen =
258      ScreenUtil::ConvertRectToScreen(window_->parent(),
259                                     window_->bounds());
260  SetRestoreBoundsInScreen(bounds_in_screen);
261}
262
263gfx::Rect WindowState::GetRestoreBoundsInScreen() const {
264  return *window_->GetProperty(aura::client::kRestoreBoundsKey);
265}
266
267gfx::Rect WindowState::GetRestoreBoundsInParent() const {
268  return ScreenUtil::ConvertRectFromScreen(window_->parent(),
269                                          GetRestoreBoundsInScreen());
270}
271
272void WindowState::SetRestoreBoundsInScreen(const gfx::Rect& bounds) {
273  window_->SetProperty(aura::client::kRestoreBoundsKey, new gfx::Rect(bounds));
274}
275
276void WindowState::SetRestoreBoundsInParent(const gfx::Rect& bounds) {
277  SetRestoreBoundsInScreen(
278      ScreenUtil::ConvertRectToScreen(window_->parent(), bounds));
279}
280
281void WindowState::ClearRestoreBounds() {
282  window_->ClearProperty(aura::client::kRestoreBoundsKey);
283}
284
285void WindowState::SetPreAutoManageWindowBounds(
286    const gfx::Rect& bounds) {
287  pre_auto_manage_window_bounds_.reset(new gfx::Rect(bounds));
288}
289
290void WindowState::AddObserver(WindowStateObserver* observer) {
291  observer_list_.AddObserver(observer);
292}
293
294void WindowState::RemoveObserver(WindowStateObserver* observer) {
295  observer_list_.RemoveObserver(observer);
296}
297
298void WindowState::CreateDragDetails(aura::Window* window,
299                                    const gfx::Point& point_in_parent,
300                                    int window_component,
301                                    aura::client::WindowMoveSource source) {
302  drag_details_.reset(
303      new DragDetails(window, point_in_parent, window_component, source));
304}
305
306void WindowState::DeleteDragDetails() {
307  drag_details_.reset();
308}
309
310void WindowState::SetAndClearRestoreBounds() {
311  DCHECK(HasRestoreBounds());
312  SetBoundsInScreen(GetRestoreBoundsInScreen());
313  ClearRestoreBounds();
314}
315
316void WindowState::AdjustSnappedBounds(gfx::Rect* bounds) {
317  if (is_dragged() || !IsSnapped())
318    return;
319  gfx::Rect maximized_bounds = ScreenUtil::GetMaximizedWindowBoundsInParent(
320      window_);
321  if (window_show_type() == SHOW_TYPE_LEFT_SNAPPED)
322    bounds->set_x(maximized_bounds.x());
323  else if (window_show_type() == SHOW_TYPE_RIGHT_SNAPPED)
324    bounds->set_x(maximized_bounds.right() - bounds->width());
325  bounds->set_y(maximized_bounds.y());
326  bounds->set_height(maximized_bounds.height());
327}
328
329void WindowState::OnWindowPropertyChanged(aura::Window* window,
330                                          const void* key,
331                                          intptr_t old) {
332  DCHECK_EQ(window, window_);
333  if (key == aura::client::kShowStateKey && !ignore_property_change_)
334    OnWMEvent(WMEventFromShowState(GetShowState()));
335}
336
337void WindowState::SnapWindow(WindowShowType left_or_right,
338                             const gfx::Rect& bounds) {
339  if (window_show_type_ == left_or_right) {
340    window_->SetBounds(bounds);
341    return;
342  }
343
344  // Compute the bounds that the window will restore to. If the window does not
345  // already have restore bounds, it will be restored (when un-snapped) to the
346  // last bounds that it had before getting snapped.
347  gfx::Rect restore_bounds_in_screen(HasRestoreBounds() ?
348      GetRestoreBoundsInScreen() : window_->GetBoundsInScreen());
349  // Set the window's restore bounds so that WorkspaceLayoutManager knows
350  // which width to use when the snapped window is moved to the edge.
351  SetRestoreBoundsInParent(bounds);
352
353  DCHECK(left_or_right == SHOW_TYPE_LEFT_SNAPPED ||
354         left_or_right == SHOW_TYPE_RIGHT_SNAPPED);
355  OnWMEvent(left_or_right == SHOW_TYPE_LEFT_SNAPPED ?
356            SNAP_LEFT : SNAP_RIGHT);
357
358  // TODO(varkha): Ideally the bounds should be changed in a LayoutManager upon
359  // observing the WindowShowType change.
360  // If the window is a child of kShellWindowId_DockedContainer such as during
361  // a drag, the window's bounds are not set in
362  // WorkspaceLayoutManager::OnWindowShowTypeChanged(). Set them here. Skip
363  // setting the bounds otherwise to avoid stopping the slide animation which
364  // was started as a result of OnWindowShowTypeChanged().
365  if (IsDocked())
366    window_->SetBounds(bounds);
367  SetRestoreBoundsInScreen(restore_bounds_in_screen);
368}
369
370void WindowState::UpdateWindowShowType(WindowShowType new_window_show_type) {
371  ui::WindowShowState new_window_state =
372      ToWindowShowState(new_window_show_type);
373  if (new_window_state != GetShowState()) {
374    base::AutoReset<bool> resetter(&ignore_property_change_, true);
375    window_->SetProperty(aura::client::kShowStateKey, new_window_state);
376  }
377  window_show_type_ = new_window_show_type;
378}
379
380void WindowState::NotifyPreShowTypeChange(WindowShowType old_window_show_type) {
381  FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
382                    OnPreWindowShowTypeChange(this, old_window_show_type));
383}
384
385void WindowState::NotifyPostShowTypeChange(
386    WindowShowType old_window_show_type) {
387  FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
388                    OnPostWindowShowTypeChange(this, old_window_show_type));
389}
390
391void WindowState::SetBoundsDirect(const gfx::Rect& bounds) {
392  BoundsSetter().SetBounds(window_, bounds);
393}
394
395void WindowState::SetBoundsDirectAnimated(const gfx::Rect& bounds) {
396  const int kBoundsChangeSlideDurationMs = 120;
397
398  ui::Layer* layer = window_->layer();
399  ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
400  slide_settings.SetPreemptionStrategy(
401      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
402  slide_settings.SetTransitionDuration(
403      base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs));
404  SetBoundsDirect(bounds);
405}
406
407WindowState* GetActiveWindowState() {
408  aura::Window* active = GetActiveWindow();
409  return active ? GetWindowState(active) : NULL;
410}
411
412WindowState* GetWindowState(aura::Window* window) {
413  if (!window)
414    return NULL;
415  WindowState* settings = window->GetProperty(internal::kWindowStateKey);
416  if(!settings) {
417    settings = new WindowState(window);
418    window->SetProperty(internal::kWindowStateKey, settings);
419  }
420  return settings;
421}
422
423const WindowState* GetWindowState(const aura::Window* window) {
424  return GetWindowState(const_cast<aura::Window*>(window));
425}
426
427}  // namespace wm
428}  // namespace ash
429