1 // Copyright (c) 2012 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/always_on_top_controller.h"
6
7 #include "ash/public/cpp/shell_window_ids.h"
8 #include "ash/public/cpp/window_properties.h"
9 #include "ash/wm/desks/desks_util.h"
10 #include "ash/wm/window_state.h"
11 #include "ash/wm/workspace/workspace_layout_manager.h"
12 #include "ui/aura/client/aura_constants.h"
13 #include "ui/aura/window.h"
14
15 namespace ash {
16
DEFINE_UI_CLASS_PROPERTY_KEY(bool,kDisallowReparentKey,false)17 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kDisallowReparentKey, false)
18
19 AlwaysOnTopController::AlwaysOnTopController(
20 aura::Window* always_on_top_container,
21 aura::Window* pip_container)
22 : always_on_top_container_(always_on_top_container),
23 pip_container_(pip_container) {
24 DCHECK(!desks_util::IsDeskContainer(always_on_top_container_));
25 DCHECK(!desks_util::IsDeskContainer(pip_container_));
26 always_on_top_container_->SetLayoutManager(
27 new WorkspaceLayoutManager(always_on_top_container_));
28 pip_container_->SetLayoutManager(new WorkspaceLayoutManager(pip_container_));
29 // Container should be empty.
30 DCHECK(always_on_top_container_->children().empty());
31 DCHECK(pip_container_->children().empty());
32 always_on_top_container_->AddObserver(this);
33 pip_container->AddObserver(this);
34 }
35
~AlwaysOnTopController()36 AlwaysOnTopController::~AlwaysOnTopController() {
37 // At this point, all windows should be removed and AlwaysOnTopController
38 // will have removed itself as an observer in OnWindowDestroying.
39 DCHECK(!always_on_top_container_);
40 DCHECK(!pip_container_);
41 }
42
GetContainer(aura::Window * window) const43 aura::Window* AlwaysOnTopController::GetContainer(aura::Window* window) const {
44 DCHECK(always_on_top_container_);
45 DCHECK(pip_container_);
46
47 // On other platforms, there are different window levels. For now, treat any
48 // window with non-normal level as "always on top". Perhaps the nuance of
49 // multiple levels will be needed later.
50 if (window->GetProperty(aura::client::kZOrderingKey) ==
51 ui::ZOrderLevel::kNormal) {
52 aura::Window* root = always_on_top_container_->GetRootWindow();
53
54 // TODO(afakhry): Do we need to worry about the context of |window| here? Or
55 // is it safe to assume that |window| should always be parented to the
56 // active desks' container.
57 return desks_util::GetActiveDeskContainerForRoot(root);
58 }
59 if (window->parent() && WindowState::Get(window)->IsPip())
60 return pip_container_;
61
62 return always_on_top_container_;
63 }
64
SetLayoutManagerForTest(std::unique_ptr<WorkspaceLayoutManager> layout_manager)65 void AlwaysOnTopController::SetLayoutManagerForTest(
66 std::unique_ptr<WorkspaceLayoutManager> layout_manager) {
67 always_on_top_container_->SetLayoutManager(layout_manager.release());
68 }
69
SetDisallowReparent(aura::Window * window)70 void AlwaysOnTopController::SetDisallowReparent(aura::Window* window) {
71 window->SetProperty(kDisallowReparentKey, true);
72 }
73
AddWindow(aura::Window * window)74 void AlwaysOnTopController::AddWindow(aura::Window* window) {
75 window->AddObserver(this);
76 WindowState::Get(window)->AddObserver(this);
77 }
78
RemoveWindow(aura::Window * window)79 void AlwaysOnTopController::RemoveWindow(aura::Window* window) {
80 window->RemoveObserver(this);
81 WindowState::Get(window)->RemoveObserver(this);
82 }
83
ReparentWindow(aura::Window * window)84 void AlwaysOnTopController::ReparentWindow(aura::Window* window) {
85 DCHECK(window->type() == aura::client::WINDOW_TYPE_NORMAL ||
86 window->type() == aura::client::WINDOW_TYPE_POPUP);
87 aura::Window* container = GetContainer(window);
88 if (window->parent() != container &&
89 !window->GetProperty(kDisallowReparentKey))
90 container->AddChild(window);
91 }
92
OnWindowHierarchyChanged(const HierarchyChangeParams & params)93 void AlwaysOnTopController::OnWindowHierarchyChanged(
94 const HierarchyChangeParams& params) {
95 if (params.old_parent == always_on_top_container_ ||
96 params.old_parent == pip_container_) {
97 RemoveWindow(params.target);
98 }
99
100 if (params.new_parent == always_on_top_container_ ||
101 params.new_parent == pip_container_) {
102 AddWindow(params.target);
103 }
104 }
105
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)106 void AlwaysOnTopController::OnWindowPropertyChanged(aura::Window* window,
107 const void* key,
108 intptr_t old) {
109 if (window != always_on_top_container_ && window != pip_container_ &&
110 key == aura::client::kZOrderingKey) {
111 ReparentWindow(window);
112 }
113 }
114
OnWindowDestroying(aura::Window * window)115 void AlwaysOnTopController::OnWindowDestroying(aura::Window* window) {
116 if (window == always_on_top_container_) {
117 always_on_top_container_->RemoveObserver(this);
118 always_on_top_container_ = nullptr;
119 } else if (window == pip_container_) {
120 pip_container_->RemoveObserver(this);
121 pip_container_ = nullptr;
122 } else {
123 RemoveWindow(window);
124 }
125 }
126
OnPreWindowStateTypeChange(WindowState * window_state,chromeos::WindowStateType old_type)127 void AlwaysOnTopController::OnPreWindowStateTypeChange(
128 WindowState* window_state,
129 chromeos::WindowStateType old_type) {
130 ReparentWindow(window_state->window());
131 }
132
133 } // namespace ash
134