blob: 91066bac6728ea3d3354169e06b9ffea3fa72fb5 [file] [log] [blame]
// Copyright (c) 2012 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/system_modal_container_layout_manager.h"
#include <cmath>
#include "ash/common/session/session_state_delegate.h"
#include "ash/common/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/dim_window.h"
#include "ash/wm/window_util.h"
#include "base/stl_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_property.h"
#include "ui/compositor/layer.h"
#include "ui/keyboard/keyboard_controller.h"
namespace ash {
namespace {
// The center point of the window can diverge this much from the center point
// If this is set to true, the window will get centered.
DEFINE_WINDOW_PROPERTY_KEY(bool, kCenteredKey, false);
// The center point of the window can diverge this much from the center point
// of the container to be kept centered upon resizing operations.
const int kCenterPixelDelta = 32;
}
////////////////////////////////////////////////////////////////////////////////
// SystemModalContainerLayoutManager, public:
SystemModalContainerLayoutManager::SystemModalContainerLayoutManager(
aura::Window* container)
: SnapToPixelLayoutManager(container),
container_(container),
modal_background_(nullptr) {}
SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() {}
////////////////////////////////////////////////////////////////////////////////
// SystemModalContainerLayoutManager, aura::LayoutManager implementation:
void SystemModalContainerLayoutManager::OnWindowResized() {
PositionDialogsAfterWorkAreaResize();
}
void SystemModalContainerLayoutManager::OnWindowAddedToLayout(
aura::Window* child) {
DCHECK(child == modal_background_ ||
child->type() == ui::wm::WINDOW_TYPE_NORMAL ||
child->type() == ui::wm::WINDOW_TYPE_POPUP);
DCHECK(
container_->id() != kShellWindowId_LockSystemModalContainer ||
Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked());
// Since this is for SystemModal, there is no goodd reason to add
// these window other than MODAL_TYPE_NONE or MODAL_TYPE_SYSTEM.
// DCHECK to avoid simple mistake.
DCHECK_NE(child->GetProperty(aura::client::kModalKey), ui::MODAL_TYPE_CHILD);
DCHECK_NE(child->GetProperty(aura::client::kModalKey), ui::MODAL_TYPE_WINDOW);
child->AddObserver(this);
if (child->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM &&
child->IsVisible()) {
AddModalWindow(child);
}
}
void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout(
aura::Window* child) {
child->RemoveObserver(this);
if (child->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM)
RemoveModalWindow(child);
}
void SystemModalContainerLayoutManager::SetChildBounds(
aura::Window* child,
const gfx::Rect& requested_bounds) {
SnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
child->SetProperty(kCenteredKey, DialogIsCentered(requested_bounds));
}
////////////////////////////////////////////////////////////////////////////////
// SystemModalContainerLayoutManager, aura::WindowObserver implementation:
void SystemModalContainerLayoutManager::OnWindowPropertyChanged(
aura::Window* window,
const void* key,
intptr_t old) {
if (key != aura::client::kModalKey || !window->IsVisible())
return;
ui::ModalType new_modal = window->GetProperty(aura::client::kModalKey);
if (static_cast<ui::ModalType>(old) == new_modal)
return;
if (new_modal == ui::MODAL_TYPE_SYSTEM) {
AddModalWindow(window);
} else {
RemoveModalWindow(window);
Shell::GetInstance()->OnModalWindowRemoved(window);
}
}
void SystemModalContainerLayoutManager::OnWindowDestroying(
aura::Window* window) {
if (modal_background_ == window) {
if (keyboard::KeyboardController::GetInstance())
keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
modal_background_ = nullptr;
}
}
void SystemModalContainerLayoutManager::OnWindowVisibilityChanged(
aura::Window* window,
bool visible) {
if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_SYSTEM)
return;
if (window->IsVisible()) {
AddModalWindow(window);
} else {
RemoveModalWindow(window);
Shell::GetInstance()->OnModalWindowRemoved(window);
}
}
////////////////////////////////////////////////////////////////////////////////
// SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver
// implementation:
void SystemModalContainerLayoutManager::OnKeyboardBoundsChanging(
const gfx::Rect& new_bounds) {
PositionDialogsAfterWorkAreaResize();
}
bool SystemModalContainerLayoutManager::IsPartOfActiveModalWindow(
aura::Window* window) {
return modal_window() && modal_window()->Contains(window);
}
bool SystemModalContainerLayoutManager::ActivateNextModalWindow() {
if (modal_windows_.empty())
return false;
wm::ActivateWindow(modal_window());
return true;
}
void SystemModalContainerLayoutManager::CreateModalBackground() {
if (!modal_background_) {
modal_background_ = new DimWindow(container_);
modal_background_->SetName(
"SystemModalContainerLayoutManager.ModalBackground");
// There isn't always a keyboard controller.
if (keyboard::KeyboardController::GetInstance())
keyboard::KeyboardController::GetInstance()->AddObserver(this);
}
modal_background_->Show();
}
void SystemModalContainerLayoutManager::DestroyModalBackground() {
// modal_background_ can be NULL when a root window is shutting down.
if (modal_background_) {
if (keyboard::KeyboardController::GetInstance())
keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
modal_background_->Hide();
// Explicitly delete instead of using scoped_ptr as the owner of the
// window is its parent.
delete modal_background_;
modal_background_ = nullptr;
}
}
// static
bool SystemModalContainerLayoutManager::IsModalBackground(
aura::Window* window) {
int id = window->parent()->id();
if (id != kShellWindowId_SystemModalContainer &&
id != kShellWindowId_LockSystemModalContainer)
return false;
SystemModalContainerLayoutManager* layout_manager =
static_cast<SystemModalContainerLayoutManager*>(
window->parent()->layout_manager());
return layout_manager->modal_background_ == window;
}
////////////////////////////////////////////////////////////////////////////////
// SystemModalContainerLayoutManager, private:
void SystemModalContainerLayoutManager::AddModalWindow(aura::Window* window) {
if (modal_windows_.empty()) {
aura::Window* capture_window = aura::client::GetCaptureWindow(container_);
if (capture_window)
capture_window->ReleaseCapture();
}
DCHECK(window->IsVisible());
DCHECK(!ContainsValue(modal_windows_, window));
modal_windows_.push_back(window);
Shell::GetInstance()->CreateModalBackground(window);
window->parent()->StackChildAtTop(window);
gfx::Rect target_bounds = window->bounds();
target_bounds.AdjustToFit(GetUsableDialogArea());
window->SetBounds(target_bounds);
}
void SystemModalContainerLayoutManager::RemoveModalWindow(
aura::Window* window) {
aura::Window::Windows::iterator it =
std::find(modal_windows_.begin(), modal_windows_.end(), window);
if (it != modal_windows_.end())
modal_windows_.erase(it);
}
void SystemModalContainerLayoutManager::PositionDialogsAfterWorkAreaResize() {
if (!modal_windows_.empty()) {
for (aura::Window::Windows::iterator it = modal_windows_.begin();
it != modal_windows_.end(); ++it) {
(*it)->SetBounds(GetCenteredAndOrFittedBounds(*it));
}
}
}
gfx::Rect SystemModalContainerLayoutManager::GetUsableDialogArea() {
// Instead of resizing the system modal container, we move only the modal
// windows. This way we avoid flashing lines upon resize animation and if the
// keyboard will not fill left to right, the background is still covered.
gfx::Rect valid_bounds = container_->bounds();
keyboard::KeyboardController* keyboard_controller =
keyboard::KeyboardController::GetInstance();
if (keyboard_controller) {
gfx::Rect bounds = keyboard_controller->current_keyboard_bounds();
if (!bounds.IsEmpty()) {
valid_bounds.set_height(
std::max(0, valid_bounds.height() - bounds.height()));
}
}
return valid_bounds;
}
gfx::Rect SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds(
const aura::Window* window) {
gfx::Rect target_bounds;
gfx::Rect usable_area = GetUsableDialogArea();
if (window->GetProperty(kCenteredKey)) {
// Keep the dialog centered if it was centered before.
target_bounds = usable_area;
target_bounds.ClampToCenteredSize(window->bounds().size());
} else {
// Keep the dialog within the usable area.
target_bounds = window->bounds();
target_bounds.AdjustToFit(usable_area);
}
if (usable_area != container_->bounds()) {
// Don't clamp the dialog for the keyboard. Keep the size as it is but make
// sure that the top remains visible.
// TODO(skuhne): M37 should add over scroll functionality to address this.
target_bounds.set_size(window->bounds().size());
}
return target_bounds;
}
bool SystemModalContainerLayoutManager::DialogIsCentered(
const gfx::Rect& window_bounds) {
gfx::Point window_center = window_bounds.CenterPoint();
gfx::Point container_center = GetUsableDialogArea().CenterPoint();
return std::abs(window_center.x() - container_center.x()) <
kCenterPixelDelta &&
std::abs(window_center.y() - container_center.y()) < kCenterPixelDelta;
}
} // namespace ash