24
votes

I've been trying to move an undecorated stage around the screen, by using the following mouse listeners:

  • onPressed
  • onReleased
  • onDragged

These events are from a rectangle. My idea is to move the undecorated window clicking on the rectangle and dragging all the window.

@FXML
protected void onRectanglePressed(MouseEvent event) {
    X = primaryStage.getX() - event.getScreenX();
    Y = primaryStage.getY() - event.getScreenY();
}

@FXML
protected void onRectangleReleased(MouseEvent event) {
    primaryStage.setX(event.getScreenX());
    primaryStage.setY(event.getScreenY());
}

@FXML
protected void onRectangleDragged(MouseEvent event) {
    primaryStage.setX(event.getScreenX() + X);
    primaryStage.setY(event.getScreenY() + Y);
}

All that I've got with these events is when I press the rectangle and start to drag the window, it moves a little bit. But, when I release the button, the window is moved to where the rectangle is.

Thanks in advance.

5
No need to handle the event onRectangleReleased.Uluk Biy

5 Answers

30
votes

I created a sample of an animated clock in an undecorated window which you can drag around.

Relevant code from the sample is:

// allow the clock background to be used to drag the clock around.
final Delta dragDelta = new Delta();
layout.setOnMousePressed(new EventHandler<MouseEvent>() {
  @Override public void handle(MouseEvent mouseEvent) {
    // record a delta distance for the drag and drop operation.
    dragDelta.x = stage.getX() - mouseEvent.getScreenX();
    dragDelta.y = stage.getY() - mouseEvent.getScreenY();
  }
});
layout.setOnMouseDragged(new EventHandler<MouseEvent>() {
  @Override public void handle(MouseEvent mouseEvent) {
    stage.setX(mouseEvent.getScreenX() + dragDelta.x);
    stage.setY(mouseEvent.getScreenY() + dragDelta.y);
  }
});

...

// records relative x and y co-ordinates.
class Delta { double x, y; } 

Code looks pretty similar to yours, so not quite sure why your code is not working for you.

14
votes

I'm using this solution for dragging undecoraqted stages by dragging any contained node.

private void addDraggableNode(final Node node) {

    node.setOnMousePressed(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent me) {
            if (me.getButton() != MouseButton.MIDDLE) {
                initialX = me.getSceneX();
                initialY = me.getSceneY();
            }
        }
    });

    node.setOnMouseDragged(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent me) {
            if (me.getButton() != MouseButton.MIDDLE) {
                node.getScene().getWindow().setX(me.getScreenX() - initialX);
                node.getScene().getWindow().setY(me.getScreenY() - initialY);
            }
        }
    });
}
4
votes

Based on jewelsea's reply i made two lamba expressions to set the MouseListeners directly on the scene in my start() method. Works fine :)

private double xOffset;
private double yOffset;
         /*
        The two following lambda expressions makes it possible to move the application without the standard StageStyle
         */
        //Lambda mouse event handler
        scene.setOnMousePressed(event -> {
            xOffset = primaryStage.getX() - event.getScreenX();
            yOffset = primaryStage.getY() - event.getScreenY();
        });
        //Lambda mouse event handler
        scene.setOnMouseDragged(event -> {
            primaryStage.setX(event.getScreenX() + xOffset);
            primaryStage.setY(event.getScreenY() + yOffset);
        });enter code here
3
votes

My guess is that your:

onRectangleReleased()

is passing the very same coordinates from your

onRectanglePressed()

This happens because as stated in the documentation the getX() method from the MouseEvent class returns

Horizontal position of the event relative to the origin of the MouseEvent's source.

then, when you release your mouse it actually puts your rectangle in the place it was when you first mouse pressed.

BTW, I use a StackPane with a Rectangle inside and then apply the following code:

private void addDragListeners(final Node n){
       n.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent me) {
                if(me.getButton()!=MouseButton.MIDDLE)
                {
                    initialX = me.getSceneX();
                    initialY = me.getSceneY();
                }
                else
                {
                    n.getScene().getWindow().centerOnScreen();
                    initialX = n.getScene().getWindow().getX();
                    initialY = n.getScene().getWindow().getY();
                }

            }
       });

       n.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent me) {
                if(me.getButton()!=MouseButton.MIDDLE)
                {
                    n.getScene().getWindow().setX( me.getScreenX() - initialX );
                    n.getScene().getWindow().setY( me.getScreenY() - initialY);
                }
            }
        });
}

addDragListeners(mainStackPane);

Besides that, I use the Middle Mouse Button to center my Window on the screen. I hoped it helped. Cheers!

1
votes
double x, y;

private void addDragListeners(final Node n, Stage primaryStage){

    n.setOnMousePressed((MouseEvent mouseEvent) -> {
        this.x = n.getScene().getWindow().getX() - mouseEvent.getScreenX();
        this.y = n.getScene().getWindow().getY() - mouseEvent.getScreenY();
    });

    n.setOnMouseDragged((MouseEvent mouseEvent) -> {
        primaryStage.setX(mouseEvent.getScreenX() + this.x);
        primaryStage.setY(mouseEvent.getScreenY() + this.y);
    });
}

public void start(Stage primaryStage) {

    try {

        ...

        addDragListeners(mainPane, primaryStage);

    } catch (Exception e) {
        e.printStackTrace(System.out);
    }

}