Home | History | Annotate | Download | only in wm
      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