autoclick_controller.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. // Copyright 2013 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/accessibility/autoclick/autoclick_controller.h"
  5. #include <tuple>
  6. #include "ash/accessibility/accessibility_controller_impl.h"
  7. #include "ash/accessibility/autoclick/autoclick_drag_event_rewriter.h"
  8. #include "ash/accessibility/autoclick/autoclick_ring_handler.h"
  9. #include "ash/accessibility/autoclick/autoclick_scroll_position_handler.h"
  10. #include "ash/constants/ash_constants.h"
  11. #include "ash/public/cpp/shell_window_ids.h"
  12. #include "ash/shelf/shelf.h"
  13. #include "ash/shell.h"
  14. #include "ash/strings/grit/ash_strings.h"
  15. #include "ash/system/accessibility/accessibility_feature_disable_dialog.h"
  16. #include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
  17. #include "ash/wm/fullscreen_window_finder.h"
  18. #include "ash/wm/window_util.h"
  19. #include "base/bind.h"
  20. #include "base/metrics/histogram_macros.h"
  21. #include "base/metrics/user_metrics.h"
  22. #include "base/timer/timer.h"
  23. #include "ui/aura/window.h"
  24. #include "ui/aura/window_tree_host.h"
  25. #include "ui/events/event.h"
  26. #include "ui/events/event_sink.h"
  27. #include "ui/events/event_utils.h"
  28. #include "ui/gfx/geometry/point.h"
  29. #include "ui/views/widget/widget.h"
  30. #include "ui/wm/core/coordinate_conversion.h"
  31. namespace ash {
  32. namespace {
  33. // The ratio of how long the gesture should take to begin after a dwell to the
  34. // total amount of time of the dwell.
  35. const float kStartGestureDelayRatio = 1 / 6.0;
  36. // How much distance to travel with each generated scroll event.
  37. const int kScrollDelta = 10;
  38. bool IsModifierKey(const ui::KeyboardCode key_code) {
  39. return key_code == ui::VKEY_SHIFT || key_code == ui::VKEY_LSHIFT ||
  40. key_code == ui::VKEY_CONTROL || key_code == ui::VKEY_LCONTROL ||
  41. key_code == ui::VKEY_RCONTROL || key_code == ui::VKEY_MENU ||
  42. key_code == ui::VKEY_LMENU || key_code == ui::VKEY_RMENU;
  43. }
  44. base::TimeDelta CalculateStartGestureDelay(base::TimeDelta total_delay) {
  45. return total_delay * kStartGestureDelayRatio;
  46. }
  47. views::Widget::InitParams CreateAutoclickOverlayWidgetParams(
  48. aura::Window* root_window) {
  49. views::Widget::InitParams params;
  50. params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
  51. params.accept_events = false;
  52. params.activatable = views::Widget::InitParams::Activatable::kNo;
  53. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  54. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  55. params.parent =
  56. Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
  57. return params;
  58. }
  59. } // namespace
  60. // static.
  61. base::TimeDelta AutoclickController::GetDefaultAutoclickDelay() {
  62. return base::Milliseconds(int64_t{kDefaultAutoclickDelayMs});
  63. }
  64. AutoclickController::AutoclickController()
  65. : delay_(GetDefaultAutoclickDelay()),
  66. autoclick_ring_handler_(std::make_unique<AutoclickRingHandler>()),
  67. drag_event_rewriter_(std::make_unique<AutoclickDragEventRewriter>()) {
  68. Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource()->AddEventRewriter(
  69. drag_event_rewriter_.get());
  70. Shell::Get()->cursor_manager()->AddObserver(this);
  71. InitClickTimers();
  72. UpdateRingSize();
  73. }
  74. AutoclickController::~AutoclickController() {
  75. // Clean up UI.
  76. HideScrollPosition();
  77. menu_bubble_controller_ = nullptr;
  78. CancelAutoclickAction();
  79. Shell::Get()->cursor_manager()->RemoveObserver(this);
  80. Shell::Get()->RemovePreTargetHandler(this);
  81. SetTapDownTarget(nullptr);
  82. Shell::GetPrimaryRootWindow()
  83. ->GetHost()
  84. ->GetEventSource()
  85. ->RemoveEventRewriter(drag_event_rewriter_.get());
  86. }
  87. float AutoclickController::GetStartGestureDelayRatioForTesting() {
  88. return kStartGestureDelayRatio;
  89. }
  90. void AutoclickController::SetTapDownTarget(aura::Window* target) {
  91. if (tap_down_target_ == target)
  92. return;
  93. if (tap_down_target_)
  94. tap_down_target_->RemoveObserver(this);
  95. tap_down_target_ = target;
  96. if (tap_down_target_)
  97. tap_down_target_->AddObserver(this);
  98. }
  99. void AutoclickController::SetEnabled(bool enabled,
  100. bool show_confirmation_dialog) {
  101. if (enabled_ == enabled)
  102. return;
  103. if (enabled) {
  104. Shell::Get()->AddPreTargetHandler(this, ui::EventTarget::Priority::kSystem);
  105. // Only create the bubble controller when needed. Most users will not enable
  106. // automatic clicks, so there's no need to use these unless the feature
  107. // is on.
  108. menu_bubble_controller_ = std::make_unique<AutoclickMenuBubbleController>();
  109. menu_bubble_controller_->ShowBubble(event_type_, menu_position_);
  110. if (event_type_ == AutoclickEventType::kScroll) {
  111. InitializeScrollLocation();
  112. UpdateScrollPosition();
  113. }
  114. enabled_ = enabled;
  115. } else {
  116. if (show_confirmation_dialog) {
  117. // If a dialog exists already, no need to show it again.
  118. if (disable_dialog_)
  119. return;
  120. // Show a confirmation dialog before disabling autoclick.
  121. auto* dialog = new AccessibilityFeatureDisableDialog(
  122. IDS_ASH_AUTOCLICK_DISABLE_CONFIRMATION_TEXT,
  123. // Callback for if the user accepts the dialog
  124. base::BindOnce([]() {
  125. // If they accept, actually disable autoclick.
  126. Shell::Get()->autoclick_controller()->SetEnabled(
  127. false, false /* do not show the dialog */);
  128. }),
  129. // Callback for if the user cancels the dialog - marks the
  130. // feature as enabled again in prefs.
  131. base::BindOnce([]() {
  132. // If they cancel, ensure autoclick is enabled.
  133. Shell::Get()->accessibility_controller()->autoclick().SetEnabled(
  134. true);
  135. }));
  136. disable_dialog_ = dialog->GetWeakPtr();
  137. } else {
  138. HideScrollPosition();
  139. Shell::Get()->RemovePreTargetHandler(this);
  140. menu_bubble_controller_ = nullptr;
  141. // Set the click type to left-click. This is the most useful click type
  142. // and users will want this type when they re-enable. If users were to
  143. // re-enable in scroll, or right-click, they would need to use the bubble
  144. // menu to change types.
  145. Shell::Get()->accessibility_controller()->SetAutoclickEventType(
  146. AutoclickEventType::kLeftClick);
  147. enabled_ = enabled;
  148. }
  149. }
  150. CancelAutoclickAction();
  151. }
  152. bool AutoclickController::IsEnabled() const {
  153. return enabled_;
  154. }
  155. void AutoclickController::SetAutoclickDelay(base::TimeDelta delay) {
  156. delay_ = delay;
  157. InitClickTimers();
  158. }
  159. void AutoclickController::SetAutoclickEventType(AutoclickEventType type) {
  160. if (menu_bubble_controller_)
  161. menu_bubble_controller_->SetEventType(type);
  162. if (type == AutoclickEventType::kScroll) {
  163. InitializeScrollLocation();
  164. UpdateScrollPosition();
  165. } else {
  166. over_scroll_button_ = false;
  167. HideScrollPosition();
  168. }
  169. if (event_type_ == type)
  170. return;
  171. CancelAutoclickAction();
  172. event_type_ = type;
  173. }
  174. void AutoclickController::SetMovementThreshold(int movement_threshold) {
  175. movement_threshold_ = movement_threshold;
  176. UpdateRingSize();
  177. }
  178. void AutoclickController::SetMenuPosition(FloatingMenuPosition menu_position) {
  179. menu_position_ = menu_position;
  180. UpdateAutoclickMenuBoundsIfNeeded();
  181. }
  182. void AutoclickController::DoScrollAction(ScrollPadAction action) {
  183. if (action == ScrollPadAction::kScrollClose) {
  184. // Set the scroll_location_ back to the default.
  185. scroll_location_ = gfx::Point(-kDefaultAutoclickMovementThreshold,
  186. -kDefaultAutoclickMovementThreshold);
  187. // Return to left click.
  188. event_type_ = AutoclickEventType::kLeftClick;
  189. Shell::Get()->accessibility_controller()->SetAutoclickEventType(
  190. event_type_);
  191. return;
  192. }
  193. // Otherwise, do a scroll action at the current scroll target point.
  194. float scroll_x = 0.0f;
  195. float scroll_y = 0.0f;
  196. switch (action) {
  197. case ScrollPadAction::kScrollUp:
  198. scroll_y = kScrollDelta;
  199. break;
  200. case ScrollPadAction::kScrollDown:
  201. scroll_y = -kScrollDelta;
  202. break;
  203. case ScrollPadAction::kScrollLeft:
  204. scroll_x = kScrollDelta;
  205. break;
  206. case ScrollPadAction::kScrollRight:
  207. scroll_x = -kScrollDelta;
  208. break;
  209. case ScrollPadAction::kScrollClose:
  210. NOTREACHED();
  211. }
  212. // Generate a scroll event at the current scroll location.
  213. aura::Window* root_window = window_util::GetRootWindowAt(scroll_location_);
  214. gfx::Point location_in_pixels(scroll_location_);
  215. ::wm::ConvertPointFromScreen(root_window, &location_in_pixels);
  216. aura::WindowTreeHost* host = root_window->GetHost();
  217. host->ConvertDIPToPixels(&location_in_pixels);
  218. ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::PointF(location_in_pixels),
  219. gfx::PointF(location_in_pixels), ui::EventTimeForNow(),
  220. mouse_event_flags_, scroll_x, scroll_y,
  221. 0 /* x_offset_ordinal */, 0 /* y_offset_ordinal */,
  222. 2 /* finger_count */);
  223. ui::MouseWheelEvent wheel(scroll);
  224. std::ignore = host->GetEventSink()->OnEventFromSource(&wheel);
  225. }
  226. void AutoclickController::OnEnteredScrollButton() {
  227. if (start_gesture_timer_)
  228. start_gesture_timer_->Stop();
  229. if (autoclick_timer_)
  230. autoclick_timer_->Stop();
  231. autoclick_ring_handler_->StopGesture();
  232. over_scroll_button_ = true;
  233. }
  234. void AutoclickController::OnExitedScrollButton() {
  235. over_scroll_button_ = false;
  236. // Reset the anchor_location_ so that gestures could begin immediately.
  237. anchor_location_ = gfx::Point(-kDefaultAutoclickMovementThreshold,
  238. -kDefaultAutoclickMovementThreshold);
  239. }
  240. void AutoclickController::HandleAutoclickScrollableBoundsFound(
  241. gfx::Rect& bounds_in_screen) {
  242. // The very first time scrollable bounds are found, the default first
  243. // position of the scrollbar to be next to the menu bubble.
  244. if (is_initial_scroll_location_)
  245. return;
  246. menu_bubble_controller_->SetScrollPosition(bounds_in_screen,
  247. scroll_location_);
  248. }
  249. void AutoclickController::UpdateAutoclickMenuBoundsIfNeeded() {
  250. if (menu_bubble_controller_)
  251. menu_bubble_controller_->SetPosition(menu_position_);
  252. }
  253. void AutoclickController::UpdateAutoclickWidgetPosition(
  254. gfx::NativeView native_view,
  255. aura::Window* root_window) {
  256. if (native_view->GetRootWindow() != root_window) {
  257. views::Widget::ReparentNativeView(
  258. native_view,
  259. Shell::GetContainer(root_window, kShellWindowId_OverlayContainer));
  260. }
  261. }
  262. void AutoclickController::DoAutoclickAction() {
  263. // The gesture_anchor_location_ is the position at which the animation is
  264. // anchored, and where the click should occur.
  265. aura::Window* root_window =
  266. window_util::GetRootWindowAt(gesture_anchor_location_);
  267. DCHECK(root_window) << "Root window not found while attempting autoclick.";
  268. // But if the thing that would be acted upon is an autoclick menu button, do a
  269. // fake click instead of whatever other action type we would have done. This
  270. // ensures that no matter the autoclick setting, users can always change to
  271. // another autoclick setting. By using a fake click we avoid closing dialogs
  272. // and menus, allowing autoclick users to interact with those items.
  273. if (!DragInProgress() &&
  274. AutoclickMenuContainsPoint(gesture_anchor_location_)) {
  275. menu_bubble_controller_->ClickOnBubble(gesture_anchor_location_,
  276. mouse_event_flags_);
  277. // Reset UI.
  278. CancelAutoclickAction();
  279. return;
  280. }
  281. // Set the in-progress event type locally so that if the event type is updated
  282. // in the middle of this event being executed it doesn't change execution.
  283. AutoclickEventType in_progress_event_type = event_type_;
  284. RecordUserAction(in_progress_event_type);
  285. if (in_progress_event_type == AutoclickEventType::kScroll) {
  286. // A dwell during a scroll.
  287. // Check if the event is over the scroll bubble controller, and if it is,
  288. // click on the scroll bubble.
  289. if (AutoclickScrollContainsPoint(gesture_anchor_location_)) {
  290. menu_bubble_controller_->ClickOnScrollBubble(gesture_anchor_location_,
  291. mouse_event_flags_);
  292. } else {
  293. scroll_location_ = gesture_anchor_location_;
  294. is_initial_scroll_location_ = false;
  295. UpdateScrollPosition();
  296. Shell::Get()
  297. ->accessibility_controller()
  298. ->RequestAutoclickScrollableBoundsForPoint(scroll_location_);
  299. base::RecordAction(
  300. base::UserMetricsAction("Accessibility.Autoclick.ChangeScrollPoint"));
  301. }
  302. return;
  303. }
  304. gfx::Point location_in_pixels(gesture_anchor_location_);
  305. ::wm::ConvertPointFromScreen(root_window, &location_in_pixels);
  306. aura::WindowTreeHost* host = root_window->GetHost();
  307. host->ConvertDIPToPixels(&location_in_pixels);
  308. bool drag_start =
  309. in_progress_event_type == AutoclickEventType::kDragAndDrop &&
  310. !drag_event_rewriter_->IsEnabled();
  311. bool drag_stop = DragInProgress();
  312. if (in_progress_event_type == AutoclickEventType::kLeftClick ||
  313. in_progress_event_type == AutoclickEventType::kRightClick ||
  314. in_progress_event_type == AutoclickEventType::kDoubleClick ||
  315. drag_start || drag_stop) {
  316. int button = in_progress_event_type == AutoclickEventType::kRightClick
  317. ? ui::EF_RIGHT_MOUSE_BUTTON
  318. : ui::EF_LEFT_MOUSE_BUTTON;
  319. ui::EventDispatchDetails details;
  320. if (!drag_stop) {
  321. // Left click, right click, double click, and beginning of a drag have
  322. // a pressed event next.
  323. ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, location_in_pixels,
  324. location_in_pixels, ui::EventTimeForNow(),
  325. mouse_event_flags_ | button, button);
  326. details = host->GetEventSink()->OnEventFromSource(&press_event);
  327. if (drag_start) {
  328. drag_event_rewriter_->SetEnabled(true);
  329. return;
  330. }
  331. if (details.dispatcher_destroyed) {
  332. OnActionCompleted(in_progress_event_type);
  333. return;
  334. }
  335. }
  336. if (drag_stop)
  337. drag_event_rewriter_->SetEnabled(false);
  338. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, location_in_pixels,
  339. location_in_pixels, ui::EventTimeForNow(),
  340. mouse_event_flags_ | button, button);
  341. details = host->GetEventSink()->OnEventFromSource(&release_event);
  342. // Now a single click, or half the drag & drop, has been completed.
  343. if (in_progress_event_type != AutoclickEventType::kDoubleClick ||
  344. details.dispatcher_destroyed) {
  345. OnActionCompleted(in_progress_event_type);
  346. return;
  347. }
  348. ui::MouseEvent double_press_event(
  349. ui::ET_MOUSE_PRESSED, location_in_pixels, location_in_pixels,
  350. ui::EventTimeForNow(),
  351. mouse_event_flags_ | button | ui::EF_IS_DOUBLE_CLICK, button);
  352. ui::MouseEvent double_release_event(
  353. ui::ET_MOUSE_RELEASED, location_in_pixels, location_in_pixels,
  354. ui::EventTimeForNow(),
  355. mouse_event_flags_ | button | ui::EF_IS_DOUBLE_CLICK, button);
  356. details = host->GetEventSink()->OnEventFromSource(&double_press_event);
  357. if (details.dispatcher_destroyed) {
  358. OnActionCompleted(in_progress_event_type);
  359. return;
  360. }
  361. details = host->GetEventSink()->OnEventFromSource(&double_release_event);
  362. OnActionCompleted(in_progress_event_type);
  363. }
  364. }
  365. void AutoclickController::StartAutoclickGesture() {
  366. if (event_type_ == AutoclickEventType::kNoAction) {
  367. // If we are set to "no action" and the gesture wouldn't occur over
  368. // the autoclick menu, cancel and return early rather than starting the
  369. // gesture.
  370. if (!AutoclickMenuContainsPoint(gesture_anchor_location_)) {
  371. CancelAutoclickAction();
  372. return;
  373. }
  374. // Otherwise, go ahead and start the gesture.
  375. }
  376. // The anchor is always the point in the screen where the timer starts, and is
  377. // used to determine when the cursor has moved far enough to cancel the
  378. // autoclick.
  379. anchor_location_ = gesture_anchor_location_;
  380. autoclick_ring_handler_->StartGesture(
  381. delay_ - CalculateStartGestureDelay(delay_), anchor_location_,
  382. ring_widget_.get());
  383. autoclick_timer_->Reset();
  384. }
  385. void AutoclickController::CancelAutoclickAction() {
  386. if (start_gesture_timer_)
  387. start_gesture_timer_->Stop();
  388. if (autoclick_timer_)
  389. autoclick_timer_->Stop();
  390. autoclick_ring_handler_->StopGesture();
  391. // If we are dragging, complete the drag, so as not to leave the UI in a
  392. // weird state.
  393. if (DragInProgress()) {
  394. DoAutoclickAction();
  395. }
  396. drag_event_rewriter_->SetEnabled(false);
  397. SetTapDownTarget(nullptr);
  398. }
  399. void AutoclickController::OnActionCompleted(
  400. AutoclickEventType completed_event_type) {
  401. // No need to change to left click if the setting is not enabled or the
  402. // event that just executed already was a left click.
  403. if (!revert_to_left_click_ || event_type_ == AutoclickEventType::kLeftClick ||
  404. completed_event_type == AutoclickEventType::kLeftClick)
  405. return;
  406. // Change the preference, but set it locally so we do not reset any state when
  407. // AutoclickController::SetAutoclickEventType is called.
  408. event_type_ = AutoclickEventType::kLeftClick;
  409. Shell::Get()->accessibility_controller()->SetAutoclickEventType(event_type_);
  410. }
  411. void AutoclickController::InitClickTimers() {
  412. CancelAutoclickAction();
  413. base::TimeDelta start_gesture_delay = CalculateStartGestureDelay(delay_);
  414. if (autoclick_timer_ && autoclick_timer_->IsRunning())
  415. autoclick_timer_->Stop();
  416. if (start_gesture_timer_ && start_gesture_timer_->IsRunning())
  417. start_gesture_timer_->Stop();
  418. autoclick_timer_ = std::make_unique<base::RetainingOneShotTimer>(
  419. FROM_HERE, delay_ - start_gesture_delay,
  420. base::BindRepeating(&AutoclickController::DoAutoclickAction,
  421. base::Unretained(this)));
  422. start_gesture_timer_ = std::make_unique<base::RetainingOneShotTimer>(
  423. FROM_HERE, start_gesture_delay,
  424. base::BindRepeating(&AutoclickController::StartAutoclickGesture,
  425. base::Unretained(this)));
  426. }
  427. void AutoclickController::UpdateRingWidget() {
  428. aura::Window* const target =
  429. window_util::GetRootWindowAt(last_mouse_location_);
  430. SetTapDownTarget(target);
  431. aura::Window* const root_window = target->GetRootWindow();
  432. if (ring_widget_) {
  433. UpdateAutoclickWidgetPosition(ring_widget_->GetNativeView(), root_window);
  434. } else {
  435. ring_widget_ = std::make_unique<views::Widget>(
  436. CreateAutoclickOverlayWidgetParams(root_window));
  437. ring_widget_->SetOpacity(1.0f);
  438. }
  439. }
  440. void AutoclickController::UpdateRingSize() {
  441. autoclick_ring_handler_->SetSize(movement_threshold_);
  442. }
  443. void AutoclickController::InitializeScrollLocation() {
  444. // Sets the scroll location to the center of the root window.
  445. scroll_location_ =
  446. Shell::Get()->GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint();
  447. is_initial_scroll_location_ = true;
  448. Shell::Get()
  449. ->accessibility_controller()
  450. ->RequestAutoclickScrollableBoundsForPoint(scroll_location_);
  451. }
  452. void AutoclickController::UpdateScrollPosition() {
  453. if (!enabled_)
  454. return;
  455. aura::Window* const target = window_util::GetRootWindowAt(scroll_location_);
  456. SetTapDownTarget(target);
  457. aura::Window* const root_window = target->GetRootWindow();
  458. if (autoclick_scroll_position_handler_) {
  459. UpdateAutoclickWidgetPosition(
  460. autoclick_scroll_position_handler_->GetNativeView(), root_window);
  461. } else {
  462. autoclick_scroll_position_handler_ =
  463. std::make_unique<AutoclickScrollPositionHandler>(
  464. std::make_unique<views::Widget>(
  465. CreateAutoclickOverlayWidgetParams(root_window)));
  466. }
  467. autoclick_scroll_position_handler_->SetScrollPointCenterInScreen(
  468. scroll_location_);
  469. }
  470. void AutoclickController::HideScrollPosition() {
  471. autoclick_scroll_position_handler_.reset();
  472. // TODO(katie): Clear any Autoclick scroll focus rings here.
  473. }
  474. bool AutoclickController::DragInProgress() const {
  475. return event_type_ == AutoclickEventType::kDragAndDrop &&
  476. drag_event_rewriter_->IsEnabled();
  477. }
  478. bool AutoclickController::AutoclickMenuContainsPoint(
  479. const gfx::Point& point) const {
  480. return menu_bubble_controller_ &&
  481. menu_bubble_controller_->ContainsPointInScreen(point);
  482. }
  483. bool AutoclickController::AutoclickScrollContainsPoint(
  484. const gfx::Point& point) const {
  485. return menu_bubble_controller_ &&
  486. menu_bubble_controller_->ScrollBubbleContainsPointInScreen(point);
  487. }
  488. void AutoclickController::RecordUserAction(
  489. AutoclickEventType event_type) const {
  490. switch (event_type) {
  491. case AutoclickEventType::kLeftClick:
  492. base::RecordAction(
  493. base::UserMetricsAction("Accessibility.Autoclick.LeftClick"));
  494. return;
  495. case AutoclickEventType::kRightClick:
  496. base::RecordAction(
  497. base::UserMetricsAction("Accessibility.Autoclick.RightClick"));
  498. return;
  499. case AutoclickEventType::kDoubleClick:
  500. base::RecordAction(
  501. base::UserMetricsAction("Accessibility.Autoclick.DoubleClick"));
  502. return;
  503. case AutoclickEventType::kDragAndDrop:
  504. // Only log drag-and-drop once per drag-and-drop. It takes two "dwells"
  505. // to complete a full drag-and-drop cycle, which could lead to double
  506. // the events logged.
  507. if (DragInProgress())
  508. return;
  509. base::RecordAction(
  510. base::UserMetricsAction("Accessibility.Autoclick.DragAndDrop"));
  511. return;
  512. case AutoclickEventType::kScroll:
  513. // Scroll users actions will be recorded from AutoclickScrollView.
  514. case AutoclickEventType::kNoAction:
  515. // No action shouldn't have a UserAction, so we return.
  516. return;
  517. }
  518. }
  519. void AutoclickController::OnMouseEvent(ui::MouseEvent* event) {
  520. DCHECK(event->target());
  521. if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED)
  522. return;
  523. last_mouse_location_ = event->target()->GetScreenLocation(*event);
  524. if (over_scroll_button_)
  525. return;
  526. if (!(event->flags() & ui::EF_IS_SYNTHESIZED) &&
  527. (event->type() == ui::ET_MOUSE_MOVED ||
  528. (event->type() == ui::ET_MOUSE_DRAGGED &&
  529. drag_event_rewriter_->IsEnabled()))) {
  530. mouse_event_flags_ = event->flags();
  531. // Update the point even if the animation is not currently being shown.
  532. UpdateRingWidget();
  533. // The distance between the mouse location and the anchor location
  534. // must exceed a certain threshold to initiate a new autoclick countdown.
  535. // This ensures that mouse jitter caused by poor motor control does not
  536. // 1. initiate an unwanted autoclick from rest
  537. // 2. prevent the autoclick from ever occurring when the mouse
  538. // arrives at the target.
  539. gfx::Vector2d delta = last_mouse_location_ - anchor_location_;
  540. if (delta.LengthSquared() >= movement_threshold_ * movement_threshold_) {
  541. anchor_location_ = last_mouse_location_;
  542. gesture_anchor_location_ = last_mouse_location_;
  543. // Stop all the timers, restarting the gesture timer only. This keeps
  544. // the animation from being drawn while the user is still moving quickly.
  545. start_gesture_timer_->Reset();
  546. if (autoclick_timer_) {
  547. autoclick_timer_->Stop();
  548. }
  549. autoclick_ring_handler_->StopGesture();
  550. } else if (start_gesture_timer_->IsRunning()) {
  551. // Keep track of where the gesture will be anchored.
  552. gesture_anchor_location_ = last_mouse_location_;
  553. } else if (autoclick_timer_->IsRunning() && !stabilize_click_position_) {
  554. // If we are not stabilizing the click position, update the gesture
  555. // center with each mouse move event.
  556. gesture_anchor_location_ = last_mouse_location_;
  557. autoclick_ring_handler_->SetGestureCenter(last_mouse_location_,
  558. ring_widget_.get());
  559. }
  560. } else if (event->type() == ui::ET_MOUSE_PRESSED ||
  561. event->type() == ui::ET_MOUSE_RELEASED ||
  562. event->type() == ui::ET_MOUSEWHEEL) {
  563. CancelAutoclickAction();
  564. }
  565. }
  566. void AutoclickController::OnKeyEvent(ui::KeyEvent* event) {
  567. int modifier_mask = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
  568. ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN |
  569. ui::EF_IS_EXTENDED_KEY;
  570. int new_modifiers = event->flags() & modifier_mask;
  571. mouse_event_flags_ = (mouse_event_flags_ & ~modifier_mask) | new_modifiers;
  572. if (!IsModifierKey(event->key_code()))
  573. CancelAutoclickAction();
  574. }
  575. void AutoclickController::OnTouchEvent(ui::TouchEvent* event) {
  576. CancelAutoclickAction();
  577. }
  578. void AutoclickController::OnGestureEvent(ui::GestureEvent* event) {
  579. CancelAutoclickAction();
  580. }
  581. void AutoclickController::OnScrollEvent(ui::ScrollEvent* event) {
  582. // A single tap can create a scroll event, so ignore scroll starts and
  583. // cancels but cancel autoclicks when scrolls actually occur.
  584. if (event->type() == ui::EventType::ET_SCROLL_FLING_START ||
  585. event->type() == ui::EventType::ET_SCROLL_FLING_CANCEL)
  586. return;
  587. CancelAutoclickAction();
  588. }
  589. void AutoclickController::OnWindowDestroying(aura::Window* window) {
  590. DCHECK_EQ(tap_down_target_, window);
  591. CancelAutoclickAction();
  592. }
  593. void AutoclickController::OnCursorVisibilityChanged(bool is_visible) {
  594. if (!menu_bubble_controller_)
  595. return;
  596. // TODO(katie): Check that the display which is fullscreen is the same as the
  597. // one containing the bubble, to determine whether to hide the bubble.
  598. // Currently just checking if the display under the mouse is fullscreen.
  599. aura::Window* window = GetWindowForFullscreenModeInRoot(
  600. window_util::GetRootWindowAt(last_mouse_location_));
  601. bool is_fullscreen = window != nullptr;
  602. // Hide the bubble when the cursor is gone in fullscreen mode.
  603. // Always show it otherwise.
  604. menu_bubble_controller_->SetBubbleVisibility(is_fullscreen ? is_visible
  605. : true);
  606. }
  607. } // namespace ash