| // Copyright 2013 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/mru_window_tracker.h" |
| |
| #include <algorithm> |
| |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/public/cpp/window_properties.h" |
| #include "ash/session/session_controller.h" |
| #include "ash/shell.h" |
| #include "ash/wm/focus_rules.h" |
| #include "ash/wm/switchable_windows.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_util.h" |
| #include "base/containers/adapters.h" |
| #include "base/stl_util.h" |
| #include "ui/aura/window.h" |
| #include "ui/wm/core/window_util.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // A predicate that determines whether |window| can be included in the MRU |
| // window list. |
| bool CanIncludeWindowInMruList(aura::Window* window) { |
| return ::wm::CanActivateWindow(window) && |
| !wm::GetWindowState(window)->IsPip(); |
| } |
| |
| // A predicate that determines whether |window| can be included in the list |
| // built for cycling through windows (alt + tab). |
| bool CanIncludeWindowInCycleList(aura::Window* window) { |
| return CanIncludeWindowInMruList(window) && |
| !wm::ShouldExcludeForCycleList(window); |
| } |
| |
| // Returns a list of windows ordered by their stacking order such that the most |
| // recently used window is at the front of the list. |
| // If |mru_windows| is passed, these windows are moved to the front of the list. |
| // It uses the given |can_include_window_predicate| to determine whether to |
| // include a window in the returned list or not. |
| template <class CanIncludeWindowPredicate> |
| MruWindowTracker::WindowList BuildWindowListInternal( |
| const std::vector<aura::Window*>* mru_windows, |
| CanIncludeWindowPredicate can_include_window_predicate) { |
| MruWindowTracker::WindowList windows; |
| |
| // Put the windows in the mru_windows list at the head, if it's available. |
| if (mru_windows) { |
| // The |mru_windows| are sorted such that the most recent window comes last, |
| // hence iterate in reverse order. |
| for (auto* window : base::Reversed(*mru_windows)) { |
| // Exclude windows in non-switchable containers and those which should not |
| // be included. |
| if ((window->parent() && !wm::IsSwitchableContainer(window->parent())) || |
| !can_include_window_predicate(window)) { |
| continue; |
| } |
| |
| windows.emplace_back(window); |
| } |
| } |
| |
| auto roots = Shell::GetAllRootWindows(); |
| |
| // Put the active root window last in |roots| so that when we iterate over the |
| // root windows in reverse order below, the active root comes first. We do |
| // this so that the top-most windows in the active root window will be added |
| // first to |windows|. |
| aura::Window* active_root = Shell::GetRootWindowForNewWindows(); |
| auto iter = std::find(roots.begin(), roots.end(), active_root); |
| // When switching to/from Unified Mode, the active root window controller |
| // might be in the process of shutting down, and its windows are being moved |
| // to another root window before the root window for new windows is updated. |
| // See WindowTreeHostManager::DeleteHost(). |
| if (iter != roots.end()) { |
| roots.erase(iter); |
| roots.emplace_back(active_root); |
| } |
| |
| for (auto* root : base::Reversed(roots)) { |
| // |wm::kSwitchableWindowContainerIds[]| contains a list of the container |
| // IDs sorted such that the ID of the top-most container comes last. Hence, |
| // we iterate in reverse order so the top-most windows are added first. |
| for (int i = wm::kSwitchableWindowContainerIdsLength - 1; i >= 0; --i) { |
| aura::Window* container = |
| root->GetChildById(wm::kSwitchableWindowContainerIds[i]); |
| for (auto* child : base::Reversed(container->children())) { |
| // Only add windows that the predicate allows. |
| if (!can_include_window_predicate(child)) |
| continue; |
| |
| // Only add windows that have not been added previously from |
| // |mru_windows| (if available). |
| if (mru_windows && base::ContainsValue(*mru_windows, child)) |
| continue; |
| |
| windows.emplace_back(child); |
| } |
| } |
| } |
| |
| return windows; |
| } |
| |
| } // namespace |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // MruWindowTracker, public: |
| |
| MruWindowTracker::MruWindowTracker() { |
| Shell::Get()->activation_client()->AddObserver(this); |
| } |
| |
| MruWindowTracker::~MruWindowTracker() { |
| Shell::Get()->activation_client()->RemoveObserver(this); |
| for (auto* window : mru_windows_) |
| window->RemoveObserver(this); |
| } |
| |
| MruWindowTracker::WindowList MruWindowTracker::BuildMruWindowList() const { |
| return BuildWindowListInternal(&mru_windows_, CanIncludeWindowInMruList); |
| } |
| |
| MruWindowTracker::WindowList MruWindowTracker::BuildWindowListIgnoreModal() |
| const { |
| return BuildWindowListInternal(nullptr, IsWindowConsideredActivatable); |
| } |
| |
| MruWindowTracker::WindowList MruWindowTracker::BuildWindowForCycleList() const { |
| return BuildWindowListInternal(&mru_windows_, CanIncludeWindowInCycleList); |
| } |
| |
| void MruWindowTracker::SetIgnoreActivations(bool ignore) { |
| ignore_window_activations_ = ignore; |
| |
| // If no longer ignoring window activations, move currently active window |
| // to front. |
| if (!ignore) |
| SetActiveWindow(wm::GetActiveWindow()); |
| } |
| |
| void MruWindowTracker::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void MruWindowTracker::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // MruWindowTracker, private: |
| |
| void MruWindowTracker::SetActiveWindow(aura::Window* active_window) { |
| if (!active_window) |
| return; |
| |
| auto iter = |
| std::find(mru_windows_.begin(), mru_windows_.end(), active_window); |
| // Observe all newly tracked windows. |
| if (iter == mru_windows_.end()) |
| active_window->AddObserver(this); |
| else |
| mru_windows_.erase(iter); |
| mru_windows_.emplace_back(active_window); |
| } |
| |
| void MruWindowTracker::OnWindowActivated(ActivationReason reason, |
| aura::Window* gained_active, |
| aura::Window* lost_active) { |
| if (!ignore_window_activations_) |
| SetActiveWindow(gained_active); |
| } |
| |
| void MruWindowTracker::OnWindowDestroyed(aura::Window* window) { |
| // It's possible for OnWindowActivated() to be called after |
| // OnWindowDestroying(). This means we need to override OnWindowDestroyed() |
| // else we may end up with a deleted window in |mru_windows_|. |
| base::Erase(mru_windows_, window); |
| window->RemoveObserver(this); |
| |
| for (auto& observer : observers_) |
| observer.OnWindowUntracked(window); |
| } |
| |
| } // namespace ash |