| // 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 "services/window_manager/basic_focus_rules.h" |
| |
| #include "base/macros.h" |
| #include "mojo/services/view_manager/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 |