Block "movable" feature of SwimLane

Hi,
I want to block “movable” feature of the swimLane element. SwimLane is inside element Pool.
The user can move only the Pool.
I have strange behavior. View image below.

This is code:
var swimLanesGroupTemplate =
GoLib(go.Group, “Spot”, swimLanesStyle(),
{
name: “Lane”,
contextMenu: laneEventMenu,
minLocation: new go.Point(NaN, -Infinity), // only allow vertical movement
maxLocation: new go.Point(NaN, Infinity),
selectionObjectName: “SHAPE”, // selecting a lane causes the body of the lane to be highlit, not the label
resizable: true, resizeObjectName: “SHAPE”, // the custom resizeAdornmentTemplate only permits two kinds of resizing
layout: GoLib(go.LayeredDigraphLayout, // automatically lay out the lane’s subgraph
{
isInitial: false, // don’t even do initial layout
isOngoing: false, // don’t invalidate layout when nodes or links are added or removed
direction: 0,
columnSpacing: 10,
layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource
}),
computesBoundsAfterDrag: true, // needed to prevent recomputing Group.placeholder bounds too soon
computesBoundsIncludingLinks: false, // to reduce occurrences of links going briefly outside the lane
computesBoundsIncludingLocation: true, // to support empty space at top-left corner of lane
handlesDragDropForMembers: true, // don’t need to define handlers on member Nodes and Links
mouseDrop: function (e, grp) { // dropping a copy of some Nodes and Links onto this Group adds them to this Group

            // don't allow drag-and-dropping a mix of regular Nodes and Groups
            if (!e.diagram.selection.any(function (n) { return (n instanceof go.Group && n.category !== "subprocess") || n.category === "privateProcess"; })) {
                var ok = grp.addMembers(grp.diagram.selection, true);
                if (ok) {
                    updateCrossLaneLinks(grp);
                    relayoutDiagram();
                } else {
                    grp.diagram.currentTool.doCancel();
                }
            }
        },
        subGraphExpandedChanged: function (grp) {
            var shp = grp.resizeObject;
            if (grp.diagram.undoManager.isUndoingRedoing) return;
            if (grp.isSubGraphExpanded) {
                shp.height = grp._savedBreadth;
            } else {
                grp._savedBreadth = shp.height;
                shp.height = NaN;
            }
            updateCrossLaneLinks(grp);
        }
    },
    GoLib(go.Shape, "Rectangle",  // this is the resized object
        {name: "SHAPE", fill: "transparent", /*stroke: null */
        stroke: "black"
    },  // need stroke null here or you gray out some of pool border.
      new go.Binding("fill", "color"),
      new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
  // the lane header consisting of a Shape and a TextBlock
      GoLib(go.Panel, "Horizontal",
      {
          name: "HEADER",
          angle: 270,  // maybe rotate the header to read sideways going up
          alignment: go.Spot.LeftCenter, alignmentFocus: go.Spot.LeftCenter
      },
        GoLib(go.TextBlock,  // the lane label
          {editable: true, margin: new go.Margin(2, 0, 0, 8) },
          new go.Binding("visible", "isSubGraphExpanded").ofObject(),
          new go.Binding("text", "text").makeTwoWay()),
        GoLib("SubGraphExpanderButton", { margin: 4, angle: -270 })  // but this remains always visible!
       ),  // end Horizontal Panel
      GoLib(go.Placeholder,
        { padding: 12, alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft }),
        GoLib(go.Panel, "Horizontal", { alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft },
            GoLib(go.TextBlock,  // this TextBlock is only seen when the swimlane is collapsed
            {
            name: "LABEL",
            editable: true, visible: false,
            angle: 0, margin: new go.Margin(6, 0, 0, 20)
        },
        new go.Binding("visible", "isSubGraphExpanded", function (e) { return !e; }).ofObject(),
        new go.Binding("text", "text").makeTwoWay())
     )
  );  // end swimLanesGroupTemplate


  function swimLanesStyle() {  // common settings for both Lane 
      return [
        {
            layerName: "Background",  // all pools and lanes are always behind all nodes and links
            background: "transparent",  // can grab anywhere in bounds
            movable: false, // allows users to re-order by dragging
            copyable: false,  // can't copy lanes or pools
            avoidable: false  // don't impede AvoidsNodes routed Links
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)
      ];
  }


    var poolGroupTemplate =
    GoLib(go.Group, "Auto", groupStyle(),
      { // use a simple layout that ignores links to stack the "lane" Groups on top of each other
          layout: GoLib(PoolLayout, { spacing: new go.Size(0, 0) })  // no space between lanes
      },
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
      GoLib(go.Shape,
        { fill: "transparent" },
        new go.Binding("fill", "color")),
      GoLib(go.Panel, "Table",
        { defaultColumnSeparatorStroke: "black" },
        GoLib(go.Panel, "Horizontal",
          { column: 0, angle: 270 },
          GoLib(go.TextBlock,
            { editable: true, margin: new go.Margin(5, 0, 5, 0) },  // margin matches private process (black box pool)
            new go.Binding("text").makeTwoWay())
        ),
        GoLib(go.Placeholder,
          { background: "transparent", column: 1 })
      )
    ); // end poolGroupTemplate


  function groupStyle() {  // common settings for both Lane and Pool Groups
      return [
        {
            layerName: "Background",  // all pools and lanes are always behind all nodes and links
            background: "transparent",  // can grab anywhere in bounds
            movable: true, // allows users to re-order by dragging
            copyable: false,  // can't copy lanes or pools
            avoidable: false  // don't impede AvoidsNodes routed Links
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)
      ];
  }

Could you please be more specific about the behaviors that you do want, and those that you do not want? Should there be any restrictions on movement of lanes? Should there be any restrictions on movement of pools?

Note that the Diagram.layout that is a PoolLayout (defined in the sample) and that the Group.layout for “Pool” groups is also a PoolLayout. So the layouts are positioning pools within the diagram and lanes within the pools.

In addition both kinds of Groups have Part.minLocation and Part.maxLocation set to only allow vertical movement.

I want to resize the Lane using the resizable bar.

But i don’t want to be able to drag a Lane indipendently like when you have a “movable” setting “True”.

Because of that I set movable to false for the Lane but now when I drag the “Pool” don’t follow.

The problem is that the DraggingTool thinks that the “Lane” Group, which presumably is not Part.movable in your app, cannot be moved. So the net result is that it only moves the “Pool” Group itself.

But I assume you want to be able to move a “Pool” Group like any typical (non-“Lane”) Group. We’ll investigate this.

Try the latest library at http://gojs.net.

In order to have moving a Group also move its non-movable member Parts, try something like:

          layout: $(PoolLayout, { isOngoing: false }),
          "draggingTool.computeEffectiveCollection": function(parts) {
            var nolanes = new go.Set();
            parts.each(function(p) { if (!(p instanceof go.Group && p.category === "")) nolanes.add(p); });
            var map = go.DraggingTool.prototype.computeEffectiveCollection.call(this, nolanes);
            return map;
          },

Don’t forget to remove the limitations on maxLocation and minLocation, and you don’t need the custom GridLayout.comparer which takes the location.y into account when ordering the pools.

Version 1.5.11 is now “latest”. The library has changed since I posted the previous reply.

Hi,
I tried to use your example with new library, but I get this error:

SCRIPT5022: Unable to find object named: draggingTool in Group#1519 when trying to set property: draggingTool.computeEffectiveCollection
File: go-debug.js, Line: 27, Column: 1

This is my code:

  var swimLanesGroupTemplate =
  GoLib(go.Group, "Spot", swimLanesStyle(), 
    {
        name: "Lane",
        contextMenu: laneEventMenu,
        /*minLocation: new go.Point(NaN, -Infinity),  // only allow vertical movement
        maxLocation: new go.Point(NaN, Infinity),            
        */
        selectionObjectName: "SHAPE",  // selecting a lane causes the body of the lane to be highlit, not the label
        resizable: true, resizeObjectName: "SHAPE",  // the custom resizeAdornmentTemplate only permits two kinds of resizing
        layout: GoLib(go.LayeredDigraphLayout,  // automatically lay out the lane's subgraph
                  {
                  isInitial: false,  // don't even do initial layout
                  isOngoing: false,  // don't invalidate layout when nodes or links are added or removed
                  direction: 0,
                  columnSpacing: 10,
                  layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource
              }),
        computesBoundsAfterDrag: true,  // needed to prevent recomputing Group.placeholder bounds too soon
        computesBoundsIncludingLinks: false,  // to reduce occurrences of links going briefly outside the lane
        computesBoundsIncludingLocation: true,  // to support empty space at top-left corner of lane
        handlesDragDropForMembers: true,  // don't need to define handlers on member Nodes and Links
        mouseDrop: function (e, grp) {  // dropping a copy of some Nodes and Links onto this Group adds them to this Group

            // don't allow drag-and-dropping a mix of regular Nodes and Groups
            if (!e.diagram.selection.any(function (n) { return (n instanceof go.Group && n.category !== "subprocess") || n.category === "privateProcess"; })) {
                var ok = grp.addMembers(grp.diagram.selection, true);
                if (ok) {
                    updateCrossLaneLinks(grp);
                    relayoutDiagram();
                } else {
                    grp.diagram.currentTool.doCancel();
                }
            }
        },
        subGraphExpandedChanged: function (grp) {
            var shp = grp.resizeObject;
            if (grp.diagram.undoManager.isUndoingRedoing) return;
            if (grp.isSubGraphExpanded) {
                shp.height = grp._savedBreadth;
            } else {
                grp._savedBreadth = shp.height;
                shp.height = NaN;
            }
            updateCrossLaneLinks(grp);
        }
    },
    GoLib(go.Shape, "Rectangle",  // this is the resized object
        {name: "SHAPE", fill: "transparent", /*stroke: null */
        stroke: "black"
    },  // need stroke null here or you gray out some of pool border.
      new go.Binding("fill", "color"),
      new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
  // the lane header consisting of a Shape and a TextBlock
      GoLib(go.Panel, "Horizontal",
      {
          name: "HEADER",
          angle: 270,  // maybe rotate the header to read sideways going up
          alignment: go.Spot.LeftCenter, alignmentFocus: go.Spot.LeftCenter
      },
        GoLib(go.TextBlock,  // the lane label
          {editable: true, margin: new go.Margin(2, 0, 0, 8) },
          new go.Binding("visible", "isSubGraphExpanded").ofObject(),
          new go.Binding("text", "text").makeTwoWay()),
        GoLib("SubGraphExpanderButton", { margin: 4, angle: -270 })  // but this remains always visible!
       ),  // end Horizontal Panel
      GoLib(go.Placeholder,
        { padding: 12, alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft }),
        GoLib(go.Panel, "Horizontal", { alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft },
            GoLib(go.TextBlock,  // this TextBlock is only seen when the swimlane is collapsed
            {
            name: "LABEL",
            editable: true, visible: false,
            angle: 0, margin: new go.Margin(6, 0, 0, 20)
        },
        new go.Binding("visible", "isSubGraphExpanded", function (e) { return !e; }).ofObject(),
        new go.Binding("text", "text").makeTwoWay())
     )
  );  // end swimLanesGroupTemplate



// define a custom resize adornment that has two resize handles if the group is expanded
//myDiagram.groupTemplate.resizeAdornmentTemplate =
swimLanesGroupTemplate.resizeAdornmentTemplate =
    GoLib(go.Adornment, "Spot",
      GoLib(go.Placeholder),
      GoLib(go.Shape,  // for changing the length of a lane
        {
        alignment: go.Spot.Right,
        desiredSize: new go.Size(7, 50),
        fill: "lightblue", stroke: "dodgerblue",
        cursor: "col-resize"
    },
        new go.Binding("visible", "", function (ad) { return ad.adornedPart.isSubGraphExpanded; }).ofObject()),
      GoLib(go.Shape,  // for changing the breadth of a lane
        {
        alignment: go.Spot.Bottom,
        desiredSize: new go.Size(50, 7),
        fill: "lightblue", stroke: "dodgerblue",
        cursor: "row-resize"
    },
        new go.Binding("visible", "", function (ad) { return ad.adornedPart.isSubGraphExpanded; }).ofObject())
    );


    var poolGroupTemplate =
    GoLib(go.Group, "Auto", groupStyle(),
      { // use a simple layout that ignores links to stack the "lane" Groups on top of each other
          layout: GoLib(PoolLayout, { spacing: new go.Size(0, 0) })  // no space between lanes
          ,"draggingTool.computeEffectiveCollection": function(parts) {
            var nolanes = new go.Set();
            parts.each(function(p) { if (!(p instanceof go.Group && p.category === "")) nolanes.add(p); });
            var map = go.DraggingTool.prototype.computeEffectiveCollection.call(this, nolanes);
            return map;
          }
      },
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
      GoLib(go.Shape,
        { fill: "transparent" },
        new go.Binding("fill", "color")),
      GoLib(go.Panel, "Table",
        { defaultColumnSeparatorStroke: "black" },
        GoLib(go.Panel, "Horizontal",
          { column: 0, angle: 270 },
          GoLib(go.TextBlock,
            { editable: true, margin: new go.Margin(5, 0, 5, 0) },  // margin matches private process (black box pool)
            new go.Binding("text").makeTwoWay())
        ),
        GoLib(go.Placeholder,
          { background: "transparent", column: 1 })
      )
    ); // end poolGroupTemplate


    var poolTemplateForPalette =
    GoLib(go.Group, "Vertical",
      { locationSpot: go.Spot.Center,
          isSubGraphExpanded: false
      },
      GoLib(go.Shape, "Process",
        { fill: "white", desiredSize: new go.Size(gatewayNodeSize, gatewayNodeSize / 2) }),
      GoLib(go.Shape, "Process",
        { fill: "white", desiredSize: new go.Size(gatewayNodeSize, gatewayNodeSize / 2) }),
      GoLib(go.TextBlock,
        { margin: 5, editable: true, text: "Pool-Lane"}/*,
        new go.Binding("text")*/)
    );

    var swimLanesGroupTemplateForPalette =
    GoLib(go.Group, "Vertical"); // empty in the palette

That error is correct: there is no “draggingTool” property in the Group class.

That kind of initialization only applies when building Diagrams using go.GraphObject.make.

I have set correctly the property “draggingTool.computeEffectiveCollection” and I’m obtaining desired effect.

But after I move “Pool” object, the value of “loc” in json doesn’t change but contains always the initial value.

Example:
{“category”:“Pool”, “key”:“501”, “text”:"", “isGroup”:true, “loc”:“253.45001220703125 114.5”},

Do you have a TwoWay Binding on a Group property that saves the location to your “loc” data property?

Yes, I have TwoWay Binding of property that saves the location.

This is code:

var poolGroupTemplate =
    GoLib(go.Group, "Auto", groupStyle(),
      { // use a simple layout that ignores links to stack the "lane" Groups on top of each other
          layout: GoLib(PoolLayout, { spacing: new go.Size(0, 0) })  // no space between lanes              
      },
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
      GoLib(go.Shape,
        { fill: "transparent" },
        new go.Binding("fill", "color")),
      GoLib(go.Panel, "Table",
        { defaultColumnSeparatorStroke: "black" },
        GoLib(go.Panel, "Horizontal",
          { column: 0, angle: 270 },
          GoLib(go.TextBlock,
            { editable: true, margin: new go.Margin(5, 0, 5, 0) },  // margin matches private process (black box pool)
            new go.Binding("text").makeTwoWay())
        ),
        GoLib(go.Placeholder,
          { background: "transparent", column: 1 })
      )
    ); // end poolGroupTemplate


  var swimLanesGroupTemplate =
  GoLib(go.Group, "Spot", swimLanesStyle(), 
    {
        name: "Lane",
        contextMenu: laneEventMenu,
        /*minLocation: new go.Point(NaN, -Infinity),  // only allow vertical movement
        maxLocation: new go.Point(NaN, Infinity),            
        */
        selectionObjectName: "SHAPE",  // selecting a lane causes the body of the lane to be highlit, not the label
        resizable: true, resizeObjectName: "SHAPE",  // the custom resizeAdornmentTemplate only permits two kinds of resizing
        layout: GoLib(go.LayeredDigraphLayout,  // automatically lay out the lane's subgraph
                  {
                  isInitial: false,  // don't even do initial layout
                  isOngoing: false,  // don't invalidate layout when nodes or links are added or removed
                  direction: 0,
                  columnSpacing: 10,
                  layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource
              }),
        computesBoundsAfterDrag: true,  // needed to prevent recomputing Group.placeholder bounds too soon
        computesBoundsIncludingLinks: false,  // to reduce occurrences of links going briefly outside the lane
        computesBoundsIncludingLocation: true,  // to support empty space at top-left corner of lane
        handlesDragDropForMembers: true,  // don't need to define handlers on member Nodes and Links
        mouseDrop: function (e, grp) {  // dropping a copy of some Nodes and Links onto this Group adds them to this Group

            // don't allow drag-and-dropping a mix of regular Nodes and Groups
            if (!e.diagram.selection.any(function (n) { return (n instanceof go.Group && n.category !== "subprocess") || n.category === "privateProcess"; })) {
                var ok = grp.addMembers(grp.diagram.selection, true);
                if (ok) {
                    updateCrossLaneLinks(grp);
                    relayoutDiagram();
                } else {
                    grp.diagram.currentTool.doCancel();
                }
            }
        },
        subGraphExpandedChanged: function (grp) {
            var shp = grp.resizeObject;
            if (grp.diagram.undoManager.isUndoingRedoing) return;
            if (grp.isSubGraphExpanded) {
                shp.height = grp._savedBreadth;
            } else {
                grp._savedBreadth = shp.height;
                shp.height = NaN;
            }
            updateCrossLaneLinks(grp);
        }
    },
    GoLib(go.Shape, "Rectangle",  // this is the resized object
        {name: "SHAPE", fill: "transparent", /*stroke: null */
        stroke: "black"
    },  // need stroke null here or you gray out some of pool border.
      new go.Binding("fill", "color"),
      new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)),
  // the lane header consisting of a Shape and a TextBlock
      GoLib(go.Panel, "Horizontal",
      {
          name: "HEADER",
          angle: 270,  // maybe rotate the header to read sideways going up
          alignment: go.Spot.LeftCenter, alignmentFocus: go.Spot.LeftCenter
      },
        GoLib(go.TextBlock,  // the lane label
          {editable: true, margin: new go.Margin(2, 0, 0, 8) },
          new go.Binding("visible", "isSubGraphExpanded").ofObject(),
          new go.Binding("text", "text").makeTwoWay()),
        GoLib("SubGraphExpanderButton", { margin: 4, angle: -270 })  // but this remains always visible!
       ),  // end Horizontal Panel
      GoLib(go.Placeholder,
        { padding: 12, alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft }),
        GoLib(go.Panel, "Horizontal", { alignment: go.Spot.TopLeft, alignmentFocus: go.Spot.TopLeft },
            GoLib(go.TextBlock,  // this TextBlock is only seen when the swimlane is collapsed
            {
            name: "LABEL",
            editable: true, visible: false,
            angle: 0, margin: new go.Margin(6, 0, 0, 20)
        },
        new go.Binding("visible", "isSubGraphExpanded", function (e) { return !e; }).ofObject(),
        new go.Binding("text", "text").makeTwoWay())
     )
  );  // end swimLanesGroupTemplate


  function swimLanesStyle() {  // common settings for both Lane 
      return [
        {
            layerName: "Background",  // all pools and lanes are always behind all nodes and links
            background: "transparent",  // can grab anywhere in bounds
            movable: false, // allows users to re-order by dragging                   
            copyable: false,  // can't copy lanes or pools
            avoidable: false  // don't impede AvoidsNodes routed Links
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)
      ];
  }

  function groupStyle() {  // common settings for both Lane and Pool Groups
      return [
        {
            layerName: "Background",  // all pools and lanes are always behind all nodes and links
            background: "transparent",  // can grab anywhere in bounds
            movable: false, // allows users to re-order by dragging
            copyable: false,  // can't copy lanes or pools
            avoidable: false  // don't impede AvoidsNodes routed Links
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)
      ];
  }

I just again made the changes to the SwimLanes sample that I specified earlier, and the user can move the “Pool” groups around and their locations are correctly saved. However I also made the change not to set the Diagram.layout at all, so that the diagram would depend on the saved locations of the “Pool” groups.

One, I started with the page Swim Lanes.

Two, I commented out the PoolLayout’s GridLayout.comparer function:

//    // This sorts based on the location of each Group.
//    // This is useful when Groups can be moved up and down in order to change their order.
//    this.comparer = function(a, b) {
//      var ay = a.location.y;
//      var by = b.location.y;
//      if (isNaN(ay) || isNaN(by)) return 0;
//      if (ay < by) return -1;
//      if (ay > by) return 1;
//      return 0;
//    };

Three, I commented out the Diagram.layout and added an override of DraggingTool.computeEffectiveCollection:

          //layout: $(PoolLayout, { isOngoing: false }),
          "draggingTool.computeEffectiveCollection": function(parts) {
            var nolanes = new go.Set();
            parts.each(function(p) { if (!(p instanceof go.Group && p.category === "")) nolanes.add(p); });
            var map = go.DraggingTool.prototype.computeEffectiveCollection.call(this, nolanes);
            return map;
          },

Four, I removed the Part.minLocation and Part.maxLocation constraints on the “Pool” group:

          avoidable: false  // don't impede AvoidsNodes routed Links
//          minLocation: new go.Point(NaN, -Infinity),  // only allow vertical movement
//          maxLocation: new go.Point(NaN, Infinity)

And everything worked as I think you are asking for.

Can you explain me how you did it? Thanks

If you search the samples, you’ll find several examples of overrides of DraggingTool.computeEffectiveCollection.

The default behavior is to move selected Parts, including the member Nodes and Links of selected Groups. But if you want to move more (or fewer) Parts, you can determine that by overriding that method.

Just a reminder – the standard DraggingTool instance is available via the Diagram.toolManager.draggingTool property.

Yeah but how to do it in a way you described in message from Nov '15? I tried what you wrote in that message and still have problem with Error: Unable to find object named: draggingTool in Group#1146 when trying to set property: draggingTool.computeEffectiveCollection. If I understand properly this way should change draggingTool behaviour only for Pool component and using a Diagram.toolManager.draggingTool changes it globally for whole diagram and it’s elements. I managed something with Diagram.toolManager.draggingTool already but have some problems. What I did is that I was changed movable to false for swim lanes and left this property to true only for Pool. In that case when I drop Pool on diagram and after that try to move it somewhere inside diagram only Pool group element is moved and swim lanes are staying in the original position, and I think you describe why is that in one of the previous message. Then I changed draggingTool.computeEffectiveCollection via diagram.toolManager.draggingTool.computeEffectiveCollection = function(parts: any) with body as you proposed and things didn’t changed at all. Then I decide to dynamically change movable property and override computeEffectiveCollection this way:

 var nolanes = new go.Set();
    VisualDesigner.diagram.toolManager.draggingTool.computeEffectiveCollection = function(parts: any) {
        nolanes = new go.Set();
        parts.each(function(p: any) {
            VisualDesigner.diagram.startTransaction("unable move");
            p.movable = true;
            VisualDesigner.diagram.commitTransaction("unable move");
            nolanes.add(p);
        });
        var map = go.DraggingTool.prototype.computeEffectiveCollection.call(this, nolanes);
        return map;
    }

And then in

VisualDesigner.diagram.toolManager.draggingTool.doDeactivate = function() {
            VisualDesigner.diagram.startTransaction("unable move");
            nolanes.each(function(p: any) {
                if (p.data.category != "coordinate")
                    p.movable = false;                
            });
            VisualDesigner.diagram.commitTransaction("unable move");
        }

I put back movable property to false on dragging end (this coordinate category is the same what Pool is in your example) . And now all parts are moving but this cause WARNING: In ToolManager.doMouseDown: UndoManager.transactionLevel is not zero and diagram components don’t behave normally anymore. Of course if I left all parts of Pool movable and don’t override anything then everything works fine except fact I don’t want to have ordering of swim lanes inside Pool. I want that feature off!

I cannot speak for @gpa, but I believe the override of DraggingTool.computeEffectiveCollection does what @gpa was asking for.

The DraggingTool instance applies to the whole Diagram. You cannot have multiple DraggingTool instances where one works only on some Parts and another one works only on some other Parts. What would happen if the user selected and dragged some Parts from each subset?

However you can customize the behavior of DraggingTool so that it has different effects on different Parts. For the most common cases you can do that by setting properties on Part and/or on Layer or Diagram or DraggingTool. For unusual cases you have to override methods on DraggingTool to get the results that you want.

For now I found solution how to prevent SwimLines to be movable while movable property is set to true for Pool as well as SwimLanes, because when this property is true moving Pool group works fine. I just set minLocation and maxLocation for SwimLanes to go.Point(NaN, NaN). Using NaN value for both x and y prevents element to change its location as it is documented DraggingTool | GoJS API

Well, if that works for you, that’s very clever.