Selecting a Group deselects it's children and makes them unselectable

How my org is utilizing gojs is if you select a group, the nodes in that group are inherently selected. So we would like to deselect any nodes already selected when selecting a group (this includes sub-groups) and make those nodes unselectable as well

We believe we know how to do this with the ClickSelectingTool by overriding standardMouseSelect. Basically, we check if any currently selected nodes are a deep child of the newly selected group and de-select them before calling super. standardMouseSelect()

Question 1: is that the way to go for that? Do you have a recommendation?

DragSelectingTool is a bit trickier. While we can apply the same logic inselectInRect, the problem is that calling super. selectInRect still adds to the selection everything in the Rect. So we know that we cannot call super. selectInRect(), which is fine, but we then need to handle raising the "ChangingSelection" and "ChangedSelection" DiagramEvents ourselves.

Question 2: When within selectInRect do we need to be raising those events? Do you have a recommendation? Should we be trying to accomplish this in a different virtual method?

My org’s license is for ~2.1.0

  1. Yes, that’s the right thing to do. The Distances sample demonstrates an override of the Tool.standardMouseSelect method. For your enlightenment, here’s its definition:
  public standardMouseSelect(): void {
    const diagram = this.diagram;
    if (!diagram.allowSelect) return;
    const e = diagram.lastInput;
    const curobj = diagram.findPartAt(e.documentPoint, false);  // to select containing Group if Part.canSelect() is false
    if (curobj !== null) {
      if (e.meta || e.control) {  // toggle the part's selection
        diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
        let part = curobj;
        while (part !== null && !part.canSelect()) part = part.containingGroup;
        if (part !== null) part.isSelected = !part.isSelected;
        diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
      } else if (e.shift) {  // add the part to the selection
        if (!curobj.isSelected) {
          diagram.raiseDiagramEvent('ChangingSelection', diagram.selection);
          let part = curobj;
          while (part !== null && !part.canSelect()) part = part.containingGroup;
          if (part !== null) part.isSelected = true;
          diagram.raiseDiagramEvent('ChangedSelection', diagram.selection);
        }
      } else {
        if (!curobj.isSelected) {
          let part = curobj;
          while (part !== null && !part.canSelect()) part = part.containingGroup;
          if (part !== null) diagram.select(part);  // also raises ChangingSelection/Finished
        }
      }
    } else if (e.left && !(e.meta || e.control) && !e.shift) {
      // left click on background with no modifier: clear selection
      diagram.clearSelection();  // also raises ChangingSelection/Finished
    }
  }

Diagram.raiseDiagramEvent isn’t officially documented, but it’s in go.d.ts and you can depend on it.

  1. The DragSelectingTool.selectInRect method does not raise any DiagramEvents. Here is its definition:
  public selectInRect(r: Rect): void {
    const diagram = this.diagram;
    const e = diagram.lastInput;

    const parts = diagram.findPartsIn(r, this.isPartialInclusion);
    if (e.meta || e.control) {  // toggle or deselect
      if (e.shift) {  // deselect only
        const it = parts.iterator;
        while (it.next()) {
          const p = it.value;
          if (p.isSelected) p.isSelected = false;
        }
      } else {  // toggle selectedness of parts
        const it = parts.iterator;
        while (it.next()) {
          const tp = it.value;
          tp.isSelected = !tp.isSelected;
        }
      }
    } else if (e.shift) {  // extend selection only
      const it = parts.iterator;
      while (it.next()) {
        const ep = it.value;
        if (!ep.isSelected) ep.isSelected = true;
      }
    } else {  // select parts, and unselect all other previously selected parts
      // this tries to avoid deselecting and then reselecting any Part
      const tounselect = new List<Part>();
      const sit = diagram.selection.iterator;
      while (sit.next()) {
        const sp = sit.value;
        if (!parts.contains(sp)) tounselect.add(sp);
      }
      const uit = tounselect.iterator;
      while (uit.next()) {
        const up = uit.value;
        up.isSelected = false;
      }
      const it = parts.iterator;
      while (it.next()) {
        const ps = it.value;
        if (!ps.isSelected) ps.isSelected = true;
      }
    }
  }

Also you might find instructive the RealtimeDragSelectingTool implementation, Realtime Drag Selecting Tool , which you can find either as JavaScript in extensions/ or as TypeScript in extensionsJSM/. Here is its override:

  public selectInRect(r: go.Rect): void {
    const diagram = this.diagram;
    const orig = this._originalSelection;
    const temp = this._temporarySelection;
    const e = diagram.lastInput;
    const found = diagram.findPartsIn(r, this.isPartialInclusion);
    if (e.control || e.meta) {  // toggle or deselect
      if (e.shift) {  // deselect only
        temp.each(function(p: go.Part) { if (!found.contains(p)) p.isSelected = orig.contains(p); });
        found.each(function(p: go.Part) { p.isSelected = false; temp.add(p); });
      } else {  // toggle selectedness of parts based on _originalSelection
        temp.each(function(p: go.Part) { if (!found.contains(p)) p.isSelected = orig.contains(p); });
        found.each(function(p: go.Part) { p.isSelected = !orig.contains(p); temp.add(p); });
      }
    } else if (e.shift) {  // extend selection only
      temp.each(function(p: go.Part) { if (!found.contains(p)) p.isSelected = orig.contains(p); });
      found.each(function(p: go.Part) { p.isSelected = true; temp.add(p); });
    } else {  // select found parts, and unselect all other previously selected parts
      temp.each(function(p: go.Part) { if (!found.contains(p)) p.isSelected = false; });
      orig.each(function(p: go.Part) { if (!found.contains(p)) p.isSelected = false; });
      found.each(function(p: go.Part) { p.isSelected = true; temp.add(p); });
    }
  }

Thank you Walter! Your response was detailed and extremely helpful!