split_view_utils.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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/wm/splitview/split_view_utils.h"
  5. #include "ash/accessibility/accessibility_controller_impl.h"
  6. #include "ash/constants/ash_features.h"
  7. #include "ash/constants/notifier_catalogs.h"
  8. #include "ash/public/cpp/system/toast_data.h"
  9. #include "ash/screen_util.h"
  10. #include "ash/shell.h"
  11. #include "ash/strings/grit/ash_strings.h"
  12. #include "ash/system/toast/toast_manager_impl.h"
  13. #include "ash/wm/mru_window_tracker.h"
  14. #include "ash/wm/overview/overview_controller.h"
  15. #include "ash/wm/screen_pinning_controller.h"
  16. #include "ash/wm/splitview/split_view_constants.h"
  17. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  18. #include "ash/wm/window_state.h"
  19. #include "base/command_line.h"
  20. #include "base/time/time.h"
  21. #include "ui/base/l10n/l10n_util.h"
  22. #include "ui/compositor/layer.h"
  23. #include "ui/compositor/layer_animation_observer.h"
  24. #include "ui/compositor/layer_animator.h"
  25. #include "ui/compositor/scoped_layer_animation_settings.h"
  26. #include "ui/views/bubble/bubble_dialog_delegate_view.h"
  27. #include "ui/views/widget/widget.h"
  28. #include "ui/views/widget/widget_delegate.h"
  29. #include "ui/wm/core/transient_window_manager.h"
  30. namespace ash {
  31. namespace {
  32. using ::chromeos::WindowStateType;
  33. // The animation speed at which the highlights fade in or out.
  34. constexpr base::TimeDelta kHighlightsFadeInOut = base::Milliseconds(250);
  35. // The animation speed which the other highlight fades in or out.
  36. constexpr base::TimeDelta kOtherFadeInOut = base::Milliseconds(133);
  37. // The delay before the other highlight starts fading in.
  38. constexpr base::TimeDelta kOtherFadeInDelay = base::Milliseconds(117);
  39. // The animation speed at which the preview area fades out (when you snap a
  40. // window).
  41. constexpr base::TimeDelta kPreviewAreaFadeOut = base::Milliseconds(67);
  42. // The time duration for the indicator label opacity animations.
  43. constexpr base::TimeDelta kLabelAnimation = base::Milliseconds(83);
  44. // The delay before the indicator labels start fading in.
  45. constexpr base::TimeDelta kLabelAnimationDelay = base::Milliseconds(167);
  46. // Toast data.
  47. constexpr char kAppCannotSnapToastId[] = "split_view_app_cannot_snap";
  48. // Gets the duration, tween type and delay before animation based on |type|.
  49. void GetAnimationValuesForType(
  50. SplitviewAnimationType type,
  51. base::TimeDelta* out_duration,
  52. gfx::Tween::Type* out_tween_type,
  53. ui::LayerAnimator::PreemptionStrategy* out_preemption_strategy,
  54. base::TimeDelta* out_delay) {
  55. *out_preemption_strategy = ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET;
  56. switch (type) {
  57. case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN:
  58. case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN_CANNOT_SNAP:
  59. case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT:
  60. case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN:
  61. case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN:
  62. case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT:
  63. case SPLITVIEW_ANIMATION_TEXT_FADE_IN_WITH_HIGHLIGHT:
  64. case SPLITVIEW_ANIMATION_TEXT_FADE_OUT_WITH_HIGHLIGHT:
  65. case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN:
  66. case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT:
  67. case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN:
  68. case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT:
  69. *out_duration = kHighlightsFadeInOut;
  70. *out_tween_type = gfx::Tween::FAST_OUT_SLOW_IN;
  71. return;
  72. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN:
  73. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN_CANNOT_SNAP:
  74. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN:
  75. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN:
  76. *out_delay = kOtherFadeInDelay;
  77. *out_duration = kOtherFadeInOut;
  78. *out_tween_type = gfx::Tween::LINEAR_OUT_SLOW_IN;
  79. *out_preemption_strategy = ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
  80. return;
  81. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_OUT:
  82. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT:
  83. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT:
  84. *out_duration = kOtherFadeInOut;
  85. *out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN;
  86. return;
  87. case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT:
  88. case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
  89. *out_duration = kPreviewAreaFadeOut;
  90. *out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN;
  91. return;
  92. case SPLITVIEW_ANIMATION_TEXT_FADE_IN:
  93. *out_delay = kLabelAnimationDelay;
  94. *out_duration = kLabelAnimation;
  95. *out_tween_type = gfx::Tween::LINEAR_OUT_SLOW_IN;
  96. *out_preemption_strategy = ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
  97. return;
  98. case SPLITVIEW_ANIMATION_TEXT_FADE_OUT:
  99. *out_duration = kLabelAnimation;
  100. *out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN;
  101. return;
  102. case SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM:
  103. *out_duration = kSplitviewWindowTransformDuration;
  104. *out_tween_type = gfx::Tween::FAST_OUT_SLOW_IN;
  105. *out_preemption_strategy =
  106. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET;
  107. return;
  108. }
  109. NOTREACHED();
  110. }
  111. // Helper function to apply animation values to |settings|.
  112. void ApplyAnimationSettings(
  113. ui::ScopedLayerAnimationSettings* settings,
  114. ui::LayerAnimator* animator,
  115. ui::LayerAnimationElement::AnimatableProperties animated_property,
  116. base::TimeDelta duration,
  117. gfx::Tween::Type tween,
  118. ui::LayerAnimator::PreemptionStrategy preemption_strategy,
  119. base::TimeDelta delay) {
  120. DCHECK_EQ(settings->GetAnimator(), animator);
  121. settings->SetTransitionDuration(duration);
  122. settings->SetTweenType(tween);
  123. settings->SetPreemptionStrategy(preemption_strategy);
  124. if (!delay.is_zero())
  125. animator->SchedulePauseForProperties(delay, animated_property);
  126. }
  127. // Returns BubbleDialogDelegateView if |transient_window| is a bubble dialog.
  128. views::BubbleDialogDelegate* AsBubbleDialogDelegate(
  129. aura::Window* transient_window) {
  130. views::Widget* widget =
  131. views::Widget::GetWidgetForNativeWindow(transient_window);
  132. if (!widget || !widget->widget_delegate())
  133. return nullptr;
  134. return widget->widget_delegate()->AsBubbleDialogDelegate();
  135. }
  136. } // namespace
  137. WindowTransformAnimationObserver::WindowTransformAnimationObserver(
  138. aura::Window* window)
  139. : window_(window) {
  140. window_->AddObserver(this);
  141. }
  142. WindowTransformAnimationObserver::~WindowTransformAnimationObserver() {
  143. if (window_)
  144. window_->RemoveObserver(this);
  145. }
  146. void WindowTransformAnimationObserver::OnImplicitAnimationsCompleted() {
  147. // After window transform animation is done and if the window's transform is
  148. // set to identity transform, force to relayout all its transient bubble
  149. // dialogs.
  150. if (!window_->layer()->GetTargetTransform().IsIdentity()) {
  151. delete this;
  152. return;
  153. }
  154. for (auto* transient_window :
  155. ::wm::TransientWindowManager::GetOrCreate(window_)
  156. ->transient_children()) {
  157. // For now we only care about bubble dialog type transient children.
  158. views::BubbleDialogDelegate* bubble_delegate_view =
  159. AsBubbleDialogDelegate(transient_window);
  160. if (bubble_delegate_view)
  161. bubble_delegate_view->OnAnchorBoundsChanged();
  162. }
  163. delete this;
  164. }
  165. void WindowTransformAnimationObserver::OnWindowDestroying(
  166. aura::Window* window) {
  167. delete this;
  168. }
  169. void DoSplitviewOpacityAnimation(ui::Layer* layer,
  170. SplitviewAnimationType type) {
  171. float target_opacity = 0.f;
  172. switch (type) {
  173. case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT:
  174. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_OUT:
  175. case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT:
  176. case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT:
  177. case SPLITVIEW_ANIMATION_TEXT_FADE_OUT:
  178. case SPLITVIEW_ANIMATION_TEXT_FADE_OUT_WITH_HIGHLIGHT:
  179. target_opacity = 0.f;
  180. break;
  181. case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN:
  182. target_opacity = features::IsDarkLightModeEnabled()
  183. ? kDarkLightPreviewAreaHighlightOpacity
  184. : kPreviewAreaHighlightOpacity;
  185. break;
  186. case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN:
  187. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN:
  188. target_opacity = features::IsDarkLightModeEnabled()
  189. ? kDarkLightHighlightOpacity
  190. : kHighlightOpacity;
  191. break;
  192. case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN_CANNOT_SNAP:
  193. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN_CANNOT_SNAP:
  194. target_opacity = features::IsDarkLightModeEnabled()
  195. ? kDarkLightHighlightCannotSnapOpacity
  196. : kHighlightOpacity;
  197. break;
  198. case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN:
  199. case SPLITVIEW_ANIMATION_TEXT_FADE_IN:
  200. case SPLITVIEW_ANIMATION_TEXT_FADE_IN_WITH_HIGHLIGHT:
  201. target_opacity = 1.f;
  202. break;
  203. default:
  204. NOTREACHED() << "Not a valid split view opacity animation type.";
  205. return;
  206. }
  207. if (layer->GetTargetOpacity() == target_opacity)
  208. return;
  209. base::TimeDelta duration;
  210. gfx::Tween::Type tween;
  211. ui::LayerAnimator::PreemptionStrategy preemption_strategy;
  212. base::TimeDelta delay;
  213. GetAnimationValuesForType(type, &duration, &tween, &preemption_strategy,
  214. &delay);
  215. ui::LayerAnimator* animator = layer->GetAnimator();
  216. ui::ScopedLayerAnimationSettings settings(animator);
  217. ApplyAnimationSettings(&settings, animator,
  218. ui::LayerAnimationElement::OPACITY, duration, tween,
  219. preemption_strategy, delay);
  220. layer->SetOpacity(target_opacity);
  221. }
  222. void DoSplitviewTransformAnimation(
  223. ui::Layer* layer,
  224. SplitviewAnimationType type,
  225. const gfx::Transform& target_transform,
  226. const std::vector<ui::ImplicitAnimationObserver*>& animation_observers) {
  227. if (layer->GetTargetTransform() == target_transform)
  228. return;
  229. switch (type) {
  230. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN:
  231. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT:
  232. case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
  233. case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN:
  234. case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT:
  235. case SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM:
  236. break;
  237. default:
  238. NOTREACHED() << "Not a valid split view transform type.";
  239. return;
  240. }
  241. base::TimeDelta duration;
  242. gfx::Tween::Type tween;
  243. ui::LayerAnimator::PreemptionStrategy preemption_strategy;
  244. base::TimeDelta delay;
  245. GetAnimationValuesForType(type, &duration, &tween, &preemption_strategy,
  246. &delay);
  247. ui::LayerAnimator* animator = layer->GetAnimator();
  248. ui::ScopedLayerAnimationSettings settings(animator);
  249. for (ui::ImplicitAnimationObserver* animation_observer : animation_observers)
  250. settings.AddObserver(animation_observer);
  251. ApplyAnimationSettings(&settings, animator,
  252. ui::LayerAnimationElement::TRANSFORM, duration, tween,
  253. preemption_strategy, delay);
  254. layer->SetTransform(target_transform);
  255. }
  256. void DoSplitviewClipRectAnimation(
  257. ui::Layer* layer,
  258. SplitviewAnimationType type,
  259. const gfx::Rect& target_clip_rect,
  260. std::unique_ptr<ui::ImplicitAnimationObserver> animation_observer) {
  261. ui::LayerAnimator* animator = layer->GetAnimator();
  262. if (animator->GetTargetClipRect() == target_clip_rect)
  263. return;
  264. switch (type) {
  265. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN:
  266. case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT:
  267. case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
  268. case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN:
  269. case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT:
  270. break;
  271. default:
  272. NOTREACHED() << "Not a valid split view clip rect type.";
  273. return;
  274. }
  275. base::TimeDelta duration;
  276. gfx::Tween::Type tween;
  277. ui::LayerAnimator::PreemptionStrategy preemption_strategy;
  278. base::TimeDelta delay;
  279. GetAnimationValuesForType(type, &duration, &tween, &preemption_strategy,
  280. &delay);
  281. ui::ScopedLayerAnimationSettings settings(animator);
  282. if (animation_observer.get())
  283. settings.AddObserver(animation_observer.release());
  284. ApplyAnimationSettings(&settings, animator, ui::LayerAnimationElement::CLIP,
  285. duration, tween, preemption_strategy, delay);
  286. layer->SetClipRect(target_clip_rect);
  287. }
  288. void MaybeRestoreSplitView(bool refresh_snapped_windows) {
  289. if (!ShouldAllowSplitView() ||
  290. !Shell::Get()->tablet_mode_controller()->InTabletMode()) {
  291. return;
  292. }
  293. // Search for snapped windows to detect if the now active user session, or
  294. // desk were in split view. In case multiple windows were snapped to one side,
  295. // one window after another, there may be multiple windows in a LEFT_SNAPPED
  296. // state or multiple windows in a RIGHT_SNAPPED state. For each of those two
  297. // state types that belongs to multiple windows, the relevant window will be
  298. // listed first among those windows, and a null check in the loop body below
  299. // will filter out the rest of them.
  300. // TODO(amusbach): The windows that were in split view may have later been
  301. // destroyed or changed to non-snapped states. Then the following for loop
  302. // could snap windows that were not in split view. Also, a window may have
  303. // become full screen, and if so, then it would be better not to reactivate
  304. // split view. See https://crbug.com/944134.
  305. SplitViewController* split_view_controller =
  306. SplitViewController::Get(Shell::GetPrimaryRootWindow());
  307. if (refresh_snapped_windows) {
  308. const MruWindowTracker::WindowList windows =
  309. Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(
  310. kActiveDesk);
  311. for (aura::Window* window : windows) {
  312. if (!split_view_controller->CanSnapWindow(window)) {
  313. // Since we are in tablet mode, and this window is not snappable, we
  314. // should maximize it.
  315. WindowState::Get(window)->Maximize();
  316. continue;
  317. }
  318. switch (WindowState::Get(window)->GetStateType()) {
  319. case WindowStateType::kPrimarySnapped:
  320. if (!split_view_controller->left_window()) {
  321. split_view_controller->SnapWindow(window,
  322. SplitViewController::LEFT);
  323. }
  324. break;
  325. case WindowStateType::kSecondarySnapped:
  326. if (!split_view_controller->right_window()) {
  327. split_view_controller->SnapWindow(window,
  328. SplitViewController::RIGHT);
  329. }
  330. break;
  331. default:
  332. break;
  333. }
  334. if (split_view_controller->state() ==
  335. SplitViewController::State::kBothSnapped)
  336. break;
  337. }
  338. }
  339. // Ensure that overview mode is active if there is a window snapped to one of
  340. // the sides. Ensure overview mode is not active if there are two snapped
  341. // windows.
  342. OverviewController* overview_controller = Shell::Get()->overview_controller();
  343. SplitViewController::State state = split_view_controller->state();
  344. if (state == SplitViewController::State::kLeftSnapped ||
  345. state == SplitViewController::State::kRightSnapped) {
  346. overview_controller->StartOverview(OverviewStartAction::kSplitView);
  347. } else if (state == SplitViewController::State::kBothSnapped) {
  348. overview_controller->EndOverview(OverviewEndAction::kSplitView);
  349. }
  350. }
  351. bool ShouldAllowSplitView() {
  352. // Don't allow split view if we're in pinned mode.
  353. if (Shell::Get()->screen_pinning_controller()->IsPinned())
  354. return false;
  355. // TODO(crubg.com/853588): Disallow window dragging and split screen while
  356. // ChromeVox is on until they are in a usable state.
  357. if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled())
  358. return false;
  359. return true;
  360. }
  361. void ShowAppCannotSnapToast() {
  362. Shell::Get()->toast_manager()->Show(
  363. ToastData(kAppCannotSnapToastId, ToastCatalogName::kAppCannotSnap,
  364. l10n_util::GetStringUTF16(IDS_ASH_SPLIT_VIEW_CANNOT_SNAP),
  365. ToastData::kDefaultToastDuration,
  366. /*visible_on_lock_screen=*/false,
  367. /*has_dismiss_button=*/true));
  368. }
  369. SplitViewController::SnapPosition GetSnapPositionForLocation(
  370. aura::Window* root_window,
  371. const gfx::Point& location_in_screen,
  372. const absl::optional<gfx::Point>& initial_location_in_screen,
  373. int snap_distance_from_edge,
  374. int minimum_drag_distance,
  375. int horizontal_edge_inset,
  376. int vertical_edge_inset) {
  377. if (!ShouldAllowSplitView())
  378. return SplitViewController::NONE;
  379. const bool horizontal = SplitViewController::IsLayoutHorizontal(root_window);
  380. const bool right_side_up = SplitViewController::IsLayoutPrimary(root_window);
  381. // Check to see if the current event location |location_in_screen| is within
  382. // the drag indicators bounds.
  383. const gfx::Rect work_area(
  384. screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
  385. root_window));
  386. SplitViewController::SnapPosition snap_position = SplitViewController::NONE;
  387. if (horizontal) {
  388. gfx::Rect area(work_area);
  389. area.Inset(gfx::Insets::VH(0, horizontal_edge_inset));
  390. if (location_in_screen.x() <= area.x()) {
  391. snap_position = right_side_up ? SplitViewController::LEFT
  392. : SplitViewController::RIGHT;
  393. } else if (location_in_screen.x() >= area.right() - 1) {
  394. snap_position = right_side_up ? SplitViewController::RIGHT
  395. : SplitViewController::LEFT;
  396. }
  397. } else {
  398. gfx::Rect area(work_area);
  399. area.Inset(gfx::Insets::VH(vertical_edge_inset, 0));
  400. if (location_in_screen.y() <= area.y()) {
  401. snap_position = right_side_up ? SplitViewController::LEFT
  402. : SplitViewController::RIGHT;
  403. } else if (location_in_screen.y() >= area.bottom() - 1) {
  404. snap_position = right_side_up ? SplitViewController::RIGHT
  405. : SplitViewController::LEFT;
  406. }
  407. }
  408. if (snap_position == SplitViewController::NONE)
  409. return snap_position;
  410. // To avoid accidental snap, the window needs to be dragged inside
  411. // |snap_distance_from_edge| from edge or dragged toward the edge for at least
  412. // |minimum_drag_distance| until it's dragged into |horizontal_edge_inset| or
  413. // |vertical_edge_inset| region.
  414. // The window should always be snapped if inside |snap_distance_from_edge|
  415. // from edge.
  416. bool drag_end_near_edge = false;
  417. gfx::Rect area(work_area);
  418. area.Inset(snap_distance_from_edge);
  419. if (horizontal ? location_in_screen.x() < area.x() ||
  420. location_in_screen.x() > area.right()
  421. : location_in_screen.y() < area.y() ||
  422. location_in_screen.y() > area.bottom()) {
  423. drag_end_near_edge = true;
  424. }
  425. if (!drag_end_near_edge && initial_location_in_screen) {
  426. // Check how far the window has been dragged.
  427. const auto distance = location_in_screen - *initial_location_in_screen;
  428. const int primary_axis_distance = horizontal ? distance.x() : distance.y();
  429. const bool is_left_or_top =
  430. SplitViewController::IsPhysicalLeftOrTop(snap_position, root_window);
  431. if ((is_left_or_top && primary_axis_distance > -minimum_drag_distance) ||
  432. (!is_left_or_top && primary_axis_distance < minimum_drag_distance)) {
  433. snap_position = SplitViewController::NONE;
  434. }
  435. }
  436. return snap_position;
  437. }
  438. SplitViewController::SnapPosition GetSnapPosition(
  439. aura::Window* root_window,
  440. aura::Window* window,
  441. const gfx::Point& location_in_screen,
  442. const gfx::Point& initial_location_in_screen,
  443. int snap_distance_from_edge,
  444. int minimum_drag_distance,
  445. int horizontal_edge_inset,
  446. int vertical_edge_inset) {
  447. if (!SplitViewController::Get(root_window)->CanSnapWindow(window)) {
  448. return SplitViewController::NONE;
  449. }
  450. absl::optional<gfx::Point> initial_location_in_current_screen = absl::nullopt;
  451. if (window->GetRootWindow() == root_window)
  452. initial_location_in_current_screen = initial_location_in_screen;
  453. return GetSnapPositionForLocation(
  454. root_window, location_in_screen, initial_location_in_current_screen,
  455. snap_distance_from_edge, minimum_drag_distance, horizontal_edge_inset,
  456. vertical_edge_inset);
  457. }
  458. } // namespace ash