Reputation: 1465
everybody,
I want to insert a graphic into my application that can be dragged and dropped out of the application. As soon as the graphic is released outside the window, an Undecoraded / Transparent window should be opened, where only this graphic is displayed.
Stage newStage = new Stage();
StackPane stack = new StackPane();
ImageView imageView = new ImageView(new Image(this.getClass().getResourceAsStream(InBoxEnum.Graphic.INBOXLOGO.getFilename())));
stack.getChildren().add(imageView);
imageView.setOnDragDetected(event -> {
Dragboard dragboard = imageView.startDragAndDrop(TransferMode.MOVE);
dragboard.setDragView(imageView.snapshot(null, null));
ClipboardContent content = new ClipboardContent();
content.put(DRAGGABLE_INBOX_TYPE, "dontcare");
dragboard.setContent(content);
event.consume();
});
imageView.setOnDragDone(event -> {
System.out.println(event.getScreenX());
System.out.println(event.getScreenY());
});
Scene scene = new Scene(stack, 500, 500);
newStage.setScene(scene);
newStage.show();
The DragDetected event works so far also without problems.
The problem is that inside the dragDone event the position of the mouse is always 0 and I can't tell if the mouse is inside or outside my application. If the mouse is released inside the application, nothing should happen.
I also tried with Robot Class, but I always get a static strange x/y position.
I am using JAVA 11 (Adopt JDK).
Thanks for your help
Upvotes: 3
Views: 688
Reputation: 82461
Using Robot
works fine for me (Oracle JDK 11 + JavaFX 12). Since you don't actually want to drag&drop any image data, you could simply work around this issue by creating a new stage immediately and use the MOUSE_DRAGGED
of the ImageView
to update the position of the window:
Stage newStage = new Stage();
StackPane stack = new StackPane();
ImageView imageView = new ImageView(new Image(...));
stack.getChildren().add(imageView);
class DragHandler implements EventHandler<MouseEvent> {
Stage dragTarget;
@Override
public void handle(MouseEvent event) {
if (dragTarget != null) {
// move stage
dragTarget.setX(event.getScreenX());
dragTarget.setY(event.getScreenY());
event.consume();
}
}
}
final DragHandler dragHandler = new DragHandler();
imageView.setOnDragDetected(event -> {
// init stage at half transparency
Group root = new Group(new ImageView(imageView.getImage()));
root.setOpacity(0.5);
Scene displayScene = new Scene(root);
displayScene.setFill(null);
Stage displayStage = new Stage();
displayStage.initStyle(StageStyle.TRANSPARENT);
displayStage.setScene(displayScene);
displayStage.setX(event.getScreenX());
displayStage.setY(event.getScreenY());
displayStage.show();
dragHandler.dragTarget = displayStage;
event.consume();
});
imageView.setOnMouseDragged(dragHandler);
imageView.setOnMouseReleased(event -> {
if (dragHandler.dragTarget != null) {
if (stack.contains(event.getX(), event.getY())) { // check, if drop happened inside the bounds of the scene root
dragHandler.dragTarget.hide();
} else {
// make stage fully opaque & cleanup
dragHandler.dragTarget.getScene().getRoot().setOpacity(1);
imageView.setImage(null);
}
dragHandler.dragTarget = null;
event.consume();
}
});
Scene scene = new Scene(stack, 500, 500);
newStage.setScene(scene);
newStage.show();
Upvotes: 2
Reputation: 18792
The proposed solution is based on fabian's answer.
The main change is encapsulated in withinBounds
method which is used to define if the drag ended within the window bounds. This method is based on this answer:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
public class DragOut extends Application {
@Override
public void start(Stage primaryStage) {
ImageView imageView = new ImageView("https://findicons.com/files/icons/345/summer/128/cake.png");
StackPane stack = new StackPane(imageView);
DragHandler dragHandler = new DragHandler();
imageView.setOnDragDetected(event -> {
Stage displayStage = makeNewStage(imageView.getImage());
displayStage.setX(event.getScreenX());
displayStage.setY(event.getScreenY());
dragHandler.setStage(displayStage);
displayStage.show();
event.consume();
});
imageView.setOnMouseDragged(dragHandler);
imageView.setOnMouseReleased(event -> {
if (! withinBounds(event, imageView.getScene().getWindow()) && dragHandler.dragTarget != null) {
// make stage fully opaque & cleanup
dragHandler.showStage();
imageView.setImage(null);
event.consume();
}else{
dragHandler.closeStage();
}
dragHandler.setStage(null);
});
Scene scene = new Scene(stack, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
private Stage makeNewStage(Image image) {
Group root = new Group(new ImageView(image));
root.setOpacity(0.5); // init stage at half transparency
Stage displayStage = new Stage(StageStyle.TRANSPARENT);
displayStage.setScene(new Scene(root, null));
return displayStage;
}
private boolean withinBounds(MouseEvent event, Window window) {
Point2D mouseLoc = new Point2D(event.getScreenX(), event.getScreenY());
Rectangle2D windowBounds = new Rectangle2D(window.getX(), window.getY(),
window.getWidth(), window.getHeight());
return windowBounds.contains(mouseLoc);
}
class DragHandler implements EventHandler<MouseEvent> {
private Stage dragTarget;
@Override
public void handle(MouseEvent event) {
if (dragTarget != null) {
// move stage
dragTarget.setX(event.getScreenX());
dragTarget.setY(event.getScreenY());
event.consume();
}
}
void setStage(Stage stage){
dragTarget = stage;
}
void showStage(){
if (dragTarget != null) {
dragTarget.getScene().getRoot().setOpacity(1);
}
}
void closeStage(){
if (dragTarget != null) {
dragTarget.close();
}
}
}
public static void main(final String[] args) {
launch(args);
}
}
Upvotes: 0