Home | History | Annotate | Download | only in wm
      1 // Copyright 2014 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/window_cycle_list.h"
      6 
      7 #include "ash/shell.h"
      8 #include "ash/wm/mru_window_tracker.h"
      9 #include "ash/wm/window_animations.h"
     10 #include "ash/wm/window_state.h"
     11 #include "ash/wm/window_util.h"
     12 #include "ui/aura/window.h"
     13 
     14 namespace ash {
     15 
     16 // Returns the window immediately below |window| in the current container.
     17 aura::Window* GetWindowBelow(aura::Window* window) {
     18   aura::Window* parent = window->parent();
     19   if (!parent)
     20     return NULL;
     21   aura::Window::Windows::const_iterator iter =
     22       std::find(parent->children().begin(), parent->children().end(), window);
     23   CHECK(*iter == window);
     24   if (iter != parent->children().begin())
     25     return *(iter - 1);
     26   else
     27     return NULL;
     28 }
     29 
     30 // This class restores and moves a window to the front of the stacking order for
     31 // the duration of the class's scope.
     32 class ScopedShowWindow : public aura::WindowObserver {
     33  public:
     34   ScopedShowWindow();
     35   virtual ~ScopedShowWindow();
     36 
     37   // Show |window| at the top of the stacking order.
     38   void Show(aura::Window* window);
     39 
     40   // Cancel restoring the window on going out of scope.
     41   void CancelRestore();
     42 
     43   aura::Window* window() { return window_; }
     44 
     45   // aura::WindowObserver:
     46   virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
     47 
     48  private:
     49   // The window being shown.
     50   aura::Window* window_;
     51 
     52   // The window immediately below where window_ belongs.
     53   aura::Window* stack_window_above_;
     54 
     55   // If true, minimize window_ on going out of scope.
     56   bool minimized_;
     57 
     58   DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow);
     59 };
     60 
     61 ScopedShowWindow::ScopedShowWindow()
     62     : window_(NULL),
     63       stack_window_above_(NULL),
     64       minimized_(false) {
     65 }
     66 
     67 ScopedShowWindow::~ScopedShowWindow() {
     68   if (window_) {
     69     window_->parent()->RemoveObserver(this);
     70 
     71     // Restore window's stacking position.
     72     if (stack_window_above_)
     73       window_->parent()->StackChildAbove(window_, stack_window_above_);
     74     else
     75       window_->parent()->StackChildAtBottom(window_);
     76 
     77     // Restore minimized state.
     78     if (minimized_)
     79       wm::GetWindowState(window_)->Minimize();
     80   }
     81 }
     82 
     83 void ScopedShowWindow::Show(aura::Window* window) {
     84   DCHECK(!window_);
     85   window_ = window;
     86   stack_window_above_ = GetWindowBelow(window);
     87   minimized_ = wm::GetWindowState(window)->IsMinimized();
     88   window_->parent()->AddObserver(this);
     89   window_->Show();
     90   wm::GetWindowState(window_)->Activate();
     91 }
     92 
     93 void ScopedShowWindow::CancelRestore() {
     94   if (!window_)
     95     return;
     96   window_->parent()->RemoveObserver(this);
     97   window_ = stack_window_above_ = NULL;
     98 }
     99 
    100 void ScopedShowWindow::OnWillRemoveWindow(aura::Window* window) {
    101   if (window == window_) {
    102     CancelRestore();
    103   } else if (window == stack_window_above_) {
    104     // If the window this window was above is removed, use the next window down
    105     // as the restore marker.
    106     stack_window_above_ = GetWindowBelow(stack_window_above_);
    107   }
    108 }
    109 
    110 WindowCycleList::WindowCycleList(const WindowList& windows)
    111     : windows_(windows),
    112       current_index_(0) {
    113   ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true);
    114 
    115   for (WindowList::const_iterator i = windows_.begin(); i != windows_.end();
    116        ++i) {
    117     (*i)->AddObserver(this);
    118   }
    119 }
    120 
    121 WindowCycleList::~WindowCycleList() {
    122   ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(false);
    123   for (WindowList::const_iterator i = windows_.begin(); i != windows_.end();
    124        ++i) {
    125     (*i)->RemoveObserver(this);
    126   }
    127   if (showing_window_)
    128     showing_window_->CancelRestore();
    129 }
    130 
    131 void WindowCycleList::Step(WindowCycleController::Direction direction) {
    132   if (windows_.empty())
    133     return;
    134 
    135   // When there is only one window, we should give feedback to the user. If the
    136   // window is minimized, we should also show it.
    137   if (windows_.size() == 1) {
    138     ::wm::AnimateWindow(windows_[0], ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
    139     windows_[0]->Show();
    140     wm::GetWindowState(windows_[0])->Activate();
    141     return;
    142   }
    143 
    144   DCHECK(static_cast<size_t>(current_index_) < windows_.size());
    145 
    146   // We're in a valid cycle, so step forward or backward.
    147   current_index_ += direction == WindowCycleController::FORWARD ? 1 : -1;
    148 
    149   // Wrap to window list size.
    150   current_index_ = (current_index_ + windows_.size()) % windows_.size();
    151   DCHECK(windows_[current_index_]);
    152 
    153   // Make sure the next window is visible.
    154   showing_window_.reset(new ScopedShowWindow);
    155   showing_window_->Show(windows_[current_index_]);
    156 }
    157 
    158 void WindowCycleList::OnWindowDestroyed(aura::Window* window) {
    159   window->RemoveObserver(this);
    160 
    161   WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
    162   DCHECK(i != windows_.end());
    163   int removed_index = static_cast<int>(i - windows_.begin());
    164   windows_.erase(i);
    165   if (current_index_ > removed_index ||
    166       current_index_ == static_cast<int>(windows_.size())) {
    167     current_index_--;
    168   }
    169 }
    170 
    171 }  // namespace ash
    172