0

I am working on a personal graph editor project, and I want to create flowchart-like graph for automatic code generation. My first idea was Canvas and GraphicsContext, but it turns out to be too complicated as I have to draw everything manually. So I have switched to Shape.

Basically, every node is a Shape with other standard ui components over it, such as Label, ChoiceBox etc. Since every node represents a program function, and I want to use these ui components to choose the function's input.

I am doing some experiments with StackPane, a Rectangle in the background with some Labels over it:

public class RectangleNode extends StackPane {

  private double x;
  private double y;
  private VBox vbox;
  private Rectangle rect;

  public RectangleNode(double x, double y) {
    super();
    setAlignment(Pos.CENTER);

    this.x = x;
    this.y = y;

    Label label1 = new Label("Hello world");
    Label label2 = new Label("Hello JavaFX");

    vbox = new VBox(label1, label2);

    rect = new Rectangle();
    rect.setStroke(Color.ALICEBLUE);
    rect.setStrokeWidth(2);
    rect.setX(this.x);
    rect.setY(this.y);

    getChildren().addAll(rect, vbox);
  }

  public void updateSize () {
    double width = vbox.getLayoutBounds().getWidth();
    double height = vbox.getLayoutBounds().getHeight();
    System.out.println("width vbox: " + width + "px");
    System.out.println("height vbox: " + height + "px");

    rect.setWidth(width + 10);
    rect.setHeight(height + 10);

  }
}

The node is placed inside a Pane as it offers absolute positioning of components:

public class StackPaneTest extends Application {

  public void start(Stage stage) throws Exception {

    Pane root = new Pane();

    stage.setTitle("StackPane Test");
    stage.setScene(new Scene(root, 480, 360));
    stage.show();

    RectangleNode pane = new RectangleNode(100, 100);
    root.getChildren().add(pane);
    pane.updateSize();
  }

  public static void main(String[] args) {
    launch(args);
  }
}

Then I have discovered a few problems here:

  1. the getLayoutBounds().getWidth() and getLayoutBounds().getHeight() always return zero, so I cannot resize the rectangle according the size of labels, how can I get the real size of vbox?
  2. The StackPane is always in the top-left corer of Pane even I have set the coordinates of Rectangle with setX and setY, how can I position it absolutely?
  3. Is StackPane the right approach?

Thanks.

vesontio
  • 381
  • 1
  • 7
  • 19

1 Answers1

1

the getLayoutBounds().getWidth() and getLayoutBounds().getHeight() always return zero, so I cannot resize the rectangle according the size of labels, how can I get the real size of vbox?

You are checking the bounds before it is rendered in the scene graph. You can get the correct values if you wrap your method call with Platform.runLater

Platform.runLater(()->pane.updateSize());

However I would recommend to get rid of the updateSize() method and bind the rectangle bounds with RectanglePane bounds, so that you dont need to worry about the bounds update of underlying shape.

rect.widthProperty().bind(widthProperty());
rect.heightProperty().bind(heightProperty());

The StackPane is always in the top-left corer of Pane even I have set the coordinates of Rectangle with setX and setY, how can I position it absolutely?

You are updating the rectangle properties, which is not correct. Instead, you have to actually update the StackPane translateX/Y properties to move your RectanglePane. Update your code in constructor to below.

//rect.setX(this.x);
//rect.setY(this.y);
setTranslateX(this.x);
setTranslateY(this.y);

Is StackPane the right approach?

As far as I understood your requirement, if you are looking for a layout to place a Shape below and your custom layout on top of it, I think StackPane is a good option.

There are other minor changes which you can go with. Below is the updated demo just for your reference.

    import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class StackPaneTest extends Application {
  public void start(Stage stage) throws Exception {
    Pane root = new Pane();
    stage.setTitle("StackPane Test");
    stage.setScene(new Scene(root, 480, 360));
    stage.show();
    root.getChildren().add(new RectangleNode(100, 100));
  }

  public static void main(String[] args) {
    launch(args);
  }
}
class RectangleNode extends Group {
  private VBox vbox;
  private Rectangle rect;
  public RectangleNode(double x, double y) {
    super();
    StackPane layout = new StackPane();
    layout.setAlignment(Pos.TOP_LEFT);

    Label label1 = new Label("Hello world");
    Label label2 = new Label("Hello JavaFX");

    vbox = new VBox(label1, label2);
    vbox.setPadding(new Insets(5));

    setTranslateX(x);
    setTranslateY(y);

    rect = new Rectangle();
    rect.setFill(Color.YELLOW);
    rect.setStrokeWidth(2);
    rect.setStroke(Color.BLACK);
    rect.widthProperty().bind(layout.widthProperty());
    rect.heightProperty().bind(layout.heightProperty());
    layout.getChildren().addAll(rect, vbox);
    getChildren().add(layout);
  }
}
Sai Dandem
  • 8,229
  • 11
  • 26
  • Still a small problem, when I launch the program, the `RectangleNode` just keeps groooowing and exceeds the border of window. – vesontio Nov 02 '18 at 04:58
  • Ha.. it is because we are not restricting the size of the RectangleNode. You can restrict this by setting the maxWidth/Height. But as your content can be dynamic, I would wrap the StackPane in a Group to keep all the contents compact. Updated my demo accordingly. Please check it. – Sai Dandem Nov 02 '18 at 05:20
  • Thank you again, I have to admit that it is more complicated than I thought ^^. – vesontio Nov 02 '18 at 06:09