Set the cursor to the click location when editing a text field.

Review Request #13207 — Created Aug. 13, 2023 and submitted

Information

Review Board
release-6.x

Reviewers

Historically, when clicking a text field on a review request to begin
editing, the cursor position would be set to wherever the browser
chooses to set it. That meant if you wanted to edit a specific part of
the text, you had to click twice.

We now preserve the client X/Y coordinates of a mouse click in
InlineEditorView and pass this along to the beginEdit callback. The
subclass for connecting a TextEditorView now passes this along to set
the cursor position.

For CodeMirror, this is fairly straight-forward. We can ask CodeMirror
to convert this into a line number/character offset, and then set that.

For <textarea>s, a different approach is needed.

Browsers sort of have some functionality to help with this, but it's not
perfect. You can set a selection point, which is a single integer
covering a range between 0..length of the text. However, there are two
functions that can now convert a client X and Y coordinate to a
selection point:

  • Document.caretPositionFromPoint: Standard but only on Firefox
  • Document.caretRangeFromPoint: Non-standard but on Chrome, Safari,
    and IE

These are both error-prone in their own ways when used on a <textarea>
(all documented in the code), so to do this correctly, a proxy element
for a <pre> must be created, carefully styled, and then used to get
the caret position.

By implementing these approaches, we now get a very smooth, natural
opening experience, where the user can begin typing exactly where they
click.

Tested on Firefox, Chrome, and Safari.

Tested with some complex documents, with bullet points, wrapping, and
other formatting. In complex documents, as content shifts, the cursor
position may not be on the exact character the user wanted to click,
but it'll always be on the X/Y coordinate.

Summary ID
Set the cursor to the click location when editing a text field.
Historically, when clicking a text field on a review request to begin editing, the cursor position would be set to wherever the browser chooses to set it. That meant if you wanted to edit a specific part of the text, you had to click twice. We now preserve the client X/Y coordinates of a mouse click in `InlineEditorView` and pass this along to the `beginEdit` callback. The subclass for connecting a `TextEditorView` now passes this along to set the cursor position. For CodeMirror, this is fairly straight-forward. We can ask CodeMirror to convert this into a line number/character offset, and then set that. For `<textarea>`s, a different approach is needed. Browsers sort of have some functionality to help with this, but it's not perfect. You can set a selection point, which is a single integer covering a range between 0..length of the text. However, there are two functions that can now convert a client X and Y coordinate to a selection point: * `Document.caretPositionFromPoint`: Standard but only on Firefox * `Document.caretRangeFromPoint`: Non-standard but on Chrome, Safari, and IE These are both error-prone in their own ways when used on a `<textarea>` (all documented in the code), so to do this correctly, a proxy element for a `<pre>` must be created, carefully styled, and then used to get the caret position. By implementing these approaches, we now get a very smooth, natural opening experience, where the user can begin typing exactly where they click.
51b2301b23234dc7be71d823f974fc57ce8b6efe
chipx86
david
  1. Ship It!
  2. 
      
maubin
  1. Ship It!
  2. 
      
chipx86
Review request changed
Status:
Completed
Change Summary:
Pushed to release-6.x (a40ddc4)