1 // Copyright 2014 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 "athena/wm/window_manager_impl.h" 6 7 #include <algorithm> 8 9 #include "athena/screen/public/screen_manager.h" 10 #include "athena/util/container_priorities.h" 11 #include "athena/wm/bezel_controller.h" 12 #include "athena/wm/public/window_manager_observer.h" 13 #include "athena/wm/split_view_controller.h" 14 #include "athena/wm/title_drag_controller.h" 15 #include "athena/wm/window_list_provider_impl.h" 16 #include "athena/wm/window_overview_mode.h" 17 #include "base/bind.h" 18 #include "base/logging.h" 19 #include "ui/aura/layout_manager.h" 20 #include "ui/aura/window.h" 21 #include "ui/compositor/closure_animation_observer.h" 22 #include "ui/compositor/scoped_layer_animation_settings.h" 23 #include "ui/gfx/display.h" 24 #include "ui/gfx/screen.h" 25 #include "ui/wm/core/shadow_controller.h" 26 #include "ui/wm/core/window_util.h" 27 #include "ui/wm/core/wm_state.h" 28 #include "ui/wm/public/activation_client.h" 29 #include "ui/wm/public/window_types.h" 30 31 namespace athena { 32 namespace { 33 class WindowManagerImpl* instance = NULL; 34 35 void SetWindowState(aura::Window* window, 36 const gfx::Rect& bounds, 37 const gfx::Transform& transform) { 38 window->SetBounds(bounds); 39 window->SetTransform(transform); 40 } 41 42 } // namespace 43 44 class AthenaContainerLayoutManager : public aura::LayoutManager { 45 public: 46 AthenaContainerLayoutManager(); 47 virtual ~AthenaContainerLayoutManager(); 48 49 private: 50 // aura::LayoutManager: 51 virtual void OnWindowResized() OVERRIDE; 52 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; 53 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE; 54 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE; 55 virtual void OnChildWindowVisibilityChanged(aura::Window* child, 56 bool visible) OVERRIDE; 57 virtual void SetChildBounds(aura::Window* child, 58 const gfx::Rect& requested_bounds) OVERRIDE; 59 60 DISALLOW_COPY_AND_ASSIGN(AthenaContainerLayoutManager); 61 }; 62 63 AthenaContainerLayoutManager::AthenaContainerLayoutManager() { 64 } 65 66 AthenaContainerLayoutManager::~AthenaContainerLayoutManager() { 67 } 68 69 void AthenaContainerLayoutManager::OnWindowResized() { 70 // Resize all the existing windows. 71 const aura::Window::Windows& list = 72 instance->window_list_provider_->GetWindowList(); 73 const gfx::Size work_area = 74 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size(); 75 bool is_splitview = instance->split_view_controller_->IsSplitViewModeActive(); 76 gfx::Size split_size; 77 if (is_splitview) { 78 CHECK(instance->split_view_controller_->left_window()); 79 split_size = 80 instance->split_view_controller_->left_window()->bounds().size(); 81 } 82 83 for (aura::Window::Windows::const_iterator iter = list.begin(); 84 iter != list.end(); 85 ++iter) { 86 aura::Window* window = *iter; 87 if (is_splitview) { 88 if (window == instance->split_view_controller_->left_window()) 89 window->SetBounds(gfx::Rect(split_size)); 90 else if (window == instance->split_view_controller_->right_window()) 91 window->SetBounds( 92 gfx::Rect(gfx::Point(split_size.width(), 0), split_size)); 93 else 94 window->SetBounds(gfx::Rect(work_area)); 95 } else { 96 window->SetBounds(gfx::Rect(work_area)); 97 } 98 } 99 } 100 101 void AthenaContainerLayoutManager::OnWindowAddedToLayout(aura::Window* child) { 102 if (!instance->window_list_provider_->IsWindowInList(child)) 103 return; 104 105 if (instance->split_view_controller_->IsSplitViewModeActive() && 106 !instance->IsOverviewModeActive()) { 107 instance->split_view_controller_->ReplaceWindow( 108 instance->split_view_controller_->left_window(), child); 109 } else { 110 gfx::Size size = 111 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size(); 112 child->SetBounds(gfx::Rect(size)); 113 } 114 115 if (instance->IsOverviewModeActive()) { 116 // TODO(pkotwicz|oshima). Creating a new window should only exit overview 117 // mode if the new window is activated. crbug.com/415266 118 instance->OnSelectWindow(child); 119 } 120 } 121 122 void AthenaContainerLayoutManager::OnWillRemoveWindowFromLayout( 123 aura::Window* child) { 124 } 125 126 void AthenaContainerLayoutManager::OnWindowRemovedFromLayout( 127 aura::Window* child) { 128 } 129 130 void AthenaContainerLayoutManager::OnChildWindowVisibilityChanged( 131 aura::Window* child, 132 bool visible) { 133 } 134 135 void AthenaContainerLayoutManager::SetChildBounds( 136 aura::Window* child, 137 const gfx::Rect& requested_bounds) { 138 if (!requested_bounds.IsEmpty()) 139 SetChildBoundsDirect(child, requested_bounds); 140 } 141 142 WindowManagerImpl::WindowManagerImpl() { 143 ScreenManager::ContainerParams params("DefaultContainer", CP_DEFAULT); 144 params.can_activate_children = true; 145 container_.reset(ScreenManager::Get()->CreateDefaultContainer(params)); 146 container_->SetLayoutManager(new AthenaContainerLayoutManager); 147 container_->AddObserver(this); 148 window_list_provider_.reset(new WindowListProviderImpl(container_.get())); 149 bezel_controller_.reset(new BezelController(container_.get())); 150 split_view_controller_.reset( 151 new SplitViewController(container_.get(), window_list_provider_.get())); 152 AddObserver(split_view_controller_.get()); 153 bezel_controller_->set_left_right_delegate(split_view_controller_.get()); 154 container_->AddPreTargetHandler(bezel_controller_.get()); 155 title_drag_controller_.reset(new TitleDragController(container_.get(), this)); 156 wm_state_.reset(new wm::WMState()); 157 aura::client::ActivationClient* activation_client = 158 aura::client::GetActivationClient(container_->GetRootWindow()); 159 shadow_controller_.reset(new wm::ShadowController(activation_client)); 160 instance = this; 161 InstallAccelerators(); 162 } 163 164 WindowManagerImpl::~WindowManagerImpl() { 165 overview_.reset(); 166 RemoveObserver(split_view_controller_.get()); 167 split_view_controller_.reset(); 168 window_list_provider_.reset(); 169 if (container_) { 170 container_->RemoveObserver(this); 171 container_->RemovePreTargetHandler(bezel_controller_.get()); 172 } 173 // |title_drag_controller_| needs to be reset before |container_|. 174 title_drag_controller_.reset(); 175 container_.reset(); 176 instance = NULL; 177 } 178 179 void WindowManagerImpl::ToggleSplitView() { 180 if (IsOverviewModeActive()) 181 return; 182 183 if (split_view_controller_->IsSplitViewModeActive()) { 184 split_view_controller_->DeactivateSplitMode(); 185 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit()); 186 // Relayout so that windows are maximzied. 187 container_->layout_manager()->OnWindowResized(); 188 } else if (split_view_controller_->CanActivateSplitViewMode()) { 189 FOR_EACH_OBSERVER(WindowManagerObserver, 190 observers_, 191 OnSplitViewModeEnter()); 192 split_view_controller_->ActivateSplitMode(NULL, NULL, NULL); 193 } 194 } 195 196 void WindowManagerImpl::ToggleOverview() { 197 if (IsOverviewModeActive()) { 198 SetInOverview(false); 199 200 // Activate the window which was active prior to entering overview. 201 const aura::Window::Windows windows = 202 window_list_provider_->GetWindowList(); 203 if (!windows.empty()) { 204 aura::Window* window = windows.back(); 205 // Show the window in case the exit overview animation has finished and 206 // |window| was hidden. 207 window->Show(); 208 209 wm::ActivateWindow(window); 210 } 211 } else { 212 SetInOverview(true); 213 } 214 } 215 216 bool WindowManagerImpl::IsOverviewModeActive() { 217 return overview_; 218 } 219 220 void WindowManagerImpl::SetInOverview(bool active) { 221 bool in_overview = !!overview_; 222 if (active == in_overview) 223 return; 224 225 bezel_controller_->set_left_right_delegate( 226 active ? NULL : split_view_controller_.get()); 227 if (active) { 228 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeEnter()); 229 230 // Note: The window_list_provider_ resembles the exact window list of the 231 // container, so no re-stacking is required before showing the OverviewMode. 232 overview_ = WindowOverviewMode::Create( 233 container_.get(), window_list_provider_.get(), 234 split_view_controller_.get(), this); 235 } else { 236 overview_.reset(); 237 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeExit()); 238 } 239 } 240 241 void WindowManagerImpl::InstallAccelerators() { 242 const AcceleratorData accelerator_data[] = { 243 {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_NONE, CMD_TOGGLE_OVERVIEW, 244 AF_NONE}, 245 {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_CONTROL_DOWN, 246 CMD_TOGGLE_SPLIT_VIEW, AF_NONE}, 247 }; 248 AcceleratorManager::Get()->RegisterAccelerators( 249 accelerator_data, arraysize(accelerator_data), this); 250 } 251 252 void WindowManagerImpl::AddObserver(WindowManagerObserver* observer) { 253 observers_.AddObserver(observer); 254 } 255 256 void WindowManagerImpl::RemoveObserver(WindowManagerObserver* observer) { 257 observers_.RemoveObserver(observer); 258 } 259 260 void WindowManagerImpl::ToggleSplitViewForTest() { 261 ToggleSplitView(); 262 } 263 264 WindowListProvider* WindowManagerImpl::GetWindowListProvider() { 265 return window_list_provider_.get(); 266 } 267 268 void WindowManagerImpl::OnSelectWindow(aura::Window* window) { 269 SetInOverview(false); 270 271 // Show the window in case the exit overview animation has finished and 272 // |window| was hidden. 273 window->Show(); 274 275 wm::ActivateWindow(window); 276 277 if (split_view_controller_->IsSplitViewModeActive()) { 278 split_view_controller_->DeactivateSplitMode(); 279 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit()); 280 } 281 // If |window| does not have the size of the work-area, then make sure it is 282 // resized. 283 const gfx::Size work_area = 284 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size(); 285 if (window->GetTargetBounds().size() != work_area) { 286 const gfx::Rect& window_bounds = window->bounds(); 287 const gfx::Rect desired_bounds(work_area); 288 gfx::Transform transform; 289 transform.Translate(desired_bounds.x() - window_bounds.x(), 290 desired_bounds.y() - window_bounds.y()); 291 transform.Scale(desired_bounds.width() / window_bounds.width(), 292 desired_bounds.height() / window_bounds.height()); 293 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); 294 settings.SetPreemptionStrategy( 295 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 296 settings.AddObserver( 297 new ui::ClosureAnimationObserver(base::Bind(&SetWindowState, 298 base::Unretained(window), 299 desired_bounds, 300 gfx::Transform()))); 301 window->SetTransform(transform); 302 } 303 } 304 305 void WindowManagerImpl::OnSelectSplitViewWindow(aura::Window* left, 306 aura::Window* right, 307 aura::Window* to_activate) { 308 SetInOverview(false); 309 FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeEnter()); 310 split_view_controller_->ActivateSplitMode(left, right, to_activate); 311 } 312 313 void WindowManagerImpl::OnWindowDestroying(aura::Window* window) { 314 if (window == container_) 315 container_.reset(); 316 } 317 318 bool WindowManagerImpl::IsCommandEnabled(int command_id) const { 319 return true; 320 } 321 322 bool WindowManagerImpl::OnAcceleratorFired(int command_id, 323 const ui::Accelerator& accelerator) { 324 switch (command_id) { 325 case CMD_TOGGLE_OVERVIEW: 326 ToggleOverview(); 327 break; 328 case CMD_TOGGLE_SPLIT_VIEW: 329 ToggleSplitView(); 330 break; 331 } 332 return true; 333 } 334 335 aura::Window* WindowManagerImpl::GetWindowBehind(aura::Window* window) { 336 const aura::Window::Windows& windows = window_list_provider_->GetWindowList(); 337 aura::Window::Windows::const_reverse_iterator iter = 338 std::find(windows.rbegin(), windows.rend(), window); 339 CHECK(iter != windows.rend()); 340 ++iter; 341 aura::Window* behind = NULL; 342 if (iter != windows.rend()) 343 behind = *iter++; 344 345 if (split_view_controller_->IsSplitViewModeActive()) { 346 aura::Window* left = split_view_controller_->left_window(); 347 aura::Window* right = split_view_controller_->right_window(); 348 CHECK(window == left || window == right); 349 if (behind == left || behind == right) 350 behind = (iter == windows.rend()) ? NULL : *iter; 351 } 352 353 return behind; 354 } 355 356 void WindowManagerImpl::OnTitleDragStarted(aura::Window* window) { 357 aura::Window* next_window = GetWindowBehind(window); 358 if (!next_window) 359 return; 360 // Make sure |window| is active. 361 wm::ActivateWindow(window); 362 363 // Make sure |next_window| is visibile. 364 next_window->Show(); 365 366 // Position |next_window| correctly (left aligned if it's larger than 367 // |window|, and center aligned otherwise). 368 int dx = window->bounds().x() - next_window->bounds().x(); 369 if (next_window->bounds().width() < window->bounds().width()) 370 dx -= (next_window->bounds().width() - window->bounds().width()) / 2; 371 372 if (dx) { 373 gfx::Transform transform; 374 transform.Translate(dx, 0); 375 next_window->SetTransform(transform); 376 } 377 } 378 379 void WindowManagerImpl::OnTitleDragCompleted(aura::Window* window) { 380 aura::Window* next_window = GetWindowBehind(window); 381 if (!next_window) 382 return; 383 if (split_view_controller_->IsSplitViewModeActive()) { 384 split_view_controller_->ReplaceWindow(window, next_window); 385 } else { 386 ui::ScopedLayerAnimationSettings 387 settings(next_window->layer()->GetAnimator()); 388 settings.AddObserver(new ui::ClosureAnimationObserver( 389 base::Bind(&SetWindowState, 390 base::Unretained(next_window), 391 window->bounds(), 392 gfx::Transform()))); 393 394 gfx::Transform transform; 395 transform.Scale(window->bounds().width() / next_window->bounds().width(), 396 window->bounds().height() / next_window->bounds().height()); 397 transform.Translate(window->bounds().x() - next_window->bounds().x(), 0); 398 next_window->SetTransform(transform); 399 400 wm::ActivateWindow(next_window); 401 } 402 window->Hide(); 403 } 404 405 void WindowManagerImpl::OnTitleDragCanceled(aura::Window* window) { 406 aura::Window* next_window = GetWindowBehind(window); 407 if (!next_window) 408 return; 409 next_window->SetTransform(gfx::Transform()); 410 next_window->Hide(); 411 } 412 413 // static 414 WindowManager* WindowManager::Create() { 415 DCHECK(!instance); 416 new WindowManagerImpl; 417 DCHECK(instance); 418 return instance; 419 } 420 421 // static 422 void WindowManager::Shutdown() { 423 DCHECK(instance); 424 delete instance; 425 DCHECK(!instance); 426 } 427 428 // static 429 WindowManager* WindowManager::Get() { 430 DCHECK(instance); 431 return instance; 432 } 433 434 } // namespace athena 435