split_view_controller.h 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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. #ifndef ASH_WM_SPLITVIEW_SPLIT_VIEW_CONTROLLER_H_
  5. #define ASH_WM_SPLITVIEW_SPLIT_VIEW_CONTROLLER_H_
  6. #include <limits>
  7. #include <memory>
  8. #include <vector>
  9. #include "ash/accessibility/accessibility_observer.h"
  10. #include "ash/ash_export.h"
  11. #include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
  12. #include "ash/public/cpp/tablet_mode_observer.h"
  13. #include "ash/shell_observer.h"
  14. #include "ash/wm/overview/overview_observer.h"
  15. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  16. #include "ash/wm/window_state_observer.h"
  17. #include "ash/wm/wm_event.h"
  18. #include "base/containers/flat_map.h"
  19. #include "base/observer_list.h"
  20. #include "base/time/time.h"
  21. #include "ui/aura/window_observer.h"
  22. #include "ui/display/display.h"
  23. #include "ui/display/display_observer.h"
  24. #include "ui/gfx/geometry/point.h"
  25. #include "ui/wm/public/activation_change_observer.h"
  26. namespace ui {
  27. class Layer;
  28. class PresentationTimeRecorder;
  29. } // namespace ui
  30. namespace ash {
  31. class OverviewSession;
  32. class SplitViewControllerTest;
  33. class SplitViewDivider;
  34. class SplitViewMetricsController;
  35. class SplitViewObserver;
  36. class SplitViewOverviewSessionTest;
  37. // `SplitViewController` handles how window snapping interacts with overview
  38. // mode and tablet mode. There is an instance for each display. In clamshell
  39. // mode, `SplitViewController` is relevant on displays with a snapped window
  40. // on one side and an overview grid (empty or not) on the other side. In
  41. // tablet mode, `SplitViewController` is relevant on all displays with snapped
  42. // windows.
  43. // TODO(xdai): Make it work for multi-display non mirror environment.
  44. class ASH_EXPORT SplitViewController : public aura::WindowObserver,
  45. public WindowStateObserver,
  46. public ShellObserver,
  47. public OverviewObserver,
  48. public display::DisplayObserver,
  49. public TabletModeObserver,
  50. public AccessibilityObserver,
  51. public ash::KeyboardControllerObserver,
  52. public wm::ActivationChangeObserver {
  53. public:
  54. // |LEFT| and |RIGHT| are named for the positions to which they correspond in
  55. // clamshell mode or primary-landscape-oriented tablet mode. In portrait-
  56. // oriented tablet mode, we actually snap windows on the top and bottom, but
  57. // in clamshell mode, although the display orientation may sometimes be
  58. // portrait, we always snap windows on the left and right (see
  59. // |IsLayoutHorizontal|). The snap positions are swapped in secondary-oriented
  60. // tablet mode (see |IsLayoutPrimary|).
  61. // TODO(crbug.com/1233194): Rename left/right to primary/secondary.
  62. enum SnapPosition { NONE, LEFT, RIGHT };
  63. // Why splitview was ended.
  64. enum class EndReason {
  65. kNormal = 0,
  66. kHomeLauncherPressed,
  67. kUnsnappableWindowActivated,
  68. kActiveUserChanged,
  69. kWindowDragStarted,
  70. kExitTabletMode,
  71. // Splitview is being ended due to a change in Virtual Desks, such as
  72. // switching desks or removing a desk.
  73. kDesksChange,
  74. // Splitview is being ended due to the `root_window_` is destroyed and the
  75. // SplitViewController is being destroyed.
  76. kRootWindowDestroyed,
  77. };
  78. // The behaviors of split view are very different when in tablet mode and in
  79. // clamshell mode. In tablet mode, split view mode will stay active until the
  80. // user explicitly ends it (e.g., by pressing home launcher, or long pressing
  81. // the overview button, or sliding the divider bar to the edge, etc). However,
  82. // in clamshell mode, there is no divider bar, and split view mode only stays
  83. // active during overview snapping, i.e., it's only possible that split view
  84. // is active when overview is active. Once the user has selected two windows
  85. // to snap to both side of the screen, split view mode is no longer active.
  86. enum class SplitViewType {
  87. kTabletType = 0,
  88. kClamshellType,
  89. };
  90. // TODO(crbug.com/1233194): Rename left/right to primary/secondary.
  91. enum class State {
  92. kNoSnap,
  93. kLeftSnapped,
  94. kRightSnapped,
  95. kBothSnapped,
  96. };
  97. // The split view resize behavior in tablet mode. The normal mode resizes
  98. // windows on drag events. In the fast mode, windows are instead moved. A
  99. // single drag "session" may involve both modes.
  100. enum class TabletResizeMode {
  101. kNormal,
  102. kFast,
  103. };
  104. // Gets the |SplitViewController| for the root window of |window|. |window| is
  105. // important in clamshell mode. In tablet mode, the working assumption for now
  106. // is mirror mode (or just one display), and so |window| can be almost any
  107. // window and it does not matter. For code that only applies to tablet mode,
  108. // you may simply use the primary root (see |Shell::GetPrimaryRootWindow|).
  109. // The user actually can go to the display settings while in tablet mode and
  110. // choose extend; we just are not yet trying to support it really well.
  111. static SplitViewController* Get(const aura::Window* window);
  112. // The return values of these two functions together indicate what actual
  113. // positions correspond to |PRIMARY| and |SECONDARY|:
  114. // |IsLayoutHorizontal| |IsLayoutPrimary| |PRIMARY| |SECONDARY|
  115. // --------------------------------------------------------------------------
  116. // true true left right
  117. // true false right left
  118. // false true top bottom
  119. // false false bottom top
  120. // In both clamshell and tablet mode, these functions return values based on
  121. // display orientation. |window| is used to find the nearest display to check
  122. // if the display layout is horizontal and is primary or not.
  123. static bool IsLayoutHorizontal(aura::Window* window);
  124. static bool IsLayoutHorizontal(const display::Display& display);
  125. static bool IsLayoutPrimary(aura::Window* window);
  126. static bool IsLayoutPrimary(const display::Display& display);
  127. // Returns true if |position| actually signifies a left or top position,
  128. // according to the return values of |IsLayoutHorizontal| and
  129. // |IsLayoutPrimary|. Physical position refers to the position of the window
  130. // on the display that is held upward.
  131. static bool IsPhysicalLeftOrTop(SnapPosition position, aura::Window* window);
  132. static bool IsPhysicalLeftOrTop(SnapPosition position,
  133. const display::Display& display);
  134. explicit SplitViewController(aura::Window* root_window);
  135. SplitViewController(const SplitViewController&) = delete;
  136. SplitViewController& operator=(const SplitViewController&) = delete;
  137. ~SplitViewController() override;
  138. // Returns true if split view mode is active. Please see SplitViewType above
  139. // to see the difference between tablet mode and clamshell mode splitview
  140. // mode.
  141. bool InSplitViewMode() const;
  142. bool BothSnapped() const;
  143. bool InClamshellSplitViewMode() const;
  144. bool InTabletSplitViewMode() const;
  145. // Checks the following criteria:
  146. // 1. Split view mode is supported (see |ShouldAllowSplitView|).
  147. // 2. |window| can be activated (see |wm::CanActivateWindow|).
  148. // 3. The |WindowState| of |window| can snap (see |WindowState::CanSnap|).
  149. // 4. |window|'s minimum size, if any, fits into the left or top with the
  150. // default divider position. (If the work area length is odd, then the
  151. // right or bottom will be one pixel larger.)
  152. // See also the |DCHECK|s in |SnapWindow|.
  153. bool CanSnapWindow(aura::Window* window) const;
  154. // Snap |window| in the split view at |snap_position|. It will send snap
  155. // WMEvent to |window| and rely on WindowState to do the actual work to
  156. // change window state and bounds. Note this function does not guarantee
  157. // |window| can be snapped in the split view (e.g., an ARC++ window may
  158. // decide to ignore the state change request), and split view state will only
  159. // be updated after the window state is changed to the desired snap window
  160. // state. If |activate_window| is true, |window| will be activated after being
  161. // snapped in splitview. Please note if |activate_window| is false, it's still
  162. // possible that |window| will be activated after being snapped, see
  163. // |to_be_activated_window_| for details.
  164. void SnapWindow(aura::Window* window,
  165. SnapPosition snap_position,
  166. bool activate_window = false);
  167. // This is called by WindowState::State when receiving a snap WMEvent (i.e.,
  168. // WM_EVENT_SNAP_PRIMARY or WM_EVENT_SNAP_SECONDARY). SplitViewController will
  169. // decide if this window needs to be snapped in split view.
  170. void OnWindowSnapWMEvent(aura::Window* window, WMEventType event_type);
  171. // Attaches the to-be-snapped |window| to split view at |snap_position|. It
  172. // will try to remove |window| from the overview window grid first if |window|
  173. // is currently showing in the overview window grid. We'll add a finishing
  174. // touch to the snap animation of |window| if split view mode is not already
  175. // active, and if |window| is not minimized and has an non-identity transform.
  176. void AttachSnappingWindow(aura::Window* window, SnapPosition snap_position);
  177. // Swaps the left and right windows. This will do nothing if one of the
  178. // windows is not snapped.
  179. void SwapWindows();
  180. // |window| should be |left_window_| or |right_window_|, and this function
  181. // returns |LEFT| or |RIGHT| accordingly.
  182. SnapPosition GetPositionOfSnappedWindow(const aura::Window* window) const;
  183. // |position| should be |LEFT| or |RIGHT|, and this function returns
  184. // |left_window_| or |right_window_| accordingly.
  185. aura::Window* GetSnappedWindow(SnapPosition position);
  186. // Returns the default snapped window. It's the window that remains open until
  187. // the split mode ends. It's decided by |default_snap_position_|. E.g., If
  188. // |default_snap_position_| equals LEFT, then the default snapped window is
  189. // |left_window_|. All the other window will open on the right side.
  190. aura::Window* GetDefaultSnappedWindow();
  191. // Gets snapped bounds based on |snap_position|, the side of the screen to
  192. // snap to, and |snap_ratio|, the ratio of the screen that the snapped window
  193. // will occupy, adjusted to accommodate the minimum size of
  194. // |window_for_minimum_size| if |window_for_minimum_size| is not null.
  195. gfx::Rect GetSnappedWindowBoundsInParent(
  196. SnapPosition snap_position,
  197. aura::Window* window_for_minimum_size,
  198. float snap_ratio);
  199. // Gets snapped bounds based on |snap_position|, |divider_position_|, and
  200. // |kDefaultSnapRatio|, adjusted to accommodate the minimum size of
  201. // |window_for_minimum_size| if |window_for_minimum_size| is not null.
  202. gfx::Rect GetSnappedWindowBoundsInParent(
  203. SnapPosition snap_position,
  204. aura::Window* window_for_minimum_size);
  205. // Gets snapped bounds in screen coordinates based on |snap_position| and
  206. // |snap_ratio|.
  207. gfx::Rect GetSnappedWindowBoundsInScreen(
  208. SnapPosition snap_position,
  209. aura::Window* window_for_minimum_size,
  210. float snap_ratio);
  211. // Gets snapped bounds in screen coordinates for |kDefaultSnapRatio|.
  212. gfx::Rect GetSnappedWindowBoundsInScreen(
  213. SnapPosition snap_position,
  214. aura::Window* window_for_minimum_size);
  215. // Returns true if we are resizing with the fast resize
  216. // mode. `GetSnappedWindowBoundsInScreen()` should then return the windows
  217. // current bounds in screen coordinates.
  218. bool ShouldUseWindowBoundsDuringFastResize();
  219. // Gets the default value of |divider_position_|.
  220. int GetDefaultDividerPosition() const;
  221. // Calculates the new divider position to move |divider_position_| to, such
  222. // that the primary window will occupy |snap_ratio| of the screen, and the
  223. // secondary window will occupy the rest.
  224. int GetDividerPosition(SnapPosition snap_position, float snap_ratio) const;
  225. // Returns true during the divider snap animation.
  226. bool IsDividerAnimating() const;
  227. void StartResize(const gfx::Point& location_in_screen);
  228. void Resize(const gfx::Point& location_in_screen);
  229. void EndResize(const gfx::Point& location_in_screen);
  230. // Ends the split view mode.
  231. void EndSplitView(EndReason end_reason = EndReason::kNormal);
  232. // Returns true if |window| is a snapped window in splitview.
  233. bool IsWindowInSplitView(const aura::Window* window) const;
  234. // This function is only supposed to be called during clamshell <-> tablet
  235. // transition or multi-user transition, when we need to carry over one/two
  236. // snapped windows into splitview, we calculate the divider position based on
  237. // the one or two to-be-snapped windows' bounds so that we can keep the
  238. // snapped windows' bounds after transition (instead of putting them always
  239. // on the middle split position).
  240. void InitDividerPositionForTransition(int divider_position);
  241. // Returns true if |window| is in a transitinal state which means that
  242. // |SplitViewController| has already changed its internal snapped state for
  243. // |window| but the snapped state has not been applied to |window|'s window
  244. // state yet. The transional state can be happen in some clients (e.g. ARC
  245. // app) which handle window states asynchronously.
  246. bool IsWindowInTransitionalState(const aura::Window* window) const;
  247. // Called when the overview button tray has been long pressed. Enters
  248. // splitview mode if the active window is snappable. Also enters overview mode
  249. // if device is not currently in overview mode.
  250. void OnOverviewButtonTrayLongPressed(const gfx::Point& event_location);
  251. // Called when a window (either it's browser window or an app window) start/
  252. // end being dragged.
  253. void OnWindowDragStarted(aura::Window* dragged_window);
  254. void OnWindowDragEnded(aura::Window* dragged_window,
  255. SnapPosition desired_snap_position,
  256. const gfx::Point& last_location_in_screen);
  257. void OnWindowDragCanceled();
  258. // Computes the snap position for a dragged window, based on the last
  259. // mouse/gesture event location. Called by |EndWindowDragImpl| when
  260. // desired_snap_position is |NONE| but because split view is already active,
  261. // the dragged window needs to be snapped anyway.
  262. SplitViewController::SnapPosition ComputeSnapPosition(
  263. const gfx::Point& last_location_in_screen);
  264. // In portrait mode split view, if the virtual keyboard occludes the input
  265. // field in the bottom window. The bottom window will be pushed up above the
  266. // virtual keyboard. In this case, we allow window state to set bounds for
  267. // snapped window.
  268. bool BoundsChangeIsFromVKAndAllowed(aura::Window* window) const;
  269. void AddObserver(SplitViewObserver* observer);
  270. void RemoveObserver(SplitViewObserver* observer);
  271. // aura::WindowObserver:
  272. void OnWindowPropertyChanged(aura::Window* window,
  273. const void* key,
  274. intptr_t old) override;
  275. void OnWindowBoundsChanged(aura::Window* window,
  276. const gfx::Rect& old_bounds,
  277. const gfx::Rect& new_bounds,
  278. ui::PropertyChangeReason reason) override;
  279. void OnWindowDestroyed(aura::Window* window) override;
  280. void OnResizeLoopStarted(aura::Window* window) override;
  281. void OnResizeLoopEnded(aura::Window* window) override;
  282. // WindowStateObserver:
  283. void OnPostWindowStateTypeChange(WindowState* window_state,
  284. chromeos::WindowStateType old_type) override;
  285. // ShellObserver:
  286. void OnPinnedStateChanged(aura::Window* pinned_window) override;
  287. // OverviewObserver:
  288. void OnOverviewModeStarting() override;
  289. void OnOverviewModeEnding(OverviewSession* overview_session) override;
  290. void OnOverviewModeEnded() override;
  291. // display::DisplayObserver:
  292. void OnDisplayRemoved(const display::Display& old_display) override;
  293. void OnDisplayMetricsChanged(const display::Display& display,
  294. uint32_t metrics) override;
  295. // TabletModeObserver:
  296. void OnTabletModeStarting() override;
  297. void OnTabletModeStarted() override;
  298. void OnTabletModeEnding() override;
  299. void OnTabletModeEnded() override;
  300. // AccessibilityObserver:
  301. void OnAccessibilityStatusChanged() override;
  302. void OnAccessibilityControllerShutdown() override;
  303. // KeyboardControllerObserver
  304. void OnKeyboardOccludedBoundsChanged(const gfx::Rect& screen_bounds) override;
  305. // wm::ActivationChangeObserver:
  306. void OnWindowActivated(ActivationReason reason,
  307. aura::Window* gained_active,
  308. aura::Window* lost_active) override;
  309. aura::Window* root_window() const { return root_window_; }
  310. aura::Window* left_window() { return left_window_; }
  311. aura::Window* right_window() { return right_window_; }
  312. int divider_position() const { return divider_position_; }
  313. State state() const { return state_; }
  314. SnapPosition default_snap_position() const { return default_snap_position_; }
  315. SplitViewDivider* split_view_divider() { return split_view_divider_.get(); }
  316. bool is_resizing() const { return is_resizing_; }
  317. EndReason end_reason() const { return end_reason_; }
  318. SplitViewMetricsController* split_view_metrics_controller() {
  319. return split_view_metrics_controller_.get();
  320. }
  321. aura::Window* to_be_activated_window() const {
  322. return to_be_activated_window_;
  323. }
  324. private:
  325. friend class SplitViewControllerTest;
  326. friend class SplitViewOverviewSessionTest;
  327. class TabDraggedWindowObserver;
  328. class DividerSnapAnimation;
  329. class AutoSnapController;
  330. class ToBeSnappedWindowsObserver;
  331. // Reason that a snapped window is detached from the splitview.
  332. enum class WindowDetachedReason {
  333. kWindowMinimized,
  334. kWindowDestroyed,
  335. kWindowDragged,
  336. };
  337. // These functions return |left_window_| and |right_window_|, swapped in
  338. // nonprimary screen orientations. Note that they may return null.
  339. aura::Window* GetPhysicalLeftOrTopWindow();
  340. aura::Window* GetPhysicalRightOrBottomWindow();
  341. // Start observing |window|.
  342. void StartObserving(aura::Window* window);
  343. // Stop observing the window at associated with |snap_position|. Also updates
  344. // shadows and sets |left_window_| or |right_window_| to nullptr.
  345. void StopObserving(SnapPosition snap_position);
  346. // Update split view state and notify its observer about the change.
  347. void UpdateStateAndNotifyObservers();
  348. // Notifies observers that the split view divider position has been changed.
  349. void NotifyDividerPositionChanged();
  350. // Notifies observers that the windows in split view is resized.
  351. void NotifyWindowResized();
  352. // Notifies observers that the windows are swappped.
  353. void NotifyWindowSwapped();
  354. // Updates the black scrim layer's bounds and opacity while dragging the
  355. // divider. The opacity increases as the split divider gets closer to the edge
  356. // of the screen.
  357. void UpdateBlackScrim(const gfx::Point& location_in_screen);
  358. // Updates the resize mode backdrop. This is drawn behind windows to ensure
  359. // that the allotted space is always filled, even if the window itself hasn't
  360. // resized yet.
  361. void UpdateResizeBackdrop();
  362. // Updates the bounds for the snapped windows and divider according to the
  363. // current snap direction.
  364. void UpdateSnappedWindowsAndDividerBounds();
  365. // Gets the position where the black scrim should show.
  366. SnapPosition GetBlackScrimPosition(const gfx::Point& location_in_screen);
  367. // Updates |divider_position_| according to the current event location during
  368. // resizing.
  369. void UpdateDividerPosition(const gfx::Point& location_in_screen);
  370. // Returns the closest fix location for |divider_position_|.
  371. int GetClosestFixedDividerPosition();
  372. // While the divider is animating to somewhere, stop it and shove it there.
  373. void StopAndShoveAnimatedDivider();
  374. // Returns true if we should end tablet split view after resizing, i.e. the
  375. // split view divider is at an edge of the work area.
  376. bool ShouldEndTabletSplitViewAfterResizing();
  377. // Ends split view if |ShouldEndTabletSplitViewAfterResizing| returns true.
  378. // Handles extra details associated with dragging the divider off the screen.
  379. void EndTabletSplitViewAfterResizingIfAppropriate();
  380. // After resizing, if we should end split view mode, returns the window that
  381. // needs to be activated. Returns nullptr if there is no such window.
  382. aura::Window* GetActiveWindowAfterResizingUponExit();
  383. // Returns the maximum value of the |divider_position_|. It is the width of
  384. // the current display's work area bounds in landscape orientation, or height
  385. // of the current display's work area bounds in portrait orientation.
  386. int GetDividerEndPosition() const;
  387. // Called after a to-be-snapped window |window| got snapped. It updates the
  388. // split view states and notifies observers about the change. It also restore
  389. // the snapped window's transform if it's not identity and activate it.
  390. void OnWindowSnapped(aura::Window* window);
  391. // If there are two snapped windows, closing/minimizing/tab-dragging one of
  392. // them will open overview window grid on the closed/minimized/tab-dragged
  393. // window side of the screen. If there is only one snapped windows, closing/
  394. // minimizing/tab-dragging the snapped window will end split view mode and
  395. // adjust the overview window grid bounds if the overview mode is active at
  396. // that moment. |reason| specifies the reason that the snapped window is
  397. // detached from splitview.
  398. void OnSnappedWindowDetached(aura::Window* window,
  399. WindowDetachedReason reason);
  400. // Returns the closest position ratio based on |distance| and |length|.
  401. float FindClosestPositionRatio(float distance, float length);
  402. // Gets the divider optional position ratios. The divider can always be
  403. // moved to the positions in |kFixedPositionRatios|. Whether the divider can
  404. // be moved to |kOneThirdPositionRatio| or |kTwoThirdPositionRatio| depends
  405. // on the minimum size of current snapped windows.
  406. void GetDividerOptionalPositionRatios(
  407. std::vector<float>* out_position_ratios);
  408. // Gets the expected window component depending on current screen orientation
  409. // for resizing purpose.
  410. int GetWindowComponentForResize(aura::Window* window);
  411. // Gets the expected end drag position for |window| depending on current
  412. // screen orientation and split divider position.
  413. gfx::Point GetEndDragLocationInScreen(aura::Window* window,
  414. const gfx::Point& location_in_screen);
  415. // Restores |window| transform to identity transform if applicable.
  416. void RestoreTransformIfApplicable(aura::Window* window);
  417. // Called after |newly_snapped| gets snapped. Updates window stacking.
  418. void UpdateWindowStackingAfterSnap(aura::Window* newly_snapped);
  419. // During resizing, it's possible that the resizing bounds of the snapped
  420. // window is smaller than its minimum bounds, in this case we apply a
  421. // translation to the snapped window to make it visually be placed outside of
  422. // the workspace area.
  423. void SetWindowsTransformDuringResizing();
  424. // Restore the snapped windows transform to identity transform after resizing.
  425. void RestoreWindowsTransformAfterResizing();
  426. // Animates to |target_transform| for |window| and its transient descendants.
  427. // |window| will be applied |start_transform| first and then animate to
  428. // |target_transform|. Note |start_transform| and |end_transform| are for
  429. // |window| and need to be adjusted for its transient child windows.
  430. void SetTransformWithAnimation(aura::Window* window,
  431. const gfx::Transform& start_transform,
  432. const gfx::Transform& target_transform);
  433. // Updates the |snapping_window_transformed_bounds_map_| on |window|. It
  434. // should be called before trying to snap the window.
  435. void UpdateSnappingWindowTransformedBounds(aura::Window* window);
  436. // Inserts |window| into overview window grid if overview mode is active. Do
  437. // nothing if overview mode is inactive at the moment.
  438. void InsertWindowToOverview(aura::Window* window, bool animate = true);
  439. // Finalizes and cleans up after stopping dragging the divider bar to resize
  440. // snapped windows.
  441. void FinishWindowResizing(aura::Window* window);
  442. // Finalizes and cleans up divider dragging/animating. Called when the divider
  443. // snapping animation completes or is interrupted or totally skipped.
  444. void EndResizeImpl();
  445. // Called from a timer during resizing. Facilitates switching between fast and
  446. // normal tablet resizing modes.
  447. void OnResizeTimer();
  448. // Figure out which resize mode we should be using. This is based on the speed
  449. // at which the divider is dragged.
  450. void UpdateTabletResizeMode(base::TimeTicks event_time_ticks,
  451. const gfx::Point& event_location);
  452. // Called by OnWindowDragEnded to do the actual work of finishing the window
  453. // dragging. If |is_being_destroyed| equals true, the dragged window is to be
  454. // destroyed, and SplitViewController should not try to put it in splitview.
  455. void EndWindowDragImpl(aura::Window* window,
  456. bool is_being_destroyed,
  457. SnapPosition desired_snap_position,
  458. const gfx::Point& last_location_in_screen);
  459. // Do the split divider spawn animation. It will add a finishing touch to the
  460. // |window| animation that generally accommodates snapping by dragging.
  461. void DoSplitDividerSpawnAnimation(aura::Window* window);
  462. // Root window the split view is in.
  463. aura::Window* root_window_;
  464. // The current left/right snapped window.
  465. aura::Window* left_window_ = nullptr;
  466. aura::Window* right_window_ = nullptr;
  467. // Observe the windows that are to be snapped in split screen.
  468. std::unique_ptr<ToBeSnappedWindowsObserver> to_be_snapped_windows_observer_;
  469. // Split view divider widget. Only exist in tablet splitview mode. It's a
  470. // black bar stretching from one edge of the screen to the other, containing a
  471. // small white drag bar in the middle. As the user presses on it and drag it
  472. // to left or right, the left and right window will be resized accordingly.
  473. std::unique_ptr<SplitViewDivider> split_view_divider_;
  474. // A black scrim layer that fades in over a window when its width drops under
  475. // 1/3 of the width of the screen, increasing in opacity as the divider gets
  476. // closer to the edge of the screen.
  477. std::unique_ptr<ui::Layer> black_scrim_layer_;
  478. // Backdrop layers that may be visible below windows when resizing.
  479. std::unique_ptr<ui::Layer> left_resize_backdrop_layer_;
  480. std::unique_ptr<ui::Layer> right_resize_backdrop_layer_;
  481. // The window observer that obseves the tab-dragged window in tablet mode.
  482. std::unique_ptr<TabDraggedWindowObserver> dragged_window_observer_;
  483. // The distance between the origin of the divider and the origin of the
  484. // current display's work area in screen coordinates.
  485. // |<--- divider_position_ --->|
  486. // ----------------------------------------------------------
  487. // | | | |
  488. // | left_window_ | | right_window_ |
  489. // | | | |
  490. // ----------------------------------------------------------
  491. int divider_position_ = -1;
  492. // The closest position ratio of divider among kFixedPositionRatios,
  493. // kOneThirdPositionRatio and kTwoThirdPositionRatio based on current
  494. // |divider_position_|. Used to update |divider_position_| on work area
  495. // changes.
  496. float divider_closest_ratio_ = std::numeric_limits<float>::quiet_NaN();
  497. // The location of the previous mouse/gesture event in screen coordinates.
  498. gfx::Point previous_event_location_;
  499. // The animation that animates the divider to a fixed position after resizing.
  500. std::unique_ptr<DividerSnapAnimation> divider_snap_animation_;
  501. // Current snap state.
  502. State state_ = State::kNoSnap;
  503. // The default snap position. It's decided by the first snapped window. If the
  504. // first window was snapped left, then |default_snap_position_| equals LEFT,
  505. // i.e., all the other windows will open snapped on the right side - and vice
  506. // versa.
  507. SnapPosition default_snap_position_ = NONE;
  508. // Whether the previous layout is right-side-up (see |IsLayoutPrimary|).
  509. // Consistent with |IsLayoutPrimary|, |is_previous_layout_right_side_up_|
  510. // is always true in clamshell mode. It is not really used in clamshell mode,
  511. // but it is kept up to date in anticipation that future code changes could
  512. // introduce a bug similar to https://crbug.com/1029181 which could be
  513. // overlooked for years while occasionally irritating or confusing real users.
  514. bool is_previous_layout_right_side_up_ = true;
  515. // True when the divider is being dragged (not during its snap animation).
  516. bool is_resizing_ = false;
  517. // Stores the reason which cause splitview to end.
  518. EndReason end_reason_ = EndReason::kNormal;
  519. // The split view type. See SplitViewType for the differences between tablet
  520. // split view and clamshell split view.
  521. SplitViewType split_view_type_ = SplitViewType::kTabletType;
  522. // The time when splitview starts. Used for metric collection purpose.
  523. base::Time splitview_start_time_;
  524. // The map from a to-be-snapped window to its transformed bounds.
  525. base::flat_map<aura::Window*, gfx::Rect>
  526. snapping_window_transformed_bounds_map_;
  527. base::ObserverList<SplitViewObserver>::Unchecked observers_;
  528. // Records the presentation time of resize operation in split view mode.
  529. std::unique_ptr<ui::PresentationTimeRecorder> presentation_time_recorder_;
  530. // Observes windows and performs auto snapping if needed.
  531. std::unique_ptr<AutoSnapController> auto_snap_controller_;
  532. // The metrics controller for the same root window.
  533. std::unique_ptr<SplitViewMetricsController> split_view_metrics_controller_;
  534. // Register for DisplayObserver callbacks.
  535. display::ScopedDisplayObserver display_observer_{this};
  536. // A pointer to the to-be-snapped window that will be activated after it's
  537. // snapped in splitview. There can be two cases when this value can be
  538. // non-nullptr, when SnapWindow() explicitly specifies the window needs to be
  539. // activated, or when the to-be-snapped is from overview and was the active
  540. // window before entering overview, so when it's snapped in splitview, it
  541. // should remain to be the active window.
  542. aura::Window* to_be_activated_window_ = nullptr;
  543. // The split view resize mode for tablet mode.
  544. TabletResizeMode tablet_resize_mode_ = TabletResizeMode::kNormal;
  545. // True *while* a resize event is being processed.
  546. bool processing_resize_event_ = false;
  547. // Accumulated drag distance, during a time interval.
  548. int accumulated_drag_distance_ = 0;
  549. base::TimeTicks accumulated_drag_time_ticks_;
  550. // Used to potentially invoke `Resize()` during resizes. This is so that
  551. // tablet resize mode can switch to normal mode (letting windows be resized)
  552. // even if the divider isn't moved.
  553. base::OneShotTimer resize_timer_;
  554. // A flag indicates the window bounds is currently changed due to the virtual
  555. // keyboard.
  556. bool changing_bounds_by_vk_ = false;
  557. };
  558. } // namespace ash
  559. #endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_CONTROLLER_H_