Draw border around a Canvas


Question

I'm in the process of coding an application that (essentially) mimics MS Paint; you can select the Pencil tool, and draw a line with a stroke of 3; you can select the Marker tool and draw a line with a stroke of 7, etc.

I wanted to have a border drawn around my Canvas. This is simple, yes. However, with other methods I have, the only way I can think to implement this revolves around a lot of spot-checking after the border is drawn. Is there an efficient way to do this without conflicting with the stroke/color of the already selected Tool?

Here is the drawBorder() method:

private void drawBorder(GraphicsContext g) {
    final double canvasWidth = g.getCanvas().getWidth();
    final double canvasHeight = g.getCanvas().getHeight();

    g.setStroke(Color.BLACK);
    g.setLineWidth(4);
    g.strokeRect(0, 0, canvasWidth, canvasHeight);

    //sets the color back to the currently selected ColorPicker color
    g.setStroke(selectedColor);
}

However, this code will conflict with my clear() action

clearTool.setOnAction(e -> {
            graphics.clearRect(0, 0,
                canvas.getWidth(), canvas.getHeight());
            drawBorder(graphics);
        });

because after the clearing of the Canvas, the stroke line width will be 4. This is an issue because if I had the pencil tool as the selected tool (stroke linewidth of 3), it will be 4 until I select another tool and switch back to the pencil tool; furthermore, the same concept applies if I had the marker tool selected at the time of pressing the clear button (stroke linewidth of 7 will be 4 until I select another tool and then reselect the marker tool).

I'm trying to avoid having to set a check for each tool, and have it reset the stroke's linewidth each and every time--while that would work, it seems convoluted.

1
2
11/22/2014 9:59:13 PM

Accepted Answer

Consider putting the canvas in a pane, and using CSS to style the pane. For example:

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class CanvasWithBorderExample extends Application {

    @Override
    public void start(Stage primaryStage) {

        final int SIZE = 400 ;
        Canvas canvas = new Canvas(SIZE, SIZE);

        GraphicsContext gc = canvas.getGraphicsContext2D() ;
        gc.setStroke(Color.RED);
        gc.moveTo(0, 0);
        gc.lineTo(SIZE, SIZE);
        gc.stroke();

        StackPane canvasContainer = new StackPane(canvas);
        canvasContainer.getStyleClass().add("canvas");

        VBox root = new VBox(10, canvasContainer, new Button("Click here"));
        root.setFillWidth(false);
        VBox.setVgrow(canvasContainer, Priority.NEVER);
        root.setAlignment(Pos.CENTER);

        Scene scene = new Scene(root);
        scene.getStylesheets().add("canvas-with-border.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

with canvas-with-border.css:

.canvas {
    -fx-background-color: antiquewhite, white ;
    -fx-background-insets: 0, 20 ;
    -fx-padding: 20 ;
}
4
11/23/2014 6:31:21 PM

EDIT: I was to quick to apply Swing Canvas knowledge to JavaFX. If you use a resizable Canvas in JavaFX, you will still need to do it as described below. If not, I would probably use a layer approach as described in the Java tutorial by Oracle.


If I understand you correctly, then it appears to me you have an incorrect approach. I am assuming you are drawing into the Canvas directly when the user draws his pencil, for example. This is not correct. The Canvas can ask you to redraw it at any time, and you must be prepared for that.

That is, do the user drawings in an off-screen GraphicsContext, with its own settings that you can keep in line with the tools the user selects. Then, in your Canvas paint() method, copy the contents of that context to the Canvas. At that time you can also add your own border or other annotations if you like.

It could be that you already use off-screen drawing and would like to draw your border into the off-screen buffer (for example due to an "add border" action from the user). Then have a look at the save() and restore() methods of GraphicsContext. They enable to save the state, change your settings, do the drawing, and then restore the old state.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon