1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
ash / wm / splitview / split_view_utils.h [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_WM_SPLITVIEW_SPLIT_VIEW_UTILS_H_
#define ASH_WM_SPLITVIEW_SPLIT_VIEW_UTILS_H_
#include <optional>
#include <vector>
#include "ash/ash_export.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_types.h"
#include "ash/wm/window_positioning_utils.h"
#include "base/memory/raw_ptr.h"
#include "ui/aura/window_observer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/views/widget/widget.h"
namespace aura {
class Window;
} // namespace aura
namespace ui {
class Layer;
} // namespace ui
namespace ash {
class SplitViewOverviewSession;
// Enum of the different splitview mode animations. Sorted by property
// (opacity/transform) and then alphabetically.
enum SplitviewAnimationType {
// Used to fade in and out the highlights on either side which indicate where
// to drag a selector item.
SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN,
SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN_CANNOT_SNAP,
SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT,
// Used to fade in and out the other highlight. There are normally two
// highlights, one on each side. When entering a state with a preview
// highlight, one highlight is the preview highlight, and the other highlight
// is the other highlight.
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN,
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN_CANNOT_SNAP,
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_OUT,
// Used to fade in and out the label on the overview item which warns users
// the item cannot be snapped. The label appears on the overview item after
// another window has been snapped.
SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN,
SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT,
// Used to fade in and out the preview area highlight which indicates the
// bounds of the window that is about to get snapped.
SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN,
SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT,
// Used to fade in and out the labels which appear on either side of overview
// mode when a overview item is selected. They indicate where to drag the
// selector item if it is snappable, or if an item cannot be snapped.
SPLITVIEW_ANIMATION_TEXT_FADE_IN,
SPLITVIEW_ANIMATION_TEXT_FADE_OUT,
// Used when the text fades in or out with the highlights, as opposed to
// fading in when the highlights change bounds. Has slightly different
// animation values.
SPLITVIEW_ANIMATION_TEXT_FADE_IN_WITH_HIGHLIGHT,
SPLITVIEW_ANIMATION_TEXT_FADE_OUT_WITH_HIGHLIGHT,
// Used to slide in and out the other highlight.
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN,
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT,
// Used to slide in and out the text label on the other highlight.
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN,
SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT,
// Used to animate the inset of the preview area to nothing.
SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET,
// Used to slide in and out the preview area highlight.
SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN,
SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT,
// Used to slide in and out the text label on the preview area highlight.
SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN,
SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT,
// Used to apply window transform on the selector item after it gets snapped
// or on the dragged window after the drag ends.
SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM,
};
// This class observes the window transform animation and relayout the window's
// transient bubble dialogs when animation is completed. This is needed in some
// splitview and overview cases as in splitview or overview, the window can have
// an un-identity transform in place when its bounds changed. And when this
// happens, its transient bubble dialogs won't have the correct bounds as the
// bounds are calculated based on the transformed window bounds. We'll need to
// manually relayout the bubble dialogs after the window's transform reset to
// the identity transform so that the bubble dialogs can have correct bounds.
class ASH_EXPORT WindowTransformAnimationObserver
: public ui::ImplicitAnimationObserver,
public aura::WindowObserver {
public:
explicit WindowTransformAnimationObserver(aura::Window* window);
~WindowTransformAnimationObserver() override;
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override;
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override;
private:
const raw_ptr<aura::Window> window_;
WindowTransformAnimationObserver(const WindowTransformAnimationObserver&) =
delete;
WindowTransformAnimationObserver& operator=(
const WindowTransformAnimationObserver&) = delete;
};
// Animates |layer|'s opacity based on |type|.
void DoSplitviewOpacityAnimation(ui::Layer* layer, SplitviewAnimationType type);
// Animates |layer|'s transform based on |type|.
void DoSplitviewTransformAnimation(
ui::Layer* layer,
SplitviewAnimationType type,
const gfx::Transform& target_transform,
const std::vector<ui::ImplicitAnimationObserver*>& animation_observers);
// Animates |layer|'s clip rect based on |type|.
void DoSplitviewClipRectAnimation(
ui::Layer* layer,
SplitviewAnimationType type,
const gfx::Rect& target_clip_rect,
std::unique_ptr<ui::ImplicitAnimationObserver> animation_observer);
// Returns the `SplitViewOverviewSession` for the root window of `window`.
ASH_EXPORT SplitViewOverviewSession* GetSplitViewOverviewSession(
aura::Window* window);
// Returns true if `window` is currently snapped.
bool IsSnapped(aura::Window* window);
// Returns the length of the window according to the screen orientation.
ASH_EXPORT int GetWindowLength(aura::Window* window, bool horizontal);
// Returns the corresponding `chromeos::WindowStateType` for the given
// `snap_position`.
chromeos::WindowStateType GetWindowStateTypeFromSnapPosition(
SnapPosition snap_position);
// Returns the corresponding `SnapPosition` for the given
// `chromeos::WindowStateType`, which must be snapped.
SnapPosition ToSnapPosition(chromeos::WindowStateType type);
// Transforms `window` based on whether it is the primary or secondary window
// and its distance from `divider_position` during split view resizing.
void SetWindowTransformDuringResizing(aura::Window* window,
int divider_position);
// Restores split view and overview based on the current split view's state.
// If |refresh_snapped_windows| is true, it will update the left and right
// snapped windows based on the MRU windows snapped states.
void MaybeRestoreSplitView(bool refresh_snapped_windows);
// Returns true if split view mode is supported.
ASH_EXPORT bool ShouldAllowSplitView();
// Displays a toast notifying users the application selected for split view is
// not compatible.
void ShowAppCannotSnapToast();
// Calculates the snap position for a dragged window at |location_in_screen|,
// ignoring any properties of the window itself. The |root_window| is of the
// current screen. `initial_location_in_screen` is the location at drag start if
// the drag began in `root_window`, and is empty otherwise. To be snappable
// (meaning the return value is not `SnapPosition::kNone`),
// `location_in_screen` must be either inside `snap_distance_from_edge` or
// dragged toward the edge for at least `minimum_drag_distance` distance until
// it's dragged into a suitable edge of the work area of `root_window` (i.e.,
// `horizontal_edge_inset` if dragged horizontally to snap, or
// `vertical_edge_inset` if dragged vertically).
SnapPosition GetSnapPositionForLocation(
aura::Window* root_window,
const gfx::Point& location_in_screen,
const std::optional<gfx::Point>& initial_location_in_screen,
int snap_distance_from_edge,
int minimum_drag_distance,
int horizontal_edge_inset,
int vertical_edge_inset);
// Returns the desired snap position. To be snappable, |window| must 1)
// satisfy |SplitViewController::CanSnapWindow| for |root_window|, and
// 2) be snappable according to
// |GetSnapPositionForLocation| above.
// |initial_location_in_screen| is the window location at drag start in
// its initial window. Otherwise, the arguments are the same as above.
ASH_EXPORT SnapPosition
GetSnapPosition(aura::Window* root_window,
aura::Window* window,
const gfx::Point& location_in_screen,
const gfx::Point& initial_location_in_screen,
int snap_distance_from_edge,
int minimum_drag_distance,
int horizontal_edge_inset,
int vertical_edge_inset);
// The return values of these two functions together indicate what actual
// positions correspond to |PRIMARY| and |SECONDARY|:
// |IsLayoutHorizontal| |IsLayoutPrimary| |PRIMARY| |SECONDARY|
// --------------------------------------------------------------------------
// true true left right
// true false right left
// false true top bottom
// false false bottom top
// In both clamshell and tablet mode, these functions return values based on
// display orientation. |window| is used to find the nearest display to check
// if the display layout is horizontal and is primary or not.
ASH_EXPORT bool IsLayoutHorizontal(aura::Window* window);
ASH_EXPORT bool IsLayoutHorizontal(const display::Display& display);
ASH_EXPORT bool IsLayoutPrimary(aura::Window* window);
ASH_EXPORT bool IsLayoutPrimary(const display::Display& display);
// Returns true if `position` actually signifies a left or top position,
// according to the return values of `IsLayoutHorizontal` and
// `IsLayoutPrimary`. Physical position refers to the position of the window
// on the display that is held upward.
ASH_EXPORT bool IsPhysicallyLeftOrTop(SnapPosition position,
aura::Window* window);
ASH_EXPORT bool IsPhysicallyLeftOrTop(SnapPosition position,
const display::Display& display);
// Returns whether `window`'s snap position is actually in the left or top
// position based on whether the display is in primary screen orientation, where
// `window` must be snapped.
ASH_EXPORT bool IsPhysicallyLeftOrTop(aura::Window* window);
// Returns the maximum value of the `divider_position_`, which is the width of
// the current display's work area bounds in landscape orientation, or height
// of the current display's work area bounds in portrait orientation.
int GetDividerPositionUpperLimit(aura::Window* root_window);
// Returns the minimum length of the window according to the screen orientation.
ASH_EXPORT int GetMinimumWindowLength(aura::Window* window, bool horizontal);
// Returns the target divider position for `root_window` for `snap_ratio` at
// `snap_position`, clamped between 0 and the upper limit of `root_window`.
// `account_for_divider_width` will decide whether the divider shorter side
// width will be subtracted or not.
int CalculateDividerPosition(aura::Window* root_window,
SnapPosition snap_position,
float snap_ratio,
bool account_for_divider_width);
// Returns the divider position, the origin of where `window` is divided on the
// work area. This will be the window length if it is physically left or top, or
// the work area length - window length if it is physically right or bottom. If
// `account_for_divider_width` is true, it will also subtract
// `kSplitviewDividerShortSideLength / 2` from the window length if is
// physically left or top, or `kSplitviewDividerShortSideLength` to `window`
// length if it is physically right or bottom.
int GetEquivalentDividerPosition(aura::Window* window,
bool account_for_divider_width);
// Returns the bounds of a snapped window at `snap_position`, where
// `divider_position` is the end of the primary window width.
// `account_for_divider_width` will decide whether the window bounds need to
// shrink to make room for the divider or not. `window_for_minimum_size` will be
// taken into consideration for the calculation while `is_resizing_with_divider`
// is false.
gfx::Rect CalculateSnappedWindowBoundsInScreen(
SnapPosition snap_position,
aura::Window* root_window,
aura::Window* window_for_minimum_size,
bool account_for_divider_width,
int divider_position,
bool is_resizing_with_divider);
// Returns the snap type of the window's `state_type`, which must be snapped.
// `snap_type` is guaranteed to be snapped already.
SnapViewType ToSnapViewType(chromeos::WindowStateType state_type);
chromeos::WindowStateType ToWindowStateType(SnapViewType snap_type);
// Returns the opposite snap type of `window`, where `window` must be snapped.
// `snap_type` is guaranteed to be snapped already.
SnapViewType GetOppositeSnapType(SnapViewType snap_type);
SnapViewType GetOppositeSnapType(aura::Window* window);
// Returns true if `snap_action_source` can be start faster split screen set up.
ASH_EXPORT bool CanSnapActionSourceStartFasterSplitView(
WindowSnapActionSource snap_action_source);
// Returns true if `window` should be *excluded* from the occluded window check,
// e.g. if it is not visible or minimized or when it is a float or pip window.
// If this is true, `window` will be ignored when determining whether to show
// partial overview or consider the window for snap to replace.
bool ShouldExcludeForOcclusionCheck(const aura::Window* window,
const aura::Window* target_root);
// Returns the set of windows which can be cycled through in the stacking order
// of the children of the active desk container of `root`. Note this excludes
// windows on other containers, e.g. always-on-top windows and floated windows.
aura::Window::Windows GetActiveDeskAppWindowsInZOrder(aura::Window* root);
// Returns the window that is fully visible (without occlusion) on the
// `target_root` and with the given `snap_type`, excluding `window_to_ignore`.
// Returns nullptr if no such window exists.
aura::Window* GetTopmostVisibleWindowOfSnapType(aura::Window* window_to_ignore,
aura::Window* target_root,
SnapViewType snap_type);
// Returns the window that is fully visible (without occlusion) and snapped to
// the opposite side of the given `window` on the same root window. Returns
// nullptr if no such window exists.
aura::Window* GetOppositeVisibleSnappedWindow(aura::Window* window);
// Given the windows `to_be_snapped` and `opposite_snapped`, returns the snap
// ratio gap or overlap that would be created by snapping them on opposite sides
// of each other.
ASH_EXPORT float GetSnapRatioGap(aura::Window* to_be_snapped,
aura::Window* opposite_snapped);
// Given the windows `to_be_snapped` and `opposite_snapped`, returns true if the
// snap ratio gap or overlap between them is within the snap ratio threshold for
// auto-group and snap-to-replace.
bool IsSnapRatioGapWithinThreshold(aura::Window* to_be_snapped,
aura::Window* opposite_snapped);
// Given `to_be_snapped_window`, the `target_root` it is being dragged to, and
// target `snap_type`, returns the auto-snap ratio for `to_be_snapped_window`
// that will be used if it can be added to a snap group.
float GetAutoSnapRatio(aura::Window* to_be_snapped_window,
aura::Window* target_root,
SnapViewType snap_type);
// Returns true if the given `window` can be considered as the candidate for
// faster split screen set up. Returns false otherwise. `snap_action_source` is
// used to filter out some unwanted snap sources.
bool ShouldConsiderWindowForSplitViewSetupView(
aura::Window* window,
WindowSnapActionSource snap_action_source);
// Returns true if `SplitViewOverviewSession` is allowed to start when the given
// `window` is snapped with given `snap_action_source`. Returns false otherwise.
bool CanStartSplitViewOverviewSessionInClamshell(
aura::Window* window,
WindowSnapActionSource snap_action_source);
// Gets the expected window component for a window in split view, depending on
// current screen orientation for resizing purpose.
int GetWindowComponentForResize(aura::Window* window);
// Returns true if the split view divider exits which should be taken into
// consideration when calculating the snap ratio.
// TODO(b/329326366): Remove this API and have clients call
// `UpdateSnappedBounds()` directly.
bool ShouldConsiderDivider(aura::Window* window);
// Returns true if the minimum size of `window1` and `window2` and the divider
// width can fit in the work area. The windows should belong to the same root
// window.
bool CanWindowsFitInWorkArea(aura::Window* window1, aura::Window* window2);
// Builds the full histogram that records whether the window layout completes on
// `SplitViewOverviewSession` exit. The full histogram is shown in the example
// below:
// |------------prefix----------|-----root_word-------------------|
// "Ash.SplitViewOverviewSession.WindowLayoutCompleteOnSessionExit"
// |--ui_mode--|
// ".ClamshellMode",
ASH_EXPORT std::string BuildWindowLayoutCompleteOnSessionExitHistogram();
// Builds the full histogram that records the exit point of the
// `SplitViewOverviewSession` by inserting the `snap_action_source` and
// appending the ui mode suffix to build the full histogram name.
// The full histogram is shown in the example below:
// |------------prefix----------|-snap_action_source-|-root_word-|--ui_mode--|
// "Ash.SplitViewOverviewSession.DragWindowEdgeToSnap.ExitPoint.ClamshellMode".
ASH_EXPORT std::string BuildSplitViewOverviewExitPointHistogramName(
WindowSnapActionSource snap_action_source);
// Builds the full histogram that records the pref value when a window is
// snapped.
// |----------prefix---------|-snap_action_source-|
// "Ash.SnapWindowSuggestions.DragWindowEdgeToSnap".
ASH_EXPORT std::string BuildSnapWindowSuggestionsHistogramName(
WindowSnapActionSource snap_action_source);
} // namespace ash
#endif // ASH_WM_SPLITVIEW_SPLIT_VIEW_UTILS_H_