quill icon indicating copy to clipboard operation
quill copied to clipboard

[Feature request] Ability to set selection based on pixel offsets

Open christianhg opened this issue 7 years ago • 17 comments

It is currently possible to getBounds in an editor as well as setSelection based on an index and a length.

I'm currently working on building a Google Docs-like application where each paragraph in the document is made up of it's own Quill RTE. Hitting ENTER in a paragraph splits it into two paragraphs.

Naturally I want to implement keyboard navigation between the paragraphs, e.g. pressing DOWN on the last line of a paragraph will navigate to the same x position on the first line of the paragraph below.

The implementation of this kind of functionality has to be based on pixel offsets. Imagine the cursor is at index 2 of this paragraph:

mm|mmmmmmmm\n

And I press DOWN to navigate to this paragraph:

iiiiiiiiii\n

By using the currently available setSelection it is possible to position the cursor in the same index as before:

ii|iiiiiiii\n

But this would not be the correct position to put the cursor. Depending on letter spacing, letter width and so on, it is not possible to set the desired selection using just an index. To do this correctly it is needed to know the pixel offset of the cursor in the mmm paragraph and set the selection in the same pixel offset in the iii paragraph. Possibly this API could be called setBounds.

keyboard-navigation

Is this a feature anybody has though of before? What do you think about it?

Another use case for this API is drag and drop of image into the RTE.

christianhg avatar Apr 20 '17 09:04 christianhg

Just a thought. Maybe it's possible to get the HTML element from the pixel position with Document.elementFromPoint() and than get the index from the element?

benbro avatar Apr 20 '17 17:04 benbro

Thanks for your input @benbro. I think, I see where you are headed, but I'm not entirely sure what HTML element you are referring to. Can you elaborate some more on your idea?

christianhg avatar Apr 20 '17 19:04 christianhg

I'm not familiar with this part of the code but this is probably a good starting point: https://github.com/quilljs/quill/blob/develop/core/selection.js#L165

benbro avatar Apr 20 '17 20:04 benbro

After getting the element from elementFromPoint, how would you get the index? I can't think of a better way than guess and check binary search with getBounds()

jhchen avatar Apr 22 '17 06:04 jhchen

I'm still not sure what element we are referring to. Are you in the know, @jhchen?

christianhg avatar Apr 22 '17 07:04 christianhg

The element returned by elementFromPoint...

jhchen avatar Apr 22 '17 22:04 jhchen

Obviously I know that Document.elementFromPoint() returns an element. Let's get this discussion back on track shall we :)

Because of getBounds(), I know the desired x and y coordinates I want to use to set the selection in the editor. Unfortunately there is no current API that allows me to set a selection based on coordinates. This is the problem, I am are trying to solve.

Let's say I got the following "view model" in my editor:

<p>iiiiiiiiii</p>

@benbro suggests the use Document.elementFromPoint() and then somehow get the index from the returned element. I'm intrigued, but I don't know what element you are hoping to get. The <p> element? How does getting the <p> element help setting the correct x coordinate?

christianhg avatar Apr 23 '17 08:04 christianhg

There's no need to get defensive. The suggestion if using elementFromPoint() is relevant to the discussion as is the element returned by elementFromPoint().

The API proposed here wants to set a selection based on X, Y pixel coordinates. The current Quill selection API allows setting the selection given an index and length, so we need to convert from X, Y coordinates -> index + length. If we use elementFromPoint we can go from X, Y coordinates -> DOM element and Parchment allows DOM element -> Blot using find(). A Blot is aware of its position in the Quill document so now we can use blot.offset() and blot.length() to get index, length of the element returned by elementFromPoint.

Now this does not get the granularity we want which is the final unknown step. As I mentioned the best way I can think of to solve this last step is guess and check binary search with getBounds(), however this is probably unacceptably inefficient so I'm just throwing it out there as a straw man.

jhchen avatar Apr 23 '17 20:04 jhchen

A naive implementation without binary search: http://codepen.io/anon/pen/dWXBwd

benbro avatar Apr 23 '17 20:04 benbro

I just finished writing a thing that takes the x, y from a drag/drop event and maps it to a Quill offset. It does what Jason suggested above, and handles a few fun edge cases. Should be easily adaptable to others' use cases: https://github.com/danfuzz/bayou/blob/master/local-modules/quill-util/QuillUtil.js

meantime avatar May 12 '17 17:05 meantime

There is Document.caretPositionFromPoint() so you can get/set caret position based on pixel offsets. I use it when I need to set caret to begging of paragraph's last line.

DmitrySkripkin avatar Oct 18 '17 09:10 DmitrySkripkin

Document.caretPositionFromPoint() is not a finalized api and isn't available on all of the platforms we have to support.

meantime avatar Oct 18 '17 18:10 meantime

There is caretRangeFromPoint which most of the browsers implemented but looks like caretPositionFromPoint will likely be the blessed standard and other browsers will switch to this. I would be okay with adding this if the implementation checked for which to use and did the right thing in both cases. Not sure if IE11 supports either but if not so this API would have to be experimental until it also works for IE11. I do not have a use case for this so I will not likely get to implementing it.

jhchen avatar Oct 23 '17 05:10 jhchen

So far I've had zero problems with my own implementation, and it provides more info than just a caret position, and handles cases like going past the end of content on a line, etc. https://github.com/danfuzz/bayou/blob/master/local-modules/quill-util/QuillUtil.js#L54

meantime avatar Oct 23 '17 15:10 meantime

@meantime is it ready for PR? Do you need any help with tests?

DmitrySkripkin avatar Oct 23 '17 17:10 DmitrySkripkin

It wasn't something I was considering for inclusion in Quill. But not opposed to it either. It did find a bug just moments ago though. If pixel position you're querying is in the leading between lines of text then it won't match as being inside any of the character bounds and it reports a position past the end of the document. Need to think about what to do about that.

meantime avatar Oct 23 '17 17:10 meantime

Has anyone developed this further? @meantime's code is no longer available.

richardgrimmett avatar Sep 12 '22 19:09 richardgrimmett