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