Home | History | Annotate | Download | only in wm
      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 "ash/wm/activation_controller.h"
      6 
      7 #include "ash/root_window_controller.h"
      8 #include "ash/shell.h"
      9 #include "ash/shell_window_ids.h"
     10 #include "ash/wm/activation_controller_delegate.h"
     11 #include "ash/wm/property_util.h"
     12 #include "ash/wm/window_util.h"
     13 #include "base/auto_reset.h"
     14 #include "ui/aura/client/activation_change_observer.h"
     15 #include "ui/aura/client/activation_delegate.h"
     16 #include "ui/aura/client/aura_constants.h"
     17 #include "ui/aura/client/focus_client.h"
     18 #include "ui/aura/env.h"
     19 #include "ui/aura/root_window.h"
     20 #include "ui/aura/window.h"
     21 #include "ui/aura/window_delegate.h"
     22 #include "ui/base/ui_base_types.h"
     23 #include "ui/compositor/layer.h"
     24 #include "ui/views/corewm/window_modality_controller.h"
     25 
     26 namespace ash {
     27 namespace internal {
     28 namespace {
     29 
     30 // These are the list of container ids of containers which may contain windows
     31 // that need to be activated in the order that they should be activated.
     32 const int kWindowContainerIds[] = {
     33     kShellWindowId_LockSystemModalContainer,
     34     kShellWindowId_SettingBubbleContainer,
     35     kShellWindowId_LockScreenContainer,
     36     kShellWindowId_SystemModalContainer,
     37     kShellWindowId_AlwaysOnTopContainer,
     38     kShellWindowId_AppListContainer,
     39     kShellWindowId_DefaultContainer,
     40 
     41     // Docked, panel, launcher and status are intentionally checked after other
     42     // containers even though these layers are higher. The user expects their
     43     // windows to be focused before these elements.
     44     kShellWindowId_DockedContainer,
     45     kShellWindowId_PanelContainer,
     46     kShellWindowId_ShelfContainer,
     47     kShellWindowId_StatusContainer,
     48 };
     49 
     50 bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window,
     51                                             int container_id) {
     52   for (; window; window = window->parent()) {
     53     if (window->id() >= container_id)
     54       return true;
     55   }
     56   return false;
     57 }
     58 
     59 // Returns true if children of |window| can be activated.
     60 // These are the only containers in which windows can receive focus.
     61 bool SupportsChildActivation(aura::Window* window) {
     62   for (size_t i = 0; i < arraysize(kWindowContainerIds); i++) {
     63     if (window->id() == kWindowContainerIds[i])
     64       return true;
     65   }
     66   return false;
     67 }
     68 
     69 bool HasModalTransientChild(aura::Window* window) {
     70   aura::Window::Windows::const_iterator it;
     71   for (it = window->transient_children().begin();
     72        it != window->transient_children().end();
     73        ++it) {
     74     if ((*it)->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW)
     75       return true;
     76   }
     77   return false;
     78 }
     79 
     80 // See description in VisibilityMatches.
     81 enum ActivateVisibilityType {
     82   TARGET_VISIBILITY,
     83   CURRENT_VISIBILITY,
     84 };
     85 
     86 // Used by CanActivateWindowWithEvent() to test the visibility of a window.
     87 // This is used by two distinct code paths:
     88 // . when activating from an event we only care about the actual visibility.
     89 // . when activating because of a keyboard accelerator, in which case we
     90 //   care about the TargetVisibility.
     91 bool VisibilityMatches(aura::Window* window, ActivateVisibilityType type) {
     92   bool visible = (type == CURRENT_VISIBILITY) ? window->IsVisible() :
     93       window->TargetVisibility();
     94   return visible || wm::IsWindowMinimized(window) ||
     95       (window->TargetVisibility() &&
     96         (window->parent()->id() == kShellWindowId_DefaultContainer ||
     97          window->parent()->id() == kShellWindowId_LockScreenContainer));
     98 }
     99 
    100 // Returns true if |window| can be activated or deactivated.
    101 // A window manager typically defines some notion of "top level window" that
    102 // supports activation/deactivation.
    103 bool CanActivateWindowWithEvent(aura::Window* window,
    104                                 const ui::Event* event,
    105                                 ActivateVisibilityType visibility_type) {
    106   return window &&
    107       VisibilityMatches(window, visibility_type) &&
    108       (!aura::client::GetActivationDelegate(window) ||
    109         aura::client::GetActivationDelegate(window)->ShouldActivate()) &&
    110       SupportsChildActivation(window->parent()) &&
    111       (BelongsToContainerWithEqualOrGreaterId(
    112           window, kShellWindowId_SystemModalContainer) ||
    113        !Shell::GetInstance()->IsSystemModalWindowOpen());
    114 }
    115 
    116 // When a modal window is activated, we bring its entire transient parent chain
    117 // to the front. This function must be called before the modal transient is
    118 // stacked at the top to ensure correct stacking order.
    119 void StackTransientParentsBelowModalWindow(aura::Window* window) {
    120   if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
    121     return;
    122 
    123   aura::Window* transient_parent = window->transient_parent();
    124   while (transient_parent) {
    125     transient_parent->parent()->StackChildAtTop(transient_parent);
    126     transient_parent = transient_parent->transient_parent();
    127   }
    128 }
    129 
    130 aura::Window* FindFocusableWindowFor(aura::Window* window) {
    131   while (window && !window->CanFocus())
    132     window = window->parent();
    133   return window;
    134 }
    135 
    136 }  // namespace
    137 
    138 ////////////////////////////////////////////////////////////////////////////////
    139 // ActivationController, public:
    140 
    141 ActivationController::ActivationController(
    142     aura::client::FocusClient* focus_client,
    143     ActivationControllerDelegate* delegate)
    144     : focus_client_(focus_client),
    145       updating_activation_(false),
    146       active_window_(NULL),
    147       observer_manager_(this),
    148       delegate_(delegate) {
    149   aura::Env::GetInstance()->AddObserver(this);
    150   focus_client_->AddObserver(this);
    151 }
    152 
    153 ActivationController::~ActivationController() {
    154   aura::Env::GetInstance()->RemoveObserver(this);
    155   focus_client_->RemoveObserver(this);
    156 }
    157 
    158 // static
    159 aura::Window* ActivationController::GetActivatableWindow(
    160     aura::Window* window,
    161     const ui::Event* event) {
    162   aura::Window* parent = window->parent();
    163   aura::Window* child = window;
    164   while (parent) {
    165     if (CanActivateWindowWithEvent(child, event, CURRENT_VISIBILITY))
    166       return child;
    167     // If |child| isn't activatable, but has transient parent, trace
    168     // that path instead.
    169     if (child->transient_parent())
    170       return GetActivatableWindow(child->transient_parent(), event);
    171     parent = parent->parent();
    172     child = child->parent();
    173   }
    174   return NULL;
    175 }
    176 
    177 bool ActivationController::CanActivateWindow(aura::Window* window) const {
    178   return CanActivateWindowWithEvent(window, NULL, TARGET_VISIBILITY) &&
    179          !HasModalTransientChild(window);
    180 }
    181 
    182 ////////////////////////////////////////////////////////////////////////////////
    183 // ActivationController, aura::client::ActivationClient implementation:
    184 
    185 void ActivationController::AddObserver(
    186     aura::client::ActivationChangeObserver* observer) {
    187   observers_.AddObserver(observer);
    188 }
    189 
    190 void ActivationController::RemoveObserver(
    191     aura::client::ActivationChangeObserver* observer) {
    192   observers_.RemoveObserver(observer);
    193 }
    194 
    195 void ActivationController::ActivateWindow(aura::Window* window) {
    196   ActivateWindowWithEvent(window, NULL);
    197 }
    198 
    199 void ActivationController::DeactivateWindow(aura::Window* window) {
    200   if (window)
    201     ActivateNextWindow(window);
    202 }
    203 
    204 aura::Window* ActivationController::GetActiveWindow() {
    205   return active_window_;
    206 }
    207 
    208 aura::Window* ActivationController::GetActivatableWindow(aura::Window* window) {
    209   return GetActivatableWindow(window, NULL);
    210 }
    211 
    212 aura::Window* ActivationController::GetToplevelWindow(aura::Window* window) {
    213   return GetActivatableWindow(window, NULL);
    214 }
    215 
    216 bool ActivationController::OnWillFocusWindow(aura::Window* window,
    217                                              const ui::Event* event) {
    218   return CanActivateWindowWithEvent(
    219       GetActivatableWindow(window, event), event, CURRENT_VISIBILITY);
    220 }
    221 
    222 ////////////////////////////////////////////////////////////////////////////////
    223 // ActivationController, aura::WindowObserver implementation:
    224 
    225 void ActivationController::OnWindowVisibilityChanged(aura::Window* window,
    226                                                      bool visible) {
    227   if (!visible) {
    228     aura::Window* next_window = ActivateNextWindow(window);
    229     if (next_window && next_window->parent() == window->parent()) {
    230       // Despite the activation change, we need to keep the window being hidden
    231       // stacked above the new window so it stays on top as it animates away.
    232       window->layer()->parent()->StackAbove(window->layer(),
    233                                             next_window->layer());
    234     }
    235   }
    236 }
    237 
    238 void ActivationController::OnWindowDestroying(aura::Window* window) {
    239   // Don't use wm::IsActiveWidnow in case the |window| has already been
    240   // removed from the root tree.
    241   if (active_window_ == window) {
    242     active_window_ = NULL;
    243     FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
    244                       observers_,
    245                       OnWindowActivated(NULL, window));
    246     ActivateWindow(GetTopmostWindowToActivate(window));
    247   }
    248   observer_manager_.Remove(window);
    249 }
    250 
    251 ////////////////////////////////////////////////////////////////////////////////
    252 // ActivationController, aura::EnvObserver implementation:
    253 
    254 void ActivationController::OnWindowInitialized(aura::Window* window) {
    255   observer_manager_.Add(window);
    256 }
    257 
    258 ////////////////////////////////////////////////////////////////////////////////
    259 // ActivationController, aura::RootWindowObserver implementation:
    260 
    261 void ActivationController::OnWindowFocused(aura::Window* gained_focus,
    262                                            aura::Window* lost_focus) {
    263   if (gained_focus)
    264     ActivateWindow(GetActivatableWindow(gained_focus, NULL));
    265 }
    266 
    267 ////////////////////////////////////////////////////////////////////////////////
    268 // ActivationController, ui::EventHandler implementation:
    269 
    270 void ActivationController::OnKeyEvent(ui::KeyEvent* event) {
    271 }
    272 
    273 void ActivationController::OnMouseEvent(ui::MouseEvent* event) {
    274   if (event->type() == ui::ET_MOUSE_PRESSED)
    275     FocusWindowWithEvent(event);
    276 }
    277 
    278 void ActivationController::OnScrollEvent(ui::ScrollEvent* event) {
    279 }
    280 
    281 void ActivationController::OnTouchEvent(ui::TouchEvent* event) {
    282   if (event->type() == ui::ET_TOUCH_PRESSED)
    283     FocusWindowWithEvent(event);
    284 }
    285 
    286 void ActivationController::OnGestureEvent(ui::GestureEvent* event) {
    287   if (event->type() == ui::ET_GESTURE_BEGIN &&
    288       event->details().touch_points() == 1) {
    289     FocusWindowWithEvent(event);
    290   }
    291 }
    292 
    293 ////////////////////////////////////////////////////////////////////////////////
    294 // ActivationController, private:
    295 
    296 void ActivationController::ActivateWindowWithEvent(aura::Window* window,
    297                                                    const ui::Event* event) {
    298   // Prevent recursion when called from focus.
    299   if (updating_activation_)
    300     return;
    301   base::AutoReset<bool> in_activate_window(&updating_activation_, true);
    302 
    303   // We allow the delegate to change which window gets activated, or to prevent
    304   // activation changes.
    305   aura::Window* original_active_window = window;
    306   window = delegate_->WillActivateWindow(window);
    307   // TODO(beng): note that this breaks the previous behavior where an activation
    308   //             attempt by a window behind the lock screen would at least
    309   //             restack that window frontmost within its container. fix this.
    310   if (!window && original_active_window != window)
    311     return;
    312 
    313   // TODO(beng): This encapsulates additional Ash-specific restrictions on
    314   //             whether activation can change. Should move to the delegate.
    315   if (window && !CanActivateWindowWithEvent(window, event, CURRENT_VISIBILITY))
    316     return;
    317 
    318   if (active_window_ == window)
    319     return;
    320 
    321   aura::Window* old_active = active_window_;
    322   active_window_ = window;
    323 
    324   if (window &&
    325       !window->Contains(aura::client::GetFocusClient(window)->
    326           GetFocusedWindow())) {
    327     aura::client::GetFocusClient(window)->FocusWindow(window);
    328   }
    329 
    330   if (window) {
    331     StackTransientParentsBelowModalWindow(window);
    332     window->parent()->StackChildAtTop(window);
    333   }
    334 
    335   FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver,
    336                     observers_,
    337                     OnWindowActivated(window, old_active));
    338   if (aura::client::GetActivationChangeObserver(old_active)) {
    339     aura::client::GetActivationChangeObserver(old_active)->OnWindowActivated(
    340         window, old_active);
    341   }
    342   if (aura::client::GetActivationChangeObserver(window)) {
    343     aura::client::GetActivationChangeObserver(window)->OnWindowActivated(
    344         window, old_active);
    345   }
    346 }
    347 
    348 aura::Window* ActivationController::ActivateNextWindow(aura::Window* window) {
    349   aura::Window* next_window = NULL;
    350   if (wm::IsActiveWindow(window)) {
    351     next_window = GetTopmostWindowToActivate(window);
    352     ActivateWindow(next_window);
    353   }
    354   return next_window;
    355 }
    356 
    357 aura::Window* ActivationController::GetTopmostWindowToActivate(
    358     aura::Window* ignore) const {
    359   size_t current_container_index = 0;
    360   // If the container of the window losing focus is in the list, start from that
    361   // container.
    362   aura::RootWindow* root = ignore->GetRootWindow();
    363   if (!root)
    364     root = Shell::GetActiveRootWindow();
    365   for (size_t i = 0; ignore && i < arraysize(kWindowContainerIds); i++) {
    366     aura::Window* container = Shell::GetContainer(root, kWindowContainerIds[i]);
    367     if (container && container->Contains(ignore)) {
    368       current_container_index = i;
    369       break;
    370     }
    371   }
    372 
    373   // Look for windows to focus in that container and below.
    374   aura::Window* window = NULL;
    375   for (; !window && current_container_index < arraysize(kWindowContainerIds);
    376        current_container_index++) {
    377     aura::Window::Windows containers = Shell::GetContainersFromAllRootWindows(
    378         kWindowContainerIds[current_container_index],
    379         root);
    380     for (aura::Window::Windows::const_iterator iter = containers.begin();
    381          iter != containers.end() && !window; ++iter) {
    382       window = GetTopmostWindowToActivateInContainer((*iter), ignore);
    383     }
    384   }
    385   return window;
    386 }
    387 
    388 aura::Window* ActivationController::GetTopmostWindowToActivateInContainer(
    389     aura::Window* container,
    390     aura::Window* ignore) const {
    391   for (aura::Window::Windows::const_reverse_iterator i =
    392            container->children().rbegin();
    393        i != container->children().rend();
    394        ++i) {
    395     if (*i != ignore &&
    396         CanActivateWindowWithEvent(*i, NULL, CURRENT_VISIBILITY) &&
    397         !wm::IsWindowMinimized(*i))
    398       return *i;
    399   }
    400   return NULL;
    401 }
    402 
    403 void ActivationController::FocusWindowWithEvent(const ui::Event* event) {
    404   aura::Window* window = static_cast<aura::Window*>(event->target());
    405   window = delegate_->WillFocusWindow(window);
    406   if (GetActiveWindow() != window) {
    407     aura::client::GetFocusClient(window)->FocusWindow(
    408         FindFocusableWindowFor(window));
    409   }
    410 }
    411 
    412 }  // namespace internal
    413 }  // namespace ash
    414