• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     : SnapToPixelLayoutManager(container),
50       container_(container),
51       modal_background_(NULL) {
52 }
53 
~SystemModalContainerLayoutManager()54 SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() {
55 }
56 
57 ////////////////////////////////////////////////////////////////////////////////
58 // SystemModalContainerLayoutManager, aura::LayoutManager implementation:
59 
OnWindowResized()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());
65   }
66   PositionDialogsAfterWorkAreaResize();
67 }
68 
OnWindowAddedToLayout(aura::Window * child)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);
74   DCHECK(
75       container_->id() != kShellWindowId_LockSystemModalContainer ||
76       Shell::GetInstance()->session_state_delegate()->IsUserSessionBlocked());
77 
78   child->AddObserver(this);
79   if (child->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE)
80     AddModalWindow(child);
81 }
82 
OnWillRemoveWindowFromLayout(aura::Window * 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);
88 }
89 
SetChildBounds(aura::Window * child,const gfx::Rect & requested_bounds)90 void SystemModalContainerLayoutManager::SetChildBounds(
91     aura::Window* child,
92     const gfx::Rect& requested_bounds) {
93   SnapToPixelLayoutManager::SetChildBounds(child, requested_bounds);
94   child->SetProperty(kCenteredKey, DialogIsCentered(requested_bounds));
95 }
96 
97 ////////////////////////////////////////////////////////////////////////////////
98 // SystemModalContainerLayoutManager, aura::WindowObserver implementation:
99 
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)100 void SystemModalContainerLayoutManager::OnWindowPropertyChanged(
101     aura::Window* window,
102     const void* key,
103     intptr_t old) {
104   if (key != aura::client::kModalKey)
105     return;
106 
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);
112   }
113 }
114 
OnWindowDestroying(aura::Window * window)115 void SystemModalContainerLayoutManager::OnWindowDestroying(
116     aura::Window* window) {
117   if (modal_background_ && modal_background_->GetNativeView() == window)
118     modal_background_ = NULL;
119 }
120 
121 ////////////////////////////////////////////////////////////////////////////////
122 // SystemModalContainerLayoutManager, Keyboard::KeybaordControllerObserver
123 // implementation:
124 
OnKeyboardBoundsChanging(const gfx::Rect & new_bounds)125 void SystemModalContainerLayoutManager::OnKeyboardBoundsChanging(
126     const gfx::Rect& new_bounds) {
127   PositionDialogsAfterWorkAreaResize();
128 }
129 
CanWindowReceiveEvents(aura::Window * window)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())
139     return true;
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)
144     return true;
145   return wm::GetActivatableWindow(window) == modal_window();
146 }
147 
ActivateNextModalWindow()148 bool SystemModalContainerLayoutManager::ActivateNextModalWindow() {
149   if (modal_windows_.empty())
150     return false;
151   wm::ActivateWindow(modal_window());
152   return true;
153 }
154 
CreateModalBackground()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);
174   }
175 
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());
183 }
184 
DestroyModalBackground()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;
196   }
197 }
198 
199 // static
IsModalBackground(aura::Window * window)200 bool SystemModalContainerLayoutManager::IsModalBackground(
201     aura::Window* window) {
202   int id = window->parent()->id();
203   if (id != kShellWindowId_SystemModalContainer &&
204       id != kShellWindowId_LockSystemModalContainer)
205     return false;
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;
211 }
212 
213 ////////////////////////////////////////////////////////////////////////////////
214 // SystemModalContainerLayoutManager, private:
215 
AddModalWindow(aura::Window * window)216 void SystemModalContainerLayoutManager::AddModalWindow(aura::Window* window) {
217   if (modal_windows_.empty()) {
218     aura::Window* capture_window = aura::client::GetCaptureWindow(container_);
219     if (capture_window)
220       capture_window->ReleaseCapture();
221   }
222   modal_windows_.push_back(window);
223   Shell::GetInstance()->CreateModalBackground(window);
224   window->parent()->StackChildAtTop(window);
225 
226   gfx::Rect target_bounds = window->bounds();
227   target_bounds.AdjustToFit(GetUsableDialogArea());
228   window->SetBounds(target_bounds);
229 }
230 
RemoveModalWindow(aura::Window * window)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);
237 }
238 
PositionDialogsAfterWorkAreaResize()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));
244     }
245   }
246 }
247 
GetUsableDialogArea()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()));
260     }
261   }
262   return valid_bounds;
263 }
264 
GetCenteredAndOrFittedBounds(const aura::Window * window)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());
273   } else {
274     // Keep the dialog within the usable area.
275     target_bounds = window->bounds();
276     target_bounds.AdjustToFit(usable_area);
277   }
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());
283   }
284   return target_bounds;
285 }
286 
DialogIsCentered(const gfx::Rect & window_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();
291   return
292       std::abs(window_center.x() - container_center.x()) < kCenterPixelDelta &&
293       std::abs(window_center.y() - container_center.y()) < kCenterPixelDelta;
294 }
295 
296 }  // namespace ash
297