| Index: mojo/services/window_manager/basic_focus_rules.cc
|
| diff --git a/mojo/services/window_manager/basic_focus_rules.cc b/mojo/services/window_manager/basic_focus_rules.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5ba49afb6f99870f794b5826cb7073899c7c1366
|
| --- /dev/null
|
| +++ b/mojo/services/window_manager/basic_focus_rules.cc
|
| @@ -0,0 +1,171 @@
|
| +// 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 "mojo/services/window_manager/basic_focus_rules.h"
|
| +
|
| +#include "base/macros.h"
|
| +#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
|
| +
|
| +using mojo::View;
|
| +
|
| +namespace window_manager {
|
| +
|
| +BasicFocusRules::BasicFocusRules(View* window_container)
|
| + : window_container_(window_container) {
|
| +}
|
| +
|
| +BasicFocusRules::~BasicFocusRules() {}
|
| +
|
| +bool BasicFocusRules::SupportsChildActivation(View* view) const {
|
| + return true;
|
| +}
|
| +
|
| +bool BasicFocusRules::IsToplevelView(View* view) const {
|
| + if (!IsViewParentedToWindowContainer(view))
|
| + return false;
|
| +
|
| + // The window must exist within a container that supports activation.
|
| + // The window cannot be blocked by a modal transient.
|
| + return SupportsChildActivation(view->parent());
|
| +}
|
| +
|
| +bool BasicFocusRules::CanActivateView(View* view) const {
|
| + if (!view)
|
| + return true;
|
| +
|
| + // Only toplevel windows can be activated
|
| + if (!IsToplevelView(view))
|
| + return false;
|
| +
|
| + // The view must be visible.
|
| + if (!view->visible())
|
| + return false;
|
| +
|
| + // TODO(erg): The aura version of this class asks the aura::Window's
|
| + // ActivationDelegate whether the window is activatable.
|
| +
|
| + // 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 (!CanFocusViewImpl(view))
|
| + return false;
|
| +
|
| + // TODO(erg): In the aura version, we also check whether the window is
|
| + // blocked by a modal transient window.
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool BasicFocusRules::CanFocusView(View* view) const {
|
| + // It is possible to focus a NULL window, it is equivalent to clearing focus.
|
| + if (!view)
|
| + return true;
|
| +
|
| + // The focused view is always inside the active view, so views that aren't
|
| + // activatable can't contain the focused view.
|
| + View* activatable = GetActivatableView(view);
|
| + if (!activatable || !activatable->Contains(view))
|
| + return false;
|
| + return CanFocusViewImpl(view);
|
| +}
|
| +
|
| +View* BasicFocusRules::GetToplevelView(View* view) const {
|
| + View* parent = view->parent();
|
| + View* child = view;
|
| + while (parent) {
|
| + if (IsToplevelView(child))
|
| + return child;
|
| +
|
| + parent = parent->parent();
|
| + child = child->parent();
|
| + }
|
| +
|
| + return nullptr;
|
| +}
|
| +
|
| +View* BasicFocusRules::GetActivatableView(View* view) const {
|
| + View* parent = view->parent();
|
| + View* child = view;
|
| + while (parent) {
|
| + if (CanActivateView(child))
|
| + return child;
|
| +
|
| + // TODO(erg): In the aura version of this class, we have a whole bunch of
|
| + // checks to support modal transient windows, and transient parents.
|
| +
|
| + parent = parent->parent();
|
| + child = child->parent();
|
| + }
|
| +
|
| + return nullptr;
|
| +}
|
| +
|
| +View* BasicFocusRules::GetFocusableView(View* view) const {
|
| + if (CanFocusView(view))
|
| + return view;
|
| +
|
| + // |view| may be in a hierarchy that is non-activatable, in which case we
|
| + // need to cut over to the activatable hierarchy.
|
| + View* activatable = GetActivatableView(view);
|
| + if (!activatable) {
|
| + // There may not be a related activatable hierarchy to cut over to, in which
|
| + // case we try an unrelated one.
|
| + View* toplevel = GetToplevelView(view);
|
| + if (toplevel)
|
| + activatable = GetNextActivatableView(toplevel);
|
| + if (!activatable)
|
| + return nullptr;
|
| + }
|
| +
|
| + if (!activatable->Contains(view)) {
|
| + // 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.
|
| + View* focused = GetFocusableView(activatable);
|
| + return activatable->Contains(focused) ? focused : activatable;
|
| + }
|
| +
|
| + while (view && !CanFocusView(view))
|
| + view = view->parent();
|
| + return view;
|
| +}
|
| +
|
| +View* BasicFocusRules::GetNextActivatableView(View* activatable) const {
|
| + DCHECK(activatable);
|
| +
|
| + // In the basic scenarios handled by BasicFocusRules, the pool of activatable
|
| + // windows is limited to the |ignore|'s siblings.
|
| + const View::Children& siblings = activatable->parent()->children();
|
| + DCHECK(!siblings.empty());
|
| +
|
| + for (auto rit = siblings.rbegin(); rit != siblings.rend(); ++rit) {
|
| + View* cur = *rit;
|
| + if (cur == activatable)
|
| + continue;
|
| + if (CanActivateView(cur))
|
| + return cur;
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +// TODO(erg): aura::Window::CanFocus() exists. View::CanFocus() does
|
| +// not. This is a hack that does everything that Window::CanFocus() currently
|
| +// does that doesn't require a delegate or an EventClient.
|
| +bool BasicFocusRules::CanFocusViewImpl(View* view) const {
|
| + // TODO(erg): In unit tests, views will never be drawn, so we can't rely on
|
| + // IsDrawn() here.
|
| + if (IsViewParentedToWindowContainer(view))
|
| + return view->visible();
|
| +
|
| + // TODO(erg): Add the intermediary delegate and event client checks once we
|
| + // have those.
|
| +
|
| + return CanFocusViewImpl(view->parent());
|
| +}
|
| +
|
| +bool BasicFocusRules::IsViewParentedToWindowContainer(View* view) const {
|
| + return view->parent() == window_container_;
|
| +}
|
| +
|
| +} // namespace mojo
|
|
|