Draggable textMark

I’m trying to allow to drag/drop a single textMark. But it looks like I miss something.

When I select a textMark and part of the text coming after or before the textMark I can drag them together and it works fine - editor allows me to drag the text and I rebuild its content after ‘changes’ event.

The issue is I’m unable to drag a textMark without selecting nearby text.

Here is a codepen with an example: https://codepen.io/vovayatsyuk/pen/PoGpoVN

(Widgets are the text marks. I’ve modified “configureMouse” config to select them on click.)

The editor will handle dragging of its selection, but not of widgets. You’ll have to add your own logic for that.

I see. Thank you. :+1:

p.s. Just curious. Can you point me to why the “onDragStart” doesn’t invoke when I’m dragging textMark? As I see from the code “dragstart” event listener is added to the “cm.display.scroller” element. Textmarks are inside of it, but why it’s not called for textMark? Can’t see the reason and I’m really curious to see it :slight_smile:

I suspect the mousedown is being preventDefault-ed by the library, which prevents browser native dragging from happening at all. The handleMouseEvents option to markText can be used to turn off built-in event handling for your widget.

I’ve edited this post to remove outdated and incorrect info

Just found a bit in “leftButtonDown” method:

var sel = cm.doc.sel, contained;
if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
    repeat == "single" && (contained = sel.contains(pos)) > -1 &&
    (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && // <-- this line
    (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
  { leftButtonStartDrag(cm, event, pos, behavior); }
else
  { leftButtonSelect(cm, event, pos, behavior); }

This line

(contained = sel.contains(pos))

returns selected textmark range, but the last check always returns false because it returns 0:

cmp(contained.to(), pos) > 0

So the reason why it’s not draggable is that the pos is outside of a textmark selection. (pos.ch is equal to contained.to().ch)

When the regular text is selected pos.ch is always less then contained.to().ch

I’ve made it work :laughing:

Since the reason that it’s not draggable is the detected cursor position, I’ve checked how it works and found xRel feature.

The logic for “collapsed” elements is working for textMarks:

  var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
  if (!collapsed) { return found }
  var rangeEnd = collapsed.find(1);
  if (rangeEnd.line == lineN) { return rangeEnd }

rangeEnd is returned and xRel is not added the result in this case. I’ve added the following line after var rangeEnd = ..:

rangeEnd.xRel = -1;

It works now!

I’ve updated codepen to show draggable textMark when the selection includes nearby chars (at line 2): https://codepen.io/vovayatsyuk/pen/PoGpoVN

Hello again.

How do you think, will it be a good patch to return xRel when clicking on a previously selected textMark?

The pseudo logic I want to implement in position_measurement.js is the following:

if (rangeEnd is found) {
    if (selection is made from right to left) { // eg. anchor > head
        rangeEnd.xRel = 1;
    } else {
        rangeEnd.xRel = -1;
    }
}

This makes textMarks draggable and droppable like any other text inside codemirror.

I don’t think you’ll be able to get CodeMirror’s own drag-drop handling to do what you want here. You’ll just have to register custom event handlers that override it.