0

I am trying to create a window consisting of a left pane and a right pane included in a GridPane layout. I am drawing the bounding box of this two widgets and I can see that they are resizing when the main window of the application is resized. In the left pane a GridPane layout is used to add Buttons. While the left pane resizes properly the buttons inside the left pane do not resize and reposition once the left pane resizes. The complete code is listed below. Can anyone help ?

public class PlanesJavaFxApplication extends Application {
static class ToolbarPane extends Pane 
{
    public ToolbarPane() {
        final HBox hbox = new HBox(5);
        hbox.getChildren().add(new Text("TOP"));
        this.getChildren().add(hbox);
    }
}

static class LeftPane extends Pane 
{
    public LeftPane() {
        final GridPane gridPane = new GridPane();

        ColumnConstraints col1 = new ColumnConstraints();
        col1.setPercentWidth(33);
        ColumnConstraints col2 = new ColumnConstraints();
        col2.setPercentWidth(33);
        ColumnConstraints col3 = new ColumnConstraints();
        col3.setPercentWidth(33);
        gridPane.getColumnConstraints().addAll(col1, col2, col3);

        Button selectButton = new Button("Select");
        Button rotateButton = new Button("Rotate");         
        Button upButton = new Button("Up");
        Button leftButton = new Button("Left");         
        Button rightButton = new Button("Right");
        Button downButton = new Button("Down");         
        Button doneButton = new Button("Done"); 

        gridPane.add(selectButton, 1, 0);
        gridPane.add(rotateButton, 1, 1);
        gridPane.add(upButton, 1, 2);
        gridPane.add(leftButton, 0, 3);
        gridPane.add(rightButton, 2, 3);
        gridPane.add(downButton, 1, 4);
        gridPane.add(doneButton, 1, 5); 

        //gridPane.prefWidth(300);
        this.getChildren().add(gridPane);
    }
}

static class RightPane extends Pane 
{
    public RightPane() {
        final HBox hbox = new HBox(5);
        hbox.getChildren().add(new Text("RIGHT"));
        this.getChildren().add(hbox);
    }
}       

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

@Override
public void start(Stage stage) throws Exception {
    // TODO Auto-generated method stub

    final GridPane gridPane = new GridPane();
    ColumnConstraints col1 = new ColumnConstraints();
    col1.setPercentWidth(40);
    ColumnConstraints col2 = new ColumnConstraints();
    col2.setPercentWidth(60);
    gridPane.getColumnConstraints().addAll(col1, col2);

    Pane leftPane = new LeftPane();
    leftPane.setStyle("-fx-border-color: red");
    Pane rightPane = new RightPane();
    rightPane.setStyle("-fx-border-color: blue");

    gridPane.add(leftPane, 0, 0);
    gridPane.add(rightPane,  1,  0);

    stage.setScene(new Scene(gridPane));
    stage.show();
}

}

Cristi
  • 648
  • 1
  • 13
  • 28

2 Answers2

0

Based on your code, the problem is that the gridpane which hosts the buttons never changes it's shape. You need to add constraints and listeners to that gridpane as well.

here's an example:

public class PlanesJavaFxApplication extends Application {

static class ToolbarPane extends Pane {

    public ToolbarPane() {
        final HBox hbox = new HBox(5);
        hbox.getChildren().add(new Text("TOP"));
        this.getChildren().add(hbox);
    }
}

static Button upButton = new Button("Up");

static class LeftPane extends Pane {

    public LeftPane() {
        final GridPane gridPane = new GridPane();

        ColumnConstraints col1 = new ColumnConstraints();
        col1.setPercentWidth(33);
        ColumnConstraints col2 = new ColumnConstraints();
        col2.setPercentWidth(33);
        ColumnConstraints col3 = new ColumnConstraints();
        col3.setPercentWidth(33);
        gridPane.getColumnConstraints().addAll(col1, col2, col3);

        Button selectButton = new Button("Select");
        Button rotateButton = new Button("Rotate");
        Button leftButton = new Button("Left");
        Button rightButton = new Button("Right");
        Button downButton = new Button("Down");
        Button doneButton = new Button("Done");

        gridPane.add(selectButton, 1, 0);
        gridPane.add(rotateButton, 1, 1);
        gridPane.add(upButton, 1, 2);
        gridPane.add(leftButton, 0, 3);
        gridPane.add(rightButton, 2, 3);
        gridPane.add(downButton, 1, 4);
        gridPane.add(doneButton, 1, 5);

        //gridPane.prefWidth(300);
        this.getChildren().add(gridPane);
    }
}

static class RightPane extends Pane {

    public RightPane() {
        final HBox hbox = new HBox(5);
        hbox.getChildren().add(new Text("RIGHT"));
        this.getChildren().add(hbox);
    }
}

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

@Override
public void start(Stage stage) throws Exception {
    // TODO Auto-generated method stub

    final GridPane gridPane = new GridPane();
    gridPane.setGridLinesVisible(true);
    ColumnConstraints col1 = new ColumnConstraints();
    col1.setPercentWidth(40);
    ColumnConstraints col2 = new ColumnConstraints();
    col2.setPercentWidth(60);
    gridPane.getColumnConstraints().addAll(col1, col2);

    Pane leftPane = new LeftPane();
    leftPane.setStyle("-fx-border-color: red");
    Pane rightPane = new RightPane();
    rightPane.setStyle("-fx-border-color: blue");

    stage.widthProperty().addListener((observable, oldValue, newValue) -> {
        upButton.setPrefWidth(newValue.doubleValue() > oldValue.doubleValue() ? upButton.getWidth() + 5 : upButton.getWidth() - 5);
    });

    gridPane.add(leftPane, 0, 0);
    gridPane.add(rightPane, 1, 0);

    stage.setScene(new Scene(gridPane));
    stage.show();
}

}

