0

I am developing an application for which it is necessary to layout nodes besides each other (or on top of each other etc.). However, this layout is only an initial placement and the user is able to move these nodes arbitrarily. How is this done in the correct way in JavaFX? I will explain my problem with a simplified example:

Assume I have 2 rectangles and want to place rect2 to the right of rect1.

// create first rectangle at position x= 5, y=5
rect1 = rectangle(5,5);
// create second rectangle to the right of rect1
rect2 = rectangle(5+rect1.width(), 5); 

In this scenario JavaFX will not yet have determined the width of rect1 and it will be zero. Intuitively, I would perform a call that lets JavaFX draw rect1 and thus determine its width and afterwards add rect2. See the following example:

// create first rectangle at position x= 5, y=5
rect1 = rectangle(5,5);
// let JavaFX draw rect1 (width will be calculated and set)
draw();
// create second rectangle to the right of rect1
rect2 = rectangle(5+rect1.width(), 5);

Unfortunately I haven't found a method that does what I want. My current workaround makes use of Platform.runLater() but this does not work properly all the time. If my understanding of bindings is correct, bindings are also not suitable for this problem. I only want to initially layout the nodes, so I would have to remove the binding after the initial layout (or else rect2 would move if rect1 is moved).

Thanks in advance for any help.

EDIT: Here is a minimal working example. The width of the button is 0. I tried calling root.layout() to force a layout pass etc. but it does not seem to work.

public class Test extends Application {

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

@Override
public void start(Stage primaryStage) {
    Button btn = new Button();
    StackPane root = new StackPane();
    Scene scene = new Scene(root, 300, 250);
    primaryStage.setScene(scene);
    primaryStage.show();
    btn.setText("Say 'Hello World'");
    root.getChildren().add(btn);

    // prints out 0
    System.out.println(btn.getWidth());
}

}
user3740372
  • 47
  • 1
  • 10
  • You can create a subclass of `Pane` and override `layoutChildren()`. However, I don't really understand "this layout is only an initial placement and the user is able to move these nodes arbitrarily". When the user moves the nodes, you should be changing some property which will be respected by the `layoutChildren()` method, so that it always positions the child nodes correctly. – James_D Oct 08 '14 at 19:22
  • Simplified, if a user moves a node I use a node's method relocate(posX, posY). The application resembles a graph editor. The user is able to layout the displayed nodes as he wishes. Therefore I am working with a normal Pane and nothing like GridPane etc. – user3740372 Oct 08 '14 at 20:39
  • @James_D I assume the width in the given example will be set correctly when JavaFX calls the layoutChildren() method? So the idea would be to create rect2 at an arbitrary position and then move it to the desired position in layoutChildren(). I can see this working and will give it a try. – user3740372 Oct 08 '14 at 21:04
  • [Cross-posted to Oracle JavaFX forums](https://community.oracle.com/thread/3617126). – jewelsea Oct 08 '14 at 22:22
  • @jewelsea applyCss() and layout() does exactly what I want. Thanks a lot. I'm kinda new to stackoverflow and the Oracle JavaFX forums, sorry for not referencing the cross post. Besides, am I supposed to accept an answer in case of a duplicate question? You have linked your answer from a different stackoverflow post, is there any way I can accept that as an answer? – user3740372 Oct 08 '14 at 23:31

1 Answers1

0

Given the example from above, if I set the scene of the stage to null and then reset it, the button width will be set correctly when calling System.out.println(). It seems that this forces a layout pass on the whole stage? However, this just seems to be another workaround, in particular I have performance concerns.

@Override
public void start(Stage primaryStage) {
    Button btn = new Button();
    StackPane root = new StackPane();
    Scene scene = new Scene(root, 300, 250);
    primaryStage.setScene(scene);
    primaryStage.show();
    btn.setText("Say 'Hello World'");
    root.getChildren().add(btn);

    // reset scene
    primaryStage.setScene(null);
    primaryStage.setScene(scene);

    // prints out 107.0
    System.out.println(btn.getWidth());
}
user3740372
  • 47
  • 1
  • 10