editor: hide mouse cursor while typing - #332287
editor: hide mouse cursor while typing#332287Aiday Marlen Kyzy (aiday-mar) wants to merge 5 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in editor setting that hides the mouse pointer during keyboard input and restores it on pointer movement, disablement, or disposal.
Changes:
- Registers and exposes
editor.hideMouseCursorOnTyping. - Implements pointer visibility handling and styling.
- Adds browser coverage for typing, movement, and disabling.
Show a summary per file
| File | Description |
|---|---|
src/vs/monaco.d.ts |
Exposes the Monaco API option. |
src/vs/editor/test/browser/widget/codeEditorWidget.test.ts |
Tests cursor visibility transitions. |
src/vs/editor/common/standalone/standaloneEnums.ts |
Adds the standalone option identifier. |
src/vs/editor/common/config/editorOptions.ts |
Defines and registers the setting. |
src/vs/editor/browser/widget/codeEditor/editor.css |
Applies hidden-pointer styling. |
src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts |
Manages cursor visibility from editor events. |
Review details
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 5/6 changed files
- Comments generated: 1
- Review effort level: Balanced
`pointerleave` was registered with `capture: true` alongside the other reveal events, so it also fired for descendants. Browsers dispatch boundary events when the element under a resting pointer is re-rendered, which is exactly what typing does to the lines below the pointer. The cursor was therefore revealed on the first keystroke whenever the pointer happened to rest over the text being edited - the main scenario in microsoft#29351. Give `pointerleave` its own non-capturing listener on the editor container so only leaving the editor itself reveals the cursor. This mirrors the movement guard already applied to `pointermove` for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Manual test checklistA checklist for verifying Terminology below: hidden = the OS mouse pointer is invisible over the editor; revealed = it is visible again. 1. Setting registration and defaults
2. Regression check — default offWith the setting unset/false, confirm nothing changed:
3. Basic hide behavior (setting on)
4. Actions that must NOT hide the pointerWith the pointer resting over the editor, each of these should leave it visible:
5. Reveal triggersEach: type to hide, then perform the action, and confirm the pointer comes back.
6. The "phantom pointer event" case (core of this PR)This is the behavior the
7. IME and compositionTest with at least one of: Japanese, Chinese Pinyin, Korean.
8. Focus and window lifecycle
9. CSS scope — where the pointer is and isn't hidden
10. Diff editor (the
|
Adds an opt-in
editor.hideMouseCursorOnTypingsetting (defaultfalse) that hides the mousepointer after keyboard text input in the editor and reveals it again on the next pointer or focus
activity.
Hiding is driven from the semantic text input path (
_type), not from raw key events, sonavigation keys, modifiers, paste, and programmatic edits leave the pointer alone.
Revealing happens on pointer move (guarded so that a re-render under a resting pointer does not
count as movement), pointer leave of the editor container, pointer down, wheel, context menu, editor
blur, and owner-window blur. The state is also cleared when the setting is turned off, when the model
is replaced, and when the editor is disposed.
IME composition deliberately keeps the pointer visible. Composition candidate windows are
positioned relative to the pointer on several platforms, and hiding it mid-composition makes the
candidate window hard to track, so
_startCompositionreveals the pointer and_compositionTypekeeps it revealed for the duration of the composition.
Scope is the editor only. The integrated terminal and the rest of the workbench are unaffected — see
the follow-ups below.
The Monaco editor API declarations (
monaco.d.ts,standaloneEnums.ts) are regenerated, and thediff editor gets a targeted CSS override because its pre-existing
!importantresize cursors wouldotherwise beat the
cursor: nonerule.Fixes #29351
Follow-ups
Deliberately out of scope for this PR:
CodeEditorWidget, so it needs a separate hook; the setting description says so explicitly.overflow container live outside the editor's DOM subtree, so the
cursor: noneclass does notreach them. Handling that means either hiding at a shared ancestor or propagating the state, both
of which affect non-editor UI.
kHideCursorWhileTyping. Chromium can do this natively at the compositor level.Once Electron ships a version exposing it, this implementation could delegate instead of managing
the class in JS.
Testing
node build/next/index.ts transpilenpx tsc --noEmit -p src/tsconfig.jsonnpm run eslint -- src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts src/vs/editor/common/config/editorOptions.ts src/vs/editor/test/browser/widget/codeEditorWidget.test.tsnpm run stylelint -- src/vs/editor/browser/widget/codeEditor/editor.css src/vs/editor/browser/widget/diffEditor/style.cssnpm run test-browser -- --browser chromium --run src/vs/editor/test/browser/widget/codeEditorWidget.test.tsThe browser tests cover: default-off, hiding on text input, key events and paste not hiding, each
reveal source, the no-movement and descendant-
pointerleaveguards, IME (including cancelled andinterrupted compositions), config change, model replacement, disposal, and the computed
cursor: noneon the editor and its descendants.