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.
5 #include "ash/wm/system_modal_container_layout_manager.h"
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"
33 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ASH_EXPORT
, bool);
37 // If this is set to true, the window will get centered.
38 DEFINE_WINDOW_PROPERTY_KEY(bool, kCenteredKey
, false);
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;
44 ////////////////////////////////////////////////////////////////////////////////
45 // SystemModalContainerLayoutManager, public:
47 SystemModalContainerLayoutManager::SystemModalContainerLayoutManager(
48 aura::Window
* container
)
49 : SnapToPixelLayoutManager(container
),
50 container_(container
),
51 modal_background_(NULL
) {
54 SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() {
57 ////////////////////////////////////////////////////////////////////////////////
58 // SystemModalContainerLayoutManager, aura::LayoutManager implementation:
60 void SystemModalContainerLayoutManager::OnWindowResized() {
61 if (modal_background_
) {
62 // Note: we have to set the entire bounds with the screen offset.
63 modal_background_
->SetBounds(
64 Shell::GetScreen()->GetDisplayNearestWindow(container_
).bounds());
66 PositionDialogsAfterWorkAreaResize();
69 void SystemModalContainerLayoutManager::OnWindowAddedToLayout(
70 aura::Window
* child
) {
71 DCHECK((modal_background_
&& child
== modal_background_
->GetNativeView()) ||
72 child
->type() == ui::wm::WINDOW_TYPE_NORMAL
||
73 child
->type() == ui::wm::WINDOW_TYPE_POPUP
);
75 container_
->id() != kShellWindowId_LockSystemModalContainer
||
76 Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked());
78 child
->AddObserver(this);
79 if (child
->GetProperty(aura::client::kModalKey
) != ui::MODAL_TYPE_NONE
)
80 AddModalWindow(child
);
83 void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout(
84 aura::Window
* child
) {
85 child
->RemoveObserver(this);
86 if (child
->GetProperty(aura::client::kModalKey
) != ui::MODAL_TYPE_NONE
)
87 RemoveModalWindow(child
);
90 void SystemModalContainerLayoutManager::SetChildBounds(
92 const gfx::Rect
& requested_bounds
) {
93 SnapToPixelLayoutManager::SetChildBounds(child
, requested_bounds
);
94 child
->SetProperty(kCenteredKey
, DialogIsCentered(requested_bounds
));
97 ////////////////////////////////////////////////////////////////////////////////
98 // SystemModalContainerLayoutManager, aura::WindowObserver implementation:
100 void SystemModalContainerLayoutManager::OnWindowPropertyChanged(
101 aura::Window
* window
,
104 if (key
!= aura::client::kModalKey
)
107 if (window
->GetProperty(aura::client::kModalKey
) != ui::MODAL_TYPE_NONE
) {
108 AddModalWindow(window
);
109 } else if (static_cast<ui::ModalType
>(old
) != ui::MODAL_TYPE_NONE
) {
110 RemoveModalWindow(window
);
111 Shell::GetInstance()->OnModalWindowRemoved(window
);
115 void SystemModalContainerLayoutManager::OnWindowDestroying(
116 aura::Window
* window
) {
117 if (modal_background_
&& modal_background_
->GetNativeView() == window
)
118 modal_background_
= NULL
;
121 ////////////////////////////////////////////////////////////////////////////////
122 // SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver
125 void SystemModalContainerLayoutManager::OnKeyboardBoundsChanging(
126 const gfx::Rect
& new_bounds
) {
127 PositionDialogsAfterWorkAreaResize();
130 bool SystemModalContainerLayoutManager::CanWindowReceiveEvents(
131 aura::Window
* window
) {
132 // We could get when we're at lock screen and there is modal window at
133 // system modal window layer which added event filter.
134 // Now this lock modal windows layer layout manager should not block events
135 // for windows at lock layer.
136 // See SystemModalContainerLayoutManagerTest.EventFocusContainers and
137 // http://crbug.com/157469
138 if (modal_windows_
.empty())
140 // This container can not handle events if the screen is locked and it is not
141 // above the lock screen layer (crbug.com/110920).
142 if (Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked() &&
143 container_
->id() < ash::kShellWindowId_LockScreenContainer
)
145 return wm::GetActivatableWindow(window
) == modal_window();
148 bool SystemModalContainerLayoutManager::ActivateNextModalWindow() {
149 if (modal_windows_
.empty())
151 wm::ActivateWindow(modal_window());
155 void SystemModalContainerLayoutManager::CreateModalBackground() {
156 if (!modal_background_
) {
157 modal_background_
= new views::Widget
;
158 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_CONTROL
);
159 params
.parent
= container_
;
160 params
.bounds
= Shell::GetScreen()->GetDisplayNearestWindow(
161 container_
).bounds();
162 modal_background_
->Init(params
);
163 modal_background_
->GetNativeView()->SetName(
164 "SystemModalContainerLayoutManager.ModalBackground");
165 views::View
* contents_view
= new views::View();
166 // TODO(jamescook): This could be SK_ColorWHITE for the new dialog style.
167 contents_view
->set_background(
168 views::Background::CreateSolidBackground(SK_ColorBLACK
));
169 modal_background_
->SetContentsView(contents_view
);
170 modal_background_
->GetNativeView()->layer()->SetOpacity(0.0f
);
171 // There isn't always a keyboard controller.
172 if (keyboard::KeyboardController::GetInstance())
173 keyboard::KeyboardController::GetInstance()->AddObserver(this);
176 ui::ScopedLayerAnimationSettings
settings(
177 modal_background_
->GetNativeView()->layer()->GetAnimator());
178 // Show should not be called with a target opacity of 0. We therefore start
179 // the fade to show animation before Show() is called.
180 modal_background_
->GetNativeView()->layer()->SetOpacity(0.5f
);
181 modal_background_
->Show();
182 container_
->StackChildAtTop(modal_background_
->GetNativeView());
185 void SystemModalContainerLayoutManager::DestroyModalBackground() {
186 // modal_background_ can be NULL when a root window is shutting down
187 // and OnWindowDestroying is called first.
188 if (modal_background_
) {
189 if (keyboard::KeyboardController::GetInstance())
190 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
191 ::wm::ScopedHidingAnimationSettings
settings(
192 modal_background_
->GetNativeView());
193 modal_background_
->Close();
194 modal_background_
->GetNativeView()->layer()->SetOpacity(0.0f
);
195 modal_background_
= NULL
;
200 bool SystemModalContainerLayoutManager::IsModalBackground(
201 aura::Window
* window
) {
202 int id
= window
->parent()->id();
203 if (id
!= kShellWindowId_SystemModalContainer
&&
204 id
!= kShellWindowId_LockSystemModalContainer
)
206 SystemModalContainerLayoutManager
* layout_manager
=
207 static_cast<SystemModalContainerLayoutManager
*>(
208 window
->parent()->layout_manager());
209 return layout_manager
->modal_background_
&&
210 layout_manager
->modal_background_
->GetNativeWindow() == window
;
213 ////////////////////////////////////////////////////////////////////////////////
214 // SystemModalContainerLayoutManager, private:
216 void SystemModalContainerLayoutManager::AddModalWindow(aura::Window
* window
) {
217 if (modal_windows_
.empty()) {
218 aura::Window
* capture_window
= aura::client::GetCaptureWindow(container_
);
220 capture_window
->ReleaseCapture();
222 modal_windows_
.push_back(window
);
223 Shell::GetInstance()->CreateModalBackground(window
);
224 window
->parent()->StackChildAtTop(window
);
226 gfx::Rect target_bounds
= window
->bounds();
227 target_bounds
.AdjustToFit(GetUsableDialogArea());
228 window
->SetBounds(target_bounds
);
231 void SystemModalContainerLayoutManager::RemoveModalWindow(
232 aura::Window
* window
) {
233 aura::Window::Windows::iterator it
=
234 std::find(modal_windows_
.begin(), modal_windows_
.end(), window
);
235 if (it
!= modal_windows_
.end())
236 modal_windows_
.erase(it
);
239 void SystemModalContainerLayoutManager::PositionDialogsAfterWorkAreaResize() {
240 if (!modal_windows_
.empty()) {
241 for (aura::Window::Windows::iterator it
= modal_windows_
.begin();
242 it
!= modal_windows_
.end(); ++it
) {
243 (*it
)->SetBounds(GetCenteredAndOrFittedBounds(*it
));
248 gfx::Rect
SystemModalContainerLayoutManager::GetUsableDialogArea() {
249 // Instead of resizing the system modal container, we move only the modal
250 // windows. This way we avoid flashing lines upon resize animation and if the
251 // keyboard will not fill left to right, the background is still covered.
252 gfx::Rect valid_bounds
= container_
->bounds();
253 keyboard::KeyboardController
* keyboard_controller
=
254 keyboard::KeyboardController::GetInstance();
255 if (keyboard_controller
) {
256 gfx::Rect bounds
= keyboard_controller
->current_keyboard_bounds();
257 if (!bounds
.IsEmpty()) {
258 valid_bounds
.set_height(std::max(
259 0, valid_bounds
.height() - bounds
.height()));
265 gfx::Rect
SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds(
266 const aura::Window
* window
) {
267 gfx::Rect target_bounds
;
268 gfx::Rect usable_area
= GetUsableDialogArea();
269 if (window
->GetProperty(kCenteredKey
)) {
270 // Keep the dialog centered if it was centered before.
271 target_bounds
= usable_area
;
272 target_bounds
.ClampToCenteredSize(window
->bounds().size());
274 // Keep the dialog within the usable area.
275 target_bounds
= window
->bounds();
276 target_bounds
.AdjustToFit(usable_area
);
278 if (usable_area
!= container_
->bounds()) {
279 // Don't clamp the dialog for the keyboard. Keep the size as it is but make
280 // sure that the top remains visible.
281 // TODO(skuhne): M37 should add over scroll functionality to address this.
282 target_bounds
.set_size(window
->bounds().size());
284 return target_bounds
;
287 bool SystemModalContainerLayoutManager::DialogIsCentered(
288 const gfx::Rect
& window_bounds
) {
289 gfx::Point window_center
= window_bounds
.CenterPoint();
290 gfx::Point container_center
= GetUsableDialogArea().CenterPoint();
292 std::abs(window_center
.x() - container_center
.x()) < kCenterPixelDelta
&&
293 std::abs(window_center
.y() - container_center
.y()) < kCenterPixelDelta
;