| // 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() : nullptr; |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // BaseFocusRules, protected: |
| |
| BaseFocusRules::BaseFocusRules() = default; |
| |
| BaseFocusRules::~BaseFocusRules() = default; |
| |
| 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 (GetActivationDelegate(window) && |
| !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 ui::Event* event) 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 nullptr; |
| } |
| |
| 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 nullptr; |
| } |
| |
| aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const { |
| if (CanFocusWindow(window, nullptr)) |
| 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 nullptr; |
| } |
| |
| 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, nullptr)) |
| 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 nullptr; |
| |
| // 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 (auto rit = siblings.rbegin(); rit != siblings.rend(); ++rit) { |
| aura::Window* cur = *rit; |
| if (cur == ignore) |
| continue; |
| if (CanActivateWindow(cur)) |
| return cur; |
| } |
| return nullptr; |
| } |
| |
| } // namespace wm |