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