blob: e38aa2e5508dcc9779ea303e54d2d5352e851713 [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 "ui/wm/core/base_focus_rules.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/wm/core/window_modality_controller.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_delegate.h"
namespace wm {
namespace {
aura::Window* GetFocusedWindow(aura::Window* context) {
aura::client::FocusClient* focus_client =
aura::client::GetFocusClient(context);
return focus_client ? focus_client->GetFocusedWindow() : NULL;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// BaseFocusRules, protected:
BaseFocusRules::BaseFocusRules() {
}
BaseFocusRules::~BaseFocusRules() {
}
bool BaseFocusRules::IsWindowConsideredVisibleForActivation(
aura::Window* window) const {
return window->IsVisible();
}
////////////////////////////////////////////////////////////////////////////////
// BaseFocusRules, FocusRules implementation:
bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const {
// The window must in a valid hierarchy.
if (!window->GetRootWindow())
return false;
// The window must exist within a container that supports activation.
// The window cannot be blocked by a modal transient.
return SupportsChildActivation(window->parent());
}
bool BaseFocusRules::CanActivateWindow(aura::Window* window) const {
// It is possible to activate a NULL window, it is equivalent to clearing
// activation.
if (!window)
return true;
// Only toplevel windows can be activated.
if (!IsToplevelWindow(window))
return false;
// The window must be visible.
if (!IsWindowConsideredVisibleForActivation(window))
return false;
// The window's activation delegate must allow this window to be activated.
if (aura::client::GetActivationDelegate(window) &&
!aura::client::GetActivationDelegate(window)->ShouldActivate()) {
return false;
}
// A window must be focusable to be activatable. We don't call
// CanFocusWindow() from here because it will call back to us via
// GetActivatableWindow().
if (!window->CanFocus())
return false;
// The window cannot be blocked by a modal transient.
return !GetModalTransient(window);
}
bool BaseFocusRules::CanFocusWindow(aura::Window* window) const {
// It is possible to focus a NULL window, it is equivalent to clearing focus.
if (!window)
return true;
// The focused window is always inside the active window, so windows that
// aren't activatable can't contain the focused window.
aura::Window* activatable = GetActivatableWindow(window);
if (!activatable || !activatable->Contains(window))
return false;
return window->CanFocus();
}
aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const {
aura::Window* parent = window->parent();
aura::Window* child = window;
while (parent) {
if (IsToplevelWindow(child))
return child;
parent = parent->parent();
child = child->parent();
}
return NULL;
}
aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
aura::Window* parent = window->parent();
aura::Window* child = window;
while (parent) {
if (CanActivateWindow(child))
return child;
// CanActivateWindow() above will return false if |child| is blocked by a
// modal transient. In this case the modal is or contains the activatable
// window. We recurse because the modal may itself be blocked by a modal
// transient.
aura::Window* modal_transient = GetModalTransient(child);
if (modal_transient)
return GetActivatableWindow(modal_transient);
if (wm::GetTransientParent(child)) {
// To avoid infinite recursion, if |child| has a transient parent
// whose own modal transient is |child| itself, just return |child|.
aura::Window* parent_modal_transient =
GetModalTransient(wm::GetTransientParent(child));
if (parent_modal_transient == child)
return child;
return GetActivatableWindow(wm::GetTransientParent(child));
}
parent = parent->parent();
child = child->parent();
}
return NULL;
}
aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const {
if (CanFocusWindow(window))
return window;
// |window| may be in a hierarchy that is non-activatable, in which case we
// need to cut over to the activatable hierarchy.
aura::Window* activatable = GetActivatableWindow(window);
if (!activatable) {
// There may not be a related activatable hierarchy to cut over to, in which
// case we try an unrelated one.
aura::Window* toplevel = GetToplevelWindow(window);
if (toplevel)
activatable = GetNextActivatableWindow(toplevel);
if (!activatable)
return NULL;
}
if (!activatable->Contains(window)) {
// If there's already a child window focused in the activatable hierarchy,
// just use that (i.e. don't shift focus), otherwise we need to at least cut
// over to the activatable hierarchy.
aura::Window* focused = GetFocusedWindow(activatable);
return activatable->Contains(focused) ? focused : activatable;
}
while (window && !CanFocusWindow(window))
window = window->parent();
return window;
}
aura::Window* BaseFocusRules::GetNextActivatableWindow(
aura::Window* ignore) const {
DCHECK(ignore);
// Can be called from the RootWindow's destruction, which has a NULL parent.
if (!ignore->parent())
return NULL;
// In the basic scenarios handled by BasicFocusRules, the pool of activatable
// windows is limited to the |ignore|'s siblings.
const aura::Window::Windows& siblings = ignore->parent()->children();
DCHECK(!siblings.empty());
for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin();
rit != siblings.rend();
++rit) {
aura::Window* cur = *rit;
if (cur == ignore)
continue;
if (CanActivateWindow(cur))
return cur;
}
return NULL;
}
} // namespace wm