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.
5 #include "ash/wm/workspace/workspace_layout_manager.h"
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"
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;
43 WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
46 root_window_(window->GetRootWindow()),
47 work_area_in_parent_(ScreenUtil::ConvertRectFromScreen(
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);
57 WorkspaceLayoutManager::~WorkspaceLayoutManager() {
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);
66 void WorkspaceLayoutManager::SetShelf(internal::ShelfLayoutManager* shelf) {
70 //////////////////////////////////////////////////////////////////////////////
71 // WorkspaceLayoutManager, aura::LayoutManager implementation:
73 void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
74 wm::WindowState* window_state = wm::GetWindowState(child);
75 AdjustWindowBoundsWhenAdded(window_state);
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);
90 void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) {
91 windows_.erase(child);
92 child->RemoveObserver(this);
93 wm::GetWindowState(child)->RemoveObserver(this);
95 if (child->TargetVisibility())
96 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
99 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
100 UpdateShelfVisibility();
101 UpdateFullscreenState();
104 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
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();
111 if (child->TargetVisibility()) {
112 WindowPositioner::RearrangeVisibleWindowOnShow(child);
114 if (wm::GetWindowState(child)->IsFullscreen())
115 UpdateFullscreenState();
116 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
118 UpdateShelfVisibility();
121 void WorkspaceLayoutManager::SetChildBounds(
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
136 gfx::Rect child_bounds(requested_bounds);
137 wm::AdjustBoundsSmallerThan(work_area_in_parent_.size(), &child_bounds);
138 SetChildBoundsDirect(child, child_bounds);
140 UpdateShelfVisibility();
143 //////////////////////////////////////////////////////////////////////////////
144 // WorkspaceLayoutManager, ash::ShellObserver implementation:
146 void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
147 const gfx::Rect work_area(ScreenUtil::ConvertRectFromScreen(
149 Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area()));
150 if (work_area != work_area_in_parent_) {
151 AdjustAllWindowsBoundsForWorkAreaChange(
152 ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED);
156 //////////////////////////////////////////////////////////////////////////////
157 // WorkspaceLayoutManager, aura::WindowObserver implementation:
159 void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window,
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);
169 void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) {
170 UpdateShelfVisibility();
171 UpdateFullscreenState();
174 void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) {
175 if (root_window_ == window) {
176 root_window_->RemoveObserver(this);
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);
188 //////////////////////////////////////////////////////////////////////////////
189 // WorkspaceLayoutManager,
190 // aura::client::ActivationChangeObserver implementation:
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());
202 //////////////////////////////////////////////////////////////////////////////
203 // WorkspaceLayoutManager, wm::WindowStateObserver implementation:
205 void WorkspaceLayoutManager::OnPostWindowShowTypeChange(
206 wm::WindowState* window_state,
207 wm::WindowShowType old_type) {
209 // Notify observers that fullscreen state may be changing.
210 if (window_state->IsFullscreen() || old_type == wm::SHOW_TYPE_FULLSCREEN)
211 UpdateFullscreenState();
213 UpdateShelfVisibility();
216 //////////////////////////////////////////////////////////////////////////////
217 // WorkspaceLayoutManager, private:
219 void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
220 AdjustWindowReason reason) {
221 work_area_in_parent_ = ScreenUtil::ConvertRectFromScreen(
223 Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area());
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())
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
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();
240 AdjustWindowBoundsForWorkAreaChange(wm::GetWindowState(*it), reason);
244 void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange(
245 wm::WindowState* window_state,
246 AdjustWindowReason reason) {
247 if (window_state->is_dragged())
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()));
262 if (SetMaximizedOrFullscreenBounds(window_state))
265 gfx::Rect bounds = window_state->window()->bounds();
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_);
272 case ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED:
273 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(
274 work_area_in_parent_, &bounds);
277 window_state->AdjustSnappedBounds(&bounds);
278 if (window_state->window()->bounds() != bounds)
279 window_state->SetBoundsDirectAnimated(bounds);
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)) {
295 Window* window = window_state->window();
296 gfx::Rect bounds = window->bounds();
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
302 gfx::Rect display_area = ScreenUtil::GetDisplayBoundsInParent(window);
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);
313 void WorkspaceLayoutManager::UpdateShelfVisibility() {
315 shelf_->UpdateVisibilityState();
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;
328 bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
329 wm::WindowState* window_state) {
330 DCHECK(!window_state->is_dragged());
332 if (window_state->IsMaximized()) {
333 SetChildBoundsDirect(
334 window_state->window(), ScreenUtil::GetMaximizedWindowBoundsInParent(
335 window_state->window()));
338 if (window_state->IsFullscreen()) {
339 SetChildBoundsDirect(
340 window_state->window(),
341 ScreenUtil::GetDisplayBoundsInParent(window_state->window()));
347 } // namespace internal