普通文本  |  211行  |  6.81 KB

// 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