Xanatos
Xanatos

Reputation: 1636

How to redirect MouseEvent.MOUSE_PRESSED to a different Node?

I have a Region called 'R', and a Node called 'N'. N is the only child on R. Consider this behaviour:

  1. the user presses the left-mouse button on some part of R
  2. N (which is somewhere else on R) moves so that it is centered on the spot where the user pressed
  3. the user releases the left-mouse button, then presses it again without moving the mouse
  4. with the left-mouse button still pressed, the user drags the mouse, and N now follows the mouse cursor around as it is dragged.

I have no problems implementing the behaviour I've just described. I put a MOUSE_PRESSED handler on R that implements step 2. And I put a MOUSE_DRAGGED handler on N that implements step 4. JavaFX automatically directs the MouseEvents to these handlers on R and N for the presses in step 1 and 3 respectively.

The Problem:

I need to do this WITHOUT step 3. That is, the user should not have to press-release-press-drag, but rather should simply press-drag, and N should "jump" to the mouse location on the "press", and then start receiving MOUSE_DRAGGED events immediately.

Unfortunately, this doesn't happen. The release-click that I'm trying to omit seems to be necessary, otherwise the drag events all happen on R instead of N.

I'm thinking the solution will involve redispatching the initial MOUSE_PRESSED, or something along those lines. Does anyone know a way to do this (or a better way to solve my problem?)

Upvotes: 4

Views: 1218

Answers (2)

kleopatra
kleopatra

Reputation: 51535

Node has api to mark it as the target of drag gestures:

public void startFullDrag()

Starts a full press-drag-release gesture with this node as gesture source. This method can be called only from a DRAG_DETECTED mouse event handler. More detail about dragging gestures can be found in the overview of MouseEvent and MouseDragEvent.

Assuming circle being your currently active node in a pane (borrowing code/names from James's answer), the collaborators are handlers on

  • mousePressed on pane that snaps the position of circle to the the current location
  • dragDetected on pane that calls startsFullDrag on circle
  • dragAny on circle that does the actual moving

In code:

pane.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        circle.setCenterX(event.getX());
        circle.setCenterY(event.getY());
    }
});
pane.setOnDragDetected(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        circle.startFullDrag();
    }
});
circle.addEventHandler(MouseDragEvent.ANY, new EventHandler<MouseDragEvent>() {
    @Override
    public void handle(MouseDragEvent event) {
        circle.setCenterX(event.getX());
        circle.setCenterY(event.getY());
    }
});

Upvotes: 2

James_D
James_D

Reputation: 209674

I chose the simplest node to work with for this, but I think this would work in general:

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class ClickAndDragTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        final Pane pane = new Pane();
        final Circle circle = new Circle(100, 100, 50, Color.CORNFLOWERBLUE);
        pane.getChildren().add(circle);

        pane.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                circle.setCenterX(event.getX());
                circle.setCenterY(event.getY());
            }
        });
        pane.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                circle.setCenterX(event.getX());
                circle.setCenterY(event.getY());
            }
        });

        final Scene scene = new Scene(pane, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Upvotes: 2

Related Questions