How to automatically adjust node bounds to the node's label

Questions & Answers

Summary

This article explains how to adjust a node's width and height according to the node's label, and contains a working demo example.
For a better user experience, please go to the integrated documentation viewer to read this article.

Description

The easiest way to adjust a node's size according to its label is to get noticed whenever the label is being changed. Afterwards one can manually set the node size so that the label will fit.

A NodeLabel can be edited by using Graph2DView.openLabelEditor(y.view.YLabel, double, double) or Graph2DView.openLabelEditor(y.view.YLabel, double, double, java.beans.PropertyChangeListener).
Calling one of these methods will show a text field where the label can be edited with the difference, that when we use the latter method, we can hand over our own PropertyChangeListener and can adjust the node size whenever the property changed event is fired.

So we can register the following Action to whatever we use to trigger our "edit label" action (PopupMenu, Button, Key Binding).

/**
   * Action that opens a text editor for the label of a node
   */
  class EditNodeLabel extends AbstractAction {
    private NodeLabel label;

    EditNodeLabel(NodeLabel label) {
      super( "Edit Label" );
      this.label = label;
    }

    public void actionPerformed( ActionEvent e ) {
      view.openLabelEditor( label,
          label.getLocation().getX(),
          label.getLocation().getY(),
          getLabelChangeListener()
      );
    }
  }

The set LabelChangeListener will then be informed when the label editing ends and can adjust the node size as follows:

class LabelChangeListener implements PropertyChangeListener{
    private double minWidth = 40.0;
    private double minHeight = 40.0;
    private boolean ignoreWidth = false;
    private boolean ignoreHeight = false;
    private double hSpace = 2.0;
    private double vSpace = 2.0;

    public void propertyChange(PropertyChangeEvent evt) {
      NodeLabel label = (NodeLabel) evt.getSource();
      adjustNodeSize(label.getRealizer());
    }

    void adjustNodeSize(NodeRealizer nr) {
      if (nr.labelCount() < 1) {
        return;
      }

      double minWidth = this.minWidth;
      double minHeight = this.minHeight;
      double maxWidth = Double.POSITIVE_INFINITY;
      double maxHeight = Double.POSITIVE_INFINITY;
      final SizeConstraintProvider scp = nr.getSizeConstraintProvider();
      if (scp != null) {
        final YDimension min = scp.getMinimumSize();
        if (min != null) {
          if (minWidth < min.width) {
            minWidth = min.width;
          }
          if (minHeight < min.height) {
            minHeight = min.height;
          }
        }
        final YDimension max = scp.getMaximumSize();
        if (max != null) {
          maxWidth = max.width;
          maxHeight = max.height;
        }
      }

      final NodeLabel nl = nr.getLabel();
      final YRectangle lb = nl.getBox();

      if (!ignoreWidth) {
        double width = lb.width + 2 * hSpace;
        if (width < minWidth) {
          width = minWidth;
        }
        if (width > maxWidth) {
          width = maxWidth;
        }
        nr.setWidth(width);
      }
      if (!ignoreHeight) {
        double height = lb.height + 2 * vSpace;
        if (height < minHeight) {
          height = minHeight;
        }
        if (height > maxHeight) {
          height = maxHeight;
        }
        nr.setHeight(height);
      }
    }
  }

Another possibility would be to register a Graph2DListener on the Graph2D using Graph2D.addGraph2DListener(y.view.Graph2DListener). A Graph2DListener will get Graph2DEvents and can handle them accordingly. To react on node label changes the listener will look as follows:

class LabelChangedListener implements Graph2DListener{
    private static final int INDENTATION = 5;

    public void onGraph2DEvent(Graph2DEvent e) {
      Node v = null;
      String p = e.getPropertyName();
      if(e.getSubject() instanceof NodeLabel && "text".equals(p)){
        v = ((NodeLabel)e.getSubject()).getNode();
      } else if(e.getSubject() instanceof Node &&  "realizer".equals(p)){
        v = (Node)e.getSubject();
      }

      if(v != null){
        adjustNodeSize(view.getGraph2D().getRealizer(v));
      }
    }

    private void adjustNodeSize(NodeRealizer realizer) {
      /** 
       do adjustment here
       */
    }
  }
When registering a Graph2DListener to the graph, the node size changes will happen everytime the label text is set or the NodeRealizer changes. This does for example also happen when loading the graph and will result in other node sizes than the loaded file really had.
Thus it is recommended to use the first solution for adjusting node labels after they have been edited.
You can download and execute the attached demos. Please save them to <yfilesDir>/src/demo/view/layout/labeling

Resources

Categories this article belongs to:
yFiles for Java > yFiles Viewer > Displaying and Editing Graphs > Realizer-Related Features
yFiles for Java > yFiles Viewer > Displaying and Editing Graphs > User Interaction
Applies to:
yFiles for Java 2: 2.6, 2.7, 2.8, 2.9, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18
Keywords:
Node - NodeLabel - size - label - node size - width - height - NodeRealizer - realizer - openLabelEditor - PropertyChangeListener - listener