// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/wm/maximize_mode/maximize_mode_window_manager.h" #include "ash/ash_switches.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" #include "ash/wm/maximize_mode/maximize_mode_window_state.h" #include "ash/wm/maximize_mode/workspace_backdrop_delegate.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/window_selector_controller.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" #include "ash/wm/wm_event.h" #include "ash/wm/workspace_controller.h" #include "base/command_line.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/gfx/screen.h" namespace ash { namespace { // The height of the area in which a touch operation leads to exiting the // full screen mode. const int kLeaveFullScreenAreaHeightInPixel = 2; // Exits overview mode if it is currently active. void CancelOverview() { WindowSelectorController* controller = Shell::GetInstance()->window_selector_controller(); if (controller && controller->IsSelecting()) controller->OnSelectionEnded(); } } // namespace MaximizeModeWindowManager::~MaximizeModeWindowManager() { // Overview mode needs to be ended before exiting maximize mode to prevent // transforming windows which are currently in // overview: http://crbug.com/366605 CancelOverview(); Shell::GetInstance()->RemovePreTargetHandler(this); Shell::GetInstance()->RemoveShellObserver(this); Shell::GetScreen()->RemoveObserver(this); EnableBackdropBehindTopWindowOnEachDisplay(false); RemoveWindowCreationObservers(); RestoreAllWindows(); } int MaximizeModeWindowManager::GetNumberOfManagedWindows() { return window_state_map_.size(); } void MaximizeModeWindowManager::AddWindow(aura::Window* window) { // Only add the window if it is a direct dependent of a container window // and not yet tracked. if (!ShouldHandleWindow(window) || window_state_map_.find(window) != window_state_map_.end() || !IsContainerWindow(window->parent())) { return; } MaximizeAndTrackWindow(window); } void MaximizeModeWindowManager::WindowStateDestroyed(aura::Window* window) { // At this time ForgetWindow() should already have been called. If not, // someone else must have replaced the "window manager's state object". DCHECK(!window->HasObserver(this)); WindowToState::iterator it = window_state_map_.find(window); DCHECK(it != window_state_map_.end()); window_state_map_.erase(it); } void MaximizeModeWindowManager::OnOverviewModeStarting() { if (backdrops_hidden_) return; EnableBackdropBehindTopWindowOnEachDisplay(false); SetDeferBoundsUpdates(true); backdrops_hidden_ = true; } void MaximizeModeWindowManager::OnOverviewModeEnded() { if (!backdrops_hidden_) return; backdrops_hidden_ = false; EnableBackdropBehindTopWindowOnEachDisplay(true); SetDeferBoundsUpdates(false); } void MaximizeModeWindowManager::OnWindowDestroying(aura::Window* window) { if (IsContainerWindow(window)) { // container window can be removed on display destruction. window->RemoveObserver(this); observed_container_windows_.erase(window); } else { // If a known window gets destroyed we need to remove all knowledge about // it. ForgetWindow(window); } } void MaximizeModeWindowManager::OnWindowAdded(aura::Window* window) { // A window can get removed and then re-added by a drag and drop operation. if (IsContainerWindow(window->parent()) && window_state_map_.find(window) == window_state_map_.end()) { MaximizeAndTrackWindow(window); // When the state got added, the "WM_EVENT_ADDED_TO_WORKSPACE" event got // already sent and we have to notify our state again. if (window_state_map_.find(window) != window_state_map_.end()) { wm::WMEvent event(wm::WM_EVENT_ADDED_TO_WORKSPACE); wm::GetWindowState(window)->OnWMEvent(&event); } } } void MaximizeModeWindowManager::OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) { // Stop managing |window| if the always-on-top property is added. if (key == aura::client::kAlwaysOnTopKey && window->GetProperty(aura::client::kAlwaysOnTopKey)) { ForgetWindow(window); } } void MaximizeModeWindowManager::OnWindowBoundsChanged( aura::Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { if (!IsContainerWindow(window)) return; // Reposition all non maximizeable windows. for (WindowToState::iterator it = window_state_map_.begin(); it != window_state_map_.end(); ++it) { it->second->UpdateWindowPosition(wm::GetWindowState(it->first)); } } void MaximizeModeWindowManager::OnDisplayAdded(const gfx::Display& display) { DisplayConfigurationChanged(); } void MaximizeModeWindowManager::OnDisplayRemoved(const gfx::Display& display) { DisplayConfigurationChanged(); } void MaximizeModeWindowManager::OnDisplayMetricsChanged(const gfx::Display&, uint32_t) { // Nothing to do here. } void MaximizeModeWindowManager::OnTouchEvent(ui::TouchEvent* event) { if (event->type() != ui::ET_TOUCH_PRESSED) return; // Find the active window (from the primary screen) to un-fullscreen. aura::Window* window = wm::GetActiveWindow(); if (!window) return; wm::WindowState* window_state = wm::GetWindowState(window); if (!window_state->IsFullscreen() || window_state->in_immersive_fullscreen()) return; // Test that the touch happened in the top or bottom lines. int y = event->y(); if (y >= kLeaveFullScreenAreaHeightInPixel && y < (window->bounds().height() - kLeaveFullScreenAreaHeightInPixel)) { return; } // Leave full screen mode. event->StopPropagation(); wm::WMEvent toggle_fullscreen(wm::WM_EVENT_TOGGLE_FULLSCREEN); window_state->OnWMEvent(&toggle_fullscreen); } MaximizeModeWindowManager::MaximizeModeWindowManager() : backdrops_hidden_(false) { // The overview mode needs to be ended before the maximize mode is started. To // guarantee the proper order, it will be turned off from here. CancelOverview(); MaximizeAllWindows(); AddWindowCreationObservers(); EnableBackdropBehindTopWindowOnEachDisplay(true); Shell::GetScreen()->AddObserver(this); Shell::GetInstance()->AddShellObserver(this); Shell::GetInstance()->AddPreTargetHandler(this); } void MaximizeModeWindowManager::MaximizeAllWindows() { MruWindowTracker::WindowList windows = ash::Shell::GetInstance()-> mru_window_tracker()->BuildWindowListIgnoreModal(); // Add all existing Mru windows. for (MruWindowTracker::WindowList::iterator window = windows.begin(); window != windows.end(); ++window) { MaximizeAndTrackWindow(*window); } } void MaximizeModeWindowManager::RestoreAllWindows() { while (window_state_map_.size()) ForgetWindow(window_state_map_.begin()->first); } void MaximizeModeWindowManager::SetDeferBoundsUpdates( bool defer_bounds_updates) { for (WindowToState::iterator it = window_state_map_.begin(); it != window_state_map_.end(); ++it) { it->second->SetDeferBoundsUpdates(defer_bounds_updates); } } void MaximizeModeWindowManager::MaximizeAndTrackWindow( aura::Window* window) { if (!ShouldHandleWindow(window)) return; DCHECK(window_state_map_.find(window) == window_state_map_.end()); window->AddObserver(this); // We create and remember a maximize mode state which will attach itself to // the provided state object. window_state_map_[window] = new MaximizeModeWindowState(window, this); } void MaximizeModeWindowManager::ForgetWindow(aura::Window* window) { WindowToState::iterator it = window_state_map_.find(window); // The following DCHECK could fail if our window state object was destroyed // earlier by someone else. However - at this point there is no other client // which replaces the state object and therefore this should not happen. DCHECK(it != window_state_map_.end()); window->RemoveObserver(this); // By telling the state object to revert, it will switch back the old // State object and destroy itself, calling WindowStateDestroyed(). it->second->LeaveMaximizeMode(wm::GetWindowState(it->first)); DCHECK(window_state_map_.find(window) == window_state_map_.end()); } bool MaximizeModeWindowManager::ShouldHandleWindow(aura::Window* window) { DCHECK(window); // Windows with the always-on-top property should be free-floating and thus // not managed by us. if (window->GetProperty(aura::client::kAlwaysOnTopKey)) return false; // Windows in the dock should not be managed by us. if (wm::GetWindowState(window)->IsDocked()) return false; return window->type() == ui::wm::WINDOW_TYPE_NORMAL; } void MaximizeModeWindowManager::AddWindowCreationObservers() { DCHECK(observed_container_windows_.empty()); // Observe window activations/creations in the default containers on all root // windows. aura::Window::Windows root_windows = Shell::GetAllRootWindows(); for (aura::Window::Windows::const_iterator iter = root_windows.begin(); iter != root_windows.end(); ++iter) { aura::Window* container = Shell::GetContainer(*iter, kShellWindowId_DefaultContainer); DCHECK(observed_container_windows_.find(container) == observed_container_windows_.end()); container->AddObserver(this); observed_container_windows_.insert(container); } } void MaximizeModeWindowManager::RemoveWindowCreationObservers() { for (std::set::iterator iter = observed_container_windows_.begin(); iter != observed_container_windows_.end(); ++iter) { (*iter)->RemoveObserver(this); } observed_container_windows_.clear(); } void MaximizeModeWindowManager::DisplayConfigurationChanged() { EnableBackdropBehindTopWindowOnEachDisplay(false); RemoveWindowCreationObservers(); AddWindowCreationObservers(); EnableBackdropBehindTopWindowOnEachDisplay(true); } bool MaximizeModeWindowManager::IsContainerWindow(aura::Window* window) { return observed_container_windows_.find(window) != observed_container_windows_.end(); } void MaximizeModeWindowManager::EnableBackdropBehindTopWindowOnEachDisplay( bool enable) { // This function should be a no-op if backdrops have been disabled. if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kAshDisableMaximizeModeWindowBackdrop)) { return; } if (backdrops_hidden_) return; // Inform the WorkspaceLayoutManager that we want to show a backdrop behind // the topmost window of its container. Shell::RootWindowControllerList controllers = Shell::GetAllRootWindowControllers(); for (Shell::RootWindowControllerList::iterator iter = controllers.begin(); iter != controllers.end(); ++iter) { RootWindowController* controller = *iter; aura::Window* container = Shell::GetContainer( controller->GetRootWindow(), kShellWindowId_DefaultContainer); controller->workspace_controller()->SetMaximizeBackdropDelegate( scoped_ptr( enable ? new WorkspaceBackdropDelegate(container) : NULL)); } } } // namespace ash