Re: [editing] What browser internals can be exposed to help us move the caret in (and against) the block direction? (#56)

I've read the whole thread today and I'd like to wrap it up a little bit as it was quite confusing due to lacking use cases and various topics that were mentioned.

## 1. Showing a box next to the caret.

* Examples:
  * Tooltip / floating toolbar – none that I know shows it next to a collapsed selection, but iOS, Medium or [AlloyEditor](http://alloyeditor.com/) do this for a non-collapsed selection. For usability reasons one may want to show the tooltip also for a collapsed selection, but perhaps none of the implementations do it right now because this is problematic.
  * Suggestion box for autocompletion feature – [At.js](http://ichord.github.io/At.js/).
* Problems:
  * Range (or a DOM position) does not include all necessary information about the caret position ([the end of line problem](https://github.com/w3c/editing/issues/56#issuecomment-107811366)),
  * [Split caret](https://github.com/w3c/editing/issues/56#issuecomment-131403158) (how the editor may understand which is "more important").
  * Vertical text (how the editor will understand that e.g. a suggestion box needs to be displayed on the right hand side instead of above the caret).
* Possible solutions:
  * [`Range.getClientRects()`](range.https://drafts.csswg.org/cssom-view/#dom-range-getclientrects) – does not solve the end of line problem as a range does not hold such information, hence while being useful, it's not a solution to this use case.
  * Other methods described in https://drafts.csswg.org/cssom-view – they are all oriented around range <-> point <-> position translations, so, again, despite being useful, none of the seem to be satisfactory.
  * `Selection.getCaretRects()` (such method would know about all the internals of the selection)
    * How would it work for a non-collapsed selection? IMO it should either return `null`/throw an error or be based on a selection direction (forward selection has a virtual caret on the right side – some editors even display it there). The latter seems most useful, but then what with directionless selections and multi-range selections?
    * What about the split carets? IMO, the editor is usually interested in the position of the next character insertion, so this rect could be marked (or be always available under index `0` of DOMRectList).
    * What about vertical text? @rniwa [mentioned](https://github.com/w3c/editing/issues/56#issuecomment-131400246) that depending on a language the "above" means actually "top", "left" or "right". Could exposing something like `Selection.caretOrientation` (top, left, right(, bottom)) solve the problem? Or perhaps such information can be represented as a range property/method?
  * `Selection.getCaretRect()` (like `selection.getCaretRects()` but returns information only for the "most important caret").
    * If we can spec "the most important caret" (based on next character insertion and direction), perhaps having `selection.getCaretRect()` instead of `selection.getCaretRects()` would be justifiable and simple.

## 2. Scrolling the page so the caret is visible.

* Examples:
  * Custom implementation of any algorithm which changes the selection such as pasting.
* Possible solutions:
  * As in use case 1.
  * `range.getClientRects()` could be enough unless this use case is affected by one of the problems from use case 1.
  * Additional argument for selection methods (`addRange()`, `extend()`) which will tell the browser whether to scroll. This would be useful, especially that IIRC some engines do scroll automatically, so ability to prevent that would be nice. I'm not sure though about bloating the API with such an argument.

## 3. Computing the next caret position the user expresses the intention to move one line up.

There weren't any super clear cases shown, so I haven't been sure myself whether the following workflow is sufficient or not:

1. Listen to `beforeselectionchange`. The browser should propose a new selection based on the user action.
2. If you don't like the proposed selection (e.g. it violates rules that you want to implement in your editor), then prevent the event and/or simply override that proposed selection using the existing APIs.

What I can see is that:

* Yes, `beforeselectionchange` which would expose what the browser is planning to do with the selection would be very useful.
* If you're building an editor without `cE=events` (so without the help of `beforeselectionchange`), then you should know yourself what to do with the selection.

However, I can think of a use cases when even with `cE=events` the `beforeselectionchange` event will not be enough. The case I've been dealing the most is caret movements (and selection in general) around non-editable blocks with editable elements inside (`^` is a selection):

```
<p>ABCDEF.</p>
<div cE=false>
    <p cE=events>
        GHIJKL.
    </p>
</div>
<p>MNO^PQR.</p>
```

Let's say that in such case browser tends to place caret inside GHIJKL when you press the UP key.

If the rules implemented by your editor say that this should select the `<div>`, then `beforeselectionchange` is enough for you – check the proposed selection and  if you didn't like it select the `<div>`.

If, however, you want the selection to jump over the `<div>` straight to ABCDEF, then you need additional API. Please also note that `Shift` may be pressed what extends this case to non-collapsed selections.

This makes a pretty clear use case for me, although IMO the whole case is not critical, as the `beforeselectionchange` enables other acceptable (from UX POV) solutions.

The additional API, as @rniwa suggested, could work on a clone of the real selection, what would solve the problem with internal X position:

```
editable.addEventListener( 'beforeselectionchange', function( evt ) {
    // If you want the Shift+Arrow behaviour.
    evt.selection.extend( DIRECTION_UP/DOWN/LEFT/RIGHT );

    // If you want the Arrow key behaviour.
    evt.selection.move( DIRECTION_UP/DOWN/LEFT/RIGHT );

    // Optionally, we could also emulate the Alt, Ctrl modifiers.
    evt.selection.move( DIRECTION_UP/DOWN/LEFT/RIGHT, JUMP_WORD/LINE );

    // If you want to probe to get out of the <div cE=false> (see the HTML example above).
    var clone = evt.selection.clone();

    while ( nonEditableDiv.contains( clone.anchorNode ) ) {
        clone.move( DIRECTION_UP );
    }
    evt.selection.move( clone ); // or clone.commit() for simplicity.
} );
```

I think that an API that emulates the arrow keys behaviour could be easiest to implement and spec as this is what browsers already handle on key presses. Also, it does not expose any browser internal states (like the X position). For instance, in the "probing" scenario, the X position could be kept for the entire loop, so the final selection would be "ABC^DEF".

## 4. Create a multi user editor and show the current position of the carets of the other editor participants (correctly).

I agree with @rniwa – this should be done on DOM position and `range.getClientRects()`.

## 5. Save the last position of the caret when closing a document and restore it (correctly) when opening the document again.

The same as 4.

---
Reply to this email directly or view it on GitHub:
https://github.com/w3c/editing/issues/56#issuecomment-131584200

Received on Sunday, 16 August 2015 17:18:27 UTC