Search code examples
javafx-8borderpane

Nested BorderPane with Canvas only grows, never shrinks


I have a BorderPane containing a Canvas, with the Canvas' height and width properties bound to the properties of the BorderPane. The BorderPane in turn is the root of a Scene.

This all works fine. If I resize the window, the BorderPane's size changes to match the size of the Scene, and the Canvas changes to match the size of the BorderPane.

However, if I introduce another BorderPane, it stops working: the inner BorderPane, and the Canvas, will grow as the Scene and outer BorderPane grow, but when I shrink the window, they don't shrink.

  Working:            Not working:

 ┌──────────────┐    ┌────────────────┐
 │    Scene     │    │     Scene      │
 │┌────────────┐│    │┌──────────────┐│
 ││ BorderPane ││    ││  BorderPane  ││
 ││┌──────────┐││    ││┌────────────┐││
 │││  Canvas  │││    │││ BorderPane │││
 ││└──────────┘││    │││┌──────────┐│││
 │└────────────┘│    ││││  Canvas  ││││
 └──────────────┘    │││└──────────┘│││
                     ││└────────────┘││
                     │└──────────────┘│
                     └────────────────┘

The code that works:

BorderPane parent = new BorderPane();
Canvas canvas = new Canvas();
canvas.widthProperty().bind(parent.widthProperty());
canvas.heightProperty().bind(parent.heightProperty());
parent.setCenter(canvas);
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.show();

The code that doesn't work:

BorderPane inner = new BorderPane();

Canvas canvas = new Canvas();
canvas.widthProperty().bind(inner.widthProperty());
canvas.heightProperty().bind(inner.heightProperty());
inner.setCenter(canvas);

BorderPane outer = new BorderPane();
outer.setCenter(inner);

Scene scene = new Scene(outer);
stage.setScene(scene);
stage.show();

I added some logging to confirm the problem:

# scene, outer pane, inner pane, and canvas growing
outer width: 1364.0 -> 1374.0
scene width: 1364.0 -> 1374.0
outer height: 339.0 -> 342.0
scene height: 339.0 -> 342.0
canvas width: 1364.0 -> 1374.0
inner width: 1364.0 -> 1374.0
canvas height: 339.0 -> 342.0
inner height: 339.0 -> 342.0
# scene and outer pane shrinking, canvas and inner not
outer width: 1374.0 -> 1327.0
scene width: 1374.0 -> 1327.0
outer height: 342.0 -> 330.0
scene height: 342.0 -> 330.0
outer width: 1327.0 -> 1290.0
scene width: 1327.0 -> 1290.0
outer height: 330.0 -> 326.0
scene height: 330.0 -> 326.0

Obviously in this toy example I can just remove the intermediate BorderPane, but what if I want to make a reusable, resizable component wrapping that Canvas? How can I make sure it's always resized with the parent? And what's the deal with nested BorderPanes, anyway?


Solution

  • Canvas is a non-resizable node. This means it's size is a lower bound for the min size of it's parent.

    If you use the BorderPane as scene root, resizing the window forces the scene to be resized and resizing the scene forces the scene root to be resized to fit the window. For this reason the parent of the Canvas shrinks below it's min size and the Canvas shrinks too.

    If you shrink a BorderPane below it's min size, it does not force it's resizable children to resize below their min sizes but sets their sizes to the min size instead. This way a BorderPane wrapped in a BorderPane is never forced to shrink below the size of the Canvas and the Canvas never shrinks.

    You can fix this by setting the managed property of the Canvas to false which results in the Canvas not being considered when calculating the min size. (Note that unmanaged children are not considered for layout at all. You should therefore wrap the Canvas in a Pane as the only child to prevent undesired effects on siblings.)

    Canvas canvas = ...
    canvas.setManaged(false);
    Pane canvasParent = new Pane(canvas);
    canvas.widthProperty().bind(canvasParent.widthProperty());
    canvas.heightProperty().bind(canvasParent.heightProperty());
    
    // TODO: put canvasParent in the scene