Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ash / wm / system_modal_container_layout_manager.cc
blobe5702fadd733646af11da42717c2764edbf34531
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"
7 #include <cmath>
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 namespace ash {
35 // If this is set to true, the window will get centered.
36 DEFINE_WINDOW_PROPERTY_KEY(bool, kCenteredKey, false);
38 // The center point of the window can diverge this much from the center point
39 // of the container to be kept centered upon resizing operations.
40 const int kCenterPixelDelta = 32;
42 ////////////////////////////////////////////////////////////////////////////////
43 // SystemModalContainerLayoutManager, public:
45 SystemModalContainerLayoutManager::SystemModalContainerLayoutManager(
46 aura::Window* container)
47 : SnapToPixelLayoutManager(container),
48 container_(container),
49 modal_background_(NULL) {
52 SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() {
55 ////////////////////////////////////////////////////////////////////////////////
56 // SystemModalContainerLayoutManager, aura::LayoutManager implementation:
58 void SystemModalContainerLayoutManager::OnWindowResized() {
59 if (modal_background_) {
60 // Note: we have to set the entire bounds with the screen offset.
61 modal_background_->SetBounds(
62 Shell::GetScreen()->GetDisplayNearestWindow(container_).bounds());
64 PositionDialogsAfterWorkAreaResize();
67 void SystemModalContainerLayoutManager::OnWindowAddedToLayout(
68 aura::Window* child) {
69 DCHECK((modal_background_ && child == modal_background_->GetNativeView()) ||
70 child->type() == ui::wm::WINDOW_TYPE_NORMAL ||
71 child->type() == ui::wm::WINDOW_TYPE_POPUP);
72 DCHECK(
73 container_->id() != kShellWindowId_LockSystemModalContainer ||
74 Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked());
76 child->AddObserver(this);
77 if (child->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE)
78 AddModalWindow(child);
81 void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout(
82 aura::Window* child) {
83 child->RemoveObserver(this);
84 if (child->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE)
85 RemoveModalWindow(child);
88 void SystemModalContainerLayoutManager::SetChildBounds(
89 aura::Window* child,
90 const gfx::Rect& requested_bounds) {
91 SnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
92 child->SetProperty(kCenteredKey, DialogIsCentered(requested_bounds));
95 ////////////////////////////////////////////////////////////////////////////////
96 // SystemModalContainerLayoutManager, aura::WindowObserver implementation:
98 void SystemModalContainerLayoutManager::OnWindowPropertyChanged(
99 aura::Window* window,
100 const void* key,
101 intptr_t old) {
102 if (key != aura::client::kModalKey)
103 return;
105 if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE) {
106 AddModalWindow(window);
107 } else if (static_cast<ui::ModalType>(old) != ui::MODAL_TYPE_NONE) {
108 RemoveModalWindow(window);
109 Shell::GetInstance()->OnModalWindowRemoved(window);
113 void SystemModalContainerLayoutManager::OnWindowDestroying(
114 aura::Window* window) {
115 if (modal_background_ && modal_background_->GetNativeView() == window) {
116 if (keyboard::KeyboardController::GetInstance())
117 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
118 modal_background_ = NULL;
122 ////////////////////////////////////////////////////////////////////////////////
123 // SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver
124 // implementation:
126 void SystemModalContainerLayoutManager::OnKeyboardBoundsChanging(
127 const gfx::Rect& new_bounds) {
128 PositionDialogsAfterWorkAreaResize();
131 bool SystemModalContainerLayoutManager::CanWindowReceiveEvents(
132 aura::Window* window) {
133 // We could get when we're at lock screen and there is modal window at
134 // system modal window layer which added event filter.
135 // Now this lock modal windows layer layout manager should not block events
136 // for windows at lock layer.
137 // See SystemModalContainerLayoutManagerTest.EventFocusContainers and
138 // http://crbug.com/157469
139 if (modal_windows_.empty())
140 return true;
141 // This container can not handle events if the screen is locked and it is not
142 // above the lock screen layer (crbug.com/110920).
143 if (Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked() &&
144 container_->id() < ash::kShellWindowId_LockScreenContainer)
145 return true;
146 return wm::GetActivatableWindow(window) == modal_window();
149 bool SystemModalContainerLayoutManager::ActivateNextModalWindow() {
150 if (modal_windows_.empty())
151 return false;
152 wm::ActivateWindow(modal_window());
153 return true;
156 void SystemModalContainerLayoutManager::CreateModalBackground() {
157 if (!modal_background_) {
158 modal_background_ = new views::Widget;
159 views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
160 params.parent = container_;
161 params.bounds = Shell::GetScreen()->GetDisplayNearestWindow(
162 container_).bounds();
163 modal_background_->Init(params);
164 modal_background_->GetNativeView()->SetName(
165 "SystemModalContainerLayoutManager.ModalBackground");
166 views::View* contents_view = new views::View();
167 // TODO(jamescook): This could be SK_ColorWHITE for the new dialog style.
168 contents_view->set_background(
169 views::Background::CreateSolidBackground(SK_ColorBLACK));
170 modal_background_->SetContentsView(contents_view);
171 modal_background_->GetNativeView()->layer()->SetOpacity(0.0f);
172 // There isn't always a keyboard controller.
173 if (keyboard::KeyboardController::GetInstance())
174 keyboard::KeyboardController::GetInstance()->AddObserver(this);
177 ui::ScopedLayerAnimationSettings settings(
178 modal_background_->GetNativeView()->layer()->GetAnimator());
179 // Show should not be called with a target opacity of 0. We therefore start
180 // the fade to show animation before Show() is called.
181 modal_background_->GetNativeView()->layer()->SetOpacity(0.5f);
182 modal_background_->Show();
183 container_->StackChildAtTop(modal_background_->GetNativeView());
186 void SystemModalContainerLayoutManager::DestroyModalBackground() {
187 // modal_background_ can be NULL when a root window is shutting down
188 // and OnWindowDestroying is called first.
189 if (modal_background_) {
190 if (keyboard::KeyboardController::GetInstance())
191 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
192 ::wm::ScopedHidingAnimationSettings settings(
193 modal_background_->GetNativeView());
194 modal_background_->Close();
195 modal_background_->GetNativeView()->layer()->SetOpacity(0.0f);
196 modal_background_ = NULL;
200 // static
201 bool SystemModalContainerLayoutManager::IsModalBackground(
202 aura::Window* window) {
203 int id = window->parent()->id();
204 if (id != kShellWindowId_SystemModalContainer &&
205 id != kShellWindowId_LockSystemModalContainer)
206 return false;
207 SystemModalContainerLayoutManager* layout_manager =
208 static_cast<SystemModalContainerLayoutManager*>(
209 window->parent()->layout_manager());
210 return layout_manager->modal_background_ &&
211 layout_manager->modal_background_->GetNativeWindow() == window;
214 ////////////////////////////////////////////////////////////////////////////////
215 // SystemModalContainerLayoutManager, private:
217 void SystemModalContainerLayoutManager::AddModalWindow(aura::Window* window) {
218 if (modal_windows_.empty()) {
219 aura::Window* capture_window = aura::client::GetCaptureWindow(container_);
220 if (capture_window)
221 capture_window->ReleaseCapture();
223 modal_windows_.push_back(window);
224 Shell::GetInstance()->CreateModalBackground(window);
225 window->parent()->StackChildAtTop(window);
227 gfx::Rect target_bounds = window->bounds();
228 target_bounds.AdjustToFit(GetUsableDialogArea());
229 window->SetBounds(target_bounds);
232 void SystemModalContainerLayoutManager::RemoveModalWindow(
233 aura::Window* window) {
234 aura::Window::Windows::iterator it =
235 std::find(modal_windows_.begin(), modal_windows_.end(), window);
236 if (it != modal_windows_.end())
237 modal_windows_.erase(it);
240 void SystemModalContainerLayoutManager::PositionDialogsAfterWorkAreaResize() {
241 if (!modal_windows_.empty()) {
242 for (aura::Window::Windows::iterator it = modal_windows_.begin();
243 it != modal_windows_.end(); ++it) {
244 (*it)->SetBounds(GetCenteredAndOrFittedBounds(*it));
249 gfx::Rect SystemModalContainerLayoutManager::GetUsableDialogArea() {
250 // Instead of resizing the system modal container, we move only the modal
251 // windows. This way we avoid flashing lines upon resize animation and if the
252 // keyboard will not fill left to right, the background is still covered.
253 gfx::Rect valid_bounds = container_->bounds();
254 keyboard::KeyboardController* keyboard_controller =
255 keyboard::KeyboardController::GetInstance();
256 if (keyboard_controller) {
257 gfx::Rect bounds = keyboard_controller->current_keyboard_bounds();
258 if (!bounds.IsEmpty()) {
259 valid_bounds.set_height(std::max(
260 0, valid_bounds.height() - bounds.height()));
263 return valid_bounds;
266 gfx::Rect SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds(
267 const aura::Window* window) {
268 gfx::Rect target_bounds;
269 gfx::Rect usable_area = GetUsableDialogArea();
270 if (window->GetProperty(kCenteredKey)) {
271 // Keep the dialog centered if it was centered before.
272 target_bounds = usable_area;
273 target_bounds.ClampToCenteredSize(window->bounds().size());
274 } else {
275 // Keep the dialog within the usable area.
276 target_bounds = window->bounds();
277 target_bounds.AdjustToFit(usable_area);
279 if (usable_area != container_->bounds()) {
280 // Don't clamp the dialog for the keyboard. Keep the size as it is but make
281 // sure that the top remains visible.
282 // TODO(skuhne): M37 should add over scroll functionality to address this.
283 target_bounds.set_size(window->bounds().size());
285 return target_bounds;
288 bool SystemModalContainerLayoutManager::DialogIsCentered(
289 const gfx::Rect& window_bounds) {
290 gfx::Point window_center = window_bounds.CenterPoint();
291 gfx::Point container_center = GetUsableDialogArea().CenterPoint();
292 return
293 std::abs(window_center.x() - container_center.x()) < kCenterPixelDelta &&
294 std::abs(window_center.y() - container_center.y()) < kCenterPixelDelta;
297 } // namespace ash