• 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     : 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