Skip to content
Advertisement

Dragging an undecorated Stage in JavaFX

I would like to have a Stage set to “UNDECORATED” made draggable and minimizable. The problem is that I can’t find a way to do so since the examples I come accross that do this do so via methods inserted inside the main method.

I would like to have this done via a method declared in the controller class, like how I managed to do with the “WindowClose()” method below.

This is my second day working with JavaFX, if this seems too much of a common knowledge question. Thank you all in advance.

// Main Class/ Method

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class Fxmltableview extends Application {

    public static String pageSource = "fxml_tableview.fxml";
    public static Scene scene;

    @Override
    public void start(Stage stage) throws Exception {
        stage.initStyle(StageStyle.UNDECORATED);
        stage.initStyle(StageStyle.TRANSPARENT);

        Parent root = FXMLLoader.load(getClass().getResource(pageSource));

        scene = new Scene(root, Color.TRANSPARENT);

        stage.setScene(scene);
        stage.show();
    }

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

..

// The Controller

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

public class FXMLTableViewController {
    @FXML private TableView<Person> tableView;
    @FXML private TextField firstNameField;
    @FXML private TextField lastNameField;
    @FXML private TextField emailField;

    @FXML
    protected void addPerson (ActionEvent event) {
        ObservableList<Person> data = tableView.getItems();
        data.add(new Person(
                firstNameField.getText(),
                lastNameField.getText(),
                emailField.getText()
                ));

        firstNameField.setText("");
        lastNameField.setText("");
        emailField.setText("");   
    }

    public void WindowClose (ActionEvent event) {
            Platform.exit();
    }
}

Advertisement

Answer

Strategy

You already have a reference to the stage in your start method.

What you need is to be able to pass the stage to your controller, so that the controller can make the stage draggable by a given node.

  1. The “Passing Parameters Directly From the Caller to the Controller” method may be used to pass a stage reference to your controller: Passing Parameters JavaFX FXML.

  2. Use the makeDraggable method from this sample code to allow the stage to be dragged by a given node.

Sample Implementation

Some psuedo-code for the Application start method follows:

stage.initStyle(StageStyle.UNDECORATED);
stage.initStyle(StageStyle.TRANSPARENT);

FXMLLoader loader = new FXMLLoader(
  getClass().getResource(
    "fxml_tableview.fxml"
  )
);

stage.setScene(
  new Scene(
    (Parent) loader.load()
  )
);

FXMLTableViewController controller = 
  loader.<FXMLTableViewController>getController();
controller.registerStage(stage);

stage.show();

And for the Controller’s new registerStage method:

@FXML private Rectangle dragNode;

public void registerStage(Stage stage) {
  EffectUtilities.makeDraggable(stage, dragNode)
}

EffectUtilities.makeDraggable() comes from the sample code I linked earlier.

Update your fxml_tableview.fxml file to include the required new dragNode referenced in the controller.

Alternate implementation

In the initialize method of the controller, add a change listener on the dragNode’s sceneProperty and the changed scene’s window property to get notified of the stage change so that you can invoke makeDraggable.

Sample Code EffectUtilities.makeDraggable(stage, byNode)

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import javafx.util.Duration;

/** Various utilities for applying different effects to nodes. */
public class EffectUtilities {
  /** makes a stage draggable using a given node */
  public static void makeDraggable(final Stage stage, final Node byNode) {
    final Delta dragDelta = new Delta();
    byNode.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();
        byNode.setCursor(Cursor.MOVE);
      }
    });
    byNode.setOnMouseReleased(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        byNode.setCursor(Cursor.HAND);
      }
    });
    byNode.setOnMouseDragged(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        stage.setX(mouseEvent.getScreenX() + dragDelta.x);
        stage.setY(mouseEvent.getScreenY() + dragDelta.y);
      }
    });
    byNode.setOnMouseEntered(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.isPrimaryButtonDown()) {
          byNode.setCursor(Cursor.HAND);
        }
      }
    });
    byNode.setOnMouseExited(new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent mouseEvent) {
        if (!mouseEvent.isPrimaryButtonDown()) {
          byNode.setCursor(Cursor.DEFAULT);
        }
      }
    });
  }

  /** records relative x and y co-ordinates. */
  private static class Delta {
    double x, y;
  }
}
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement