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