| // 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/solo_window_tracker.h" |
| |
| #include <algorithm> |
| |
| #include "ash/ash_constants.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/shell.h" |
| #include "ash/shell_window_ids.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_state_observer.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/window.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // A flag to enable/disable the solo window header across all root windows. |
| bool g_solo_header_enabled = true; |
| |
| // Returns the containers from which a solo window is chosen. |
| std::vector<aura::Window*> GetContainers(aura::RootWindow* root_window) { |
| int kContainerIds[] = { |
| internal::kShellWindowId_DefaultContainer, |
| internal::kShellWindowId_AlwaysOnTopContainer, |
| // Docked windows never use the solo header, but regular windows move to the |
| // docked container when dragged. |
| internal::kShellWindowId_DockedContainer, |
| }; |
| std::vector<aura::Window*> containers; |
| for (size_t i = 0; i < arraysize(kContainerIds); ++i) { |
| containers.push_back( |
| Shell::GetContainer(root_window->window(), kContainerIds[i])); |
| } |
| return containers; |
| } |
| |
| // Returns true if |child| and all of its ancestors are visible and neither |
| // |child| nor any its ancestors is animating hidden. |
| bool GetTargetVisibility(aura::Window* child) { |
| for (aura::Window* window = child; window; window = window->parent()) { |
| if (!window->TargetVisibility()) |
| return false; |
| } |
| return true; |
| } |
| |
| // Returns true if |window| can use the solo window header. Returns false for |
| // windows that are: |
| // * Not drawn (for example, DragDropTracker uses one for mouse capture) |
| // * Modal alerts (it looks odd for headers to change when an alert opens) |
| // * Constrained windows (ditto) |
| bool IsValidCandidate(aura::Window* window) { |
| return window->type() == aura::client::WINDOW_TYPE_NORMAL && |
| window->layer() && |
| window->layer()->type() != ui::LAYER_NOT_DRAWN && |
| window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE && |
| !window->GetProperty(aura::client::kConstrainedWindowKey); |
| } |
| |
| // Schedule's a paint of the window's entire bounds. |
| void SchedulePaint(aura::Window* window) { |
| window->SchedulePaintInRect(gfx::Rect(window->bounds().size())); |
| } |
| |
| } // namespace |
| |
| |
| // Class which triggers a repaint of the window which is passed to the |
| // constructor whenever the window's show type changes. The window's non client |
| // view is responsible for updating whether it uses the solo header as part of |
| // the repaint by querying GetWindowWithSoloHeader(). |
| class SoloWindowTracker::SoloWindowObserver |
| : public ash::wm::WindowStateObserver { |
| public: |
| explicit SoloWindowObserver(aura::Window* window) : window_(window) { |
| wm::GetWindowState(window_)->AddObserver(this); |
| } |
| |
| virtual ~SoloWindowObserver() { |
| wm::GetWindowState(window_)->RemoveObserver(this); |
| } |
| |
| private: |
| // ash::wm::WindowStateObserver override. |
| virtual void OnWindowShowTypeChanged( |
| ash::wm::WindowState* window_state, |
| ash::wm::WindowShowType old_type) OVERRIDE { |
| SchedulePaint(window_); |
| } |
| |
| aura::Window* window_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SoloWindowObserver); |
| }; |
| |
| SoloWindowTracker::SoloWindowTracker(aura::RootWindow* root_window) |
| : containers_(GetContainers(root_window)), |
| solo_window_(NULL) { |
| for (size_t i = 0; i < containers_.size(); ++i) |
| containers_[i]->AddObserver(this); |
| } |
| |
| SoloWindowTracker::~SoloWindowTracker() { |
| for (size_t i = 0; i < containers_.size(); ++i) |
| containers_[i]->RemoveObserver(this); |
| } |
| |
| // static |
| void SoloWindowTracker::SetSoloHeaderEnabled(bool enabled) { |
| g_solo_header_enabled = enabled; |
| std::vector<aura::Window*> root_windows = |
| Shell::GetInstance()->GetAllRootWindows(); |
| for (size_t i = 0; i < root_windows.size(); ++i) { |
| SoloWindowTracker* tracker = |
| internal::GetRootWindowController(root_windows[i])-> |
| solo_window_tracker(); |
| if (tracker) |
| tracker->UpdateSoloWindow(NULL); |
| } |
| } |
| |
| aura::Window* SoloWindowTracker::GetWindowWithSoloHeader() { |
| bool use_solo_header = solo_window_ && |
| !wm::GetWindowState(solo_window_)->IsMaximizedOrFullscreen(); |
| return use_solo_header ? solo_window_ : NULL; |
| } |
| |
| void SoloWindowTracker::UpdateSoloWindow(aura::Window* ignore_window) { |
| std::vector<aura::Window*> candidates; |
| // Avoid memory allocations for typical window counts. |
| candidates.reserve(16); |
| for (size_t i = 0; i < containers_.size(); ++i) { |
| candidates.insert(candidates.end(), |
| containers_[i]->children().begin(), |
| containers_[i]->children().end()); |
| } |
| |
| aura::Window* old_solo_window = solo_window_; |
| solo_window_ = NULL; |
| if (g_solo_header_enabled && !AnyVisibleWindowDocked()) { |
| for (size_t i = 0; i < candidates.size(); ++i) { |
| aura::Window* candidate = candidates[i]; |
| // Various sorts of windows "don't count" for this computation. |
| if (candidate == ignore_window || |
| !IsValidCandidate(candidate) || |
| !GetTargetVisibility(candidate)) { |
| continue; |
| } |
| |
| if (solo_window_) { |
| // A window can only use the solo header if it is the only visible valid |
| // candidate (and there are no visible docked windows). |
| solo_window_ = NULL; |
| break; |
| } else { |
| solo_window_ = candidate; |
| } |
| } |
| } |
| |
| if (solo_window_ == old_solo_window) |
| return; |
| |
| solo_window_observer_.reset(solo_window_ ? |
| new SoloWindowObserver(solo_window_) : NULL); |
| if (old_solo_window) |
| SchedulePaint(old_solo_window); |
| if (solo_window_) |
| SchedulePaint(solo_window_); |
| } |
| |
| bool SoloWindowTracker::AnyVisibleWindowDocked() const { |
| // For the purpose of SoloWindowTracker, there is a visible docked window if |
| // it causes the dock to have non-empty bounds. This is intentionally |
| // different from: |
| // DockedWindowLayoutManager::IsAnyWindowDocked() and |
| // DockedWindowLayoutManager::is_dragged_window_docked(). |
| return !dock_bounds_.IsEmpty(); |
| } |
| |
| void SoloWindowTracker::OnWindowAdded(aura::Window* new_window) { |
| UpdateSoloWindow(NULL); |
| } |
| |
| void SoloWindowTracker::OnWillRemoveWindow(aura::Window* window) { |
| UpdateSoloWindow(window); |
| } |
| |
| void SoloWindowTracker::OnWindowVisibilityChanged(aura::Window* window, |
| bool visible) { |
| // |window| may be a grandchild of |containers_|. |
| std::vector<aura::Window*>::const_iterator it = std::find( |
| containers_.begin(), containers_.end(), window->parent()); |
| if (it != containers_.end()) |
| UpdateSoloWindow(NULL); |
| } |
| |
| void SoloWindowTracker::OnDockBoundsChanging(const gfx::Rect& new_bounds, |
| Reason reason) { |
| dock_bounds_ = new_bounds; |
| UpdateSoloWindow(NULL); |
| } |
| |
| } // namespace ash |