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