• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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