// Copyright (c) 2012 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/always_on_top_controller.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/window_properties.h" #include "ash/wm/desks/desks_controller.h" #include "ash/wm/desks/desks_util.h" #include "ash/wm/window_state.h" #include "ash/wm/workspace/workspace_layout_manager.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" namespace ash { DEFINE_UI_CLASS_PROPERTY_KEY(bool, kDisallowReparentKey, false) AlwaysOnTopController::AlwaysOnTopController( aura::Window* always_on_top_container, aura::Window* pip_container) : always_on_top_container_(always_on_top_container), pip_container_(pip_container) { DCHECK(!desks_util::IsDeskContainer(always_on_top_container_)); DCHECK(!desks_util::IsDeskContainer(pip_container_)); always_on_top_container_->SetLayoutManager( new WorkspaceLayoutManager(always_on_top_container_)); pip_container_->SetLayoutManager(new WorkspaceLayoutManager(pip_container_)); // Container should be empty. DCHECK(always_on_top_container_->children().empty()); DCHECK(pip_container_->children().empty()); always_on_top_container_->AddObserver(this); pip_container->AddObserver(this); } AlwaysOnTopController::~AlwaysOnTopController() { // At this point, all windows should be removed and AlwaysOnTopController // will have removed itself as an observer in OnWindowDestroying. DCHECK(!always_on_top_container_); DCHECK(!pip_container_); } // static void AlwaysOnTopController::SetDisallowReparent(aura::Window* window) { window->SetProperty(kDisallowReparentKey, true); } aura::Window* AlwaysOnTopController::GetContainer(aura::Window* window) const { DCHECK(always_on_top_container_); DCHECK(pip_container_); // On other platforms, there are different window levels. For now, treat any // window with non-normal level as "always on top". Perhaps the nuance of // multiple levels will be needed later. if (window->GetProperty(aura::client::kZOrderingKey) == ui::ZOrderLevel::kNormal) { aura::Window* root = always_on_top_container_->GetRootWindow(); // TODO(afakhry): Do we need to worry about the context of |window| here? Or // is it safe to assume that |window| should always be parented to the // active desks' container. const int window_workspace = window->GetProperty(aura::client::kWindowWorkspaceKey); if (window_workspace != aura::client::kWindowWorkspaceUnassignedWorkspace) { auto* desk_container = DesksController::Get()->GetDeskContainer(root, window_workspace); if (desk_container) return desk_container; } return desks_util::GetActiveDeskContainerForRoot(root); } if (window->parent() && WindowState::Get(window)->IsPip()) return pip_container_; return always_on_top_container_; } void AlwaysOnTopController::ClearLayoutManagers() { always_on_top_container_->SetLayoutManager(nullptr); pip_container_->SetLayoutManager(nullptr); } void AlwaysOnTopController::SetLayoutManagerForTest( std::unique_ptr layout_manager) { always_on_top_container_->SetLayoutManager(layout_manager.release()); } void AlwaysOnTopController::AddWindow(aura::Window* window) { window->AddObserver(this); WindowState::Get(window)->AddObserver(this); } void AlwaysOnTopController::RemoveWindow(aura::Window* window) { window->RemoveObserver(this); WindowState::Get(window)->RemoveObserver(this); } void AlwaysOnTopController::ReparentWindow(aura::Window* window) { DCHECK(window->GetType() == aura::client::WINDOW_TYPE_NORMAL || window->GetType() == aura::client::WINDOW_TYPE_POPUP); aura::Window* container = GetContainer(window); if (window->parent() != container && !window->GetProperty(kDisallowReparentKey)) container->AddChild(window); } void AlwaysOnTopController::OnWindowHierarchyChanged( const HierarchyChangeParams& params) { if (params.old_parent == always_on_top_container_ || params.old_parent == pip_container_) { RemoveWindow(params.target); } if (params.new_parent == always_on_top_container_ || params.new_parent == pip_container_) { AddWindow(params.target); } } void AlwaysOnTopController::OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) { if (window != always_on_top_container_ && window != pip_container_ && key == aura::client::kZOrderingKey) { ReparentWindow(window); } } void AlwaysOnTopController::OnWindowDestroying(aura::Window* window) { if (window == always_on_top_container_) { always_on_top_container_->RemoveObserver(this); always_on_top_container_ = nullptr; } else if (window == pip_container_) { pip_container_->RemoveObserver(this); pip_container_ = nullptr; } else { RemoveWindow(window); } } void AlwaysOnTopController::OnPreWindowStateTypeChange( WindowState* window_state, chromeos::WindowStateType old_type) { ReparentWindow(window_state->window()); } } // namespace ash