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 Doh, I totally overlooked DRAG_DROPPED
event, there is no drop coordinate information available.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);
}
}