How to drag children outside of parent layout JavaFX

78 Views Asked by At

Hi and thanks in advance for any advice.

Basically I have a flowpane as the content of a scrollpane for showing a user Buttons containing data. I need to be able to drag these anywhere on the screen but the buttons clip at the edges of the scrollpane. I use this code for dragging the buttons:

dataButton.setOnMousePressed(e -> {
            nodeCoordinates[0] = e.getSceneX() - dataButton.getTranslateX();
            nodeCoordinates[1] = e.getSceneY() - dataButton.getTranslateY();
            dragData = data;
        });
dataButton.setOnMouseDragged(e -> {
            dataButton.setTranslateX(e.getSceneX()-nodeCoordinates[0]);
            dataButton.setTranslateY(e.getSceneY()-nodeCoordinates[1]);
        });
dataButton.setOnMouseReleased(e -> {
            dataButton.setTranslateX(0);
            dataButton.setTranslateY(0);
            if(e.getButton().equals(MouseButton.SECONDARY)){
                openDataPanel((Credential) data);
            }
            DragClearer clear= new DragClearer();//An extension of Thread for nulling a drag variable I have which gets filled with the respective data when the button is dragged
            clear.start();
        });

the flowpane is what contains each button and the scrollpane doesnt take up the entire screen there are other things displayed. I need the buttons to return to their original position unless they are dragged to specific areas which they do. A while a go i did use the in-built dragging and dropping but didnt really work for what I needed it to. I would say im an intermediate at javaFX so my knowledge doesnt really go that far. And when searching online I havent really found anything similar to my problem so im either doing something wrong or im definetly doing something wrong.

Again any advice you guys can give is really appreciated.

1

There are 1 best solutions below

2
SedJ601 On

Here is an example that hopefully can get you in the right direction.

This code takes a snapshot of the node and then sets the node invisible. The snapshot is draggable to any part of the Scene. On release on a target node, the Button is recreated using information from the original button. I haven't tried it, but I would guess that on release on a target node, you can add the button as a child to the target node.

I altered the code from here. @James_D answer may be better from here. I did not try his code.

Main

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * Demonstrates a drag-and-drop feature.
 */
public class App extends Application
{

    @Override
    public void start(Stage stage)
    {
        //Source Buttons.
        final Button boat1 = new Button("boat1");
        final Button boat2 = new Button("boat2");
        final Button boat3 = new Button("boat3");
        final Button boat4 = new Button("boat4");

        //Adding OnDragDetected to source Buttons.
        setOnDragDetected(boat1);
        setOnDragDetected(boat2);
        setOnDragDetected(boat3);
        setOnDragDetected(boat4);

        //Adding onDragDone to source Buttons.
        setOnDragDone(boat1);
        setOnDragDone(boat2);
        setOnDragDone(boat3);
        setOnDragDone(boat4);

        //Creating GridPane
        GridPane gridPane = new GridPane();
        gridPane.setVgap(5);
        gridPane.setHgap(5);
        gridPane.setPadding(new Insets(5, 5, 5, 5));
        gridPane.setStyle("-fx-background-color: black;");
        //Adding StackPane to every Cell in the GridPane and Adding the Target Events to each StackPane.
        for (int i = 0; i < 6; i++) {
            StackPane stackPane = new StackPane();
            stackPane.setPrefSize(150, 50);
            stackPane.setStyle("-fx-background-color: yellow;");
            setOnDragOver(stackPane);
            setOnDragEntered(stackPane);
            setOnDragExited(stackPane);
            setOnDragDropped(stackPane);

            gridPane.add(stackPane, i / 3, i % 3);
        }

        HBox root = new HBox(new VBox(boat1, boat2, boat3, boat4), gridPane);
        stage.setTitle("Hello Drag And Drop");
        Scene scene = new Scene(root, 400, 200);
        stage.setScene(scene);
        stage.show();
    }

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

    //source events handlers
    public void setOnDragDetected(Button source)
    {
        source.setOnDragDetected((MouseEvent event) -> {
            /* drag was detected, start drag-and-drop gesture*/
            System.out.println("onDragDetected");
            final ImageView preview = new ImageView(source.snapshot(null, null));
            
            /* allow any transfer mode */
            Dragboard db = source.startDragAndDrop(TransferMode.ANY);

            /* put a string on dragboard */
            ClipboardContent content = new ClipboardContent();
            content.putString(source.getText());
            db.setContent(content);
            db.setDragView(preview.getImage());
            source.setVisible(false);
            event.consume();
        });
    }

    public void setOnDragDone(Button source)
    {
        source.setOnDragDone((DragEvent event) -> {
            /* the drag-and-drop gesture ended */
            System.out.println("onDragDone");
            /* if the data was successfully moved, clear it */
//            if (event.getTransferMode() == TransferMode.MOVE) {
//                source.setText("");
//            }

            event.consume();
        });
    }

    //target event handlers
    public void setOnDragOver(StackPane target)
    {
        target.setOnDragOver((DragEvent event) -> {
            /* data is dragged over the target */
            System.out.println("onDragOver");

            /* accept it only if it is  not dragged from the same node
            * and if it has a string data */
            if (event.getGestureSource() != target
                    && event.getDragboard().hasString()) {
                /* allow for both copying and moving, whatever user chooses */
                event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
            }

            event.consume();
        });
    }

    public void setOnDragEntered(StackPane target)
    {
        target.setOnDragEntered((DragEvent event) -> {
            /* the drag-and-drop gesture entered the target */
            System.out.println("onDragEntered");
            /* show to the user that it is an actual gesture target */
            if (event.getGestureSource() != target
                    && event.getDragboard().hasString()) {
                target.setStyle("-fx-background-color: green;");
            }

            event.consume();
        });
    }

    public void setOnDragExited(StackPane target)
    {
        target.setOnDragExited((DragEvent event) -> {
            /* mouse moved away, remove the graphical cues */
            target.setStyle("-fx-background-color: yellow;");

            event.consume();
        });
    }

    public void setOnDragDropped(StackPane target)
    {
        target.setOnDragDropped((DragEvent event) -> {
            /* data dropped */
            System.out.println("onDragDropped");
            /* if there is a string data on dragboard, read it and use it */
            Dragboard db = event.getDragboard();
            boolean success = false;
            if (db.hasString()) {
                //target.setText(db.getString());
                Button tempBoat = new Button(db.getString());
                tempBoat.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
                target.getChildren().clear();
                target.getChildren().add(tempBoat);
                success = true;
            }
            /* let the source know whether the string was successfully
            * transferred and used */
            event.setDropCompleted(success);

            event.consume();
        });
    }    
}

Output

enter image description here