1 // Copyright 2013 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/solo_window_tracker.h" 6 7 #include <algorithm> 8 9 #include "ash/ash_constants.h" 10 #include "ash/root_window_controller.h" 11 #include "ash/shell.h" 12 #include "ash/shell_window_ids.h" 13 #include "ash/wm/window_state.h" 14 #include "ash/wm/window_state_observer.h" 15 #include "ui/aura/client/aura_constants.h" 16 #include "ui/aura/root_window.h" 17 #include "ui/aura/window.h" 18 19 namespace ash { 20 21 namespace { 22 23 // A flag to enable/disable the solo window header across all root windows. 24 bool g_solo_header_enabled = true; 25 26 // Returns the containers from which a solo window is chosen. 27 std::vector<aura::Window*> GetContainers(aura::RootWindow* root_window) { 28 int kContainerIds[] = { 29 internal::kShellWindowId_DefaultContainer, 30 internal::kShellWindowId_AlwaysOnTopContainer, 31 // Docked windows never use the solo header, but regular windows move to the 32 // docked container when dragged. 33 internal::kShellWindowId_DockedContainer, 34 }; 35 std::vector<aura::Window*> containers; 36 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { 37 containers.push_back( 38 Shell::GetContainer(root_window->window(), kContainerIds[i])); 39 } 40 return containers; 41 } 42 43 // Returns true if |child| and all of its ancestors are visible and neither 44 // |child| nor any its ancestors is animating hidden. 45 bool GetTargetVisibility(aura::Window* child) { 46 for (aura::Window* window = child; window; window = window->parent()) { 47 if (!window->TargetVisibility()) 48 return false; 49 } 50 return true; 51 } 52 53 // Returns true if |window| can use the solo window header. Returns false for 54 // windows that are: 55 // * Not drawn (for example, DragDropTracker uses one for mouse capture) 56 // * Modal alerts (it looks odd for headers to change when an alert opens) 57 // * Constrained windows (ditto) 58 bool IsValidCandidate(aura::Window* window) { 59 return window->type() == aura::client::WINDOW_TYPE_NORMAL && 60 window->layer() && 61 window->layer()->type() != ui::LAYER_NOT_DRAWN && 62 window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE && 63 !window->GetProperty(aura::client::kConstrainedWindowKey); 64 } 65 66 // Schedule's a paint of the window's entire bounds. 67 void SchedulePaint(aura::Window* window) { 68 window->SchedulePaintInRect(gfx::Rect(window->bounds().size())); 69 } 70 71 } // namespace 72 73 74 // Class which triggers a repaint of the window which is passed to the 75 // constructor whenever the window's show type changes. The window's non client 76 // view is responsible for updating whether it uses the solo header as part of 77 // the repaint by querying GetWindowWithSoloHeader(). 78 class SoloWindowTracker::SoloWindowObserver 79 : public ash::wm::WindowStateObserver { 80 public: 81 explicit SoloWindowObserver(aura::Window* window) : window_(window) { 82 wm::GetWindowState(window_)->AddObserver(this); 83 } 84 85 virtual ~SoloWindowObserver() { 86 wm::GetWindowState(window_)->RemoveObserver(this); 87 } 88 89 private: 90 // ash::wm::WindowStateObserver override. 91 virtual void OnWindowShowTypeChanged( 92 ash::wm::WindowState* window_state, 93 ash::wm::WindowShowType old_type) OVERRIDE { 94 SchedulePaint(window_); 95 } 96 97 aura::Window* window_; 98 99 DISALLOW_COPY_AND_ASSIGN(SoloWindowObserver); 100 }; 101 102 SoloWindowTracker::SoloWindowTracker(aura::RootWindow* root_window) 103 : containers_(GetContainers(root_window)), 104 solo_window_(NULL) { 105 for (size_t i = 0; i < containers_.size(); ++i) 106 containers_[i]->AddObserver(this); 107 } 108 109 SoloWindowTracker::~SoloWindowTracker() { 110 for (size_t i = 0; i < containers_.size(); ++i) 111 containers_[i]->RemoveObserver(this); 112 } 113 114 // static 115 void SoloWindowTracker::SetSoloHeaderEnabled(bool enabled) { 116 g_solo_header_enabled = enabled; 117 std::vector<aura::Window*> root_windows = 118 Shell::GetInstance()->GetAllRootWindows(); 119 for (size_t i = 0; i < root_windows.size(); ++i) { 120 SoloWindowTracker* tracker = 121 internal::GetRootWindowController(root_windows[i])-> 122 solo_window_tracker(); 123 if (tracker) 124 tracker->UpdateSoloWindow(NULL); 125 } 126 } 127 128 aura::Window* SoloWindowTracker::GetWindowWithSoloHeader() { 129 bool use_solo_header = solo_window_ && 130 !wm::GetWindowState(solo_window_)->IsMaximizedOrFullscreen(); 131 return use_solo_header ? solo_window_ : NULL; 132 } 133 134 void SoloWindowTracker::UpdateSoloWindow(aura::Window* ignore_window) { 135 std::vector<aura::Window*> candidates; 136 // Avoid memory allocations for typical window counts. 137 candidates.reserve(16); 138 for (size_t i = 0; i < containers_.size(); ++i) { 139 candidates.insert(candidates.end(), 140 containers_[i]->children().begin(), 141 containers_[i]->children().end()); 142 } 143 144 aura::Window* old_solo_window = solo_window_; 145 solo_window_ = NULL; 146 if (g_solo_header_enabled && !AnyVisibleWindowDocked()) { 147 for (size_t i = 0; i < candidates.size(); ++i) { 148 aura::Window* candidate = candidates[i]; 149 // Various sorts of windows "don't count" for this computation. 150 if (candidate == ignore_window || 151 !IsValidCandidate(candidate) || 152 !GetTargetVisibility(candidate)) { 153 continue; 154 } 155 156 if (solo_window_) { 157 // A window can only use the solo header if it is the only visible valid 158 // candidate (and there are no visible docked windows). 159 solo_window_ = NULL; 160 break; 161 } else { 162 solo_window_ = candidate; 163 } 164 } 165 } 166 167 if (solo_window_ == old_solo_window) 168 return; 169 170 solo_window_observer_.reset(solo_window_ ? 171 new SoloWindowObserver(solo_window_) : NULL); 172 if (old_solo_window) 173 SchedulePaint(old_solo_window); 174 if (solo_window_) 175 SchedulePaint(solo_window_); 176 } 177 178 bool SoloWindowTracker::AnyVisibleWindowDocked() const { 179 // For the purpose of SoloWindowTracker, there is a visible docked window if 180 // it causes the dock to have non-empty bounds. This is intentionally 181 // different from: 182 // DockedWindowLayoutManager::IsAnyWindowDocked() and 183 // DockedWindowLayoutManager::is_dragged_window_docked(). 184 return !dock_bounds_.IsEmpty(); 185 } 186 187 void SoloWindowTracker::OnWindowAdded(aura::Window* new_window) { 188 UpdateSoloWindow(NULL); 189 } 190 191 void SoloWindowTracker::OnWillRemoveWindow(aura::Window* window) { 192 UpdateSoloWindow(window); 193 } 194 195 void SoloWindowTracker::OnWindowVisibilityChanged(aura::Window* window, 196 bool visible) { 197 // |window| may be a grandchild of |containers_|. 198 std::vector<aura::Window*>::const_iterator it = std::find( 199 containers_.begin(), containers_.end(), window->parent()); 200 if (it != containers_.end()) 201 UpdateSoloWindow(NULL); 202 } 203 204 void SoloWindowTracker::OnDockBoundsChanging(const gfx::Rect& new_bounds, 205 Reason reason) { 206 dock_bounds_ = new_bounds; 207 UpdateSoloWindow(NULL); 208 } 209 210 } // namespace ash 211