system_modal_container_layout_manager.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. #include "ash/wm/system_modal_container_layout_manager.h"
  5. #include <cmath>
  6. #include <memory>
  7. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  8. #include "ash/public/cpp/shell_window_ids.h"
  9. #include "ash/root_window_controller.h"
  10. #include "ash/session/session_controller_impl.h"
  11. #include "ash/shell.h"
  12. #include "ash/wm/window_dimmer.h"
  13. #include "ash/wm/window_util.h"
  14. #include "base/containers/contains.h"
  15. #include "ui/aura/client/aura_constants.h"
  16. #include "ui/aura/window.h"
  17. #include "ui/wm/core/coordinate_conversion.h"
  18. #include "ui/wm/core/window_util.h"
  19. namespace ash {
  20. namespace {
  21. // The center point of the window can diverge this much from the center point
  22. // of the container to be kept centered upon resizing operations.
  23. const int kCenterPixelDelta = 32;
  24. ui::ModalType GetModalType(aura::Window* window) {
  25. return window->GetProperty(aura::client::kModalKey);
  26. }
  27. bool HasTransientAncestor(const aura::Window* window,
  28. const aura::Window* ancestor) {
  29. const aura::Window* transient_parent = ::wm::GetTransientParent(window);
  30. if (transient_parent == ancestor)
  31. return true;
  32. return transient_parent ? HasTransientAncestor(transient_parent, ancestor)
  33. : false;
  34. }
  35. }
  36. ////////////////////////////////////////////////////////////////////////////////
  37. // SystemModalContainerLayoutManager, public:
  38. SystemModalContainerLayoutManager::SystemModalContainerLayoutManager(
  39. aura::Window* container)
  40. : container_(container) {
  41. Shelf* shelf = RootWindowController::ForWindow(container_)->shelf();
  42. shelf_observation_.Observe(shelf);
  43. }
  44. SystemModalContainerLayoutManager::~SystemModalContainerLayoutManager() {
  45. auto* keyboard_controller = keyboard::KeyboardUIController::Get();
  46. if (keyboard_controller->HasObserver(this))
  47. keyboard_controller->RemoveObserver(this);
  48. }
  49. ////////////////////////////////////////////////////////////////////////////////
  50. // SystemModalContainerLayoutManager, aura::LayoutManager implementation:
  51. void SystemModalContainerLayoutManager::OnChildWindowVisibilityChanged(
  52. aura::Window* window,
  53. bool visible) {
  54. if (GetModalType(window) != ui::MODAL_TYPE_SYSTEM)
  55. return;
  56. if (window->IsVisible()) {
  57. DCHECK(!base::Contains(modal_windows_, window));
  58. AddModalWindow(window);
  59. } else {
  60. if (RemoveModalWindow(window))
  61. OnModalWindowRemoved(window);
  62. }
  63. }
  64. void SystemModalContainerLayoutManager::OnWindowResized() {
  65. PositionDialogsAfterWorkAreaResize();
  66. }
  67. void SystemModalContainerLayoutManager::OnWindowAddedToLayout(
  68. aura::Window* child) {
  69. DCHECK(child->GetType() == aura::client::WINDOW_TYPE_NORMAL ||
  70. child->GetType() == aura::client::WINDOW_TYPE_POPUP);
  71. DCHECK(container_->GetId() != kShellWindowId_LockSystemModalContainer ||
  72. Shell::Get()->session_controller()->IsUserSessionBlocked());
  73. // Since this is for SystemModal, there is no good reason to add windows
  74. // other than MODAL_TYPE_NONE or MODAL_TYPE_SYSTEM. DCHECK to avoid simple
  75. // mistake.
  76. DCHECK_NE(GetModalType(child), ui::MODAL_TYPE_CHILD);
  77. DCHECK_NE(GetModalType(child), ui::MODAL_TYPE_WINDOW);
  78. child->AddObserver(this);
  79. if (GetModalType(child) == ui::MODAL_TYPE_SYSTEM && child->IsVisible())
  80. AddModalWindow(child);
  81. }
  82. void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout(
  83. aura::Window* child) {
  84. child->RemoveObserver(this);
  85. windows_to_center_.erase(child);
  86. if (GetModalType(child) == ui::MODAL_TYPE_SYSTEM)
  87. RemoveModalWindow(child);
  88. }
  89. void SystemModalContainerLayoutManager::SetChildBounds(
  90. aura::Window* child,
  91. const gfx::Rect& requested_bounds) {
  92. WmDefaultLayoutManager::SetChildBounds(child, requested_bounds);
  93. if (IsBoundsCentered(requested_bounds))
  94. windows_to_center_.insert(child);
  95. else
  96. windows_to_center_.erase(child);
  97. }
  98. ////////////////////////////////////////////////////////////////////////////////
  99. // SystemModalContainerLayoutManager, aura::WindowObserver implementation:
  100. void SystemModalContainerLayoutManager::OnWindowPropertyChanged(
  101. aura::Window* window,
  102. const void* key,
  103. intptr_t old) {
  104. if (key != aura::client::kModalKey || !window->IsVisible())
  105. return;
  106. if (window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM) {
  107. if (base::Contains(modal_windows_, window))
  108. return;
  109. AddModalWindow(window);
  110. } else {
  111. if (RemoveModalWindow(window))
  112. OnModalWindowRemoved(window);
  113. }
  114. }
  115. ////////////////////////////////////////////////////////////////////////////////
  116. // SystemModalContainerLayoutManager, Keyboard::KeyboardControllerObserver
  117. // implementation:
  118. void SystemModalContainerLayoutManager::OnKeyboardOccludedBoundsChanged(
  119. const gfx::Rect& new_bounds_in_screen) {
  120. PositionDialogsAfterWorkAreaResize();
  121. }
  122. bool SystemModalContainerLayoutManager::IsPartOfActiveModalWindow(
  123. aura::Window* window) {
  124. return modal_window() &&
  125. (modal_window()->Contains(window) ||
  126. HasTransientAncestor(::wm::GetToplevelWindow(window),
  127. modal_window()));
  128. }
  129. bool SystemModalContainerLayoutManager::ActivateNextModalWindow() {
  130. if (modal_windows_.empty())
  131. return false;
  132. wm::ActivateWindow(modal_window());
  133. return true;
  134. }
  135. void SystemModalContainerLayoutManager::CreateModalBackground() {
  136. if (!window_dimmer_) {
  137. window_dimmer_ = std::make_unique<WindowDimmer>(container_);
  138. window_dimmer_->window()->SetName(
  139. "SystemModalContainerLayoutManager.ModalBackground");
  140. // The keyboard isn't always enabled.
  141. if (keyboard::KeyboardUIController::Get()->IsEnabled())
  142. keyboard::KeyboardUIController::Get()->AddObserver(this);
  143. }
  144. window_dimmer_->window()->Show();
  145. }
  146. void SystemModalContainerLayoutManager::DestroyModalBackground() {
  147. if (!window_dimmer_)
  148. return;
  149. auto* keyboard_controller = keyboard::KeyboardUIController::Get();
  150. if (keyboard_controller->HasObserver(this))
  151. keyboard_controller->RemoveObserver(this);
  152. window_dimmer_.reset();
  153. }
  154. // static
  155. bool SystemModalContainerLayoutManager::IsModalBackground(
  156. aura::Window* window) {
  157. int id = window->parent()->GetId();
  158. if (id != kShellWindowId_SystemModalContainer &&
  159. id != kShellWindowId_LockSystemModalContainer)
  160. return false;
  161. SystemModalContainerLayoutManager* layout_manager =
  162. static_cast<SystemModalContainerLayoutManager*>(
  163. window->parent()->layout_manager());
  164. return layout_manager->window_dimmer_ &&
  165. layout_manager->window_dimmer_->window() == window;
  166. }
  167. // This is invoked when the work area changes.
  168. // * SystemModalContainerLayoutManager windows depend on
  169. // changes to the accessibility panel insets, which are
  170. // stored and handled globally via ShelfLayoutManager.
  171. void SystemModalContainerLayoutManager::WillChangeVisibilityState(
  172. ShelfVisibilityState new_state) {
  173. PositionDialogsAfterWorkAreaResize();
  174. }
  175. ////////////////////////////////////////////////////////////////////////////////
  176. // SystemModalContainerLayoutManager, private:
  177. void SystemModalContainerLayoutManager::AddModalWindow(aura::Window* window) {
  178. if (modal_windows_.empty()) {
  179. aura::Window* capture_window = window_util::GetCaptureWindow();
  180. if (capture_window)
  181. capture_window->ReleaseCapture();
  182. }
  183. DCHECK(window->IsVisible());
  184. DCHECK(!base::Contains(modal_windows_, window));
  185. modal_windows_.push_back(window);
  186. // Create the modal background on all displays for |window|.
  187. for (aura::Window* root_window : Shell::GetAllRootWindows()) {
  188. RootWindowController::ForWindow(root_window)
  189. ->GetSystemModalLayoutManager(window)
  190. ->CreateModalBackground();
  191. }
  192. window->parent()->StackChildAtTop(window);
  193. gfx::Rect target_bounds = window->bounds();
  194. target_bounds.AdjustToFit(GetUsableDialogArea());
  195. window->SetBounds(target_bounds);
  196. }
  197. bool SystemModalContainerLayoutManager::RemoveModalWindow(
  198. aura::Window* window) {
  199. auto it = std::find(modal_windows_.begin(), modal_windows_.end(), window);
  200. if (it == modal_windows_.end())
  201. return false;
  202. modal_windows_.erase(it);
  203. return true;
  204. }
  205. void SystemModalContainerLayoutManager::OnModalWindowRemoved(
  206. aura::Window* removed) {
  207. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  208. for (aura::Window* root_window : root_windows) {
  209. if (RootWindowController::ForWindow(root_window)
  210. ->GetSystemModalLayoutManager(removed)
  211. ->ActivateNextModalWindow()) {
  212. return;
  213. }
  214. }
  215. for (aura::Window* root_window : root_windows) {
  216. RootWindowController::ForWindow(root_window)
  217. ->GetSystemModalLayoutManager(removed)
  218. ->DestroyModalBackground();
  219. }
  220. }
  221. void SystemModalContainerLayoutManager::PositionDialogsAfterWorkAreaResize() {
  222. if (modal_windows_.empty())
  223. return;
  224. for (aura::Window* window : modal_windows_)
  225. window->SetBounds(GetCenteredAndOrFittedBounds(window));
  226. }
  227. gfx::Rect SystemModalContainerLayoutManager::GetUsableDialogArea() const {
  228. // Instead of resizing the system modal container, we move only the modal
  229. // windows. This way we avoid flashing lines upon resize animation and if the
  230. // keyboard will not fill left to right, the background is still covered.
  231. gfx::Rect valid_bounds = container_->bounds();
  232. const auto& display =
  233. display::Screen::GetScreen()->GetDisplayNearestWindow(container_);
  234. gfx::Rect work_area = display.work_area();
  235. // Convert work area in screen global coordinates to root local coordinates.
  236. wm::ConvertRectFromScreen(container_->GetRootWindow(), &work_area);
  237. // Similarly to NativeWidgetAura::CenterWindow, when centering window,
  238. // we take the intersection of the host and the container bounds.
  239. // The existing tests, SystemModalContainerLayoutManagerTest.KeepVisible and
  240. // KeepCentered, include the cases that container bounds are resizable.
  241. valid_bounds.Intersect(work_area);
  242. keyboard::KeyboardUIController* keyboard_controller =
  243. keyboard::KeyboardUIController::Get();
  244. if (keyboard_controller->IsEnabled()) {
  245. gfx::Rect bounds =
  246. keyboard_controller->GetWorkspaceOccludedBoundsInScreen();
  247. valid_bounds.set_height(
  248. std::max(0, valid_bounds.height() - bounds.height()));
  249. }
  250. return valid_bounds;
  251. }
  252. gfx::Rect SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds(
  253. const aura::Window* window) {
  254. gfx::Rect target_bounds;
  255. gfx::Rect usable_area = GetUsableDialogArea();
  256. if (windows_to_center_.count(window) > 0) {
  257. // Keep the dialog centered if it was centered before.
  258. target_bounds = usable_area;
  259. target_bounds.ClampToCenteredSize(window->bounds().size());
  260. } else {
  261. // Keep the dialog within the usable area.
  262. target_bounds = window->bounds();
  263. target_bounds.AdjustToFit(usable_area);
  264. }
  265. if (keyboard::KeyboardUIController::Get()->IsEnabled()) {
  266. // Don't clamp the dialog for the keyboard. Keep the size as it is but make
  267. // sure that the top remains visible.
  268. // TODO(skuhne): M37 should add over scroll functionality to address this.
  269. target_bounds.set_size(window->bounds().size());
  270. }
  271. return target_bounds;
  272. }
  273. bool SystemModalContainerLayoutManager::IsBoundsCentered(
  274. const gfx::Rect& bounds) const {
  275. gfx::Point window_center = bounds.CenterPoint();
  276. gfx::Point container_center = GetUsableDialogArea().CenterPoint();
  277. return std::abs(window_center.x() - container_center.x()) <
  278. kCenterPixelDelta &&
  279. std::abs(window_center.y() - container_center.y()) < kCenterPixelDelta;
  280. }
  281. } // namespace ash