container_floating_behavior.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // Copyright 2017 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/keyboard/ui/container_floating_behavior.h"
  5. #include <memory>
  6. #include "ash/keyboard/ui/display_util.h"
  7. #include "ash/keyboard/ui/drag_descriptor.h"
  8. #include "base/numerics/safe_conversions.h"
  9. #include "third_party/abseil-cpp/absl/types/optional.h"
  10. #include "ui/aura/window.h"
  11. #include "ui/compositor/layer.h"
  12. #include "ui/compositor/scoped_layer_animation_settings.h"
  13. #include "ui/display/display.h"
  14. #include "ui/events/event.h"
  15. #include "ui/gfx/geometry/point.h"
  16. #include "ui/gfx/geometry/size.h"
  17. #include "ui/wm/core/window_animations.h"
  18. namespace keyboard {
  19. // The virtual keyboard show/hide animation durations.
  20. constexpr auto kShowAnimationDuration = base::Milliseconds(200);
  21. constexpr auto kHideAnimationDuration = base::Milliseconds(100);
  22. // Distance the keyboard moves during the animation
  23. constexpr int kAnimationDistance = 30;
  24. ContainerFloatingBehavior::ContainerFloatingBehavior(Delegate* delegate)
  25. : ContainerBehavior(delegate) {}
  26. ContainerFloatingBehavior::~ContainerFloatingBehavior() = default;
  27. ContainerType ContainerFloatingBehavior::GetType() const {
  28. return ContainerType::kFloating;
  29. }
  30. void ContainerFloatingBehavior::DoHidingAnimation(
  31. aura::Window* container,
  32. ::wm::ScopedHidingAnimationSettings* animation_settings) {
  33. animation_settings->layer_animation_settings()->SetTransitionDuration(
  34. kHideAnimationDuration);
  35. gfx::Transform transform;
  36. transform.Translate(0, kAnimationDistance);
  37. container->SetTransform(transform);
  38. container->layer()->SetOpacity(0.f);
  39. }
  40. void ContainerFloatingBehavior::DoShowingAnimation(
  41. aura::Window* container,
  42. ui::ScopedLayerAnimationSettings* animation_settings) {
  43. animation_settings->SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
  44. animation_settings->SetTransitionDuration(kShowAnimationDuration);
  45. container->SetTransform(gfx::Transform());
  46. container->layer()->SetOpacity(1.0);
  47. }
  48. void ContainerFloatingBehavior::InitializeShowAnimationStartingState(
  49. aura::Window* container) {
  50. aura::Window* root_window = container->GetRootWindow();
  51. SetCanonicalBounds(container, root_window->bounds());
  52. gfx::Transform transform;
  53. transform.Translate(0, kAnimationDistance);
  54. container->SetTransform(transform);
  55. container->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
  56. }
  57. gfx::Rect ContainerFloatingBehavior::AdjustSetBoundsRequest(
  58. const gfx::Rect& display_bounds,
  59. const gfx::Rect& requested_bounds_in_screen) {
  60. gfx::Rect keyboard_bounds_in_screen = ContainKeyboardToDisplayBounds(
  61. requested_bounds_in_screen, display_bounds);
  62. SavePosition(keyboard_bounds_in_screen, display_bounds.size());
  63. return keyboard_bounds_in_screen;
  64. }
  65. void ContainerFloatingBehavior::SavePosition(
  66. const gfx::Rect& keyboard_bounds_in_screen,
  67. const gfx::Size& screen_size) {
  68. int left_distance = keyboard_bounds_in_screen.x();
  69. int right_distance = screen_size.width() - keyboard_bounds_in_screen.right();
  70. int top_distance = keyboard_bounds_in_screen.y();
  71. int bottom_distance =
  72. screen_size.height() - keyboard_bounds_in_screen.bottom();
  73. double available_width = left_distance + right_distance;
  74. double available_height = top_distance + bottom_distance;
  75. if (!default_position_in_screen_) {
  76. default_position_in_screen_ = std::make_unique<KeyboardPosition>();
  77. }
  78. default_position_in_screen_->left_padding_allotment_ratio =
  79. left_distance / available_width;
  80. default_position_in_screen_->top_padding_allotment_ratio =
  81. top_distance / available_height;
  82. }
  83. gfx::Rect ContainerFloatingBehavior::ConvertAreaInKeyboardToScreenBounds(
  84. const gfx::Rect& area_in_keyboard_window,
  85. const gfx::Rect& keyboard_window_bounds_in_display) const {
  86. gfx::Point origin_in_screen(
  87. keyboard_window_bounds_in_display.x() + area_in_keyboard_window.x(),
  88. keyboard_window_bounds_in_display.y() + area_in_keyboard_window.y());
  89. return gfx::Rect(origin_in_screen, area_in_keyboard_window.size());
  90. }
  91. gfx::Rect ContainerFloatingBehavior::GetBoundsWithinDisplay(
  92. const gfx::Rect& bounds,
  93. const gfx::Rect& display_bounds) const {
  94. gfx::Rect new_bounds = bounds;
  95. if (bounds.x() < display_bounds.x()) {
  96. new_bounds.set_origin(gfx::Point(display_bounds.x(), new_bounds.y()));
  97. }
  98. if (bounds.right() >= display_bounds.right()) {
  99. new_bounds.set_origin(
  100. gfx::Point(display_bounds.right() - bounds.width(), new_bounds.y()));
  101. }
  102. if (bounds.y() < display_bounds.y()) {
  103. new_bounds.set_origin(gfx::Point(new_bounds.x(), display_bounds.y()));
  104. }
  105. if (bounds.bottom() >= display_bounds.bottom()) {
  106. new_bounds.set_origin(gfx::Point(
  107. new_bounds.x(), display_bounds.bottom() - new_bounds.height()));
  108. }
  109. return new_bounds;
  110. }
  111. gfx::Rect ContainerFloatingBehavior::ContainKeyboardToDisplayBounds(
  112. const gfx::Rect& keyboard_window_bounds_in_screen,
  113. const gfx::Rect& display_bounds) const {
  114. if (!area_in_window_to_remain_on_screen_) {
  115. return GetBoundsWithinDisplay(keyboard_window_bounds_in_screen,
  116. display_bounds);
  117. }
  118. // This area is relative to the origin of the keyboard window not the
  119. // screen.
  120. gfx::Rect inner_area_of_keyboard_window =
  121. *area_in_window_to_remain_on_screen_;
  122. gfx::Rect area_to_remain_on_display = ConvertAreaInKeyboardToScreenBounds(
  123. inner_area_of_keyboard_window, keyboard_window_bounds_in_screen);
  124. gfx::Rect area_constrained_to_display =
  125. GetBoundsWithinDisplay(area_to_remain_on_display, display_bounds);
  126. // We need to calculate the new keyboard window bounds in this method,
  127. // and at the moment we have constrained only an area inside the window
  128. // to the display, not the entire keyboard window. So now we must
  129. // derive the containing keyboard window bounds from this constrained
  130. // inner area.
  131. gfx::Point containing_keyboard_window_origin(
  132. area_constrained_to_display.x() - inner_area_of_keyboard_window.x(),
  133. area_constrained_to_display.y() - inner_area_of_keyboard_window.y());
  134. return gfx::Rect(containing_keyboard_window_origin,
  135. keyboard_window_bounds_in_screen.size());
  136. }
  137. bool ContainerFloatingBehavior::IsOverscrollAllowed() const {
  138. return false;
  139. }
  140. gfx::Point ContainerFloatingBehavior::GetPositionForShowingKeyboard(
  141. const gfx::Size& keyboard_size,
  142. const gfx::Rect& display_bounds) const {
  143. // Start with the last saved position
  144. gfx::Point top_left_offset;
  145. KeyboardPosition* position = default_position_in_screen_.get();
  146. if (position == nullptr) {
  147. // If there is none, center the keyboard along the bottom of the screen.
  148. top_left_offset.set_x(display_bounds.width() - keyboard_size.width() -
  149. kDefaultDistanceFromScreenRight);
  150. top_left_offset.set_y(display_bounds.height() - keyboard_size.height() -
  151. kDefaultDistanceFromScreenBottom);
  152. } else {
  153. double left = (display_bounds.width() - keyboard_size.width()) *
  154. position->left_padding_allotment_ratio;
  155. double top = (display_bounds.height() - keyboard_size.height()) *
  156. position->top_padding_allotment_ratio;
  157. top_left_offset.set_x(base::ClampFloor(left));
  158. top_left_offset.set_y(base::ClampFloor(top));
  159. }
  160. // Make sure that this location is valid according to the current size of the
  161. // screen.
  162. gfx::Rect keyboard_bounds =
  163. gfx::Rect(top_left_offset.x() + display_bounds.x(),
  164. top_left_offset.y() + display_bounds.y(), keyboard_size.width(),
  165. keyboard_size.height());
  166. gfx::Rect valid_keyboard_bounds =
  167. ContainKeyboardToDisplayBounds(keyboard_bounds, display_bounds);
  168. return valid_keyboard_bounds.origin();
  169. }
  170. bool ContainerFloatingBehavior::HandlePointerEvent(
  171. const ui::LocatedEvent& event,
  172. const display::Display& current_display) {
  173. const gfx::Vector2d kb_offset(base::ClampFloor(event.x()),
  174. base::ClampFloor(event.y()));
  175. const gfx::Rect& keyboard_bounds_in_screen = delegate_->GetBoundsInScreen();
  176. // Don't handle events if this runs in a partially initialized state.
  177. if (keyboard_bounds_in_screen.height() <= 0)
  178. return false;
  179. ui::PointerId pointer_id = ui::kPointerIdMouse;
  180. if (event.IsTouchEvent()) {
  181. const ui::TouchEvent* te = event.AsTouchEvent();
  182. pointer_id = te->pointer_details().id;
  183. }
  184. const ui::EventType type = event.type();
  185. switch (type) {
  186. case ui::ET_TOUCH_PRESSED:
  187. case ui::ET_MOUSE_PRESSED:
  188. if (!draggable_area_.Contains(kb_offset.x(), kb_offset.y())) {
  189. drag_descriptor_.reset();
  190. } else if (type == ui::ET_MOUSE_PRESSED &&
  191. !static_cast<const ui::MouseEvent&>(event)
  192. .IsOnlyLeftMouseButton()) {
  193. // Mouse events are limited to just the left mouse button.
  194. drag_descriptor_.reset();
  195. } else if (!drag_descriptor_) {
  196. drag_descriptor_ = std::make_unique<DragDescriptor>(DragDescriptor{
  197. keyboard_bounds_in_screen.origin(), kb_offset, pointer_id});
  198. }
  199. break;
  200. case ui::ET_MOUSE_DRAGGED:
  201. case ui::ET_TOUCH_MOVED:
  202. if (drag_descriptor_ && drag_descriptor_->pointer_id == pointer_id) {
  203. // Drag continues.
  204. // If there is an active drag, use it to determine the new location
  205. // of the keyboard.
  206. const gfx::Point original_click_location =
  207. drag_descriptor_->original_keyboard_location +
  208. drag_descriptor_->original_click_offset;
  209. const gfx::Point current_drag_location =
  210. keyboard_bounds_in_screen.origin() + kb_offset;
  211. const gfx::Vector2d cumulative_drag_offset =
  212. current_drag_location - original_click_location;
  213. const gfx::Point new_keyboard_location =
  214. drag_descriptor_->original_keyboard_location +
  215. cumulative_drag_offset;
  216. gfx::Rect new_bounds_in_local =
  217. gfx::Rect(new_keyboard_location, keyboard_bounds_in_screen.size());
  218. DisplayUtil display_util;
  219. const display::Display& new_display =
  220. display_util.FindAdjacentDisplayIfPointIsNearMargin(
  221. current_display, current_drag_location);
  222. if (current_display.id() == new_display.id()) {
  223. delegate_->MoveKeyboardWindow(new_bounds_in_local);
  224. } else {
  225. // Since the keyboard has jumped across screens, cancel the current
  226. // drag descriptor as though the user has lifted their finger.
  227. drag_descriptor_.reset();
  228. gfx::Rect new_bounds_in_screen =
  229. new_bounds_in_local +
  230. current_display.bounds().origin().OffsetFromOrigin();
  231. gfx::Rect contained_new_bounds_in_screen =
  232. ContainKeyboardToDisplayBounds(new_bounds_in_screen,
  233. new_display.bounds());
  234. // Enqueue a transition to the adjacent display.
  235. new_bounds_in_local =
  236. contained_new_bounds_in_screen -
  237. new_display.bounds().origin().OffsetFromOrigin();
  238. delegate_->MoveKeyboardWindowToDisplay(new_display,
  239. new_bounds_in_local);
  240. }
  241. SavePosition(delegate_->GetBoundsInScreen(), new_display.size());
  242. return true;
  243. }
  244. break;
  245. default:
  246. drag_descriptor_.reset();
  247. break;
  248. }
  249. return false;
  250. }
  251. bool ContainerFloatingBehavior::HandleGestureEvent(
  252. const ui::GestureEvent& event,
  253. const gfx::Rect& bounds_in_screen) {
  254. return false;
  255. }
  256. void ContainerFloatingBehavior::SetCanonicalBounds(
  257. aura::Window* container,
  258. const gfx::Rect& display_bounds) {
  259. gfx::Point keyboard_location =
  260. GetPositionForShowingKeyboard(container->bounds().size(), display_bounds);
  261. gfx::Rect keyboard_bounds_in_screen =
  262. gfx::Rect(keyboard_location, container->bounds().size());
  263. SavePosition(keyboard_bounds_in_screen, display_bounds.size());
  264. container->SetBounds(keyboard_bounds_in_screen);
  265. }
  266. bool ContainerFloatingBehavior::TextBlurHidesKeyboard() const {
  267. return true;
  268. }
  269. gfx::Rect ContainerFloatingBehavior::GetOccludedBounds(
  270. const gfx::Rect& visual_bounds_in_screen) const {
  271. return {};
  272. }
  273. bool ContainerFloatingBehavior::OccludedBoundsAffectWorkspaceLayout() const {
  274. return false;
  275. }
  276. void ContainerFloatingBehavior::SetDraggableArea(const gfx::Rect& rect) {
  277. draggable_area_ = rect;
  278. }
  279. void ContainerFloatingBehavior::SetAreaToRemainOnScreen(const gfx::Rect& rect) {
  280. area_in_window_to_remain_on_screen_ = rect;
  281. }
  282. } // namespace keyboard