I'm trying to drag and drop a node from one window to another. The code creates two windows and adds a dragable lable to one of them. The goal is too move the node from one window to the other and back again. This is what I have currently.
public class Main extends Application {
private static Node transferNode;
@Override
public void start(Stage primaryStage) {
initFirstWindow();
initSecondWindow();
}
public static void initFirstWindow() {
BorderPane borderPane = new BorderPane();
Label label = new Label("clickable");
label.setOnDragDetected(event -> {
label.startFullDrag();
System.out.println("Full drag");
event.consume();
});
borderPane.setCenter(label);
Stage stage = new Stage();
stage.setTitle("Window number 1");
stage.setScene(new Scene(borderPane, 300, 275));
makeSceneDragAware(stage.getScene());
stage.show();
}
public static void initSecondWindow() {
Stage stage = new Stage();
stage.setTitle("Window number 2");
stage.setScene(new Scene(new BorderPane(), 300, 275));
makeSceneDragAware(stage.getScene());
stage.show();
}
public static void makeSceneDragAware(Scene scene) {
scene.setOnMouseDragReleased(event -> {
System.out.println("Released drag");
if (transferNode != null) {
((BorderPane)scene.getRoot()).setCenter(transferNode);
transferNode = null;
event.consume();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
The problem with this, is that setOnMouseDragReleased
doesn't get triggered when releasing over the target window. It only gets triggered in the same window as the dragging node.
UPDATE: Making an update to hopefully get a bit more traction. I wanna point out some code that uses JavaFX's D&D feature.
public class Main extends Application {
private final DataFormat NODE_TYPE = new DataFormat("transferable-node");
private Node transferNode;
@Override
public void start(Stage primaryStage) {
initFirstWindow();
initSecondWindow();
}
public void initFirstWindow() {
BorderPane borderPane = new BorderPane();
Label label = new Label("clickable");
label.setOnDragDetected(event -> {
System.out.println("Full drag");
Dragboard db = label.startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
transferNode = label;
content.put(NODE_TYPE, "");
db.setContent(content);
event.consume();
});
borderPane.setCenter(label);
Stage stage = new Stage();
stage.setTitle("Window number 1");
stage.setScene(new Scene(borderPane, 300, 275));
makeSceneDragAware(stage.getScene());
stage.show();
}
public void initSecondWindow() {
Stage stage = new Stage();
stage.setTitle("Window number 2");
stage.setScene(new Scene(new BorderPane(), 300, 275));
makeSceneDragAware(stage.getScene());
stage.show();
}
public void makeSceneDragAware(Scene scene) {
scene.setOnDragOver(event -> {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
});
scene.setOnDragDropped(event -> {
if (transferNode != null) {
((BorderPane)scene.getRoot()).setCenter(transferNode);
transferNode = null;
event.setDropCompleted(true);
event.consume();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
This code will technically work and satisfies the goal of moving a node between two windows, however it won't work for me as I don't want any dragged element to have the dragging interaction with external programs. Using D&D will also stop firing the setOnMouseDragged
event which I need for other parts of my application.
So I've managed to achive the goal with the following code.
public class Main extends Application {
private Node transferNode;
private final ArrayList<Stage> stageList = new ArrayList<>();
@Override
public void start(Stage primaryStage) {
initFirstWindow();
initSecondWindow();
}
public void initFirstWindow() {
BorderPane borderPane = new BorderPane();
Label label = new Label("clickable");
label.setOnDragDetected(event -> {
label.startFullDrag();
transferNode = label;
});
label.setOnMouseReleased(event -> {
Point point = MouseInfo.getPointerInfo().getLocation().getLocation();
Stage stage = windowUnderPoint(point);
if (stage != null) {
MouseDragEvent mouseEvent = new MouseDragEvent(
MouseDragEvent.MOUSE_DRAG_RELEASED, 0, 0, point.getX(), point.getY(),
MouseButton.PRIMARY, 1,
false, false, false, false, false, false, false,
false, false, new PickResult(stage, point.getX(), point.getY()), label);
stage.fireEvent(mouseEvent);
}
});
borderPane.setCenter(label);
Stage stage = new Stage();
stage.setTitle("Window number 1");
stage.setScene(new Scene(borderPane, 300, 275));
stage.setX(400);
makeStageDragAware(stage);
stage.show();
stageList.add(stage);
}
public void initSecondWindow() {
Stage stage = new Stage();
stage.setTitle("Window number 2");
stage.setScene(new Scene(new BorderPane(), 300, 275));
stage.setX(800);
makeStageDragAware(stage);
stage.show();
stageList.add(stage);
}
public void makeStageDragAware(Stage stage) {
stage.addEventHandler(MouseDragEvent.MOUSE_DRAG_RELEASED, event -> {
if (transferNode != null && transferNode.getScene().getWindow() != stage) {
((BorderPane) stage.getScene().getRoot()).setCenter(transferNode);
transferNode = null;
event.consume();
}
});
}
public Stage windowUnderPoint(Point point) {
for (Stage stage : stageList) {
if (inRange(stage.getX(), stage.getX() + stage.getWidth(), point.getX()) &&
inRange(stage.getY(), stage.getY() + stage.getHeight(), point.getY())) {
return stage;
}
}
return null;
}
public boolean inRange(double low, double high, double number) {
return (number >= low && number <= high);
}
public static void main(String[] args) {
launch(args);
}
}
Basically when the dragging node is released it gets the current mouse coordinates relative to the display and checks if any known/registered stages exists under it. If if finds a valid stage, it'll trigger the stages MOUSE_DRAG_RELEASED
event.