2

I am attempting to put together a simple POC for an Image Viewer desktop product. My viewer needs to support annotations, so I have decided to attempt this with a main canvas for the image I am looking at, then using smaller canvas objects to place the annotations.

The annotations need to be abled to be dragged, deleted, reordered etc.

However, to show which canvas is selected, I need to highlight it, or put a shadow on it, or wrap a border around it.

I can't see a CSS style that I can apply there. I have wrapped it in an HBox to simulate the effect, but I was wondering if it's possible another way?

Thanks!

purring pigeon
  • 4,141
  • 5
  • 35
  • 68

1 Answers1

3

Canvas is a direct subclass of Node, so it only supports the CSS properties defined for Node. These do not include the standard ways to apply borders, such as -fx-background-color or -fx-border-color.

However, Node supports the -fx-effect property, so you could use that. Here is a "quick-and-dirty" example using inline styles (in a real app I would recommend using an external stylesheet; perhaps, depending on your requirements, subclassing Canvas to provide the "selection" behavior):

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class CanvasCSSEffect extends Application {

    @Override
    public void start(Stage primaryStage) {
        Canvas canvas1 = new Canvas(400, 400);
        Canvas canvas2 = new Canvas(400, 400);

        GraphicsContext gc1 = canvas1.getGraphicsContext2D();
        gc1.setFill(Color.CORNSILK);
        gc1.fillRect(0, 0, 400, 400);

        GraphicsContext gc2 = canvas2.getGraphicsContext2D();
        gc2.setFill(Color.ANTIQUEWHITE);
        gc2.fillRect(0, 0, 400, 400);

        ObjectProperty<Canvas> selectedCanvas = new SimpleObjectProperty<>();
        selectedCanvas.addListener((obs, oldCanvas, newCanvas) -> {
            if (oldCanvas != null) {
                oldCanvas.setStyle("");
            }
            if (newCanvas != null) {
                newCanvas.setStyle("-fx-effect: innershadow(gaussian, #039ed3, 10, 1.0, 0, 0);");
            }
        });

        canvas1.setOnMouseClicked(e -> selectedCanvas.set(canvas1));
        canvas2.setOnMouseClicked(e -> selectedCanvas.set(canvas2));

        HBox root = new HBox(10, canvas1, canvas2);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • When I tried your sample it placed a box around the canvas as intended. When I applied this to my canvas which just had text on it and was otherwise transparent, it changed the text color. Is there a way to have this perform the same as your example on a canvas with only text in it? – purring pigeon May 05 '15 at 20:37
  • Well, isn't that fun... I'll need to experiment with that a bit more. I guess the effect is applied to the first non-transparent pixels, or some such algorithm. Unless you can fill the canvas with a base color, you may have to resort to wrapping in a `Pane`. – James_D May 05 '15 at 20:54
  • I have been able to mimic that using an HBox around it so that is giving me what I need. The selection is causing me grief. If I add a new class to handle the selectionHandler, (just extending pane) I can't use it in scenebuilder. But it has the functionality. – purring pigeon May 05 '15 at 21:41
  • When I'm back at the computer I'll update with an FXML-friendly class that manages selection. Do you need multiple selection (if not, supporting ToggleGroup is a neat way to go...) – James_D May 05 '15 at 21:45
  • I posted another question on the topic here: http://stackoverflow.com/questions/30063792/adding-a-custom-component-to-scenebuilder-2-0, I just need to select the highlighted node and deselect any node that is currently selected. This all works, but the integration with SceneBuilder is just painful. I really appreciate your help. – purring pigeon May 05 '15 at 21:46