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/system_modal_container_layout_manager.h"
6
7 #include <cmath>
8
9 #include "ash/session/session_state_delegate.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/wm/system_modal_container_event_filter.h"
13 #include "ash/wm/window_animations.h"
14 #include "ash/wm/window_util.h"
15 #include "base/bind.h"
16 #include "ui/aura/client/aura_constants.h"
17 #include "ui/aura/client/capture_client.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_event_dispatcher.h"
20 #include "ui/aura/window_property.h"
21 #include "ui/base/ui_base_switches_util.h"
22 #include "ui/compositor/layer.h"
23 #include "ui/compositor/layer_animator.h"
24 #include "ui/compositor/scoped_layer_animation_settings.h"
25 #include "ui/events/event.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/keyboard/keyboard_controller.h"
28 #include "ui/views/background.h"
29 #include "ui/views/view.h"
30 #include "ui/views/widget/widget.h"
31 #include "ui/wm/core/compound_event_filter.h"
32
33 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ASH_EXPORT, bool);
34
35 namespace ash {
36
37 // If this is set to true, the window will get centered.
38 DEFINE_WINDOW_PROPERTY_KEY(bool, kCenteredKey, false);
39
40 // The center point of the window can diverge this much from the center point
41 // of the container to be kept centered upon resizing operations.
42 const int kCenterPixelDelta = 32;
43
44 ////////////////////////////////////////////////////////////////////////////////
45 // SystemModalContainerLayoutManager, public:
46
SystemModalContainerLayoutManager(aura::Window * container)47 SystemModalContainerLayoutManager::SystemModalContainerLayoutManager(
48 aura::Window* container)
49 : container_(container),
50 modal_background_(NULL) {
51 }
52
~SystemModalContainerLayoutManager()53 SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() {
54 }
55
56 ////////////////////////////////////////////////////////////////////////////////
57 // SystemModalContainerLayoutManager, aura::LayoutManager implementation:
58
OnWindowResized()59 void SystemModalContainerLayoutManager::OnWindowResized() {
60 if (modal_background_) {
61 // Note: we have to set the entire bounds with the screen offset.
62 modal_background_->SetBounds(
63 Shell::GetScreen()->GetDisplayNearestWindow(container_).bounds());
64 }
65 PositionDialogsAfterWorkAreaResize();
66 }
67
OnWindowAddedToLayout(aura::Window * child)68 void SystemModalContainerLayoutManager::OnWindowAddedToLayout(
69 aura::Window* child) {
70 DCHECK((modal_background_ && child == modal_background_->GetNativeView()) ||
71 child->type() == ui::wm::WINDOW_TYPE_NORMAL ||
72 child->type() == ui::wm::WINDOW_TYPE_POPUP);
73 DCHECK(
74 container_->id() != kShellWindowId_LockSystemModalContainer ||
75 Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked());
76
77 child->AddObserver(this);
78 if (child->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE)
79 AddModalWindow(child);
80 }
81
OnWillRemoveWindowFromLayout(aura::Window * child)82 void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout(
83 aura::Window* child) {
84 child->RemoveObserver(this);
85 if (child->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE)
86 RemoveModalWindow(child);
87 }
88
OnWindowRemovedFromLayout(aura::Window * child)89 void SystemModalContainerLayoutManager::OnWindowRemovedFromLayout(
90 aura::Window* child) {
91 }
92
OnChildWindowVisibilityChanged(aura::Window * child,bool visible)93 void SystemModalContainerLayoutManager::OnChildWindowVisibilityChanged(
94 aura::Window* child,
95 bool visible) {
96 }
97
SetChildBounds(aura::Window * child,const gfx::Rect & requested_bounds)98 void SystemModalContainerLayoutManager::SetChildBounds(
99 aura::Window* child,
100 const gfx::Rect& requested_bounds) {
101 SetChildBoundsDirect(child, requested_bounds);
102 child->SetProperty(kCenteredKey, DialogIsCentered(requested_bounds));
103 }
104
105 ////////////////////////////////////////////////////////////////////////////////
106 // SystemModalContainerLayoutManager, aura::WindowObserver implementation:
107
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)108 void SystemModalContainerLayoutManager::OnWindowPropertyChanged(
109 aura::Window* window,
110 const void* key,
111 intptr_t old) {
112 if (key != aura::client::kModalKey)
113 return;
114
115 if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE) {
116 AddModalWindow(window);
117 } else if (static_cast<ui::ModalType>(old) != ui::MODAL_TYPE_NONE) {
118 RemoveModalWindow(window);
119 Shell::GetInstance()->OnModalWindowRemoved(window);
120 }
121 }
122
OnWindowDestroying(aura::Window * window)123 void SystemModalContainerLayoutManager::OnWindowDestroying(
124 aura::Window* window) {
125 if (modal_background_ && modal_background_->GetNativeView() == window)
126 modal_background_ = NULL;
127 }
128
129 ////////////////////////////////////////////////////////////////////////////////
130 // SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver
131 // implementation:
132
OnKeyboardBoundsChanging(const gfx::Rect & new_bounds)133 void SystemModalContainerLayoutManager::OnKeyboardBoundsChanging(
134 const gfx::Rect& new_bounds) {
135 PositionDialogsAfterWorkAreaResize();
136 }
137
CanWindowReceiveEvents(aura::Window * window)138 bool SystemModalContainerLayoutManager::CanWindowReceiveEvents(
139 aura::Window* window) {
140 // We could get when we're at lock screen and there is modal window at
141 // system modal window layer which added event filter.
142 // Now this lock modal windows layer layout manager should not block events
143 // for windows at lock layer.
144 // See SystemModalContainerLayoutManagerTest.EventFocusContainers and
145 // http://crbug.com/157469
146 if (modal_windows_.empty())
147 return true;
148 // This container can not handle events if the screen is locked and it is not
149 // above the lock screen layer (crbug.com/110920).
150 if (Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked() &&
151 container_->id() < ash::kShellWindowId_LockScreenContainer)
152 return true;
153 return wm::GetActivatableWindow(window) == modal_window();
154 }
155
ActivateNextModalWindow()156 bool SystemModalContainerLayoutManager::ActivateNextModalWindow() {
157 if (modal_windows_.empty())
158 return false;
159 wm::ActivateWindow(modal_window());
160 return true;
161 }
162
CreateModalBackground()163 void SystemModalContainerLayoutManager::CreateModalBackground() {
164 if (!modal_background_) {
165 modal_background_ = new views::Widget;
166 views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
167 params.parent = container_;
168 params.bounds = Shell::GetScreen()->GetDisplayNearestWindow(
169 container_).bounds();
170 modal_background_->Init(params);
171 modal_background_->GetNativeView()->SetName(
172 "SystemModalContainerLayoutManager.ModalBackground");
173 views::View* contents_view = new views::View();
174 // TODO(jamescook): This could be SK_ColorWHITE for the new dialog style.
175 contents_view->set_background(
176 views::Background::CreateSolidBackground(SK_ColorBLACK));
177 modal_background_->SetContentsView(contents_view);
178 modal_background_->GetNativeView()->layer()->SetOpacity(0.0f);
179 // There isn't always a keyboard controller.
180 if (keyboard::KeyboardController::GetInstance())
181 keyboard::KeyboardController::GetInstance()->AddObserver(this);
182 }
183
184 ui::ScopedLayerAnimationSettings settings(
185 modal_background_->GetNativeView()->layer()->GetAnimator());
186 // Show should not be called with a target opacity of 0. We therefore start
187 // the fade to show animation before Show() is called.
188 modal_background_->GetNativeView()->layer()->SetOpacity(0.5f);
189 modal_background_->Show();
190 container_->StackChildAtTop(modal_background_->GetNativeView());
191 }
192
DestroyModalBackground()193 void SystemModalContainerLayoutManager::DestroyModalBackground() {
194 // modal_background_ can be NULL when a root window is shutting down
195 // and OnWindowDestroying is called first.
196 if (modal_background_) {
197 if (keyboard::KeyboardController::GetInstance())
198 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
199 ::wm::ScopedHidingAnimationSettings settings(
200 modal_background_->GetNativeView());
201 modal_background_->Close();
202 modal_background_->GetNativeView()->layer()->SetOpacity(0.0f);
203 modal_background_ = NULL;
204 }
205 }
206
207 // static
IsModalBackground(aura::Window * window)208 bool SystemModalContainerLayoutManager::IsModalBackground(
209 aura::Window* window) {
210 int id = window->parent()->id();
211 if (id != kShellWindowId_SystemModalContainer &&
212 id != kShellWindowId_LockSystemModalContainer)
213 return false;
214 SystemModalContainerLayoutManager* layout_manager =
215 static_cast<SystemModalContainerLayoutManager*>(
216 window->parent()->layout_manager());
217 return layout_manager->modal_background_ &&
218 layout_manager->modal_background_->GetNativeWindow() == window;
219 }
220
221 ////////////////////////////////////////////////////////////////////////////////
222 // SystemModalContainerLayoutManager, private:
223
AddModalWindow(aura::Window * window)224 void SystemModalContainerLayoutManager::AddModalWindow(aura::Window* window) {
225 if (modal_windows_.empty()) {
226 aura::Window* capture_window = aura::client::GetCaptureWindow(container_);
227 if (capture_window)
228 capture_window->ReleaseCapture();
229 }
230 modal_windows_.push_back(window);
231 Shell::GetInstance()->CreateModalBackground(window);
232 window->parent()->StackChildAtTop(window);
233
234 gfx::Rect target_bounds = window->bounds();
235 target_bounds.AdjustToFit(GetUsableDialogArea());
236 window->SetBounds(target_bounds);
237 }
238
RemoveModalWindow(aura::Window * window)239 void SystemModalContainerLayoutManager::RemoveModalWindow(
240 aura::Window* window) {
241 aura::Window::Windows::iterator it =
242 std::find(modal_windows_.begin(), modal_windows_.end(), window);
243 if (it != modal_windows_.end())
244 modal_windows_.erase(it);
245 }
246
PositionDialogsAfterWorkAreaResize()247 void SystemModalContainerLayoutManager::PositionDialogsAfterWorkAreaResize() {
248 if (!modal_windows_.empty()) {
249 for (aura::Window::Windows::iterator it = modal_windows_.begin();
250 it != modal_windows_.end(); ++it) {
251 (*it)->SetBounds(GetCenteredAndOrFittedBounds(*it));
252 }
253 }
254 }
255
GetUsableDialogArea()256 gfx::Rect SystemModalContainerLayoutManager::GetUsableDialogArea() {
257 // Instead of resizing the system modal container, we move only the modal
258 // windows. This way we avoid flashing lines upon resize animation and if the
259 // keyboard will not fill left to right, the background is still covered.
260 gfx::Rect valid_bounds = container_->bounds();
261 keyboard::KeyboardController* keyboard_controller =
262 keyboard::KeyboardController::GetInstance();
263 if (keyboard_controller) {
264 gfx::Rect bounds = keyboard_controller->current_keyboard_bounds();
265 if (!bounds.IsEmpty()) {
266 valid_bounds.set_height(std::max(
267 0, valid_bounds.height() - bounds.height()));
268 }
269 }
270 return valid_bounds;
271 }
272
GetCenteredAndOrFittedBounds(const aura::Window * window)273 gfx::Rect SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds(
274 const aura::Window* window) {
275 gfx::Rect target_bounds;
276 gfx::Rect usable_area = GetUsableDialogArea();
277 if (window->GetProperty(kCenteredKey)) {
278 // Keep the dialog centered if it was centered before.
279 target_bounds = usable_area;
280 target_bounds.ClampToCenteredSize(window->bounds().size());
281 } else {
282 // Keep the dialog within the usable area.
283 target_bounds = window->bounds();
284 target_bounds.AdjustToFit(usable_area);
285 }
286 if (usable_area != container_->bounds()) {
287 // Don't clamp the dialog for the keyboard. Keep the size as it is but make
288 // sure that the top remains visible.
289 // TODO(skuhne): M37 should add over scroll functionality to address this.
290 target_bounds.set_size(window->bounds().size());
291 }
292 return target_bounds;
293 }
294
DialogIsCentered(const gfx::Rect & window_bounds)295 bool SystemModalContainerLayoutManager::DialogIsCentered(
296 const gfx::Rect& window_bounds) {
297 gfx::Point window_center = window_bounds.CenterPoint();
298 gfx::Point container_center = GetUsableDialogArea().CenterPoint();
299 return
300 std::abs(window_center.x() - container_center.x()) < kCenterPixelDelta &&
301 std::abs(window_center.y() - container_center.y()) < kCenterPixelDelta;
302 }
303
304 } // namespace ash
305