blob: 20a4bd5d33f89374d21f343deab9e4beb509e428 [file] [log] [blame]
// 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 "athena/screen/screen_manager_impl.h"
#include "athena/input/public/accelerator_manager.h"
#include "athena/screen/modal_window_controller.h"
#include "athena/screen/screen_accelerator_handler.h"
#include "athena/util/container_priorities.h"
#include "athena/util/fill_layout_manager.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/window.h"
#include "ui/aura/window_property.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tree_host.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
#include "ui/wm/core/base_focus_rules.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/default_screen_position_client.h"
#include "ui/wm/core/focus_controller.h"
#include "ui/wm/core/window_util.h"
// This is to avoid creating type definitoin for kAlwaysOnTopKey.
DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ATHENA_EXPORT, bool);
namespace athena {
namespace {
DEFINE_OWNED_WINDOW_PROPERTY_KEY(ScreenManager::ContainerParams,
kContainerParamsKey,
nullptr);
ScreenManagerImpl* instance = nullptr;
// A functor to find a container that has the higher priority.
struct HigherPriorityFinder {
HigherPriorityFinder(int p) : priority(p) {}
bool operator()(aura::Window* window) {
return window->GetProperty(kContainerParamsKey)->z_order_priority >
priority;
}
int priority;
};
bool BlockEvents(aura::Window* container) {
ScreenManager::ContainerParams* params =
container->GetProperty(kContainerParamsKey);
return params && params->block_events && container->IsVisible();
}
bool DefaultContainer(aura::Window* container) {
ScreenManager::ContainerParams* params =
container->GetProperty(kContainerParamsKey);
return params && params->default_parent;
}
bool HasModalContainerPriority(aura::Window* container) {
ScreenManager::ContainerParams* params =
container->GetProperty(kContainerParamsKey);
return params && params->modal_container_priority != -1;
}
bool IsSystemModal(aura::Window* window) {
return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM;
}
// Returns the container which contains |window|.
aura::Window* GetContainer(aura::Window* window) {
aura::Window* container = window;
while (container && !container->GetProperty(kContainerParamsKey))
container = container->parent();
return container;
}
class AthenaFocusRules : public wm::BaseFocusRules {
public:
AthenaFocusRules() {}
~AthenaFocusRules() override {}
// wm::BaseFocusRules:
virtual bool SupportsChildActivation(aura::Window* window) const override {
ScreenManager::ContainerParams* params =
window->GetProperty(kContainerParamsKey);
return params && params->can_activate_children;
}
virtual bool CanActivateWindow(aura::Window* window) const override {
if (!window)
return true;
// Check if containers of higher z-order than |window| have 'block_events'
// fields.
if (window->GetRootWindow()) {
const aura::Window::Windows& containers =
window->GetRootWindow()->children();
aura::Window::Windows::const_iterator iter =
std::find(containers.begin(), containers.end(), GetContainer(window));
DCHECK(iter != containers.end());
for (++iter; iter != containers.end(); ++iter) {
if (BlockEvents(*iter))
return false;
}
}
return BaseFocusRules::CanActivateWindow(window);
}
aura::Window* GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const {
for (aura::Window::Windows::const_reverse_iterator i =
container->children().rbegin();
i != container->children().rend();
++i) {
if (*i != ignore && CanActivateWindow(*i))
return *i;
}
return NULL;
}
virtual aura::Window* GetNextActivatableWindow(
aura::Window* ignore) const override {
const aura::Window::Windows& containers =
ignore->GetRootWindow()->children();
auto starting_container_iter = containers.begin();
for (auto container_iter = containers.begin();
container_iter != containers.end();
container_iter++) {
if ((*container_iter)->Contains(ignore)) {
starting_container_iter = container_iter;
break;
}
}
// Find next window from the front containers.
aura::Window* next = nullptr;
for (auto container_iter = starting_container_iter;
!next && container_iter != containers.end();
container_iter++) {
next = GetTopmostWindowToActivateInContainer(*container_iter, ignore);
}
// Find next window from the back containers.
auto container_iter = starting_container_iter;
while (!next && container_iter != containers.begin()) {
container_iter--;
next = GetTopmostWindowToActivateInContainer(*container_iter, ignore);
}
return next;
}
private:
DISALLOW_COPY_AND_ASSIGN(AthenaFocusRules);
};
class AthenaScreenPositionClient : public wm::DefaultScreenPositionClient {
public:
AthenaScreenPositionClient() {
}
~AthenaScreenPositionClient() override {}
private:
// aura::client::ScreenPositionClient:
void ConvertHostPointToScreen(aura::Window* window,
gfx::Point* point) override {
// TODO(oshima): Implement this when adding multiple display support.
NOTREACHED();
}
DISALLOW_COPY_AND_ASSIGN(AthenaScreenPositionClient);
};
class AthenaWindowTargeter : public aura::WindowTargeter {
public:
explicit AthenaWindowTargeter(aura::Window* root_window)
: root_window_(root_window) {}
~AthenaWindowTargeter() override {}
private:
// aura::WindowTargeter:
virtual bool SubtreeCanAcceptEvent(
ui::EventTarget* target,
const ui::LocatedEvent& event) const override {
const aura::Window::Windows& containers = root_window_->children();
auto r_iter =
std::find_if(containers.rbegin(), containers.rend(), &BlockEvents);
if (r_iter == containers.rend())
return aura::WindowTargeter::SubtreeCanAcceptEvent(target, event);
aura::Window* window = static_cast<aura::Window*>(target);
for (;; --r_iter) {
if ((*r_iter)->Contains(window))
return aura::WindowTargeter::SubtreeCanAcceptEvent(target, event);
if (r_iter == containers.rbegin())
break;
}
return false;
}
virtual ui::EventTarget* FindTargetForLocatedEvent(
ui::EventTarget* root,
ui::LocatedEvent* event) override {
ui::EventTarget* target =
aura::WindowTargeter::FindTargetForLocatedEvent(root, event);
if (target)
return target;
// If the root target is blocking the event, return the container even if
// there is no target found so that windows behind it will not be searched.
const ScreenManager::ContainerParams* params =
static_cast<aura::Window*>(root)->GetProperty(kContainerParamsKey);
return (params && params->block_events) ? root : nullptr;
}
// Not owned.
aura::Window* root_window_;
DISALLOW_COPY_AND_ASSIGN(AthenaWindowTargeter);
};
} // namespace
ScreenManagerImpl::ScreenManagerImpl(aura::Window* root_window)
: root_window_(root_window),
last_requested_rotation_(gfx::Display::ROTATE_0),
rotation_locked_(false) {
DCHECK(root_window_);
DCHECK(!instance);
instance = this;
}
ScreenManagerImpl::~ScreenManagerImpl() {
aura::client::SetScreenPositionClient(root_window_, nullptr);
aura::client::SetWindowTreeClient(root_window_, nullptr);
wm::FocusController* focus_controller =
static_cast<wm::FocusController*>(focus_client_.get());
root_window_->RemovePreTargetHandler(focus_controller);
aura::client::SetActivationClient(root_window_, nullptr);
aura::client::SetFocusClient(root_window_, nullptr);
aura::Window::Windows children = root_window_->children();
// Close All children:
for (aura::Window::Windows::iterator iter = children.begin();
iter != children.end();
++iter) {
delete *iter;
}
instance = nullptr;
}
void ScreenManagerImpl::Init() {
wm::FocusController* focus_controller =
new wm::FocusController(new AthenaFocusRules());
aura::client::SetFocusClient(root_window_, focus_controller);
root_window_->AddPreTargetHandler(focus_controller);
aura::client::SetActivationClient(root_window_, focus_controller);
focus_client_.reset(focus_controller);
root_window_->SetLayoutManager(new FillLayoutManager(root_window_));
capture_client_.reset(new ::wm::ScopedCaptureClient(root_window_));
accelerator_handler_.reset(new ScreenAcceleratorHandler());
aura::client::SetWindowTreeClient(root_window_, this);
screen_position_client_.reset(new AthenaScreenPositionClient());
aura::client::SetScreenPositionClient(root_window_,
screen_position_client_.get());
root_window_->SetEventTargeter(
make_scoped_ptr(new AthenaWindowTargeter(root_window_)));
}
aura::Window* ScreenManagerImpl::FindContainerByPriority(int priority) {
for (aura::Window* window : root_window_->children()) {
if (window->GetProperty(kContainerParamsKey)->z_order_priority == priority)
return window;
}
return nullptr;
}
aura::Window* ScreenManagerImpl::CreateContainer(
const ContainerParams& params) {
const aura::Window::Windows& children = root_window_->children();
if (params.default_parent) {
CHECK(std::find_if(children.begin(), children.end(), &DefaultContainer) ==
children.end());
}
// mmodal container's priority must be higher than the container's priority.
DCHECK(params.modal_container_priority == -1 ||
params.modal_container_priority > params.z_order_priority);
// Default parent must specify modal_container_priority.
DCHECK(!params.default_parent || params.modal_container_priority != -1);
aura::Window* container = new aura::Window(nullptr);
CHECK_GE(params.z_order_priority, 0);
container->Init(aura::WINDOW_LAYER_NOT_DRAWN);
container->SetName(params.name);
DCHECK(!FindContainerByPriority(params.z_order_priority))
<< "The container with the priority " << params.z_order_priority
<< " already exists.";
container->SetProperty(kContainerParamsKey, new ContainerParams(params));
root_window_->AddChild(container);
aura::Window::Windows::const_iterator iter =
std::find_if(children.begin(),
children.end(),
HigherPriorityFinder(params.z_order_priority));
if (iter != children.end())
root_window_->StackChildBelow(container, *iter);
container->Show();
return container;
}
aura::Window* ScreenManagerImpl::GetContext() {
return root_window_;
}
void ScreenManagerImpl::SetRotation(gfx::Display::Rotation rotation) {
last_requested_rotation_ = rotation;
if (rotation_locked_ || rotation ==
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().rotation()) {
return;
}
// TODO(flackr): Use display manager to update display rotation:
// http://crbug.com/401044.
static_cast<aura::TestScreen*>(gfx::Screen::GetNativeScreen())->
SetDisplayRotation(rotation);
}
void ScreenManagerImpl::SetRotationLocked(bool rotation_locked) {
rotation_locked_ = rotation_locked;
if (!rotation_locked_)
SetRotation(last_requested_rotation_);
}
int ScreenManagerImpl::GetModalContainerPriority(aura::Window* window,
aura::Window* parent) {
const aura::Window::Windows& children = root_window_->children();
if (window->GetProperty(aura::client::kAlwaysOnTopKey)) {
// Use top most modal container.
auto iter = std::find_if(
children.rbegin(), children.rend(), &HasModalContainerPriority);
DCHECK(iter != children.rend());
return (*iter)->GetProperty(kContainerParamsKey)->modal_container_priority;
} else {
// use the container closest to the parent which has modal
// container priority.
auto iter = std::find(children.rbegin(), children.rend(), parent);
DCHECK(iter != children.rend());
iter = std::find_if(iter, children.rend(), &HasModalContainerPriority);
DCHECK(iter != children.rend());
return (*iter)->GetProperty(kContainerParamsKey)->modal_container_priority;
}
}
aura::Window* ScreenManagerImpl::GetDefaultParent(aura::Window* context,
aura::Window* window,
const gfx::Rect& bounds) {
aura::Window* parent = wm::GetTransientParent(window);
if (parent)
parent = GetContainer(parent);
else
parent = GetDefaultContainer();
if (IsSystemModal(window)) {
DCHECK(window->type() == ui::wm::WINDOW_TYPE_NORMAL ||
window->type() == ui::wm::WINDOW_TYPE_POPUP);
int priority = GetModalContainerPriority(window, parent);
parent = FindContainerByPriority(priority);
if (!parent) {
ModalWindowController* controller = new ModalWindowController(priority);
parent = controller->modal_container();
}
}
return parent;
}
aura::Window* ScreenManagerImpl::GetDefaultContainer() {
const aura::Window::Windows& children = root_window_->children();
return *(std::find_if(children.begin(), children.end(), &DefaultContainer));
}
ScreenManager::ContainerParams::ContainerParams(const std::string& n,
int priority)
: name(n),
can_activate_children(false),
block_events(false),
z_order_priority(priority),
default_parent(false),
modal_container_priority(-1) {
}
// static
ScreenManager* ScreenManager::Create(aura::Window* root_window) {
(new ScreenManagerImpl(root_window))->Init();
DCHECK(instance);
return instance;
}
// static
ScreenManager* ScreenManager::Get() {
DCHECK(instance);
return instance;
}
// static
void ScreenManager::Shutdown() {
DCHECK(instance);
delete instance;
DCHECK(!instance);
}
} // namespace athena