Layout invalidated when node change size in SwimmingLane

There is a wrong display when changing node size in swim lane. Very likely the layout is invalid. Here are codes and screenshot

  1. PoolTemplate
basicPoolTemplate(config:) {
    return $(go.Group, 'Spot',
        {
            name: MainContent,
            computesBoundsAfterDrag: true,
            computesBoundsIncludingLinks: false,
            computesBoundsIncludingLocation: true,
            handlesDragDropForMembers: true,
            isSubGraphExpanded: true,
            // use a simple layout that ignores links to stack the "lane" Groups on top of each other
            layout: $(TableLayout as any,
                {
                    isOngoing: true
                }
            ),
            subGraphExpandedChanged: (grp: go.Group) => {
                if (!grp.isSubGraphExpanded) return
                setImmediate(() => grp.layout?.invalidateLayout());
            },
            memberRemoved: (pool: go.Group) => {
                const lanes = pool.memberParts;
                lanes.each(lane => lane.updateTargetBindings('deletable'));
            }
        },
        new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
        new go.Binding("isSubGraphExpanded", "isSubGraphExpanded").makeTwoWay(),
        $(go.Panel, go.Panel.Spot,
            $(go.Panel, 'Auto',
                $(go.Shape,
                    {
                        fill: 'transparent',
                        stroke: config.stroke,
                        strokeWidth: NodeTempStrokeWidth,
                    },
                ),
                $(go.Panel, 'Table',
                    {
                        defaultColumnSeparatorStroke: config.stroke,
                        defaultColumnSeparatorStrokeWidth: NodeTempStrokeWidth / 2,
                        margin: new go.Margin(NodeTempStrokeWidth / 2, NodeTempStrokeWidth / 2)
                    },
                    // left bar area
                    $(go.Shape,
                        {
                            column: 0,
                            fill: config.backgroundColor,
                            width: PoolBarWidth,
                            strokeWidth: 0,
                            stretch: go.GraphObject.Vertical,
                            // portId: EventInputPortKey,
                            // toSpot: go.Spot.LeftCenter,
                            // toLinkable: true,
                        },
                    ),
                    // place holder for members
                    $(go.Placeholder,
                        {
                            column: 1,
                            background: config.mainContentFill,
                            opacity: config.mainContentOpacity,
                            margin: new go.Margin(-NodeTempStrokeWidth / 2, 0),
                            // minSize: go.Size.parse(PoolDefaultSize),
                        },
                        new go.Binding('minSize', '', (obj: WNodeData) => {
                            return obj.isSubGraphExpanded ? go.Size.parse(PoolDefaultSize) : new go.Size(WGroupCollapsePhW, NodeTempContentWidth)
                        }),
                    ),
                    // right bar area
                    $(go.Shape,
                        {
                            column: 2,
                            fill: config.backgroundColor,
                            width: PoolBarWidth,
                            strokeWidth: 0,
                            stretch: go.GraphObject.Vertical
                        },
                    ),
                )
            ),
            // collapse icon
            $(go.Picture, '',
                {
                    column: 1,
                    height: NodeTempContentIconHeight,
                    imageStretch: go.GraphObject.Uniform,
                },
                new go.Binding('source', "", (wLNodeData: WNodeData) => {
                    return wLNodeData.iconBase64;
                }),
                new go.Binding('visible', "isSubGraphExpanded", (isSubGraphExpanded) => {
                    return !isSubGraphExpanded
                })
            ),
        ),
    );
}
  1. LaneTemplate
basicLaneTemplate(config) {
    return $(go.Group, 'Auto',
        {
            stretch: go.GraphObject.Fill,
            selectionObjectName: "SHAPE", 
            movable: false,
            computesBoundsAfterDrag: true, 
            computesBoundsIncludingLinks: false,
            computesBoundsIncludingLocation: true,  
            handlesDragDropForMembers: true, 
            // highlight when dragging into the Group
            mouseDragEnter: function (e, grp, prev) { (grp as go.Group).isSubGraphExpanded && highlightGroup(e, grp as go.Group, true); },
            mouseDragLeave: function (e, grp, next) { (grp as go.Group).isSubGraphExpanded && highlightGroup(e, grp as go.Group, false); },
            subGraphExpandedChanged: (grp: go.Group) => {
                grp?.invalidateLayout();
            },
            memberAdded: (thisGroup: go.Group, newPart: go.Part) => {
                setImmediate(() => {
                    thisGroup?.invalidateLayout();
                    thisGroup.containingGroup?.invalidateLayout();
                });
            },
            memberRemoved: (thisGroup: go.Group, newPart: go.Part) => {
                thisGroup?.invalidateLayout();
                thisGroup.containingGroup?.invalidateLayout();
            },
            mouseDrop: (e: go.InputEvent, grp: go.GraphObject) => {
                const group = grp.part as go.Group;
                if (!(group instanceof go.Group)) return;
            },
        },
        new go.Binding("minSize", "isSubGraphExpanded", (isSubGraphExpanded: boolean) => isSubGraphExpanded ? new go.Size(400, 200) : new go.Size()).ofObject(),
        new go.Binding('row', 'row'),
        new go.Binding('column', 'column'),
        new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
        new go.Binding("background", "", h => (h.isHighlighted && h.isSubGraphExpanded) ? WGroupHighlightColor : "transparent").ofObject(),
        $(go.Shape, "Rectangle",
            {
                name: "SHAPE",
                fill: "transparent",
                strokeWidth: 1,
                stroke: config.stroke,
            },
        ),
        $(go.Placeholder,
            {
                padding: new go.Margin(60, 30, 30, 90),
                opacity: config.mainContentOpacity,
                background: 'transparent',
                alignment: new go.Spot(0, 0),
            },
            new go.Binding('padding', 'isSubGraphExpanded', isSubGraphExpanded => {
                return isSubGraphExpanded ? new go.Margin(60, 30, 30, 90) : new go.Margin(0, 30, 30, 90)
            }).ofObject(),
        )
    );
}
  1. screenShot


It appears that your basic lane template has its Group.layout property set to its default value, which is an instance of Layout class, because you do not set or bind that property. I assume you do not really want each lane group moving any nodes automatically, so I suggest you set in the group template:

     layout: $(go.Layout, { isInitial: false, isOngoing: false })

Hi walter

Thanks for your answer!

However, If I set layout in the lane template, this problem still exists. In addition, it displays wrong when node changes visibility.


I try to set layout: $(go.Layout, { isInitial: false, isOngoing: false }) in PoolTemplate, it display as follows.(lane2 overlaps lane1)
image

However, lane would change size according to the node size changes in this case.


Oh, I solve the problem that a wrong display when node changes size. It is my fault that I just set isLayoutPositioned to false in imageBlock nodeTemplate.

In addition, I do not set layout: $(go.Layout, { isInitial: false, isOngoing: false }) in laneTemplate. If user wants to layout the whole Pool, I set the layout in laneTemplate programmatically and reset layout after Lane layout ends. Could you give any suggestions?

Thanks very much!

When you want to programmatically force all invalid layouts to be performed, one calls Diagram.layoutDiagram().

When you want to force all of the layouts to be performed, one calls Diagram.layoutDiagram(true).

That way you can leave the Group.layout with Layout.isInitial and Layout.isOngoing set to false.