| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/wm/window_cycle_list.h" |
| |
| #include "ash/shell.h" |
| #include "ash/wm/mru_window_tracker.h" |
| #include "ash/wm/window_animations.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_util.h" |
| #include "ui/aura/window.h" |
| |
| namespace ash { |
| |
| // Returns the window immediately below |window| in the current container. |
| aura::Window* GetWindowBelow(aura::Window* window) { |
| aura::Window* parent = window->parent(); |
| if (!parent) |
| return NULL; |
| aura::Window::Windows::const_iterator iter = |
| std::find(parent->children().begin(), parent->children().end(), window); |
| CHECK(*iter == window); |
| if (iter != parent->children().begin()) |
| return *(iter - 1); |
| else |
| return NULL; |
| } |
| |
| // This class restores and moves a window to the front of the stacking order for |
| // the duration of the class's scope. |
| class ScopedShowWindow : public aura::WindowObserver { |
| public: |
| ScopedShowWindow(); |
| virtual ~ScopedShowWindow(); |
| |
| // Show |window| at the top of the stacking order. |
| void Show(aura::Window* window); |
| |
| // Cancel restoring the window on going out of scope. |
| void CancelRestore(); |
| |
| aura::Window* window() { return window_; } |
| |
| // aura::WindowObserver: |
| virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE; |
| |
| private: |
| // The window being shown. |
| aura::Window* window_; |
| |
| // The window immediately below where window_ belongs. |
| aura::Window* stack_window_above_; |
| |
| // If true, minimize window_ on going out of scope. |
| bool minimized_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow); |
| }; |
| |
| ScopedShowWindow::ScopedShowWindow() |
| : window_(NULL), |
| stack_window_above_(NULL), |
| minimized_(false) { |
| } |
| |
| ScopedShowWindow::~ScopedShowWindow() { |
| if (window_) { |
| window_->parent()->RemoveObserver(this); |
| |
| // Restore window's stacking position. |
| if (stack_window_above_) |
| window_->parent()->StackChildAbove(window_, stack_window_above_); |
| else |
| window_->parent()->StackChildAtBottom(window_); |
| |
| // Restore minimized state. |
| if (minimized_) |
| wm::GetWindowState(window_)->Minimize(); |
| } |
| } |
| |
| void ScopedShowWindow::Show(aura::Window* window) { |
| DCHECK(!window_); |
| window_ = window; |
| stack_window_above_ = GetWindowBelow(window); |
| minimized_ = wm::GetWindowState(window)->IsMinimized(); |
| window_->parent()->AddObserver(this); |
| window_->Show(); |
| wm::GetWindowState(window_)->Activate(); |
| } |
| |
| void ScopedShowWindow::CancelRestore() { |
| if (!window_) |
| return; |
| window_->parent()->RemoveObserver(this); |
| window_ = stack_window_above_ = NULL; |
| } |
| |
| void ScopedShowWindow::OnWillRemoveWindow(aura::Window* window) { |
| if (window == window_) { |
| CancelRestore(); |
| } else if (window == stack_window_above_) { |
| // If the window this window was above is removed, use the next window down |
| // as the restore marker. |
| stack_window_above_ = GetWindowBelow(stack_window_above_); |
| } |
| } |
| |
| WindowCycleList::WindowCycleList(const WindowList& windows) |
| : windows_(windows), |
| current_index_(0) { |
| ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true); |
| |
| for (WindowList::const_iterator i = windows_.begin(); i != windows_.end(); |
| ++i) { |
| (*i)->AddObserver(this); |
| } |
| } |
| |
| WindowCycleList::~WindowCycleList() { |
| ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(false); |
| for (WindowList::const_iterator i = windows_.begin(); i != windows_.end(); |
| ++i) { |
| (*i)->RemoveObserver(this); |
| } |
| if (showing_window_) |
| showing_window_->CancelRestore(); |
| } |
| |
| void WindowCycleList::Step(WindowCycleController::Direction direction) { |
| if (windows_.empty()) |
| return; |
| |
| // When there is only one window, we should give feedback to the user. If the |
| // window is minimized, we should also show it. |
| if (windows_.size() == 1) { |
| ::wm::AnimateWindow(windows_[0], ::wm::WINDOW_ANIMATION_TYPE_BOUNCE); |
| windows_[0]->Show(); |
| wm::GetWindowState(windows_[0])->Activate(); |
| return; |
| } |
| |
| DCHECK(static_cast<size_t>(current_index_) < windows_.size()); |
| |
| // We're in a valid cycle, so step forward or backward. |
| current_index_ += direction == WindowCycleController::FORWARD ? 1 : -1; |
| |
| // Wrap to window list size. |
| current_index_ = (current_index_ + windows_.size()) % windows_.size(); |
| DCHECK(windows_[current_index_]); |
| |
| // Make sure the next window is visible. |
| showing_window_.reset(new ScopedShowWindow); |
| showing_window_->Show(windows_[current_index_]); |
| } |
| |
| void WindowCycleList::OnWindowDestroyed(aura::Window* window) { |
| window->RemoveObserver(this); |
| |
| WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window); |
| DCHECK(i != windows_.end()); |
| int removed_index = static_cast<int>(i - windows_.begin()); |
| windows_.erase(i); |
| if (current_index_ > removed_index || |
| current_index_ == static_cast<int>(windows_.size())) { |
| current_index_--; |
| } |
| } |
| |
| } // namespace ash |