Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ash / wm / workspace / workspace_layout_manager.cc
1 // Copyright (c) 2012 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/workspace/workspace_layout_manager.h"
6
7 #include "ash/display/display_controller.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/screen_util.h"
10 #include "ash/session_state_delegate.h"
11 #include "ash/shelf/shelf_layout_manager.h"
12 #include "ash/shell.h"
13 #include "ash/wm/always_on_top_controller.h"
14 #include "ash/wm/window_animations.h"
15 #include "ash/wm/window_positioner.h"
16 #include "ash/wm/window_properties.h"
17 #include "ash/wm/window_state.h"
18 #include "ash/wm/window_util.h"
19 #include "ui/aura/client/activation_client.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_observer.h"
23 #include "ui/base/ui_base_types.h"
24 #include "ui/compositor/layer.h"
25 #include "ui/events/event.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/views/corewm/window_util.h"
28
29 using aura::Window;
30
31 namespace ash {
32
33 namespace internal {
34
35 namespace {
36
37 // This specifies how much percent 30% of a window rect (width / height)
38 // must be visible when the window is added to the workspace.
39 const float kMinimumPercentOnScreenArea = 0.3f;
40
41 }  // namespace
42
43 WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
44     : shelf_(NULL),
45       window_(window),
46       root_window_(window->GetRootWindow()),
47       work_area_in_parent_(ScreenUtil::ConvertRectFromScreen(
48           window_,
49           Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area())),
50       is_fullscreen_(GetRootWindowController(
51           window->GetRootWindow())->GetWindowForFullscreenMode() != NULL) {
52   Shell::GetInstance()->activation_client()->AddObserver(this);
53   Shell::GetInstance()->AddShellObserver(this);
54   root_window_->AddObserver(this);
55 }
56
57 WorkspaceLayoutManager::~WorkspaceLayoutManager() {
58   if (root_window_)
59     root_window_->RemoveObserver(this);
60   for (WindowSet::const_iterator i = windows_.begin(); i != windows_.end(); ++i)
61     (*i)->RemoveObserver(this);
62   Shell::GetInstance()->RemoveShellObserver(this);
63   Shell::GetInstance()->activation_client()->RemoveObserver(this);
64 }
65
66 void WorkspaceLayoutManager::SetShelf(internal::ShelfLayoutManager* shelf) {
67   shelf_ = shelf;
68 }
69
70 //////////////////////////////////////////////////////////////////////////////
71 // WorkspaceLayoutManager, aura::LayoutManager implementation:
72
73 void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
74   wm::WindowState* window_state = wm::GetWindowState(child);
75   AdjustWindowBoundsWhenAdded(window_state);
76
77   windows_.insert(child);
78   child->AddObserver(this);
79   window_state->AddObserver(this);
80   // TODO(oshima): This is necessary as the call in
81   // AdjustWindowBoundsWhenAdded is skipped when the bounds is
82   // empty. Investigate if we can eliminate this dup.
83   if (!window_state->is_dragged())
84     SetMaximizedOrFullscreenBounds(window_state);
85   UpdateShelfVisibility();
86   UpdateFullscreenState();
87   WindowPositioner::RearrangeVisibleWindowOnShow(child);
88 }
89
90 void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) {
91   windows_.erase(child);
92   child->RemoveObserver(this);
93   wm::GetWindowState(child)->RemoveObserver(this);
94
95   if (child->TargetVisibility())
96     WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
97 }
98
99 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
100   UpdateShelfVisibility();
101   UpdateFullscreenState();
102 }
103
104 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
105                                                             bool visible) {
106   wm::WindowState* window_state = wm::GetWindowState(child);
107   // Attempting to show a minimized window. Unminimize it.
108   if (visible && window_state->IsMinimized())
109     window_state->Unminimize();
110
111   if (child->TargetVisibility()) {
112     WindowPositioner::RearrangeVisibleWindowOnShow(child);
113   } else {
114     if (wm::GetWindowState(child)->IsFullscreen())
115       UpdateFullscreenState();
116     WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
117   }
118   UpdateShelfVisibility();
119 }
120
121 void WorkspaceLayoutManager::SetChildBounds(
122     Window* child,
123     const gfx::Rect& requested_bounds) {
124   wm::WindowState* window_state = wm::GetWindowState(child);
125   if (window_state->is_dragged()) {
126     SetChildBoundsDirect(child, requested_bounds);
127   } else if (window_state->IsSnapped()) {
128     gfx::Rect child_bounds(requested_bounds);
129     wm::AdjustBoundsSmallerThan(work_area_in_parent_.size(), &child_bounds);
130     window_state->AdjustSnappedBounds(&child_bounds);
131     SetChildBoundsDirect(child, child_bounds);
132   } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
133     // Some windows rely on this to set their initial bounds.
134     // Non-maximized/full-screen windows have their size constrained to the
135     // work-area.
136     gfx::Rect child_bounds(requested_bounds);
137     wm::AdjustBoundsSmallerThan(work_area_in_parent_.size(), &child_bounds);
138     SetChildBoundsDirect(child, child_bounds);
139   }
140   UpdateShelfVisibility();
141 }
142
143 //////////////////////////////////////////////////////////////////////////////
144 // WorkspaceLayoutManager, ash::ShellObserver implementation:
145
146 void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
147   const gfx::Rect work_area(ScreenUtil::ConvertRectFromScreen(
148       window_,
149       Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area()));
150   if (work_area != work_area_in_parent_) {
151     AdjustAllWindowsBoundsForWorkAreaChange(
152         ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED);
153   }
154 }
155
156 //////////////////////////////////////////////////////////////////////////////
157 // WorkspaceLayoutManager, aura::WindowObserver implementation:
158
159 void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window,
160                                                      const void* key,
161                                                      intptr_t old) {
162   if (key == aura::client::kAlwaysOnTopKey &&
163       window->GetProperty(aura::client::kAlwaysOnTopKey)) {
164     GetRootWindowController(window->GetRootWindow())->
165         always_on_top_controller()->GetContainer(window)->AddChild(window);
166   }
167 }
168
169 void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) {
170   UpdateShelfVisibility();
171   UpdateFullscreenState();
172 }
173
174 void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) {
175   if (root_window_ == window) {
176     root_window_->RemoveObserver(this);
177     root_window_ = NULL;
178   }
179 }
180
181 void WorkspaceLayoutManager::OnWindowBoundsChanged(aura::Window* window,
182                                               const gfx::Rect& old_bounds,
183                                               const gfx::Rect& new_bounds) {
184   if (root_window_ == window)
185     AdjustAllWindowsBoundsForWorkAreaChange(ADJUST_WINDOW_DISPLAY_SIZE_CHANGED);
186 }
187
188 //////////////////////////////////////////////////////////////////////////////
189 // WorkspaceLayoutManager,
190 // aura::client::ActivationChangeObserver implementation:
191
192 void WorkspaceLayoutManager::OnWindowActivated(aura::Window* gained_active,
193                                           aura::Window* lost_active) {
194   wm::WindowState* window_state = wm::GetWindowState(gained_active);
195   if (window_state && window_state->IsMinimized() &&
196       !gained_active->IsVisible()) {
197     window_state->Unminimize();
198     DCHECK(!window_state->IsMinimized());
199   }
200 }
201
202 //////////////////////////////////////////////////////////////////////////////
203 // WorkspaceLayoutManager, wm::WindowStateObserver implementation:
204
205 void WorkspaceLayoutManager::OnPostWindowShowTypeChange(
206     wm::WindowState* window_state,
207     wm::WindowShowType old_type) {
208
209   // Notify observers that fullscreen state may be changing.
210   if (window_state->IsFullscreen() || old_type == wm::SHOW_TYPE_FULLSCREEN)
211     UpdateFullscreenState();
212
213   UpdateShelfVisibility();
214 }
215
216 //////////////////////////////////////////////////////////////////////////////
217 // WorkspaceLayoutManager, private:
218
219 void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
220     AdjustWindowReason reason) {
221   work_area_in_parent_ = ScreenUtil::ConvertRectFromScreen(
222       window_,
223       Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area());
224
225   // Don't do any adjustments of the insets while we are in screen locked mode.
226   // This would happen if the launcher was auto hidden before the login screen
227   // was shown and then gets shown when the login screen gets presented.
228   if (reason == ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED &&
229       Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
230     return;
231
232   // If a user plugs an external display into a laptop running Aura the
233   // display size will change.  Maximized windows need to resize to match.
234   // We also do this when developers running Aura on a desktop manually resize
235   // the host window.
236   // We also need to do this when the work area insets changes.
237   for (WindowSet::const_iterator it = windows_.begin();
238        it != windows_.end();
239        ++it) {
240     AdjustWindowBoundsForWorkAreaChange(wm::GetWindowState(*it), reason);
241   }
242 }
243
244 void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange(
245     wm::WindowState* window_state,
246     AdjustWindowReason reason) {
247   if (window_state->is_dragged())
248     return;
249
250   // Do not cross fade here: the window's layer hierarchy may be messed up for
251   // the transition between mirroring and extended. See also: crbug.com/267698
252   // TODO(oshima): Differentiate display change and shelf visibility change, and
253   // bring back CrossFade animation.
254   if (window_state->IsMaximized() &&
255       reason == ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED) {
256     SetChildBoundsDirect(window_state->window(),
257                          ScreenUtil::GetMaximizedWindowBoundsInParent(
258                              window_state->window()));
259     return;
260   }
261
262   if (SetMaximizedOrFullscreenBounds(window_state))
263     return;
264
265   gfx::Rect bounds = window_state->window()->bounds();
266   switch (reason) {
267     case ADJUST_WINDOW_DISPLAY_SIZE_CHANGED:
268       // The work area may be smaller than the full screen.  Put as much of the
269       // window as possible within the display area.
270       bounds.AdjustToFit(work_area_in_parent_);
271       break;
272     case ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED:
273       ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(
274           work_area_in_parent_, &bounds);
275       break;
276   }
277   window_state->AdjustSnappedBounds(&bounds);
278   if (window_state->window()->bounds() != bounds)
279     window_state->SetBoundsDirectAnimated(bounds);
280 }
281
282 void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded(
283     wm::WindowState* window_state) {
284   // Don't adjust window bounds if the bounds are empty as this
285   // happens when a new views::Widget is created.
286   // When a window is dragged and dropped onto a different
287   // root window, the bounds will be updated after they are added
288   // to the root window.
289   if (window_state->window()->bounds().IsEmpty() ||
290       window_state->is_dragged() ||
291       SetMaximizedOrFullscreenBounds(window_state)) {
292     return;
293   }
294
295   Window* window = window_state->window();
296   gfx::Rect bounds = window->bounds();
297
298   // Use entire display instead of workarea because the workarea can
299   // be further shrunk by the docked area. The logic ensures 30%
300   // visibility which should be enough to see where the window gets
301   // moved.
302   gfx::Rect display_area = ScreenUtil::GetDisplayBoundsInParent(window);
303
304   int min_width = bounds.width() * kMinimumPercentOnScreenArea;
305   int min_height = bounds.height() * kMinimumPercentOnScreenArea;
306   ash::wm::AdjustBoundsToEnsureWindowVisibility(
307       display_area, min_width, min_height, &bounds);
308   window_state->AdjustSnappedBounds(&bounds);
309   if (window->bounds() != bounds)
310     window->SetBounds(bounds);
311 }
312
313 void WorkspaceLayoutManager::UpdateShelfVisibility() {
314   if (shelf_)
315     shelf_->UpdateVisibilityState();
316 }
317
318 void WorkspaceLayoutManager::UpdateFullscreenState() {
319   bool is_fullscreen = GetRootWindowController(
320       window_->GetRootWindow())->GetWindowForFullscreenMode() != NULL;
321   if (is_fullscreen != is_fullscreen_) {
322     ash::Shell::GetInstance()->NotifyFullscreenStateChange(
323         is_fullscreen, window_->GetRootWindow());
324     is_fullscreen_ = is_fullscreen;
325   }
326 }
327
328 bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
329     wm::WindowState* window_state) {
330   DCHECK(!window_state->is_dragged());
331
332   if (window_state->IsMaximized()) {
333     SetChildBoundsDirect(
334         window_state->window(), ScreenUtil::GetMaximizedWindowBoundsInParent(
335             window_state->window()));
336     return true;
337   }
338   if (window_state->IsFullscreen()) {
339     SetChildBoundsDirect(
340         window_state->window(),
341         ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
342     return true;
343   }
344   return false;
345 }
346
347 }  // namespace internal
348 }  // namespace ash