2

Is it possible to use a Node as a mouse cursor? I'm thinking in a ProgressIndicator. For example a determinate one, letting the user know how much percentage of the current task is done.

Kurospidey
  • 393
  • 1
  • 4
  • 18

2 Answers2

3

Probably the most reliable way to do this is to set the cursor to Cursor.NONE, and have a label with the progress indicator as its graphic, which tracks the mouse coordinates.

I tried using an ImageCursor which updated, but nothing appeared: I am guessing the images couldn't be computed quickly enough.

Here's an SSCCE of the first technique:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ProgressIndicatorAsCursor extends Application {

    @Override
    public void start(Stage primaryStage) {

        Button button = new Button("Start");

        Service<Void> service = new Service<Void>() {
            @Override
            public Task<Void> createTask() {
                return new Task<Void>() {
                    @Override
                    public Void call() throws Exception {
                        for (int i = 1 ; i <= 1000; i++) {
                            Thread.sleep(10);
                            updateProgress(i, 1000);
                        }
                        return null ;
                    }
                };
            }
        };

        button.disableProperty().bind(service.runningProperty());
        button.setOnAction(e -> service.restart());

        ProgressIndicator pi = new ProgressIndicator();
        pi.progressProperty().bind(service.progressProperty());

        Pane pane = new Pane();

        // fill pane with rectangle as task progresses:
        Rectangle rectangle = new Rectangle();
        rectangle.setFill(Color.CORNFLOWERBLUE);
        rectangle.setX(0);
        rectangle.widthProperty().bind(pane.widthProperty());

        rectangle.heightProperty().bind(pane.heightProperty().multiply(service.progressProperty()));
        rectangle.yProperty().bind(pane.heightProperty().subtract(rectangle.heightProperty()));

        pane.getChildren().add(rectangle);

        Label label = new Label();
        label.graphicProperty().bind(
                Bindings.when(service.runningProperty())
                    .then(pi)
                    .otherwise((ProgressIndicator)null));

        pane.setOnMouseEntered(e -> 
            pane.getChildren().add(label));
        pane.setOnMouseExited(e -> 
            pane.getChildren().remove(label));
        pane.setOnMouseMoved(e -> label.relocate(e.getX(), e.getY()));

        pane.cursorProperty().bind(
                Bindings.when(service.runningProperty())
                    .then(Cursor.NONE)
                    .otherwise(Cursor.DEFAULT));

        BorderPane.setAlignment(button, Pos.CENTER);
        BorderPane.setMargin(button, new Insets(10));
        BorderPane root = new BorderPane(pane, new Rectangle(0,0,0,20), null, button, null);

        primaryStage.setScene(new Scene(root, 400, 600));
        primaryStage.show();

    }

    public static void main(String[] args) {
        launch(args);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
0

Sees it's impossible, but you could obtain the cursor property and keep updating it with an image as your desire.

Mordechai
  • 15,437
  • 2
  • 41
  • 82