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
ash / accessibility / autoclick / autoclick_controller.h [blame]
// Copyright 2013 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_ACCESSIBILITY_AUTOCLICK_AUTOCLICK_CONTROLLER_H_
#define ASH_ACCESSIBILITY_AUTOCLICK_AUTOCLICK_CONTROLLER_H_
#include "ash/ash_export.h"
#include "ash/constants/ash_constants.h"
#include "ash/public/cpp/ash_constants.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "ui/aura/client/cursor_client_observer.h"
#include "ui/aura/window_observer.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/native_widget_types.h"
namespace base {
class RetainingOneShotTimer;
} // namespace base
namespace views {
class Widget;
} // namespace views
namespace ash {
class AccessibilityFeatureDisableDialog;
class DragEventRewriter;
class AutoclickMenuBubbleController;
class AutoclickRingHandler;
class AutoclickScrollPositionHandler;
// Autoclick is one of the accessibility features. If enabled, two circles will
// animate at the mouse event location and an automatic mouse event event will
// happen after a certain amount of time at that location. The event type is
// determined by SetAutoclickEventType.
class ASH_EXPORT AutoclickController
: public ui::EventHandler,
public aura::WindowObserver,
public aura::client::CursorClientObserver {
public:
// Autoclick's scroll event types.
enum ScrollPadAction {
kScrollUp = 1,
kScrollDown = 2,
kScrollLeft = 3,
kScrollRight = 4,
kScrollClose = 5,
};
AutoclickController();
AutoclickController(const AutoclickController&) = delete;
AutoclickController& operator=(const AutoclickController&) = delete;
~AutoclickController() override;
// Set whether autoclicking is enabled. If |show_confirmation_dialog|, a
// confirmation dialog will be shown when disabling autoclick to ensure
// the user doesn't accidentally lock themselves out of the feature.
void SetEnabled(bool enabled, bool show_confirmation_dialog);
// Returns true if autoclicking is enabled.
bool IsEnabled() const;
// Set the time to wait in milliseconds from when the mouse stops moving
// to when the autoclick event is sent.
void SetAutoclickDelay(base::TimeDelta delay);
// Gets the default wait time as a base::TimeDelta object.
static base::TimeDelta GetDefaultAutoclickDelay();
// Sets the event type.
void SetAutoclickEventType(AutoclickEventType type);
// Sets the movement threshold beyond which mouse movements cancel or begin
// a new Autoclick event.
void SetMovementThreshold(int movement_threshold);
// Sets the menu position and updates the UI.
void SetMenuPosition(FloatingMenuPosition menu_position);
// Performs the given ScrollPadAction at the current scrolling point.
void DoScrollAction(ScrollPadAction action);
// The cursor is over a scroll (up/down/left/right) button.
void OnEnteredScrollButton();
// The cursor has exited a scroll (up/down/left/right) button.
void OnExitedScrollButton();
// The Accessibility Common extension has found scrollble bounds at the
// current scroll point.
void HandleAutoclickScrollableBoundsFound(const gfx::Rect& bounds_in_screen);
// Update the bubble menu bounds if necessary to avoid system UI.
void UpdateAutoclickMenuBoundsIfNeeded();
// Sets whether to revert to a left click after any other event type.
void set_revert_to_left_click(bool revert_to_left_click) {
revert_to_left_click_ = revert_to_left_click;
}
// Sets whether to stabilize the cursor position during a click.
// If |stabilize_position|, the click position will not change after the
// autoclick timer and gesture animation begin, so long as the cursor does
// not move outside of the movement threshold. If the position is not
// stabilized, the cursor movements will translate into autoclick position
// movements (but a cursor movement larger than the movement threshold from
// the starting position will still cancel the click).
void set_stabilize_click_position(bool stabilize_position) {
stabilize_click_position_ = stabilize_position;
}
// Functionality for testing.
static float GetStartGestureDelayRatioForTesting();
AutoclickMenuBubbleController* GetMenuBubbleControllerForTesting() {
return menu_bubble_controller_.get();
}
AccessibilityFeatureDisableDialog* GetDisableDialogForTesting() {
return disable_dialog_.get();
}
void SetScrollableBoundsCallbackForTesting(
base::RepeatingCallback<void(const gfx::Rect&)> callback) {
scrollable_bounds_callback_for_testing_ = callback;
}
private:
void SetTapDownTarget(aura::Window* target);
void UpdateAutoclickWidgetPosition(gfx::NativeView native_view,
aura::Window* root_window);
void DoAutoclickAction();
void StartAutoclickGesture();
void CancelAutoclickAction();
void OnActionCompleted(AutoclickEventType event_type);
void InitClickTimers();
void UpdateRingWidget();
void UpdateRingSize();
void InitializeScrollLocation();
void UpdateScrollPosition();
void HideScrollPosition();
void RecordUserAction(AutoclickEventType event_type) const;
bool DragInProgress() const;
void CreateMenuBubbleController();
bool AutoclickMenuContainsPoint(const gfx::Point& point) const;
bool AutoclickScrollContainsPoint(const gfx::Point& point) const;
// ui::EventHandler overrides:
void OnMouseEvent(ui::MouseEvent* event) override;
void OnKeyEvent(ui::KeyEvent* event) override;
void OnTouchEvent(ui::TouchEvent* event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
void OnScrollEvent(ui::ScrollEvent* event) override;
// aura::WindowObserver overrides:
void OnWindowDestroying(aura::Window* window) override;
// aura::client::CursorClientObserver overrides:
void OnCursorVisibilityChanged(bool is_visible) override;
// TODO(katie): Override OnCursorDisplayChanged to move the autoclick
// bubble menu to the same display as the cursor.
// Whether Autoclick is currently enabled.
bool enabled_ = false;
AutoclickEventType event_type_ = kDefaultAutoclickEventType;
bool revert_to_left_click_ = true;
bool stabilize_click_position_ = false;
int movement_threshold_ = kDefaultAutoclickMovementThreshold;
// TODO(katie): The default position should flex with the user's choice of
// language (RTL vs LTR) and shelf position, following the same behavior
// as the volume slider bubble. However, once the user changes the position
// manually, the position will be fixed regardless of language direction and
// shelf position. This probably means adding a new AutoclickMenuPostion
// enum for "system default".
FloatingMenuPosition menu_position_ = kDefaultAutoclickMenuPosition;
int mouse_event_flags_ = ui::EF_NONE;
// The target window is observed by AutoclickController for the duration
// of a autoclick gesture.
raw_ptr<aura::Window> tap_down_target_ = nullptr;
// The most recent mouse location.
gfx::Point last_mouse_location_{-kDefaultAutoclickMovementThreshold,
-kDefaultAutoclickMovementThreshold};
// The position in screen coordinates used to determine the distance the
// mouse has moved since dwell began. It is used to determine
// if move events should cancel the gesture.
gfx::Point anchor_location_{-kDefaultAutoclickMovementThreshold,
-kDefaultAutoclickMovementThreshold};
// The position in screen coodinates tracking where the autoclick gesture
// should be anchored. While the |start_gesture_timer_| is running and before
// the animation is drawn, subtle mouse movements will update the
// |gesture_anchor_location_|, so that once animation begins it can focus on
// the most recent mose point.
gfx::Point gesture_anchor_location_{-kDefaultAutoclickMovementThreshold,
-kDefaultAutoclickMovementThreshold};
// The point at which the next scroll event will occur.
gfx::Point scroll_location_{-kDefaultAutoclickMovementThreshold,
-kDefaultAutoclickMovementThreshold};
// Whether the current scroll_location_ is the initial one set automatically,
// or if false, it was chosen explicitly by the user. The scroll bubble
// positions are different in these two cases.
bool is_initial_scroll_location_ = true;
// Whether the cursor is currently over a scroll button. If true, new gestures
// will not be started. This ensures the autoclick ring is not drawn over
// the scroll position buttons, and extra clicks will not be generated there.
bool over_scroll_button_ = false;
base::RepeatingCallback<void(const gfx::Rect&)>
scrollable_bounds_callback_for_testing_;
// The widget containing the autoclick ring.
std::unique_ptr<views::Widget> ring_widget_;
base::TimeDelta delay_;
// The timer that counts down from the beginning of a gesture until a click.
std::unique_ptr<base::RetainingOneShotTimer> autoclick_timer_;
// The timer that counts from when the user stops moving the mouse
// until the start of the animated gesture. This keeps the animation from
// showing up when the mouse cursor is moving quickly across the screen,
// instead waiting for the mouse to begin a dwell.
std::unique_ptr<base::RetainingOneShotTimer> start_gesture_timer_;
std::unique_ptr<AutoclickRingHandler> autoclick_ring_handler_;
std::unique_ptr<AutoclickScrollPositionHandler>
autoclick_scroll_position_handler_;
std::unique_ptr<DragEventRewriter> drag_event_rewriter_;
std::unique_ptr<AutoclickMenuBubbleController> menu_bubble_controller_;
// Holds a weak pointer to the dialog shown when autoclick is being disabled.
base::WeakPtr<AccessibilityFeatureDisableDialog> disable_dialog_;
};
} // namespace ash
#endif // ASH_ACCESSIBILITY_AUTOCLICK_AUTOCLICK_CONTROLLER_H_