workspace_layout_manager.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 <algorithm> 8 9#include "ash/display/display_controller.h" 10#include "ash/root_window_controller.h" 11#include "ash/screen_util.h" 12#include "ash/session/session_state_delegate.h" 13#include "ash/shelf/shelf_layout_manager.h" 14#include "ash/shell.h" 15#include "ash/wm/always_on_top_controller.h" 16#include "ash/wm/window_animations.h" 17#include "ash/wm/window_positioner.h" 18#include "ash/wm/window_properties.h" 19#include "ash/wm/window_state.h" 20#include "ash/wm/window_util.h" 21#include "ash/wm/wm_event.h" 22#include "ash/wm/workspace/workspace_layout_manager_delegate.h" 23#include "ui/aura/client/aura_constants.h" 24#include "ui/aura/window.h" 25#include "ui/aura/window_observer.h" 26#include "ui/base/ime/input_method.h" 27#include "ui/base/ime/text_input_client.h" 28#include "ui/base/ui_base_types.h" 29#include "ui/compositor/layer.h" 30#include "ui/events/event.h" 31#include "ui/gfx/screen.h" 32#include "ui/keyboard/keyboard_controller_observer.h" 33#include "ui/wm/core/window_util.h" 34#include "ui/wm/public/activation_client.h" 35 36using aura::Window; 37 38namespace ash { 39 40WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window) 41 : shelf_(NULL), 42 window_(window), 43 root_window_(window->GetRootWindow()), 44 work_area_in_parent_(ScreenUtil::ConvertRectFromScreen( 45 window_, 46 Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area())), 47 is_fullscreen_(GetRootWindowController( 48 window->GetRootWindow())->GetWindowForFullscreenMode() != NULL) { 49 Shell::GetInstance()->activation_client()->AddObserver(this); 50 Shell::GetInstance()->AddShellObserver(this); 51 root_window_->AddObserver(this); 52 DCHECK(window->GetProperty(kSnapChildrenToPixelBoundary)); 53} 54 55WorkspaceLayoutManager::~WorkspaceLayoutManager() { 56 if (root_window_) 57 root_window_->RemoveObserver(this); 58 for (WindowSet::const_iterator i = windows_.begin(); i != windows_.end(); ++i) 59 (*i)->RemoveObserver(this); 60 Shell::GetInstance()->RemoveShellObserver(this); 61 Shell::GetInstance()->activation_client()->RemoveObserver(this); 62} 63 64void WorkspaceLayoutManager::SetShelf(ShelfLayoutManager* shelf) { 65 shelf_ = shelf; 66} 67 68void WorkspaceLayoutManager::SetMaximizeBackdropDelegate( 69 scoped_ptr<WorkspaceLayoutManagerDelegate> delegate) { 70 backdrop_delegate_.reset(delegate.release()); 71} 72 73////////////////////////////////////////////////////////////////////////////// 74// WorkspaceLayoutManager, aura::LayoutManager implementation: 75 76void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) { 77 wm::WindowState* window_state = wm::GetWindowState(child); 78 wm::WMEvent event(wm::WM_EVENT_ADDED_TO_WORKSPACE); 79 window_state->OnWMEvent(&event); 80 windows_.insert(child); 81 child->AddObserver(this); 82 window_state->AddObserver(this); 83 UpdateShelfVisibility(); 84 UpdateFullscreenState(); 85 if (backdrop_delegate_) 86 backdrop_delegate_->OnWindowAddedToLayout(child); 87 WindowPositioner::RearrangeVisibleWindowOnShow(child); 88} 89 90void 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 99void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) { 100 UpdateShelfVisibility(); 101 UpdateFullscreenState(); 102 if (backdrop_delegate_) 103 backdrop_delegate_->OnWindowRemovedFromLayout(child); 104} 105 106void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child, 107 bool visible) { 108 wm::WindowState* window_state = wm::GetWindowState(child); 109 // Attempting to show a minimized window. Unminimize it. 110 if (visible && window_state->IsMinimized()) 111 window_state->Unminimize(); 112 113 if (child->TargetVisibility()) 114 WindowPositioner::RearrangeVisibleWindowOnShow(child); 115 else 116 WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child); 117 UpdateFullscreenState(); 118 UpdateShelfVisibility(); 119 if (backdrop_delegate_) 120 backdrop_delegate_->OnChildWindowVisibilityChanged(child, visible); 121} 122 123void WorkspaceLayoutManager::SetChildBounds( 124 Window* child, 125 const gfx::Rect& requested_bounds) { 126 wm::WindowState* window_state = wm::GetWindowState(child); 127 wm::SetBoundsEvent event(wm::WM_EVENT_SET_BOUNDS, requested_bounds); 128 window_state->OnWMEvent(&event); 129 UpdateShelfVisibility(); 130} 131 132////////////////////////////////////////////////////////////////////////////// 133// WorkspaceLayoutManager, keyboard::KeyboardControllerObserver implementation: 134 135void WorkspaceLayoutManager::OnKeyboardBoundsChanging( 136 const gfx::Rect& new_bounds) { 137 aura::Window* root_window = window_->GetRootWindow(); 138 ui::InputMethod* input_method = 139 root_window->GetProperty(aura::client::kRootWindowInputMethodKey); 140 ui::TextInputClient* text_input_client = input_method->GetTextInputClient(); 141 if (!text_input_client) 142 return; 143 aura::Window *window = text_input_client->GetAttachedWindow(); 144 if (!window || !window_->Contains(window)) 145 return; 146 aura::Window *toplevel_window = window->GetToplevelWindow(); 147 wm::WindowState* toplevel_window_state = wm::GetWindowState(toplevel_window); 148 if (!new_bounds.IsEmpty()) { 149 // Store existing bounds to be restored before resizing for keyboard if it 150 // is not already stored. 151 if (!toplevel_window_state->HasRestoreBounds()) 152 toplevel_window_state->SaveCurrentBoundsForRestore(); 153 154 gfx::Rect window_bounds = ScreenUtil::ConvertRectToScreen( 155 window_, 156 window->GetTargetBounds()); 157 gfx::Rect intersect = gfx::IntersectRects(window_bounds, new_bounds); 158 int shift = std::min(intersect.height(), 159 window->bounds().y() - work_area_in_parent_.y()); 160 if (shift > 0) { 161 gfx::Point origin(window->bounds().x(), window->bounds().y() - shift); 162 SetChildBounds(window, gfx::Rect(origin, window->bounds().size())); 163 } 164 } else if (toplevel_window_state->HasRestoreBounds()) { 165 // Keyboard hidden, restore original bounds if they exist. 166 toplevel_window_state->SetAndClearRestoreBounds(); 167 } 168} 169 170////////////////////////////////////////////////////////////////////////////// 171// WorkspaceLayoutManager, ash::ShellObserver implementation: 172 173void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() { 174 const gfx::Rect work_area(ScreenUtil::ConvertRectFromScreen( 175 window_, 176 Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area())); 177 if (work_area != work_area_in_parent_) { 178 const wm::WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); 179 AdjustAllWindowsBoundsForWorkAreaChange(&event); 180 } 181 if (backdrop_delegate_) 182 backdrop_delegate_->OnDisplayWorkAreaInsetsChanged(); 183} 184 185////////////////////////////////////////////////////////////////////////////// 186// WorkspaceLayoutManager, aura::WindowObserver implementation: 187 188void WorkspaceLayoutManager::OnWindowHierarchyChanged( 189 const WindowObserver::HierarchyChangeParams& params) { 190 if (!wm::GetWindowState(params.target)->IsActive()) 191 return; 192 // If the window is already tracked by the workspace this update would be 193 // redundant as the fullscreen and shelf state would have been handled in 194 // OnWindowAddedToLayout. 195 if (windows_.find(params.target) != windows_.end()) 196 return; 197 198 // If the active window has moved to this root window then update the 199 // fullscreen state. 200 // TODO(flackr): Track the active window leaving this root window and update 201 // the fullscreen state accordingly. 202 if (params.new_parent && params.new_parent->GetRootWindow() == root_window_) { 203 UpdateFullscreenState(); 204 UpdateShelfVisibility(); 205 } 206} 207 208void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window, 209 const void* key, 210 intptr_t old) { 211 if (key == aura::client::kAlwaysOnTopKey && 212 window->GetProperty(aura::client::kAlwaysOnTopKey)) { 213 GetRootWindowController(window->GetRootWindow())-> 214 always_on_top_controller()->GetContainer(window)->AddChild(window); 215 } 216} 217 218void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) { 219 UpdateShelfVisibility(); 220 UpdateFullscreenState(); 221 if (backdrop_delegate_) 222 backdrop_delegate_->OnWindowStackingChanged(window); 223} 224 225void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) { 226 if (root_window_ == window) { 227 root_window_->RemoveObserver(this); 228 root_window_ = NULL; 229 } 230} 231 232void WorkspaceLayoutManager::OnWindowBoundsChanged(aura::Window* window, 233 const gfx::Rect& old_bounds, 234 const gfx::Rect& new_bounds) { 235 if (root_window_ == window) { 236 const wm::WMEvent wm_event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED); 237 AdjustAllWindowsBoundsForWorkAreaChange(&wm_event); 238 } 239} 240 241////////////////////////////////////////////////////////////////////////////// 242// WorkspaceLayoutManager, 243// aura::client::ActivationChangeObserver implementation: 244 245void WorkspaceLayoutManager::OnWindowActivated(aura::Window* gained_active, 246 aura::Window* lost_active) { 247 wm::WindowState* window_state = wm::GetWindowState(gained_active); 248 if (window_state && window_state->IsMinimized() && 249 !gained_active->IsVisible()) { 250 window_state->Unminimize(); 251 DCHECK(!window_state->IsMinimized()); 252 } 253 UpdateFullscreenState(); 254 UpdateShelfVisibility(); 255} 256 257////////////////////////////////////////////////////////////////////////////// 258// WorkspaceLayoutManager, wm::WindowStateObserver implementation: 259 260void WorkspaceLayoutManager::OnPostWindowStateTypeChange( 261 wm::WindowState* window_state, 262 wm::WindowStateType old_type) { 263 264 // Notify observers that fullscreen state may be changing. 265 if (window_state->IsFullscreen() || 266 old_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) { 267 UpdateFullscreenState(); 268 } 269 270 UpdateShelfVisibility(); 271 if (backdrop_delegate_) 272 backdrop_delegate_->OnPostWindowStateTypeChange(window_state, old_type); 273} 274 275////////////////////////////////////////////////////////////////////////////// 276// WorkspaceLayoutManager, private: 277 278void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange( 279 const wm::WMEvent* event) { 280 DCHECK(event->type() == wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED || 281 event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED); 282 283 work_area_in_parent_ = ScreenUtil::ConvertRectFromScreen( 284 window_, 285 Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area()); 286 287 // Don't do any adjustments of the insets while we are in screen locked mode. 288 // This would happen if the launcher was auto hidden before the login screen 289 // was shown and then gets shown when the login screen gets presented. 290 if (event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED && 291 Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) 292 return; 293 294 // If a user plugs an external display into a laptop running Aura the 295 // display size will change. Maximized windows need to resize to match. 296 // We also do this when developers running Aura on a desktop manually resize 297 // the host window. 298 // We also need to do this when the work area insets changes. 299 for (WindowSet::const_iterator it = windows_.begin(); 300 it != windows_.end(); 301 ++it) { 302 wm::GetWindowState(*it)->OnWMEvent(event); 303 } 304} 305 306void WorkspaceLayoutManager::UpdateShelfVisibility() { 307 if (shelf_) 308 shelf_->UpdateVisibilityState(); 309} 310 311void WorkspaceLayoutManager::UpdateFullscreenState() { 312 // TODO(flackr): The fullscreen state is currently tracked per workspace 313 // but the shell notification implies a per root window state. Currently 314 // only windows in the default workspace container will go fullscreen but 315 // this should really be tracked by the RootWindowController since 316 // technically any container could get a fullscreen window. 317 if (!shelf_) 318 return; 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} // namespace ash 329