OLD | NEW |
| (Empty) |
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/common/wm/system_modal_container_layout_manager.h" | |
6 | |
7 #include <cmath> | |
8 | |
9 #include "ash/common/session/session_state_delegate.h" | |
10 #include "ash/common/wm/window_dimmer.h" | |
11 #include "ash/common/wm_shell.h" | |
12 #include "ash/common/wm_window.h" | |
13 #include "ash/public/cpp/shell_window_ids.h" | |
14 #include "base/memory/ptr_util.h" | |
15 #include "base/stl_util.h" | |
16 #include "ui/aura/client/aura_constants.h" | |
17 #include "ui/aura/window.h" | |
18 #include "ui/keyboard/keyboard_controller.h" | |
19 | |
20 namespace ash { | |
21 namespace { | |
22 | |
23 // The center point of the window can diverge this much from the center point | |
24 // of the container to be kept centered upon resizing operations. | |
25 const int kCenterPixelDelta = 32; | |
26 | |
27 ui::ModalType GetModalType(WmWindow* window) { | |
28 return static_cast<ui::ModalType>( | |
29 window->aura_window()->GetProperty(aura::client::kModalKey)); | |
30 } | |
31 | |
32 bool HasTransientAncestor(const WmWindow* window, const WmWindow* ancestor) { | |
33 const WmWindow* transient_parent = window->GetTransientParent(); | |
34 if (transient_parent == ancestor) | |
35 return true; | |
36 return transient_parent ? HasTransientAncestor(transient_parent, ancestor) | |
37 : false; | |
38 } | |
39 } | |
40 | |
41 //////////////////////////////////////////////////////////////////////////////// | |
42 // SystemModalContainerLayoutManager, public: | |
43 | |
44 SystemModalContainerLayoutManager::SystemModalContainerLayoutManager( | |
45 WmWindow* container) | |
46 : container_(container) {} | |
47 | |
48 SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() { | |
49 if (keyboard::KeyboardController::GetInstance()) | |
50 keyboard::KeyboardController::GetInstance()->RemoveObserver(this); | |
51 } | |
52 | |
53 //////////////////////////////////////////////////////////////////////////////// | |
54 // SystemModalContainerLayoutManager, WmLayoutManager implementation: | |
55 | |
56 void SystemModalContainerLayoutManager::OnChildWindowVisibilityChanged( | |
57 WmWindow* window, | |
58 bool visible) { | |
59 if (GetModalType(window) != ui::MODAL_TYPE_SYSTEM) | |
60 return; | |
61 | |
62 if (window->IsVisible()) { | |
63 DCHECK(!base::ContainsValue(modal_windows_, window)); | |
64 AddModalWindow(window); | |
65 } else { | |
66 if (RemoveModalWindow(window)) | |
67 WmShell::Get()->OnModalWindowRemoved(window); | |
68 } | |
69 } | |
70 | |
71 void SystemModalContainerLayoutManager::OnWindowResized() { | |
72 PositionDialogsAfterWorkAreaResize(); | |
73 } | |
74 | |
75 void SystemModalContainerLayoutManager::OnWindowAddedToLayout(WmWindow* child) { | |
76 DCHECK(child->GetType() == ui::wm::WINDOW_TYPE_NORMAL || | |
77 child->GetType() == ui::wm::WINDOW_TYPE_POPUP); | |
78 // TODO(mash): IsUserSessionBlocked() depends on knowing the login state. We | |
79 // need a non-stub version of SessionStateDelegate. crbug.com/648964 | |
80 if (!WmShell::Get()->IsRunningInMash()) { | |
81 DCHECK(container_->GetShellWindowId() != | |
82 kShellWindowId_LockSystemModalContainer || | |
83 WmShell::Get()->GetSessionStateDelegate()->IsUserSessionBlocked()); | |
84 } | |
85 // Since this is for SystemModal, there is no good reason to add windows | |
86 // other than MODAL_TYPE_NONE or MODAL_TYPE_SYSTEM. DCHECK to avoid simple | |
87 // mistake. | |
88 DCHECK_NE(GetModalType(child), ui::MODAL_TYPE_CHILD); | |
89 DCHECK_NE(GetModalType(child), ui::MODAL_TYPE_WINDOW); | |
90 | |
91 child->aura_window()->AddObserver(this); | |
92 if (GetModalType(child) == ui::MODAL_TYPE_SYSTEM && child->IsVisible()) | |
93 AddModalWindow(child); | |
94 } | |
95 | |
96 void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout( | |
97 WmWindow* child) { | |
98 child->aura_window()->RemoveObserver(this); | |
99 windows_to_center_.erase(child); | |
100 if (GetModalType(child) == ui::MODAL_TYPE_SYSTEM) | |
101 RemoveModalWindow(child); | |
102 } | |
103 | |
104 void SystemModalContainerLayoutManager::SetChildBounds( | |
105 WmWindow* child, | |
106 const gfx::Rect& requested_bounds) { | |
107 WmSnapToPixelLayoutManager::SetChildBounds(child, requested_bounds); | |
108 if (IsBoundsCentered(requested_bounds)) | |
109 windows_to_center_.insert(child); | |
110 else | |
111 windows_to_center_.erase(child); | |
112 } | |
113 | |
114 //////////////////////////////////////////////////////////////////////////////// | |
115 // SystemModalContainerLayoutManager, aura::WindowObserver implementation: | |
116 | |
117 void SystemModalContainerLayoutManager::OnWindowPropertyChanged( | |
118 aura::Window* window, | |
119 const void* key, | |
120 intptr_t old) { | |
121 if (key != aura::client::kModalKey || !window->IsVisible()) | |
122 return; | |
123 | |
124 WmWindow* wm_window = WmWindow::Get(window); | |
125 if (window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM) { | |
126 if (base::ContainsValue(modal_windows_, wm_window)) | |
127 return; | |
128 AddModalWindow(wm_window); | |
129 } else { | |
130 if (RemoveModalWindow(wm_window)) | |
131 WmShell::Get()->OnModalWindowRemoved(wm_window); | |
132 } | |
133 } | |
134 | |
135 //////////////////////////////////////////////////////////////////////////////// | |
136 // SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver | |
137 // implementation: | |
138 | |
139 void SystemModalContainerLayoutManager::OnKeyboardBoundsChanging( | |
140 const gfx::Rect& new_bounds) { | |
141 PositionDialogsAfterWorkAreaResize(); | |
142 } | |
143 | |
144 void SystemModalContainerLayoutManager::OnKeyboardClosed() {} | |
145 | |
146 bool SystemModalContainerLayoutManager::IsPartOfActiveModalWindow( | |
147 WmWindow* window) { | |
148 return modal_window() && | |
149 (modal_window()->Contains(window) || | |
150 HasTransientAncestor(window->GetToplevelWindowForFocus(), | |
151 modal_window())); | |
152 } | |
153 | |
154 bool SystemModalContainerLayoutManager::ActivateNextModalWindow() { | |
155 if (modal_windows_.empty()) | |
156 return false; | |
157 modal_window()->Activate(); | |
158 return true; | |
159 } | |
160 | |
161 void SystemModalContainerLayoutManager::CreateModalBackground() { | |
162 if (!window_dimmer_) { | |
163 window_dimmer_ = base::MakeUnique<WindowDimmer>(container_); | |
164 window_dimmer_->window()->SetName( | |
165 "SystemModalContainerLayoutManager.ModalBackground"); | |
166 // There isn't always a keyboard controller. | |
167 if (keyboard::KeyboardController::GetInstance()) | |
168 keyboard::KeyboardController::GetInstance()->AddObserver(this); | |
169 } | |
170 window_dimmer_->window()->Show(); | |
171 } | |
172 | |
173 void SystemModalContainerLayoutManager::DestroyModalBackground() { | |
174 if (!window_dimmer_) | |
175 return; | |
176 | |
177 if (keyboard::KeyboardController::GetInstance()) | |
178 keyboard::KeyboardController::GetInstance()->RemoveObserver(this); | |
179 window_dimmer_.reset(); | |
180 } | |
181 | |
182 // static | |
183 bool SystemModalContainerLayoutManager::IsModalBackground(WmWindow* window) { | |
184 int id = window->GetParent()->GetShellWindowId(); | |
185 if (id != kShellWindowId_SystemModalContainer && | |
186 id != kShellWindowId_LockSystemModalContainer) | |
187 return false; | |
188 SystemModalContainerLayoutManager* layout_manager = | |
189 static_cast<SystemModalContainerLayoutManager*>( | |
190 window->GetParent()->GetLayoutManager()); | |
191 return layout_manager->window_dimmer_ && | |
192 layout_manager->window_dimmer_->window() == window; | |
193 } | |
194 | |
195 //////////////////////////////////////////////////////////////////////////////// | |
196 // SystemModalContainerLayoutManager, private: | |
197 | |
198 void SystemModalContainerLayoutManager::AddModalWindow(WmWindow* window) { | |
199 if (modal_windows_.empty()) { | |
200 WmWindow* capture_window = WmShell::Get()->GetCaptureWindow(); | |
201 if (capture_window) | |
202 capture_window->ReleaseCapture(); | |
203 } | |
204 DCHECK(window->IsVisible()); | |
205 DCHECK(!base::ContainsValue(modal_windows_, window)); | |
206 | |
207 modal_windows_.push_back(window); | |
208 WmShell::Get()->CreateModalBackground(window); | |
209 window->GetParent()->StackChildAtTop(window); | |
210 | |
211 gfx::Rect target_bounds = window->GetBounds(); | |
212 target_bounds.AdjustToFit(GetUsableDialogArea()); | |
213 window->SetBounds(target_bounds); | |
214 } | |
215 | |
216 bool SystemModalContainerLayoutManager::RemoveModalWindow(WmWindow* window) { | |
217 auto it = std::find(modal_windows_.begin(), modal_windows_.end(), window); | |
218 if (it == modal_windows_.end()) | |
219 return false; | |
220 modal_windows_.erase(it); | |
221 return true; | |
222 } | |
223 | |
224 void SystemModalContainerLayoutManager::PositionDialogsAfterWorkAreaResize() { | |
225 if (modal_windows_.empty()) | |
226 return; | |
227 | |
228 for (WmWindow* window : modal_windows_) | |
229 window->SetBounds(GetCenteredAndOrFittedBounds(window)); | |
230 } | |
231 | |
232 gfx::Rect SystemModalContainerLayoutManager::GetUsableDialogArea() const { | |
233 // Instead of resizing the system modal container, we move only the modal | |
234 // windows. This way we avoid flashing lines upon resize animation and if the | |
235 // keyboard will not fill left to right, the background is still covered. | |
236 gfx::Rect valid_bounds = container_->GetBounds(); | |
237 keyboard::KeyboardController* keyboard_controller = | |
238 keyboard::KeyboardController::GetInstance(); | |
239 if (keyboard_controller) { | |
240 gfx::Rect bounds = keyboard_controller->current_keyboard_bounds(); | |
241 if (!bounds.IsEmpty()) { | |
242 valid_bounds.set_height( | |
243 std::max(0, valid_bounds.height() - bounds.height())); | |
244 } | |
245 } | |
246 return valid_bounds; | |
247 } | |
248 | |
249 gfx::Rect SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds( | |
250 const WmWindow* window) { | |
251 gfx::Rect target_bounds; | |
252 gfx::Rect usable_area = GetUsableDialogArea(); | |
253 if (windows_to_center_.count(window) > 0) { | |
254 // Keep the dialog centered if it was centered before. | |
255 target_bounds = usable_area; | |
256 target_bounds.ClampToCenteredSize(window->GetBounds().size()); | |
257 } else { | |
258 // Keep the dialog within the usable area. | |
259 target_bounds = window->GetBounds(); | |
260 target_bounds.AdjustToFit(usable_area); | |
261 } | |
262 if (usable_area != container_->GetBounds()) { | |
263 // Don't clamp the dialog for the keyboard. Keep the size as it is but make | |
264 // sure that the top remains visible. | |
265 // TODO(skuhne): M37 should add over scroll functionality to address this. | |
266 target_bounds.set_size(window->GetBounds().size()); | |
267 } | |
268 return target_bounds; | |
269 } | |
270 | |
271 bool SystemModalContainerLayoutManager::IsBoundsCentered( | |
272 const gfx::Rect& bounds) const { | |
273 gfx::Point window_center = bounds.CenterPoint(); | |
274 gfx::Point container_center = GetUsableDialogArea().CenterPoint(); | |
275 return std::abs(window_center.x() - container_center.x()) < | |
276 kCenterPixelDelta && | |
277 std::abs(window_center.y() - container_center.y()) < kCenterPixelDelta; | |
278 } | |
279 | |
280 } // namespace ash | |
OLD | NEW |