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
ash / wm / tile_group / window_splitter.h [blame]
// Copyright 2023 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_TILE_GROUP_WINDOW_SPLITTER_H_
#define ASH_WM_TILE_GROUP_WINDOW_SPLITTER_H_
#include <memory>
#include <optional>
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/aura/window_observer.h"
#include "ui/events/velocity_tracker/velocity_tracker.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
namespace aura {
class Window;
} // namespace aura
namespace ash {
class PhantomWindowController;
// The WindowSplitter is responsible for detecting when a window can be split,
// showing the split preview, and performing the actual window splitting.
// It is meant to be used during dragging by a WindowResizer.
class ASH_EXPORT WindowSplitter : public aura::WindowObserver {
public:
// The region of a window from which to initiate a split.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Keep this in sync with `WindowSplittingSplitRegion` in
// tools/metrics/histograms/metadata/ash/enums.xml.
enum class SplitRegion {
kNone = 0,
kLeft = 1,
kRight = 2,
kTop = 3,
kBottom = 4,
kMaxValue = kBottom,
};
// The type of action resulting from a completed drag, for logging only.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Keep this in sync with `WindowSplittingDragType` in
// tools/metrics/histograms/metadata/ash/enums.xml.
enum class DragType {
kIncomplete = 0,
kNoSplit = 1,
kSplit = 2,
kMaxValue = kSplit,
};
// Holds info about windows after splitting.
struct SplitWindowInfo {
gfx::Rect topmost_window_bounds;
gfx::Rect dragged_window_bounds;
SplitRegion split_region = SplitRegion::kNone;
bool operator==(const SplitWindowInfo&) const;
};
// The default inset region of a window that could trigger window splitting.
static constexpr gfx::Insets kBaseTriggerMargins = gfx::Insets::VH(35, 45);
// Amount of time the cursor has to dwell to activate the window splitting
// phantom window.
static constexpr base::TimeDelta kDwellActivationDuration =
base::Milliseconds(450);
// Amount of time the phantom window stays shown before cancelling.
static constexpr base::TimeDelta kDwellCancellationDuration =
base::Milliseconds(1500);
// Max cursor movement velocity threshold, which if exceeded will reset window
// splitting activation.
static constexpr double kDwellMaxVelocityPixelsPerSec = 60.0;
// Calculates window bounds and other info resulting from window splitting.
// `topmost_window` is the window to be split.
// `dragged_window` is the window being dragged over the `topmost_window`.
// `screen_location` is the screen coordinate of the input event. It must be
// within the `topmost_window`.
// Returns nullopt if window can't be split, e.g. the location is not within
// any trigger area, or the resulting size is smaller than minimum size, etc.
static std::optional<SplitWindowInfo> MaybeSplitWindow(
aura::Window* topmost_window,
aura::Window* dragged_window,
const gfx::PointF& screen_location);
explicit WindowSplitter(aura::Window* dragged_window);
WindowSplitter(const WindowSplitter&) = delete;
WindowSplitter& operator=(const WindowSplitter&) = delete;
~WindowSplitter() override;
// Called during drag to determine window splitting activation.
void UpdateDrag(const gfx::PointF& location_in_screen, bool can_split);
// Called when drag is completed to apply splitting.
void CompleteDrag(const gfx::PointF& last_location_in_screen);
// Disengages window splitting.
void Disengage();
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override;
const PhantomWindowController* GetPhantomWindowControllerForTesting() const {
return phantom_window_controller_.get();
}
private:
aura::Window* dragged_window() {
return dragged_window_observation_.GetSource();
}
aura::Window* topmost_window() {
return topmost_window_observation_.GetSource();
}
void UpdateTopMostWindow(aura::Window* topmost_window);
// Starts or refreshes the dwell timer.
void RestartDwellTimer();
// Removes any phantom window and any running dwell timer.
void RemovePhantomWindow();
// Callback to show the phantom window with precondition checking.
void ShowPhantomWindowCallback();
// Shows the phantom window.
void ShowPhantomWindow(const gfx::Rect& bounds);
// Whether the window is ready to split upon completing drag.
bool ReadyToSplit() const { return !!phantom_window_controller_; }
void RecordMetricsOnEndDrag();
DragType GetDragType() const;
void UpdateCursorLocation(const gfx::PointF& location_in_screen);
double GetCursorVelocitySquared() const;
base::ScopedObservation<aura::Window, aura::WindowObserver>
dragged_window_observation_{this};
base::ScopedObservation<aura::Window, aura::WindowObserver>
topmost_window_observation_{this};
gfx::PointF last_location_in_screen_;
std::optional<SplitWindowInfo> last_split_window_info_;
// Whether the cursor actually moved enough to be considered a drag.
bool is_drag_updated_ = false;
// Whether the drag operation was completed successfully (instead of e.g.
// cancelled).
bool is_drag_completed_ = false;
// The region of a window the split happened, if any;
SplitRegion completed_split_region_ = SplitRegion::kNone;
// Gives a preview of how the window will be split.
std::unique_ptr<PhantomWindowController> phantom_window_controller_;
// Number of times the phantom window was shown.
uint32_t phantom_window_shown_count_ = 0;
// Time ticks when the drag action started.
const base::TimeTicks drag_start_time_;
// Timer for activating phantom window.
base::OneShotTimer dwell_activation_timer_;
// Timer for cancelling window splitting.
base::OneShotTimer dwell_cancellation_timer_;
// Tracks cursor velocity.
ui::VelocityTracker velocity_tracker_;
base::WeakPtrFactory<WindowSplitter> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_WM_TILE_GROUP_WINDOW_SPLITTER_H_