EverNight
  • 964
  • 7
  • 16
0

Simply add a binding between your LeftPane and your GridPane as below into LeftPane (at the end of the class):

static class LeftPane extends Pane {
    public LeftPane() {
        final GridPane gridPane = new GridPane();

        ColumnConstraints col1 = new ColumnConstraints();
        col1.setPercentWidth(33);
        ColumnConstraints col2 = new ColumnConstraints();
        col2.setPercentWidth(33);
        ColumnConstraints col3 = new ColumnConstraints();
        col3.setPercentWidth(33);
        gridPane.getColumnConstraints().addAll(col1, col2, col3);

        Button selectButton = new Button("Select");
        Button rotateButton = new Button("Rotate");         
        Button upButton = new Button("Up");
        Button leftButton = new Button("Left");         
        Button rightButton = new Button("Right");
        Button downButton = new Button("Down");         
        Button doneButton = new Button("Done"); 


        gridPane.add(selectButton, 1, 0);
        gridPane.add(rotateButton, 1, 1);
        gridPane.add(upButton, 1, 2);
        gridPane.add(leftButton, 0, 3);
        gridPane.add(rightButton, 2, 3);
        gridPane.add(downButton, 1, 4);
        gridPane.add(doneButton, 1, 5);

        // In order to see the GridPane extends with the LeftPane, remove it further
        gridPane.setGridLinesVisible(true);
        // Those 2 following lines enable the gridpane to stretch/shrink according the LeftPane
        gridPane.prefWidthProperty().bind(this.widthProperty());
        gridPane.prefHeightProperty().bind(this.heightProperty());

        //gridPane.prefWidth(300);
        this.getChildren().add(gridPane);
    }
}

You can also center your buttons for a better effect (GridPane.setValignment(Node pNode, VPos pVPos) and GridPane.setHalignment(Node pNode, HPos pHPos).

Some remarks (not applicable if the code you posted is an MCVE/SSCCE) :

  • Use FXML files as much as possible for a cleaner code
  • LeftPane could directly extends GridPane instead of Pane (in this case)
  • Pagbo
    • 691
    • 5
    • 13
    • be advised that property value bindings are known to cause resource leaks. – EverNight Jan 10 '19 at 13:53
    • @EverNight AFAIK you can't say that in a general manner (and especially not for this case), or I miss some information and it would be interesting to get some documentation about this subject. – Pagbo Jan 10 '19 at 14:18
    • @EverNight I'd be interested in your source as well. – Slaw Jan 10 '19 at 14:32
    • 1
      @Slaw, @ Pagbo https://stackoverflow.com/questions/14558266/clean-javafx-property-listeners-and-bindings-memory-leaks This guy explains stuff relatively well. You can always google " javafx bindings memory leak ". I know for a fact this happens as I've used bindings for a UI belonging to an automated software which refreshed data almost constantly over a few hours. Had a big problem dealing with out of memory errors and huge heaps until i used explicitly used listeners instead of bindings in whatever i was doing. – EverNight Jan 14 '19 at 16:35
    • @pagbo See previous comment. – EverNight Jan 14 '19 at 16:45
    • 1
      @EverNight Definitely agree with the principle. However, JavaFX bindings (at least the standard ones) are implemented using weak listeners to the best of their ability. That said, you have to be careful with unidirectional bindings as the bounded property will hold onto the target; you must `unbind` as necessary. Bidirectional bindings are, as far as I know, "fully weak" (i.e. neither side holds a strong reference to the other). But, again, I do agree it's something to watch out for. – Slaw Jan 14 '19 at 17:01
    • debatable. It is extremely contextual as it depends on many other things as well. For example the way you manage your threads has an impact of garbage collection and invalidation listeners, and most importantly on self-disposers. Even if it's a fully weak listener, if the conditions are right (code is stupid enough), there will be leaks. I've found several different ways to cause them through bindings while trying unrelated things and unless you're planning on using a short-lived stage, unidirectional and bidirectional bindings will cause leaks as the GC will kick in relatively rarely. – EverNight Jan 17 '19 at 16:17
    • @EverNight I agree it's not foolproof, but you can have the same problems when using listeners directly. Bindings are just a "higher-level" API for adding listeners. With both bindings and listeners you must remember to dispose/remove them when necessary. – Slaw Jan 18 '19 at 16:22
    • 1
      @Slaw fair enough. I agree that in-depth knowledge of whatever construct you're using in your code is necessary to prevent issues with said code and both listener and binding implementations have their uses and advantages as well as "risks". In my experience, listeners represent the lesser pain in the ass though. – EverNight Jan 21 '19 at 14:39
    • 1
      @EverNight To my mind, bindings are softer (in the ass). But I agree with both of you and the pain scale totally depends on the application you develop. and especially on the application size, the environment on which it is dedicated to be, if the app should be switched on 24/7 or not, and so on. – Pagbo Jan 21 '19 at 14:53