1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/ash_focus_rules.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/wm/container_finder.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/float/float_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/window_restore/window_restore_controller.h"
#include "ash/wm/window_state.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "components/app_restore/full_restore_utils.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window,
int container_id) {
for (; window; window = window->parent()) {
if (window->GetId() >= container_id)
return true;
}
return false;
}
bool BelongsToContainerWithId(const aura::Window* window, int container_id) {
for (; window; window = window->parent()) {
if (window->GetId() == container_id)
return true;
}
return false;
}
bool IsInactiveDeskContainerId(int id) {
return desks_util::IsDeskContainerId(id) &&
id != desks_util::GetActiveDeskContainerId();
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, public:
AshFocusRules::AshFocusRules()
: activatable_container_ids_(GetActivatableShellWindowIds()) {}
AshFocusRules::~AshFocusRules() = default;
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, ::wm::FocusRules:
bool AshFocusRules::IsToplevelWindow(const aura::Window* window) const {
DCHECK(window);
// The window must be in a valid hierarchy.
if (!window->GetRootWindow() || !window->parent())
return false;
// The window must exist within a container that supports activation.
// The window cannot be blocked by a modal transient.
return base::Contains(activatable_container_ids_, window->parent()->GetId());
}
bool AshFocusRules::SupportsChildActivation(const aura::Window* window) const {
return base::Contains(activatable_container_ids_, window->GetId());
}
bool AshFocusRules::IsWindowConsideredVisibleForActivation(
const aura::Window* window) const {
DCHECK(window);
Shell* shell = Shell::Get();
// If the |window| doesn't belong to the current active user and also doesn't
// show for the current active user, then it should not be activated.
if (!shell->shell_delegate()->CanShowWindowForUser(window))
return false;
if (window->IsVisible())
return true;
const WindowState* window_state = WindowState::Get(window);
// Minimized windows are hidden in their minimized state, but they can always
// be activated.
if (window_state->IsMinimized())
return true;
if (window_state->IsFloated()) {
auto* float_controller = shell->float_controller();
// Floated windows are hidden if they belong to inactive desks, but they can
// always be activated.
if (float_controller->FindDeskOfFloatedWindow(window) !=
DesksController::Get()->active_desk()) {
return true;
}
// Tucked windows are hidden offscreen, but they can be activated.
if (float_controller->IsFloatedWindowTuckedForTablet(window)) {
return true;
}
}
if (!window->TargetVisibility())
return false;
const aura::Window* const parent = window->parent();
return desks_util::IsDeskContainer(parent) ||
parent->GetId() == kShellWindowId_LockScreenContainer;
}
bool AshFocusRules::CanActivateWindow(const aura::Window* window) const {
// Clearing activation is always permissible.
if (!window)
return true;
if (!WindowRestoreController::CanActivateRestoredWindow(window))
return false;
// Special case during Full Restore that prevents the app list from being
// activated during tablet mode if the topmost window of any root window is a
// Full Restore'd window. See http://crbug/1202923.
if (!WindowRestoreController::CanActivateAppList(window))
return false;
if (!BaseFocusRules::CanActivateWindow(window))
return false;
// Special case to allow the login shelf to be activatable when the OOBE
// modal is visible. See http://crbug/871184
// TODO: remove this special case once login shelf is moved into a child
// widget of the lock screen (https://crbug.com/767235).
if (Shell::Get()->session_controller()->IsUserSessionBlocked() &&
BelongsToContainerWithId(window, kShellWindowId_ShelfContainer)) {
return true;
}
int modal_container_id = Shell::GetOpenSystemModalWindowContainerId();
if (modal_container_id >= 0)
return BelongsToContainerWithEqualOrGreaterId(window, modal_container_id);
return true;
}
bool AshFocusRules::CanFocusWindow(const aura::Window* window,
const ui::Event* event) const {
if (!window)
return true;
if (event && (event->IsMouseEvent() || event->IsGestureEvent()) &&
!window->GetProperty(aura::client::kActivateOnPointerKey)) {
return false;
}
return BaseFocusRules::CanFocusWindow(window, event);
}
aura::Window* AshFocusRules::GetNextActivatableWindow(
aura::Window* ignore) const {
DCHECK(ignore);
// If the window that just lost focus |ignore| has a transient parent, then
// start from the container of that parent, otherwise start from the container
// of the most-recently-used window. If the list of MRU windows is empty, then
// start from the container of |ignore|.
aura::Window* starting_window = nullptr;
aura::Window* transient_parent = ::wm::GetTransientParent(ignore);
OverviewController* overview_controller = Shell::Get()->overview_controller();
// It's possible for this to be called either on shutdown or when a session is
// not yet active, so we need to check for the existence of the overview
// controller.
if (overview_controller && overview_controller->InOverviewSession() &&
overview_controller->overview_session()->IsSavedDeskUiLosingActivation(
ignore)) {
starting_window =
overview_controller->overview_session()->GetOverviewFocusWindow();
} else if (transient_parent) {
starting_window = transient_parent;
} else {
MruWindowTracker* mru = Shell::Get()->mru_window_tracker();
aura::Window::Windows windows = mru->BuildMruWindowList(kActiveDesk);
starting_window = windows.empty() ? ignore : windows[0];
}
DCHECK(starting_window);
// Look for windows to focus in |starting_window|'s container. If none are
// found, we look in all the containers in front of |starting_window|'s
// container, then all behind.
int starting_container_index = 0;
aura::Window* root = starting_window->GetRootWindow();
if (!root)
root = Shell::GetRootWindowForNewWindows();
const int container_count = activatable_container_ids_.size();
for (int i = 0; i < container_count; i++) {
aura::Window* container =
Shell::GetContainer(root, activatable_container_ids_[i]);
if (container && container->Contains(starting_window)) {
starting_container_index = i;
break;
}
}
aura::Window* window = nullptr;
for (int i = starting_container_index; !window && i < container_count; i++)
window = GetTopmostWindowToActivateForContainerIndex(i, ignore, root);
if (!window && starting_container_index > 0) {
for (int i = starting_container_index - 1; !window && i >= 0; i--)
window = GetTopmostWindowToActivateForContainerIndex(i, ignore, root);
}
return window;
}
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, private:
aura::Window* AshFocusRules::GetTopmostWindowToActivateForContainerIndex(
int index,
aura::Window* ignore,
aura::Window* priority_root) const {
const int container_id = activatable_container_ids_[index];
// Inactive desk containers should be ignored, since windows in them should
// never be returned as a next activatable window.
if (IsInactiveDeskContainerId(container_id))
return nullptr;
aura::Window* window = nullptr;
aura::Window::Windows containers =
GetContainersForAllRootWindows(container_id, priority_root);
// Favor the top-most window (if any) on `priority_root`, since
// `GetContainersForAllRootWindows()` will put the container belonging to
// `priority_root` first.
for (aura::Window* container : containers) {
window = GetTopmostWindowToActivateInContainer(container, ignore);
if (window)
return window;
}
return window;
}
aura::Window* AshFocusRules::GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const {
for (aura::Window* child : base::Reversed(container->children())) {
WindowState* window_state = WindowState::Get(child);
// A floated window should not be activatable if it's hidden on an inactive
// desk.
if (child != ignore && window_state->CanActivate() &&
!window_state->IsMinimized() &&
!(window_state->IsFloated() && !child->IsVisible())) {
return child;
}
}
return nullptr;
}
} // namespace ash
|