12

I'm working on the GUI of my level editor that I built in JavaFX, and I want to be able to resize the canvas object to the new split pane dimensions. It seems that everything I've tried has failed. This includes passing the pane object in and using its width directly, using window size listeners and binding the width and height property to that of the split pane. Any ideas? This is what it looks like before a resize:

enter image description here

And after a resize:

enter image description here

Does anybody have any ideas? The code for the class is pretty extensive, but the code for the resizing will be included here:

public Canvas canvas;
public String tabTitle;
public VBox layout;
public GraphicsContext g;
public Core core;

public CanvasTab(Core core, String tabTitle){
    this.core = core;
    this.canvas = new Canvas(core.scene.getWidth() - 70, core.scene.getHeight() - 70);
    layout = VBoxBuilder.create().spacing(0).padding(new Insets(10, 10, 10, 10)).children(canvas).build();
    
    this.g = canvas.getGraphicsContext2D();

    g.setFill(Color.BLACK);
    g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
    
    HBox.setHgrow(layout, Priority.ALWAYS);
    
    this.setContent(layout);
    this.setText(tabTitle);
    
    canvas.widthProperty().bind(layout.widthProperty().subtract(20));
    canvas.heightProperty().bind(layout.heightProperty().subtract(20));
}

public CanvasTab(Canvas canvas){
    this.canvas = canvas;
}
Mark Nutt
  • 348
  • 1
  • 3
  • 13
  • 1
    The canvas is (I assume) resizing correctly, but you fill the black rectangle for the size of the canvas when the canvas tab is constructed. If you're going to use a canvas, and want it to re-size, you'll probably need to re-draw everything (or at least some things) on a resize. – James_D May 03 '14 at 23:10

2 Answers2

13

As James_D pointed out, you need to redraw the content of your canvas when resizing. This can be done by adding a listener to your canvas' width and height property as follows:

InvalidationListener listener = new InvalidationListener(){
    @Override
    public void invalidated(Observable o) {
        redraw();       
    }           
});
canvas.widthProperty().addListener(listener);
canvas.heightProperty().addListener(listener);

or in Java 8 using functional interfaces:

canvas.widthProperty().addListener(observable -> redraw());
canvas.heightProperty().addListener(observable -> redraw());

where redraw() is your own method which would look like this for your example (drawing a black rectangle:

private void redraw() {
    g.setFill(Color.BLACK);
    g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
schauk11erd
  • 624
  • 9
  • 21
  • 2
    In my limited experience, you have to get the scene's widthProperty and heightProperty, not the canvas's: https://blog.idrsolutions.com/2012/11/adding-a-window-resize-listener-to-javafx-scene/ – bbarker Jun 18 '16 at 16:03
7

To make a JavaFx canvas resizable all that needs to be done is override the min/pref/max methods. Make it resizable and implement the resize method.

With this method no width/height listeners are necessary to trigger a redraw. It is also no longer necessary to bind the size of the width and height to the container.

public class ResizableCanvas extends Canvas {

@Override
public double minHeight(double width)
{
    return 64;
}

@Override
public double maxHeight(double width)
{
    return 1000;
}

@Override
public double prefHeight(double width)
{
    return minHeight(width);
}

@Override
public double minWidth(double height)
{
    return 0;
}

@Override
public double maxWidth(double height)
{
    return 10000;
}

@Override
public boolean isResizable()
{
    return true;
}

@Override
public void resize(double width, double height)
{
    super.setWidth(width);
    super.setHeight(height);
    paint();
}

Note that the resize method cannot simply call Node.resize(width,height), because the standard implementation is effectivele empty.