7

I'm building an application in JavaFx 2.2 which consist in a splitpane with a panel of component on the left side and a working sheet on the right side. Basically what i would like to do is a simple wysiwyg editor where you drag component from the left to the right, then arrange them on the right side.

I've spent the last couple of days trying to implement the same drag-and-drop feature that has SceneBuilder, without luck..

Following the sample at http://docs.oracle.com/javafx/2/drag_drop/HelloDragAndDrop.java.html i've managed to get a drag-and-drop working but i can't find any way to change the default file icon appearing when you are dragging (and replace it with a snapshot of the component i'm dragging) and how to show the forbidden icon when you are over something you can't dropped on.

Any help (could be advice, code snippet, sample or else) would be greatly appreciated :)

Thanks !

Badisi
  • 469
  • 1
  • 3
  • 12

2 Answers2

19

[UPDATE]

Finally managed it myself:

/* The 'sceneRoot' object is the root Node of the scene graph
 *    stage.setScene(new Scene(sceneRoot, 1280, 1024));
 */

private ImageView dragImageView = new ImageView();
private Node dragItem;

_

rightPane.setOnMouseDragEntered(new EventHandler<MouseDragEvent>() {
    public void handle(MouseDragEvent e) {
        rightPane.setStyle("-fx-border-color:red;-fx-border-width:2;-fx-border-style:solid;");
        e.consume();
    }
});
rightPane.setOnMouseDragExited(new EventHandler<MouseDragEvent>() {
    public void handle(MouseDragEvent e) {
        rightPane.setStyle("-fx-border-style:none;");
        e.consume();
    }
});
rightPane.setOnMouseDragReleased(new EventHandler<MouseDragEvent>() {
    public void handle(MouseDragEvent e) {
        //TODO: add new instance of dragItem to rightPane
        e.consume();
    }
});

_

private void addGesture(final Node node) {
    node.setOnDragDetected(new EventHandler<MouseEvent>() {
        public void handle(MouseEvent e) {

            SnapshotParameters snapParams = new SnapshotParameters();
            snapParams.setFill(Color.TRANSPARENT);
            dragImageView.setImage(node.snapshot(snapParams, null));

            sceneRoot.getChildren().add(dragImageView);

            dragImageView.startFullDrag();
            e.consume();
        }
    });
    node.setOnMouseDragged(new EventHandler<MouseEvent>() {
        public void handle(MouseEvent e) {
            Point2D localPoint = sceneRoot.sceneToLocal(new Point2D(e.getSceneX(), e.getSceneY()));
            dragImageView.relocate(
                    (int)(localPoint.getX() - dragImageView.getBoundsInLocal().getWidth() / 2),
                    (int)(localPoint.getY() - dragImageView.getBoundsInLocal().getHeight() / 2)
            );
            e.consume();
        }
    });
    node.setOnMouseEntered(new EventHandler<MouseEvent>() {
        public void handle(MouseEvent e) {
            node.setCursor(Cursor.HAND);
        }
    });
    node.setOnMousePressed(new EventHandler<MouseEvent>() {
        public void handle(MouseEvent e) {
            dragItem = node;
            dragImageView.setMouseTransparent(true);
            node.setMouseTransparent(true);
            node.setCursor(Cursor.CLOSED_HAND);
        }
    });
    node.setOnMouseReleased(new EventHandler<MouseEvent>() {
        public void handle(MouseEvent e) {
            dragItem = null;
            dragImageView.setMouseTransparent(false);
            node.setMouseTransparent(false);
            node.setCursor(Cursor.DEFAULT);
            sceneRoot.getChildren().remove(dragImageView);
        }
    });

}
Badisi
  • 469
  • 1
  • 3
  • 12
  • 1
    Nice work. The [drag view feature](http://javafx-jira.kenai.com/browse/RT-14730?focusedCommentId=317064#) is scheduled to be added to the JavaFX platform in a future release, likely jdk8. – jewelsea Nov 30 '12 at 11:10
  • I try to reimplement the solution for a ListView, but do not know, what type the scene variable in the setOnDragDetected handler is. It's no Scene, as Scene has not getChildern() method. – andreas Mar 22 '13 at 10:54
  • The 'scene' object is actually the root Node of the scene graph. I have updated my code with comments and renamed scene by sceneRoot for better understanding. Thanks for highlighting that ;) – Badisi Mar 24 '13 at 20:24
  • I wonder what if the purpose of node.setMouseTransparent(true) in node.setOnMousePressed(). It caused an intermittent bug for me. I use Badisi code in ListView to implement draggable list item. It works wonderfully. But somehow particular list item ignore any mouse event. – amru Apr 11 '13 at 16:32
  • @amru It was to prevent the node from listening to mouse events if you drag on it during dragging (ie. if you drag an item on itself then setOnMouseEntered is called again). Using setMouseTransparent make the node totally transparent to mouse events but you could use setDisable instead or use a mechanism with an isDragging boolean. – Badisi Apr 13 '13 at 14:39
  • Thanks for the useful code. I find it interesting that it works pretty well even in the typical case in which sceneRoot is a managed pane, where Node#relocate would normally be ineffectual. I notice that if I drag and sit still for a few seconds, JavaFX will sometimes throw in a layout pass just for fun, and the dragImageView suddenly jumps to match the pane's alignment property (center in my case). But I suspect my users can put up with that :). – vanmelle Aug 30 '13 at 18:43
3

Maybe late, but with the setDragView option, is more simpple by now :)

// Cursor Display for Drag&Drop
source.setOnMouseEntered(e -> source.setCursor(Cursor.OPEN_HAND));
source.setOnMousePressed(e -> source.setCursor(Cursor.CLOSED_HAND));
source.setOnMouseReleased(e -> source.setCursor(Cursor.DEFAULT));

// Manage drag
source.setOnDragDetected(event -> {
    /* drag was detected, start a drag-and-drop gesture*/
    Dragboard db = source.startDragAndDrop(TransferMode.MOVE);

    // Visual during drag
    SnapshotParameters snapshotParameters = new SnapshotParameters();
    snapshotParameters.setFill(Color.TRANSPARENT);
    db.setDragView(source.snapshot(snapshotParameters, null));

    /* Put a string on a dragboard */
    ClipboardContent content = new ClipboardContent();
    content.putString(source.getText());
    db.setContent(content);

    event.consume();
});
LE GALL Benoît
  • 7,159
  • 1
  • 36
  • 48