0

I am trying to create a JavaFX chess board and I encounter a problem with the drag and drop event on a piece.

chess board image

My chess board is composed of Case class extending Label and these Cases can have a Piece as a graphic (this class extends ImageView).

I am trying to make the pieces movable with a drag and drop event (setOnMouseDragged). I've tried this code without success:

(we have c, a Case and p, a Piece)

c.setOnMousePressed(mouseEvent -> {
    p.mouseAnchorX = mouseEvent.getX();
    p.mouseAnchorY = mouseEvent.getY();
});
c.setOnMouseDragged(mouseEvent ->{
    p.setLayoutX(mouseEvent.getSceneX() - p.mouseAnchorX);
    p.setLayoutY(mouseEvent.getSceneY() - p.mouseAnchorY);

    /*
    p.setX(mouseEvent.getSceneX() - p.mouseAnchorX);
    p.setY(mouseEvent.getSceneY() - p.mouseAnchorY);
    */

    /*
    p.setTranslateX(mouseEvent.getSceneX()/30 - 25.5);
    p.setTranslateY(mouseEvent.getSceneY()/30 - 17);
    */
});

Both [setX / setY] and [setLayoutX / setLayoutY] are not moving the piece.

The closest I got was with the setTranslateX and setTranslateY methods : the pieces are moving but not appropriately.

Ideally, I'd like to use the setLayoutX and setLayoutY methods but I don't get why they don't work in my case.

Abra
  • 19,142
  • 7
  • 29
  • 41
il_tonino
  • 5
  • 1
  • 1
    Consider posting a [mcve]. – Abra Jul 16 '22 at 11:02
  • 1
    The example is too short. At least the parent of p would need to be known to diagnose your issue. If the Parent is a Region, it will set the layout coordinates for you during each layout pass, thus overriding your changes. To prevent this, either use a Group as parent node or set the managed property to false to be able to position the pieces yourself. You might also want to consider using setOnDragDetected(...) instead. With this you can use Dragboard::setDragView to set an Image that follows the cursor during the drag and drop gesture. Check the docs for DragEvent for more details. – Duff Jul 16 '22 at 15:49
  • "_Ideally, I'd like to use the setLayoutX and setLayoutY methods but I don't get why they don't work in my case_" -- That's not necessarily ideal. It would probably work if the parent node was a `Pane` or a `Group`, or if the `ImageView` was unmanaged. However, any layout that positions its managed children will set the layout properties of said children. It's more likely to work in general if you use the translate properties. For instance, the `x` position of a node will be `layoutX` + `translateX` (ignoring other transforms such as rotate and scale). – Slaw Jul 16 '22 at 18:01
  • Also, note that you're setting the "anchor" positions using the **local** coordinates of the mouse (e.g., `getX()`), but then you're using the **scene** coordinates in the on-mouse-dragged handler (e.g., `getSceneX()`). You should probably only use the **local** coordinates in this case. – Slaw Jul 16 '22 at 18:04
  • See related: [How to change positions of node in a GridPane](https://stackoverflow.com/questions/71953676/how-to-change-positions-of-node-in-a-gridpane/71962336#71962336), which is similar, in some ways, to dragging pieces around a chess board. The referenced example makes use of the DragView mentioned in Duff's comment. – jewelsea Jul 18 '22 at 20:16

1 Answers1

2

We create a method with the name makeDraggable(), which has a Node as a parameter. In your case, you can use a for-loop to loop through each piece.

In the method, you create a Group which contains the Node. Then you add EventFilters for dragging and pressing. To remember the initial mouse cursor coordinates use DragContext. To disable MouseEvents for the children we need to add the following lines.

// adds EventFilter
wrapGroup.addEventFilter(
    // for any MouseEvent
    MouseEvent.ANY,
    new EventHandler<MouseEvent>() {
        public void handle(final MouseEvent mouseEvent) {
            if (dragModeActiveProperty.get()) {
                // disable mouse events for all children
                mouseEvent.consume();
            }
         }
    });

Then you're ready to add the mouse-pressed EventFilter.

// adds EventFilter
wrapGroup.addEventFilter(
    // for mouse pressed
    MouseEvent.MOUSE_PRESSED,
    new EventHandler<MouseEvent>() {
        public void handle(final MouseEvent mouseEvent) {
            if (dragModeActiveProperty.get()) {
                // remember initial mouse cursor coordinates
                // and node position
                dragContext.mouseAnchorX = mouseEvent.getX();
                dragContext.mouseAnchorY = mouseEvent.getY();
                dragContext.initialTranslateX =
                    node.getTranslateX();
                dragContext.initialTranslateY =
                    node.getTranslateY();
            }
        }
    });

Afterwards, we only need to add the drag EventFilter.

// adds EventFilter
wrapGroup.addEventFilter(
    // for mouse dragged
    MouseEvent.MOUSE_DRAGGED,
    new EventHandler<MouseEvent>() {
        public void handle(final MouseEvent mouseEvent) {
            if (dragModeActiveProperty.get()) {
                // shift node from its initial position by delta
                // calculated from mouse cursor movement
                node.setTranslateX(
                    dragContext.initialTranslateX
                        + mouseEvent.getX()
                        - dragContext.mouseAnchorX);
                node.setTranslateY(
                    dragContext.initialTranslateY
                        + mouseEvent.getY()
                        - dragContext.mouseAnchorY);
            }
        }
    });

According to Oracles article about Working with Event Filters the full method can look like this:

private Node makeDraggable(final Node node) {
final DragContext dragContext = new DragContext();
final Group wrapGroup = new Group(node);

wrapGroup.addEventFilter(
    MouseEvent.ANY,
    new EventHandler<MouseEvent>() {
        public void handle(final MouseEvent mouseEvent) {
            if (dragModeActiveProperty.get()) {
                // disable mouse events for all children
                mouseEvent.consume();
            }
         }
    });

wrapGroup.addEventFilter(
    MouseEvent.MOUSE_PRESSED,
    new EventHandler<MouseEvent>() {
        public void handle(final MouseEvent mouseEvent) {
            if (dragModeActiveProperty.get()) {
                // remember initial mouse cursor coordinates
                // and node position
                dragContext.mouseAnchorX = mouseEvent.getX();
                dragContext.mouseAnchorY = mouseEvent.getY();
                dragContext.initialTranslateX =
                    node.getTranslateX();
                dragContext.initialTranslateY =
                    node.getTranslateY();
            }
        }
    });

wrapGroup.addEventFilter(
    MouseEvent.MOUSE_DRAGGED,
    new EventHandler<MouseEvent>() {
        public void handle(final MouseEvent mouseEvent) {
            if (dragModeActiveProperty.get()) {
                // shift node from its initial position by delta
                // calculated from mouse cursor movement
                node.setTranslateX(
                    dragContext.initialTranslateX
                        + mouseEvent.getX()
                        - dragContext.mouseAnchorX);
                node.setTranslateY(
                    dragContext.initialTranslateY
                        + mouseEvent.getY()
                        - dragContext.mouseAnchorY);
            }
        }
    });

return wrapGroup;

}

Similar to your question: Have a look at this stack overflow discussion.

GregGott
  • 95
  • 9