How to automatically adjust node bounds to the node's label
Questions & AnswersSummary
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
*/
}
}