1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/wm/core/base_focus_rules.h" 6 7 #include "ui/aura/client/focus_client.h" 8 #include "ui/aura/window.h" 9 #include "ui/aura/window_event_dispatcher.h" 10 #include "ui/wm/core/window_modality_controller.h" 11 #include "ui/wm/core/window_util.h" 12 #include "ui/wm/public/activation_delegate.h" 13 14 namespace wm { 15 namespace { 16 17 aura::Window* GetFocusedWindow(aura::Window* context) { 18 aura::client::FocusClient* focus_client = 19 aura::client::GetFocusClient(context); 20 return focus_client ? focus_client->GetFocusedWindow() : NULL; 21 } 22 23 } // namespace 24 25 //////////////////////////////////////////////////////////////////////////////// 26 // BaseFocusRules, protected: 27 28 BaseFocusRules::BaseFocusRules() { 29 } 30 31 BaseFocusRules::~BaseFocusRules() { 32 } 33 34 bool BaseFocusRules::IsWindowConsideredVisibleForActivation( 35 aura::Window* window) const { 36 return window->IsVisible(); 37 } 38 39 //////////////////////////////////////////////////////////////////////////////// 40 // BaseFocusRules, FocusRules implementation: 41 42 bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const { 43 // The window must in a valid hierarchy. 44 if (!window->GetRootWindow()) 45 return false; 46 47 // The window must exist within a container that supports activation. 48 // The window cannot be blocked by a modal transient. 49 return SupportsChildActivation(window->parent()); 50 } 51 52 bool BaseFocusRules::CanActivateWindow(aura::Window* window) const { 53 // It is possible to activate a NULL window, it is equivalent to clearing 54 // activation. 55 if (!window) 56 return true; 57 58 // Only toplevel windows can be activated. 59 if (!IsToplevelWindow(window)) 60 return false; 61 62 // The window must be visible. 63 if (!IsWindowConsideredVisibleForActivation(window)) 64 return false; 65 66 // The window's activation delegate must allow this window to be activated. 67 if (aura::client::GetActivationDelegate(window) && 68 !aura::client::GetActivationDelegate(window)->ShouldActivate()) { 69 return false; 70 } 71 72 // A window must be focusable to be activatable. We don't call 73 // CanFocusWindow() from here because it will call back to us via 74 // GetActivatableWindow(). 75 if (!window->CanFocus()) 76 return false; 77 78 // The window cannot be blocked by a modal transient. 79 return !GetModalTransient(window); 80 } 81 82 bool BaseFocusRules::CanFocusWindow(aura::Window* window) const { 83 // It is possible to focus a NULL window, it is equivalent to clearing focus. 84 if (!window) 85 return true; 86 87 // The focused window is always inside the active window, so windows that 88 // aren't activatable can't contain the focused window. 89 aura::Window* activatable = GetActivatableWindow(window); 90 if (!activatable || !activatable->Contains(window)) 91 return false; 92 return window->CanFocus(); 93 } 94 95 aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const { 96 aura::Window* parent = window->parent(); 97 aura::Window* child = window; 98 while (parent) { 99 if (IsToplevelWindow(child)) 100 return child; 101 102 parent = parent->parent(); 103 child = child->parent(); 104 } 105 return NULL; 106 } 107 108 aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const { 109 aura::Window* parent = window->parent(); 110 aura::Window* child = window; 111 while (parent) { 112 if (CanActivateWindow(child)) 113 return child; 114 115 // CanActivateWindow() above will return false if |child| is blocked by a 116 // modal transient. In this case the modal is or contains the activatable 117 // window. We recurse because the modal may itself be blocked by a modal 118 // transient. 119 aura::Window* modal_transient = GetModalTransient(child); 120 if (modal_transient) 121 return GetActivatableWindow(modal_transient); 122 123 if (wm::GetTransientParent(child)) { 124 // To avoid infinite recursion, if |child| has a transient parent 125 // whose own modal transient is |child| itself, just return |child|. 126 aura::Window* parent_modal_transient = 127 GetModalTransient(wm::GetTransientParent(child)); 128 if (parent_modal_transient == child) 129 return child; 130 131 return GetActivatableWindow(wm::GetTransientParent(child)); 132 } 133 134 parent = parent->parent(); 135 child = child->parent(); 136 } 137 return NULL; 138 } 139 140 aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const { 141 if (CanFocusWindow(window)) 142 return window; 143 144 // |window| may be in a hierarchy that is non-activatable, in which case we 145 // need to cut over to the activatable hierarchy. 146 aura::Window* activatable = GetActivatableWindow(window); 147 if (!activatable) { 148 // There may not be a related activatable hierarchy to cut over to, in which 149 // case we try an unrelated one. 150 aura::Window* toplevel = GetToplevelWindow(window); 151 if (toplevel) 152 activatable = GetNextActivatableWindow(toplevel); 153 if (!activatable) 154 return NULL; 155 } 156 157 if (!activatable->Contains(window)) { 158 // If there's already a child window focused in the activatable hierarchy, 159 // just use that (i.e. don't shift focus), otherwise we need to at least cut 160 // over to the activatable hierarchy. 161 aura::Window* focused = GetFocusedWindow(activatable); 162 return activatable->Contains(focused) ? focused : activatable; 163 } 164 165 while (window && !CanFocusWindow(window)) 166 window = window->parent(); 167 return window; 168 } 169 170 aura::Window* BaseFocusRules::GetNextActivatableWindow( 171 aura::Window* ignore) const { 172 DCHECK(ignore); 173 174 // Can be called from the RootWindow's destruction, which has a NULL parent. 175 if (!ignore->parent()) 176 return NULL; 177 178 // In the basic scenarios handled by BasicFocusRules, the pool of activatable 179 // windows is limited to the |ignore|'s siblings. 180 const aura::Window::Windows& siblings = ignore->parent()->children(); 181 DCHECK(!siblings.empty()); 182 183 for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin(); 184 rit != siblings.rend(); 185 ++rit) { 186 aura::Window* cur = *rit; 187 if (cur == ignore) 188 continue; 189 if (CanActivateWindow(cur)) 190 return cur; 191 } 192 return NULL; 193 } 194 195 } // namespace wm 196