Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 1 | # Automatic clicks (for developers) |
| 2 | |
| 3 | Automatic clicks is a Chrome OS feature to automatically generate mouse events |
| 4 | when the cursor dwells in one location. |
| 5 | |
| 6 | |
| 7 | Dwell control supports users with motor impairments. A user who is unable to |
| 8 | use a mouse or unable to click with a mouse, but who is able to control the |
| 9 | cursor position (often using an alternative input method) will need to use |
| 10 | dwell control to perform mouse or trackpad actions, including left-click, |
| 11 | right-click, double-click, click-and-drag and scroll. |
| 12 | |
| 13 | ## Using automatic clicks |
| 14 | |
| 15 | Go to Chrome settings, Accessibility settings, “Manage accessibility Features”, |
| 16 | and in the “mouse and input” section enable “Automatically click when the |
| 17 | cursor stops”. You can adjust timing, radius, stabilization, and whether to |
| 18 | revert to a left-click after another type of action has been taken from the |
| 19 | settings page. |
| 20 | |
| 21 | |
| 22 | A on-screen menu bubble will appear in the corner. Dwell over this menu to |
| 23 | change the action taken, or pause the feature. There is also a button to |
| 24 | re-position the menu to another corner of the screen. |
| 25 | |
| 26 | ## Reporting bugs |
| 27 | |
| 28 | Use bugs.chromium.org, filing bugs under the component UI>Accessibility with |
| 29 | the label “autoclick” (or, use |
| 30 | [this template](https://bugs.chromium.org/p/chromium/issues/entry?summary=Autoclick%20-%20&status=Available&cc=katie%40chromium.org%2C%20qqwangxin%40google.com&labels=Pri-3%2C%20autoclick%2C&components=UI>Accessibility)). |
| 31 | |
| 32 | |
Anatoliy Potapchuk | ae8526c | 2020-04-09 12:52:01 | [diff] [blame] | 33 | Open bugs have the label |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 34 | “[autoclick](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=label%3Aautoclick)”. |
| 35 | |
| 36 | ## Developing |
| 37 | |
| 38 | ### Code location |
| 39 | |
| 40 | Automatic clicks code lives mainly in three places: |
| 41 | |
| 42 | - A controller, event-rewriter, and widgets to draw around click locations, in |
Nick Yamane | dea6989 | 2023-09-20 15:11:39 | [diff] [blame] | 43 | ash/accessibility/autoclick/ |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 44 | |
| 45 | - UI through menu bubbles and their controllers, in |
| 46 | ash/system/accessibility/autoclick* |
| 47 | |
| 48 | - A component extension to provide Accessibility tree information, in |
Katie D | 38d4421b | 2020-06-22 18:50:46 | [diff] [blame] | 49 | chrome/browser/resources/chromeos/accessibility/accessibility_common/ |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 50 | |
| 51 | In addition, there are settings for automatic clicks in |
Josiah Krutz | 07abfcc4 | 2023-08-01 20:47:06 | [diff] [blame] | 52 | chrome/browser/resources/ash/settings/os_a11y_page/cursor_and_touchpad_page.* |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 53 | |
| 54 | ### Tests |
| 55 | |
| 56 | Tests are in ash_unittests and in browser_tests: |
| 57 | |
| 58 | ``` |
| 59 | out/Release/ash_unittests --gtest_filter=”Autoclick*” |
| 60 | out/Release/browser_tests --gtest_filter=”Autoclick*” |
| 61 | ``` |
| 62 | |
| 63 | ### Debugging |
| 64 | |
| 65 | Developers can add log lines to any of the autoclick C++ files and see output |
Katie D | 38d4421b | 2020-06-22 18:50:46 | [diff] [blame] | 66 | in the console. To debug the Accessibility Common extension, the easiest way is |
| 67 | from an external browser. Start Chrome OS on Linux with this command-line flag: |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 68 | |
| 69 | ``` |
| 70 | out/Release/chrome --remote-debugging-port=9222 |
| 71 | ``` |
| 72 | |
| 73 | Now open http://localhost:9222 in a separate instance of the browser, and debug |
Katie D | 38d4421b | 2020-06-22 18:50:46 | [diff] [blame] | 74 | the Accessibility Common extension background page from there. |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 75 | |
| 76 | ## How it works |
| 77 | |
| 78 | AutoclickController is a pre-target EventHandler with very high priority, |
| 79 | which means it can receive and act on mouse events before other parts of |
| 80 | Chrome OS get them. |
| 81 | |
| 82 | |
| 83 | AutoclickController::OnMouseEvent receives mouse events and checks whether |
| 84 | the event is close to the current dwell target (in which case dwell count-down |
| 85 | should continue), or far enough away to clear the target and set a new one. |
| 86 | |
| 87 | |
| 88 | There is a small delay before the user sees the count-down timer ring UI appear |
| 89 | (AutoclickRingHandler) show up, which is controlled by the start_gesture_timer_. |
| 90 | Performing the click is controlled by the autoclick_timer_. |
| 91 | |
| 92 | |
| 93 | When the autoclick_timer_ completes it calls |
| 94 | AutoclickController::DoAutoclickAction. This function first checks if the |
| 95 | target point is over either of the autoclick bubbles, the menu or the scroll |
| 96 | bubble. If it is over a bubble the gesture will not be handled with a synthetic |
| 97 | event, but instead sent directly to that menu. This keeps focus from shifting |
| 98 | and things like dialogs or context menus from closing when the user interacts |
| 99 | with an autoclick bubble. But, if the target was not over the bubble, a |
| 100 | synthetic event is generated as follows: |
| 101 | |
| 102 | ### Left-click, right-click and double-click |
| 103 | |
Avi Drissman | ae99ae28 | 2024-07-22 20:44:28 | [diff] [blame] | 104 | Synthetic mouse events for ui::EventType::kMousePressed and |
| 105 | ui::EventType::kMouseReleased are created with the appropriate mouse button |
| 106 | flags, and sent to the WindowTreeHost under the target point for processing. |
| 107 | For double-click, a second press and release pair are also sent. |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 108 | |
| 109 | ### Click-and-drag |
| 110 | |
Avi Drissman | ae99ae28 | 2024-07-22 20:44:28 | [diff] [blame] | 111 | A synthetic mouse event for ui::EventType::kMousePressed is created at the first |
| 112 | dwell. An AutoclickDragEventRewriter is enabled and begins re-writing all |
| 113 | ui::EventType::kMouseMoved events to ui::EventType::kMouseDragged events to |
| 114 | create the illusion of a drag. This occurs in |
| 115 | AutoclickDragEventRewriter::RewriteEvent. |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 116 | |
Avi Drissman | ae99ae28 | 2024-07-22 20:44:28 | [diff] [blame] | 117 | A final synthetic mouse event for ui::EventType::kMouseReleased is created at |
| 118 | the second dwell, and the AutoclickDragEventRewriter is disabled. |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 119 | |
| 120 | ### Scroll |
| 121 | |
| 122 | On a dwell during scroll, the scroll target point is changed. No scroll events |
| 123 | are generated from AutoclickController until the user hovers over the scroll |
| 124 | pad buttons (AutoclickScrollButton class). The AutoclickScrollButtons track |
| 125 | whether they are currently hovered, and while hovered they fire a repeating |
| 126 | timer to ask the AutoclickController to do a scroll event. |
| 127 | |
| 128 | #### Scroll location |
| 129 | |
| 130 | By default, the scroll position is at the center of the active screen. Users |
| 131 | may dwell anywhere on the screen to change the scroll location. |
| 132 | |
| 133 | |
| 134 | When the scroll location is changed, the AutoclickController will request the |
Katie D | 38d4421b | 2020-06-22 18:50:46 | [diff] [blame] | 135 | bounds of the nearest scrollable view from the Accessibility Common extension |
| 136 | via the AccessibilityPrivate API. The Accessibility Common component extension |
| 137 | has access to accessibility tree information, and using a HitTest is able to |
| 138 | find the view at the scroll location, then walks up the tree to find the first |
| 139 | view which can scroll, or stops at the nearest window or dialog bounds. This |
| 140 | logic takes place in autoclick.js, onAutomationHitTestResult_. When the |
| 141 | scrolling location is found, the bounds of the scrollable area are highlighted |
| 142 | with a focus ring. In addition, the bounds are sent back through the |
| 143 | AccessibilityPrivate API, routed to the AutoclickController, which passes it via |
| 144 | the AutoclickMenuBubbleController to the AutoclickScrollBubbleController, which |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 145 | does layout accordingly. |
| 146 | |
| 147 | ### Bubble Menus: interface and positioning |
| 148 | |
| 149 | The AutoclickController owns the AutoclickMenuBubbleController, which controls |
| 150 | the widget and AutoclickMenuView view for the autoclick bubble menu. |
| 151 | AutoclickMenuView inherits from TrayBubbleView for styling, and contains all |
| 152 | the buttons to change click types, pause, and update the menu position. |
| 153 | |
| 154 | |
| 155 | Similarly, AutoclickMenuBubbleController also owns |
| 156 | AutoclickScrollBubbleController so that it can pass on messages about |
| 157 | positioning and activation from AutoclickController. |
| 158 | AutoclickScrollBubbleController owns the widgets and AutoclickScrollView for |
| 159 | the autoclick scroll bubble. AutoclickScrollView inherits from TrayBubbleView |
| 160 | for styling, and contains the scroll pad buttons, including all the logic to |
| 161 | draw the custom shape buttons for left/right/up/down scrolling. |
| 162 | |
| 163 | #### Menu positioning |
| 164 | |
| 165 | The autoclick bubble menu can be positioned in the four corners of the screen |
| 166 | and defaults to the same location as the volume widget (which depends on |
| 167 | LTR/RTL language). AutoclickMenuBubbleController takes a preferred |
Anatoliy Potapchuk | ae8526c | 2020-04-09 12:52:01 | [diff] [blame] | 168 | FloatingMenuPosition enum and uses that to determine the best position for |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 169 | the menu in AutoclickMenuBubbleController::SetPosition. This function finds |
Anatoliy Potapchuk | ae8526c | 2020-04-09 12:52:01 | [diff] [blame] | 170 | the ideal corner of the screen, then uses CollisionDetectionUtils (also used |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 171 | by Picture-in-Picture) to refine the position to avoid collisions with system |
| 172 | UI. |
| 173 | |
| 174 | #### Scroll bubble positioning |
| 175 | |
| 176 | The scroll bubble starts out anchored to the automatic clicks bubble menu, but |
| 177 | if the user selects a new scroll point it will move. When a scroll point is |
Katie D | 38d4421b | 2020-06-22 18:50:46 | [diff] [blame] | 178 | selected, if the scrollable region found by the Accessibility Common extension |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 179 | is large enough, the scroll bubble will be anchored near the scroll point |
| 180 | itself, similarly to the way the context menu is anchored near the cursor on |
Anatoliy Potapchuk | ae8526c | 2020-04-09 12:52:01 | [diff] [blame] | 181 | a right click. When the scrollable region is small, the scroll bubble will be |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 182 | anchored to the closest side of the scrollable region to the scroll point, as |
| 183 | long as there is space for it on that side. |
| 184 | |
| 185 | #### Clicking on the bubble menus |
| 186 | |
Nick Yamane | dea6989 | 2023-09-20 15:11:39 | [diff] [blame] | 187 | The AutoclickController cannot generate synthetic click events over the |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 188 | bubbles, because that would cause context and focus changes. For example, if |
| 189 | the user has a drop-down menu open, clicking the autoclick menu bubble will |
| 190 | cause the drop-down to close. Instead, the AutoclickController must check to |
Anatoliy Potapchuk | ae8526c | 2020-04-09 12:52:01 | [diff] [blame] | 191 | see if an event will take place over a bubble menu, and if so, request that |
| 192 | AutoclickMenuBubbleController forward the event to the bubble via |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 193 | AutoclickMenuBubbleController::ClickOnBubble. This generates a synthetic mouse |
Anatoliy Potapchuk | ae8526c | 2020-04-09 12:52:01 | [diff] [blame] | 194 | event which does not propagate through the system, so there is no focus or |
Katie D | b05e9c5 | 2019-07-23 01:10:14 | [diff] [blame] | 195 | context change, allowing users to continue to interact with whatever was on |
| 196 | screen. |
| 197 | |
| 198 | ## For Googlers |
| 199 | |
| 200 | Googlers could check out the automatic clicks feature design docs for more |
| 201 | details on design as well as autoclick’s UMA. |
| 202 | |
| 203 | - Overall product design, [go/chromeos-dwell-design](go/chromeos-dwell-design) |
| 204 | |
| 205 | - On-screen menu design, |
| 206 | [go/chromeos-dwell-menu-design](go/chromeos-dwell-menu-design) |
| 207 | |
| 208 | - Scrolling design, |
| 209 | [go/chromeos-dwell-scroll-design](go/chromeos-dwell-scroll-design) |
| 210 | |
| 211 | - UX mocks, [go/cros-dwell-ux](go/cros-dwell-ux) |