maximize_mode_window_state.cc revision 010d83a9304c5a91596085d917d248abff47903a
1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/maximize_mode/maximize_mode_window_state.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/display/display_controller.h"
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/screen_util.h"
9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ash/shell.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/shell_window_ids.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/coordinate_conversion.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/maximize_mode/maximize_mode_window_manager.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/wm/window_animations.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_properties.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_state_delegate.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_state_util.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/window_util.h"
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/wm_event.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ash/wm/workspace/workspace_window_resizer.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/aura/client/aura_constants.h"
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/aura/window.h"
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/aura/window_delegate.h"
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/display.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/rect.h"
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/view_constants_aura.h"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/widget/widget.h"
277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace ash {
297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace {
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Returns the biggest possible size for a window which is about to be
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// maximized.
33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochgfx::Size GetMaximumSizeOfWindow(wm::WindowState* window_state) {
34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK(window_state->CanMaximize() || window_state->CanResize());
35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  gfx::Size workspace_size = ScreenUtil::GetMaximizedWindowBoundsInParent(
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      window_state->window()).size();
38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  aura::WindowDelegate* delegate = window_state->window()->delegate();
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!delegate)
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return workspace_size;
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Size size = delegate->GetMaximumSize();
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (size.IsEmpty())
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return workspace_size;
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  size.SetToMin(workspace_size);
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return size;
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns the centered bounds of the given bounds in the work area.
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)gfx::Rect GetCenteredBounds(const gfx::Rect& bounds_in_parent,
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            wm::WindowState* state_object) {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Rect work_area_in_parent =
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ScreenUtil::GetDisplayWorkAreaBoundsInParent(state_object->window());
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  work_area_in_parent.ClampToCenteredSize(bounds_in_parent.size());
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return work_area_in_parent;
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns the maximized/full screen and/or centered bounds of a window.
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)gfx::Rect GetBoundsInMaximizedMode(wm::WindowState* state_object) {
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_object->IsFullscreen())
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return ScreenUtil::GetDisplayBoundsInParent(state_object->window());
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Rect bounds_in_parent;
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Make the window as big as possible.
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (state_object->CanMaximize() || state_object->CanResize()) {
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    bounds_in_parent.set_size(GetMaximumSizeOfWindow(state_object));
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // We prefer the user given window dimensions over the current windows
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // dimensions since they are likely to be the result from some other state
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // object logic.
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (state_object->HasRestoreBounds())
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bounds_in_parent = state_object->GetRestoreBoundsInParent();
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bounds_in_parent = state_object->window()->bounds();
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return GetCenteredBounds(bounds_in_parent, state_object);
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MaximizeModeWindowState::UpdateWindowPosition(
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    wm::WindowState* window_state, bool animated) {
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (bounds_in_parent == window_state->window()->bounds())
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (animated)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    window_state->SetBoundsDirect(bounds_in_parent);
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  else
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    window_state->SetBoundsDirectAnimated(bounds_in_parent);
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MaximizeModeWindowState::MaximizeModeWindowState(
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    aura::Window* window, MaximizeModeWindowManager* creator)
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : window_(window),
100      creator_(creator),
101      current_state_type_(wm::GetWindowState(window)->GetStateType()) {
102  old_state_.reset(
103      wm::GetWindowState(window)->SetStateObject(
104          scoped_ptr<State>(this).Pass()).release());
105}
106
107MaximizeModeWindowState::~MaximizeModeWindowState() {
108  creator_->WindowStateDestroyed(window_);
109}
110
111void MaximizeModeWindowState::LeaveMaximizeMode(wm::WindowState* window_state) {
112  // Note: When we return we will destroy ourselves with the |our_reference|.
113  scoped_ptr<wm::WindowState::State> our_reference =
114      window_state->SetStateObject(old_state_.Pass());
115}
116
117void MaximizeModeWindowState::OnWMEvent(wm::WindowState* window_state,
118                                        const wm::WMEvent* event) {
119  switch (event->type()) {
120    case wm::WM_EVENT_TOGGLE_FULLSCREEN:
121      ToggleFullScreen(window_state, window_state->delegate());
122      break;
123    case wm::WM_EVENT_FULLSCREEN:
124      UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_FULLSCREEN, true);
125      break;
126    case wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
127    case wm::WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
128    case wm::WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
129    case wm::WM_EVENT_TOGGLE_MAXIMIZE:
130    case wm::WM_EVENT_CENTER:
131    case wm::WM_EVENT_SNAP_LEFT:
132    case wm::WM_EVENT_SNAP_RIGHT:
133    case wm::WM_EVENT_NORMAL:
134    case wm::WM_EVENT_MAXIMIZE:
135      UpdateWindow(window_state,
136                   GetMaximizedOrCenteredWindowType(window_state),
137                   true);
138      return;
139    case wm::WM_EVENT_MINIMIZE:
140      UpdateWindow(window_state, wm::WINDOW_STATE_TYPE_MINIMIZED, true);
141      return;
142    case wm::WM_EVENT_SHOW_INACTIVE:
143      return;
144    case wm::WM_EVENT_SET_BOUNDS:
145      if (current_state_type_ == wm::WINDOW_STATE_TYPE_MAXIMIZED) {
146        // Having a maximized window, it could have been created with an empty
147        // size and the caller should get his size upon leaving the maximized
148        // mode. As such we set the restore bounds to the requested bounds.
149        gfx::Rect bounds_in_parent =
150            (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds();
151        if (!bounds_in_parent.IsEmpty())
152          window_state->SetRestoreBoundsInParent(bounds_in_parent);
153      } else if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED &&
154                 current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
155                 current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) {
156        // In all other cases (except for minimized windows) we respect the
157        // requested bounds and center it to a fully visible area on the screen.
158        gfx::Rect bounds_in_parent =
159            (static_cast<const wm::SetBoundsEvent*>(event))->requested_bounds();
160        bounds_in_parent = GetCenteredBounds(bounds_in_parent, window_state);
161        if (bounds_in_parent != window_state->window()->bounds()) {
162          if (window_state->window()->IsVisible())
163            window_state->SetBoundsDirectAnimated(bounds_in_parent);
164          else
165            window_state->SetBoundsDirect(bounds_in_parent);
166        }
167      }
168      break;
169    case wm::WM_EVENT_ADDED_TO_WORKSPACE:
170      if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
171          current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN &&
172          current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED) {
173        wm::WindowStateType new_state =
174            GetMaximizedOrCenteredWindowType(window_state);
175        UpdateWindow(window_state, new_state, true);
176      }
177      break;
178    case wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED:
179    case wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED:
180      if (current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED)
181        UpdateBounds(window_state, true);
182      break;
183  }
184}
185
186wm::WindowStateType MaximizeModeWindowState::GetType() const {
187  return current_state_type_;
188}
189
190void MaximizeModeWindowState::AttachState(
191    wm::WindowState* window_state,
192    wm::WindowState::State* previous_state) {
193  current_state_type_ = previous_state->GetType();
194
195  views::Widget* widget =
196      views::Widget::GetWidgetForNativeWindow(window_state->window());
197  if (widget) {
198    gfx::Rect bounds = widget->GetRestoredBounds();
199    if (!bounds.IsEmpty()) {
200      // We do not want to do a session restore to our window states. Therefore
201      // we tell the window to use the current default states instead.
202      window_state->window()->SetProperty(ash::kRestoreShowStateOverrideKey,
203                                          window_state->GetShowState());
204      window_state->window()->SetProperty(ash::kRestoreBoundsOverrideKey,
205          new gfx::Rect(widget->GetRestoredBounds()));
206    }
207  }
208
209  // Initialize the state to a good preset.
210  if (current_state_type_ != wm::WINDOW_STATE_TYPE_MAXIMIZED &&
211      current_state_type_ != wm::WINDOW_STATE_TYPE_MINIMIZED &&
212      current_state_type_ != wm::WINDOW_STATE_TYPE_FULLSCREEN) {
213    UpdateWindow(window_state,
214                 GetMaximizedOrCenteredWindowType(window_state),
215                 true);
216  }
217
218  window_state->set_can_be_dragged(false);
219}
220
221void MaximizeModeWindowState::DetachState(wm::WindowState* window_state) {
222  // From now on, we can use the default session restore mechanism again.
223  window_state->window()->ClearProperty(ash::kRestoreBoundsOverrideKey);
224  window_state->set_can_be_dragged(true);
225}
226
227void MaximizeModeWindowState::UpdateWindow(wm::WindowState* window_state,
228                                           wm::WindowStateType target_state,
229                                           bool animated) {
230  DCHECK(target_state == wm::WINDOW_STATE_TYPE_MINIMIZED ||
231         target_state == wm::WINDOW_STATE_TYPE_MAXIMIZED ||
232         (target_state == wm::WINDOW_STATE_TYPE_NORMAL &&
233              !window_state->CanMaximize()) ||
234         target_state == wm::WINDOW_STATE_TYPE_FULLSCREEN);
235
236  if (target_state == wm::WINDOW_STATE_TYPE_MINIMIZED) {
237    if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED)
238      return;
239
240    current_state_type_ = target_state;
241    ::wm::SetWindowVisibilityAnimationType(
242        window_state->window(), WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
243    window_state->window()->Hide();
244    if (window_state->IsActive())
245      window_state->Deactivate();
246    return;
247  }
248
249  if (current_state_type_ == target_state) {
250    // If the state type did not change, update it accordingly.
251    UpdateBounds(window_state, animated);
252    return;
253  }
254
255  const wm::WindowStateType old_state_type = current_state_type_;
256  current_state_type_ = target_state;
257  window_state->UpdateWindowShowStateFromStateType();
258  window_state->NotifyPreStateTypeChange(old_state_type);
259  UpdateBounds(window_state, animated);
260  window_state->NotifyPostStateTypeChange(old_state_type);
261
262  if ((window_state->window()->TargetVisibility() ||
263      old_state_type == wm::WINDOW_STATE_TYPE_MINIMIZED) &&
264      !window_state->window()->layer()->visible()) {
265    // The layer may be hidden if the window was previously minimized. Make
266    // sure it's visible.
267    window_state->window()->Show();
268  }
269}
270
271wm::WindowStateType MaximizeModeWindowState::GetMaximizedOrCenteredWindowType(
272      wm::WindowState* window_state) {
273  return window_state->CanMaximize() ? wm::WINDOW_STATE_TYPE_MAXIMIZED :
274                                       wm::WINDOW_STATE_TYPE_NORMAL;
275}
276
277void MaximizeModeWindowState::UpdateBounds(wm::WindowState* window_state,
278                                           bool animated) {
279  gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
280  // If we have a target bounds rectangle, we center it and set it
281  // accordingly.
282  if (!bounds_in_parent.IsEmpty() &&
283      bounds_in_parent != window_state->window()->bounds()) {
284    if (current_state_type_ == wm::WINDOW_STATE_TYPE_MINIMIZED ||
285        !window_state->window()->IsVisible() ||
286        !animated) {
287      window_state->SetBoundsDirect(bounds_in_parent);
288    } else {
289      // If we animate (to) maximized mode, we want to use the cross fade to
290      // avoid flashing.
291      if (window_state->IsMaximized())
292        window_state->SetBoundsDirectCrossFade(bounds_in_parent);
293      else
294        window_state->SetBoundsDirectAnimated(bounds_in_parent);
295    }
296  }
297}
298
299}  // namespace ash
300