pip_positioner.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Copyright 2018 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/pip/pip_positioner.h"
  5. #include <algorithm>
  6. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  7. #include "ash/public/cpp/shell_window_ids.h"
  8. #include "ash/public/cpp/window_properties.h"
  9. #include "ash/root_window_controller.h"
  10. #include "ash/shelf/shelf.h"
  11. #include "ash/shell.h"
  12. #include "ash/wm/collision_detection/collision_detection_utils.h"
  13. #include "ash/wm/window_state.h"
  14. #include "ash/wm/window_util.h"
  15. #include "ash/wm/work_area_insets.h"
  16. #include "base/numerics/safe_conversions.h"
  17. #include "ui/aura/window.h"
  18. #include "ui/gfx/geometry/insets.h"
  19. #include "ui/wm/core/coordinate_conversion.h"
  20. namespace ash {
  21. namespace {
  22. const float kPipDismissMovementProportion = 1.5f;
  23. } // namespace
  24. gfx::Rect PipPositioner::GetBoundsForDrag(const display::Display& display,
  25. const gfx::Rect& bounds_in_screen) {
  26. gfx::Rect drag_bounds = bounds_in_screen;
  27. drag_bounds.AdjustToFit(CollisionDetectionUtils::GetMovementArea(display));
  28. drag_bounds = CollisionDetectionUtils::AvoidObstacles(
  29. display, drag_bounds,
  30. CollisionDetectionUtils::RelativePriority::kPictureInPicture);
  31. return drag_bounds;
  32. }
  33. gfx::Rect PipPositioner::GetDismissedPosition(
  34. const display::Display& display,
  35. const gfx::Rect& bounds_in_screen) {
  36. gfx::Rect work_area = CollisionDetectionUtils::GetMovementArea(display);
  37. const CollisionDetectionUtils::Gravity gravity =
  38. CollisionDetectionUtils::GetGravityToClosestEdge(bounds_in_screen,
  39. work_area);
  40. // Allow the bounds to move at most |kPipDismissMovementProportion| of the
  41. // length of the bounds in the direction of movement.
  42. gfx::Rect bounds_movement_area = bounds_in_screen;
  43. bounds_movement_area.Inset(gfx::Insets::VH(
  44. -bounds_in_screen.height() * kPipDismissMovementProportion,
  45. -bounds_in_screen.width() * kPipDismissMovementProportion));
  46. gfx::Rect dismissed_bounds =
  47. CollisionDetectionUtils::GetAdjustedBoundsByGravity(
  48. bounds_in_screen, bounds_movement_area, gravity);
  49. // If the PIP window isn't close enough to the edge of the screen, don't slide
  50. // it out.
  51. return work_area.Intersects(dismissed_bounds) ? bounds_in_screen
  52. : dismissed_bounds;
  53. }
  54. gfx::Rect PipPositioner::GetPositionAfterMovementAreaChange(
  55. WindowState* window_state) {
  56. // Restore to previous bounds if we have them. This lets us move the PIP
  57. // window back to its original bounds after transient movement area changes,
  58. // like the keyboard popping up and pushing the PIP window up.
  59. gfx::Rect bounds_in_screen = window_state->window()->GetBoundsInScreen();
  60. float* snap_fraction =
  61. window_state->window()->GetProperty(ash::kPipSnapFractionKey);
  62. if (snap_fraction)
  63. bounds_in_screen = GetSnapFractionAppliedBounds(window_state);
  64. return CollisionDetectionUtils::GetRestingPosition(
  65. window_state->GetDisplay(), bounds_in_screen,
  66. CollisionDetectionUtils::RelativePriority::kPictureInPicture);
  67. }
  68. gfx::Rect PipPositioner::GetSnapFractionAppliedBounds(
  69. WindowState* window_state) {
  70. gfx::Rect bounds = window_state->window()->GetBoundsInScreen();
  71. gfx::Rect movement_area =
  72. CollisionDetectionUtils::GetMovementArea(window_state->GetDisplay());
  73. if (!HasSnapFraction(window_state))
  74. return bounds;
  75. float snap_fraction =
  76. *(window_state->window()->GetProperty(ash::kPipSnapFractionKey));
  77. if (snap_fraction < 1.) {
  78. int offset = movement_area.x() +
  79. base::ClampRound(snap_fraction *
  80. (movement_area.width() - bounds.width()));
  81. return gfx::Rect(offset, movement_area.y(), bounds.width(),
  82. bounds.height());
  83. } else if (snap_fraction < 2.) {
  84. snap_fraction -= 1.;
  85. int offset = movement_area.y() +
  86. base::ClampRound(snap_fraction *
  87. (movement_area.height() - bounds.height()));
  88. return gfx::Rect(movement_area.right() - bounds.width(), offset,
  89. bounds.width(), bounds.height());
  90. } else if (snap_fraction < 3.) {
  91. snap_fraction -= 2.;
  92. int offset = movement_area.x() +
  93. base::ClampRound((1. - snap_fraction) *
  94. (movement_area.width() - bounds.width()));
  95. return gfx::Rect(offset, movement_area.bottom() - bounds.height(),
  96. bounds.width(), bounds.height());
  97. } else {
  98. snap_fraction -= 3.;
  99. int offset = movement_area.y() +
  100. base::ClampRound((1. - snap_fraction) *
  101. (movement_area.height() - bounds.height()));
  102. return gfx::Rect(movement_area.x(), offset, bounds.width(),
  103. bounds.height());
  104. }
  105. }
  106. void PipPositioner::ClearSnapFraction(WindowState* window_state) {
  107. return window_state->window()->ClearProperty(ash::kPipSnapFractionKey);
  108. }
  109. bool PipPositioner::HasSnapFraction(WindowState* window_state) {
  110. return window_state->window()->GetProperty(ash::kPipSnapFractionKey) !=
  111. nullptr;
  112. }
  113. void PipPositioner::SaveSnapFraction(WindowState* window_state,
  114. const gfx::Rect& bounds) {
  115. // Ensure that |bounds| is along one of the edges of the movement area.
  116. // If the PIP window is drag-moved onto some system UI, it's possible that
  117. // the PIP window is detached from any of them.
  118. gfx::Rect snapped_bounds =
  119. ash::CollisionDetectionUtils::AdjustToFitMovementAreaByGravity(
  120. window_state->GetDisplay(), bounds);
  121. gfx::Rect movement_area =
  122. CollisionDetectionUtils::GetMovementArea(window_state->GetDisplay());
  123. float width_fraction = (float)(snapped_bounds.x() - movement_area.x()) /
  124. (movement_area.width() - snapped_bounds.width());
  125. float height_fraction = (float)(snapped_bounds.y() - movement_area.y()) /
  126. (movement_area.height() - snapped_bounds.height());
  127. float snap_fraction;
  128. if (snapped_bounds.y() == movement_area.y()) {
  129. snap_fraction = width_fraction;
  130. } else if (snapped_bounds.right() == movement_area.right()) {
  131. snap_fraction = 1. + height_fraction;
  132. } else if (snapped_bounds.bottom() == movement_area.bottom()) {
  133. snap_fraction = 2. + (1. - width_fraction);
  134. } else {
  135. snap_fraction = 3. + (1. - height_fraction);
  136. }
  137. window_state->window()->SetProperty(ash::kPipSnapFractionKey,
  138. new float(snap_fraction));
  139. }
  140. } // namespace ash