JavaFX: Setting Dragboard contents gives wrong wrong PickResult in Mouse Released event

538 views Asked by At

In my question regarding handling dragging from a TreeView, I got a great answer from James_D. This helped me cook up most of what I needed.

There's just one issue I need to resolve: On the drop I need to know the exact coordinates of the drop location as this will actually determine what needs to be done. The problem is that in the DRAG_DROPPED event, there is no drop coordinate information available. Doh, I totally overlooked getX() and getY() from the DragEvent. So that actually fully solves my problem

Before I knew that, however I thought of using the MOUSE_RELEASED event's X and Y coordinates. But I found that when you set the content of the Dragboard in the DRAG_DETECTED handler (of the cell), these coordinates (and the PickResult get all wrong. This feels like a bug to me…

I have modified James_D's sample to show you what I mean. When the line with db.setContent(cc); disabled, the Pick result and coordinates shown when dragging from the TreeView onto e.g., the canvas (that's what I am dragging onto in my application too) are fine. Doing the exact same thing with the line enabled, results in a totally wrong PickResult... Is this a bug, or am I doing something silly?

Here is the output from the sample code dragging an element to the top left of the canvas:

(line disabled) : PR=PickResult [node = Canvas@4e91835c, point = Point3D [x = 4.0, y = 2.0, z = 0.0], distance = 746.4101615137755

(line enabled): PR=PickResult [node = null, point = Point3D [x = 842.0, y = -500.0, z = 0.0], distance = 746.4101615137755

Notice the coordinates are totally of, as well as the pick result suddenly not finding the node any longer…

Code (modified sample from James_D in answer to my earlier question.

import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Label;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.PickResult;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class DragBoard extends Application {

    private static int cellCount = 0;
    private final DataFormat objectDataFormat = new DataFormat("x-application/java-serialized-object");

    @Override
    public void start(Stage primaryStage) {
        TreeView<Integer> tree = new TreeView<>();
        tree.setShowRoot(false);

        Task<TreeItem<Integer>> buildTreeTask = new Task<TreeItem<Integer>>() {

            @Override
            protected TreeItem<Integer> call() throws Exception {
                TreeItem<Integer> treeRoot = new TreeItem<>(0);
                IntStream.range(1, 10).mapToObj(this::createItem)
                        .forEach(treeRoot.getChildren()::add);
                return treeRoot;
            }

            private TreeItem<Integer> createItem(int value) {
                TreeItem<Integer> item = new TreeItem<>(value);
                if (value < 100) {
                    for (int i = 0; i < 10; i++) {
                        item.getChildren().add(createItem(value * 10 + i));
                    }
                }
                return item;
            }

        };

        tree.setCellFactory(tv -> new TreeCell<Integer>() {

            {
                System.out.println("Cells created: " + (++cellCount));

                setOnDragDetected(e -> {
                    if (!isEmpty()) {
                        Dragboard db = startDragAndDrop(TransferMode.COPY);
                        ClipboardContent cc = new ClipboardContent();
                        cc.put(objectDataFormat, getItem());
                        db.setContent(cc);
                        Label label = new Label(String.format("Add %,d", getItem()));
                        new Scene(label);
                        db.setDragView(label.snapshot(null, null));
                    }
                });

                setOnMouseReleased(e -> {
                    Object es = e.getSource();
                    PickResult pr = e.getPickResult();

                    System.out.println("PR=" + pr);
                });
            }

            @Override
            public void updateItem(Integer value, boolean empty) {
                super.updateItem(value, empty);
                if (empty) {
                    setText(null);
                } else {
                    setText(String.format("%,d", value));
                }
            }
        });

        IntegerProperty total = new SimpleIntegerProperty();
        Label label = new Label();
        label.textProperty().bind(total.asString("Total: %,d"));

        label.setOnDragOver(e
                -> e.acceptTransferModes(TransferMode.COPY));

        // in real life use a CSS pseudoclass and external CSS file for the background:
        label.setOnDragEntered(e -> label.setStyle("-fx-background-color: yellow;"));
        label.setOnDragExited(e -> label.setStyle(""));

        label.setOnDragDropped(e -> {
            Dragboard db = e.getDragboard();
            if (db.hasContent(objectDataFormat)) {
                Integer value = (Integer) db.getContent(objectDataFormat);
                total.set(total.get() + value);
                e.setDropCompleted(true);
            }
        });

        BorderPane.setMargin(label, new Insets(10));
        label.setMaxWidth(Double.MAX_VALUE);
        label.setAlignment(Pos.CENTER);

        Canvas canvas = new Canvas(250, 100);
        BorderPane root = new BorderPane(new Label("Loading..."));

        buildTreeTask.setOnSucceeded(e -> {
            tree.setRoot(buildTreeTask.getValue());
            root.setTop(canvas);
            root.setCenter(tree);
            root.setBottom(label);
        });

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

        Thread t = new Thread(buildTreeTask);
        t.setDaemon(true);
        t.start();

    }

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

There are 0 answers