Drag without selection

is it possible, when dragging objects, not to affect selection? I’ve tried a few different methods, but writing my own DraggingTool for this seems like overkill.

If that is the way, is there perhaps a more bulletproof example than my hacked together code?

Are you asking about how to get the DraggingTool to work without there being any selected Parts? How should the tool know which Parts should be dragged?

I want to be able to decouple the DraggingTool from selection, yes.

In my application, the selected nodes of the graph drive an editing interface in another pane. I want to be able to select one or more nodes, but while those nodes are selected, be able to reposition a single node via a drag without affecting the editing interface or the selection adornment.

any thoughts?

OK, it wasn’t clear to me when, if ever, you wanted the regular DraggingTool to operate. I’ll assume you want it to run normally when there are no Parts selected, but that this custom single-node no-select non-copying dragging tool is run if there are any selected Parts.

  // A custom Tool for moving a Node without changing the selection.
  // This tool can only start if there are no selected Parts.

  /**
  * @constructor
  * @extends Tool
  * @class
  */
  function SinglePartNoSelectDraggingTool() {
    go.Tool.call(this);
    this.name = "SinglePartDragging";

    /** @type {Part} */
    this.currentPart = null;
    /** @type {Point} */
    this._offset = new go.Point();  // of the mouse relative to the position
    /** @type {Point} */
    this._originalPosition = new go.Point();
  }
  go.Diagram.inherit(SinglePartNoSelectDraggingTool, go.Tool);

  /**
  * This tool can only start if the mouse has moved enough so that it is not a click,
  * and if the mouse down point is on a Part.
  * @this {SinglePartNoSelectDraggingTool}
  * @return {boolean}
  */
  SinglePartNoSelectDraggingTool.prototype.canStart = function() {
    if (!go.Tool.prototype.canStart.call(this)) return false;
    var diagram = this.diagram;
    if (diagram === null) return false;

    if (diagram.selection.count === 0) return false;
    // require left button & that it has moved far enough away from the mouse down point, so it isn't a click
    var e = diagram.lastInput;
    if (!e.left) return false;
    if (!this.isBeyondDragSize()) return false;

    var part = diagram.findPartAt(e.documentPoint, false);
    return part !== null && part.canMove();
  }

  /**
  * Start a transaction, remember the current Part and its original position.
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.doActivate = function() {
    this.startTransaction(this.name);
    this.currentPart = this.diagram.findPartAt(this.diagram.lastInput.documentPoint, false);
    if (this.currentPart !== null) {
      this._originalPosition = this.currentPart.position.copy();
      // compute the offset of the mouse-down point relative to the currentPart's position
      this._offset = this.diagram.firstInput.documentPoint.copy().subtract(this._originalPosition);
    }
    go.Tool.prototype.doActivate.call(this);
  }

  /**
  * Stop any ongoing transaction.
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.doDeactivate = function() {
    go.Tool.prototype.doDeactivate.call(this);
    this.stopTransaction();
  }

  /**
  * Clear any reference to the currentPart.
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.doStop = function() {
    this.currentPart = null;
    go.Tool.prototype.doStop.call(this);
  }

  /**
  * Restore the currentPart's original position.
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.doCancel = function() {
    if (this.currentPart !== null) {
      this.currentPart.position = this._originalPosition;
    }
    go.Tool.prototype.doCancel.call(this);
  }

  /**
  * During the drag, call updatePosition to move the currentPart
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.doMouseMove = function() {
    if (!this.isActive) return;
    this.updatePosition();
  }

  /**
  * At the end of the drag, update the position of the currentPart and finish the tool,
  * completing a transaction.
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.doMouseUp = function() {
    if (!this.isActive) return;
    this.updatePosition();
    this.transactionResult = this.name;
    this.stopTool();
  }

  /**
  * @this {SinglePartNoSelectDraggingTool}
  */
  SinglePartNoSelectDraggingTool.prototype.updatePosition = function() {
    if (this.currentPart === null) return;
    var last = this.diagram.lastInput.documentPoint;
    this.currentPart.position = new go.Point(last.x - this._offset.x, last.y - this._offset.y);
  }

Install with:

    myDiagram.toolManager.mouseMoveTools.insertAt(0, new SinglePartNoSelectDraggingTool());

i want it to run when the part being dragged is not part of selection, and the regular tool to run otherwise. i’ll see if i can make the doActivate logic work for my use case.

Thanks so much for this code!

To achieve that behavior, modify the canStart method.

Once again, thanks so much for the example. This does almost all of what I want, except it doesn’t give me hooks for doDragOver and doDropOnto. It also doesn’t do the auto scroll at document edges. I think maybe i just need to inherit from DraggingTool instead of tool? Are there other things I need to worry about if i do that?

Add the functionality that you want to the overrides of doMouseMove and doMouseUp.

I can try that. I edited the my previous post, but you replied quite quickly, so maybe you didn’t see it. I was hoping to get some more of the capabilites of the regular dragging tool in this one (like the auto scrolling at document edges)

If you inherit from DraggingTool, then it will work on the Diagram.selection, which you explicitly did not want.

For autoscrolling, try this in your doMouseMove override:

  if (diagram.allowHorizontalScroll || diagram.allowVerticalScroll) {
    diagram.doAutoScroll(diagram.lastInput.viewPoint);
  }

gotcha. ok, thanks!

I’ve got this all mostly working nicely now, it was a breeze with your help!

I do have a small issue with doAutoScroll, though. My canvas is in DocumentScroll mode.

Calling doAutoScroll as you’ve suggested both extends and shrinks the bounds of a canvas on a move, which is not what the normal drag tool does. The normal drag tool seems to just extend and won’t shrink it. I’d like to mimic that behavior if possible.

I looked for documentation on doAutoScroll but it seems like it might be undocumented?

What version are you using? We changed autoScroll behavior in 1.8.32.

we are on 1.8.32

That’s odd, with 1.8.32 we made it so the document bounds will not shrink during autoScroll, which I suppose is the behavior you’d like to mimic.

It might actually be easier to turn off selection adornments (selectionAdorned: false on your node templates) and instead toggle/highlight some components in a node like we do in the selectable rows sample (except you’d just do it for the whole node, not bits of a node) or the later highlighting samples (but the only thing you’d highlight is the last-clicked node, etc)

You would also want to set Diagram’s maxSelectionCount to 1.

The result of this is that selection would be totally hidden, and instead of ever “selecting” an object, you are really just highingting and un-highlighting on a click. Selection works normally, except it gives no visual aid on its own, since no selection adornments are shown. The benefit of doing things this way is that you should not need to customize any tool to get the behavior you want, if I read you correctly.

We are definitely on 1.8.32. We actually filed a different autoscroll issue, and that fix is in this most recent build. (thanks again, heh)

I can try to do as you suggest, but walters proposal actually works better for me. We were previously trying to manage selection/clicking/dragging on our own and because of the may ways there are to select things (clicks, drag select, key modified click / drag, clicking off a node onto empty space) it is actually nicer to have our apps “focus” simply follow selection.

i’ll see if i can whip up my own version of autoscroll that mimics DraggingTool, any help would be greatly appreciated though.