Home | History | Annotate | Download | only in corewm
      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/views/corewm/base_focus_rules.h"
      6 
      7 #include "ui/aura/client/activation_delegate.h"
      8 #include "ui/aura/client/focus_client.h"
      9 #include "ui/aura/root_window.h"
     10 #include "ui/aura/window.h"
     11 #include "ui/views/corewm/window_modality_controller.h"
     12 
     13 namespace views {
     14 namespace corewm {
     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 (child->transient_parent()) {
    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(child->transient_parent());
    128       if (parent_modal_transient == child)
    129         return child;
    130 
    131       return GetActivatableWindow(child->transient_parent());
    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 corewm
    196 }  // namespace views
    197