// 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 "ui/views/widget/window_reorderer.h" #include <map> #include <vector> #include "ui/aura/window.h" #include "ui/views/view.h" #include "ui/views/view_constants_aura.h" namespace views { namespace { // Sets |hosted_windows| to a mapping of the views with an associated window to // the window that they are associated to. Only views associated to a child of // |parent_window| are returned. void GetViewsWithAssociatedWindow( const aura::Window& parent_window, std::map<views::View*, aura::Window*>* hosted_windows) { const std::vector<aura::Window*>& child_windows = parent_window.children(); for (size_t i = 0; i < child_windows.size(); ++i) { aura::Window* child = child_windows[i]; View* host_view = child->GetProperty(kHostViewKey); if (host_view) (*hosted_windows)[host_view] = child; } } // Sets |order| to the list of views whose layer / associated window's layer // is a child of |parent_layer|. |order| is sorted in ascending z-order of // the views. // |hosts| are the views with an associated window whose layer is a child of // |parent_layer|. void GetOrderOfViewsWithLayers( views::View* view, ui::Layer* parent_layer, const std::map<views::View*, aura::Window*>& hosts, std::vector<views::View*>* order) { DCHECK(view); DCHECK(parent_layer); DCHECK(order); if (view->layer() && view->layer()->parent() == parent_layer) { order->push_back(view); // |hosts| may contain a child of |view|. } else if (hosts.find(view) != hosts.end()) { order->push_back(view); } for (int i = 0; i < view->child_count(); ++i) GetOrderOfViewsWithLayers(view->child_at(i), parent_layer, hosts, order); } } // namespace // Class which reorders windows as a result of the kHostViewKey property being // set on the window. class WindowReorderer::AssociationObserver : public aura::WindowObserver { public: explicit AssociationObserver(WindowReorderer* reorderer); virtual ~AssociationObserver(); // Start/stop observing changes in the kHostViewKey property on |window|. void StartObserving(aura::Window* window); void StopObserving(aura::Window* window); private: // aura::WindowObserver overrides: virtual void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) OVERRIDE; virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; // Not owned. WindowReorderer* reorderer_; std::set<aura::Window*> windows_; DISALLOW_COPY_AND_ASSIGN(AssociationObserver); }; WindowReorderer::AssociationObserver::AssociationObserver( WindowReorderer* reorderer) : reorderer_(reorderer) { } WindowReorderer::AssociationObserver::~AssociationObserver() { while (!windows_.empty()) StopObserving(*windows_.begin()); } void WindowReorderer::AssociationObserver::StartObserving( aura::Window* window) { windows_.insert(window); window->AddObserver(this); } void WindowReorderer::AssociationObserver::StopObserving( aura::Window* window) { windows_.erase(window); window->RemoveObserver(this); } void WindowReorderer::AssociationObserver::OnWindowPropertyChanged( aura::Window* window, const void* key, intptr_t old) { if (key == kHostViewKey) reorderer_->ReorderChildWindows(); } void WindowReorderer::AssociationObserver::OnWindowDestroying( aura::Window* window) { windows_.erase(window); window->RemoveObserver(this); } WindowReorderer::WindowReorderer(aura::Window* parent_window, View* root_view) : parent_window_(parent_window), root_view_(root_view), association_observer_(new AssociationObserver(this)) { parent_window_->AddObserver(this); const std::vector<aura::Window*>& windows = parent_window_->children(); for (size_t i = 0; i < windows.size(); ++i) association_observer_->StartObserving(windows[i]); ReorderChildWindows(); } WindowReorderer::~WindowReorderer() { if (parent_window_) { parent_window_->RemoveObserver(this); // |association_observer_| stops observing any windows it is observing upon // destruction. } } void WindowReorderer::ReorderChildWindows() { if (!parent_window_) return; std::map<View*, aura::Window*> hosted_windows; GetViewsWithAssociatedWindow(*parent_window_, &hosted_windows); if (hosted_windows.empty()) { // Exit early if there are no views with associated windows. // View::ReorderLayers() should have already reordered the layers owned by // views. return; } // Compute the desired z-order of the layers based on the order of the views // with layers and views with associated windows in the view tree. std::vector<View*> view_with_layer_order; GetOrderOfViewsWithLayers(root_view_, parent_window_->layer(), hosted_windows, &view_with_layer_order); // For the sake of simplicity, reorder both the layers owned by views and the // layers of windows associated with a view. Iterate through // |view_with_layer_order| backwards and stack windows at the bottom so that // windows not associated to a view are stacked above windows with an // associated view. for (std::vector<View*>::reverse_iterator it = view_with_layer_order.rbegin(); it != view_with_layer_order.rend(); ++it) { View* view = *it; ui::Layer* layer = view->layer(); aura::Window* window = NULL; std::map<View*, aura::Window*>::iterator hosted_window_it = hosted_windows.find(view); if (hosted_window_it != hosted_windows.end()) { window = hosted_window_it->second; layer = window->layer(); } DCHECK(layer); if (window) parent_window_->StackChildAtBottom(window); parent_window_->layer()->StackAtBottom(layer); } } void WindowReorderer::OnWindowAdded(aura::Window* new_window) { association_observer_->StartObserving(new_window); ReorderChildWindows(); } void WindowReorderer::OnWillRemoveWindow(aura::Window* window) { association_observer_->StopObserving(window); } void WindowReorderer::OnWindowDestroying(aura::Window* window) { parent_window_->RemoveObserver(this); parent_window_ = NULL; association_observer_.reset(); } } // namespace views