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.
GetContainers(aura::RootWindow * root_window)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.
GetTargetVisibility(aura::Window * child)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)
IsValidCandidate(aura::Window * window)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.
SchedulePaint(aura::Window * window)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:
SoloWindowObserver(aura::Window * window)81 explicit SoloWindowObserver(aura::Window* window) : window_(window) {
82 wm::GetWindowState(window_)->AddObserver(this);
83 }
84
~SoloWindowObserver()85 virtual ~SoloWindowObserver() {
86 wm::GetWindowState(window_)->RemoveObserver(this);
87 }
88
89 private:
90 // ash::wm::WindowStateObserver override.
OnWindowShowTypeChanged(ash::wm::WindowState * window_state,ash::wm::WindowShowType old_type)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
SoloWindowTracker(aura::RootWindow * root_window)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
~SoloWindowTracker()109 SoloWindowTracker::~SoloWindowTracker() {
110 for (size_t i = 0; i < containers_.size(); ++i)
111 containers_[i]->RemoveObserver(this);
112 }
113
114 // static
SetSoloHeaderEnabled(bool enabled)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
GetWindowWithSoloHeader()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
UpdateSoloWindow(aura::Window * ignore_window)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
AnyVisibleWindowDocked() const178 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
OnWindowAdded(aura::Window * new_window)187 void SoloWindowTracker::OnWindowAdded(aura::Window* new_window) {
188 UpdateSoloWindow(NULL);
189 }
190
OnWillRemoveWindow(aura::Window * window)191 void SoloWindowTracker::OnWillRemoveWindow(aura::Window* window) {
192 UpdateSoloWindow(window);
193 }
194
OnWindowVisibilityChanged(aura::Window * window,bool visible)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
OnDockBoundsChanging(const gfx::Rect & new_bounds,Reason reason)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