July 16, 2016
In the previous article, the standard windowing chrome and controls of a Stage were removed by calling the initStyle() method with StageStyle.UNDECORATED. An app-specific title bar including a window close button was added. However, there wasn't a mechanism to move the window. The window was displayed in the middle of the screen and could only be pushed to the background or closed.
This article will allow the user to move the window by selecting the title bar and dragging to another location on the screen.
These steps wire several controller methods to the FXML file. There will be event handlers for the start of the move operation, the end of the move operation, and one for the during the move operation.
Next, add the methods to the controller.
@FXML
public void startMoveWindow(MouseEvent evt) {
System.out.println("start");
}
@FXML
public void moveWindow(MouseEvent evt) {
System.out.println("moving");
}
@FXML
public void endMoveWindow(MouseEvent evt) {
System.out.println("end");
}
Test to make sure that the FXML is wired correctly to the controller. Start the app and click-drag-release on the title bar. printlns for the start, moving, and end events should be displayed.
The controller class will need several variables to track the moving operation.
@FXML
VBox vbox;
private double startMoveX = -1, startMoveY = -1;
private Boolean dragging = false;
private Rectangle moveTrackingRect;
private Popup moveTrackingPopup;
vbox is referenced from the fxml file. It is the container of all the components including the title bar.
startMoveX and startMoveY are recorded at the start of a move operation and they are used to compute an ending position. They are also used in a computation that moves around a Rectangle, moveTrackingRect, during the operation. The Rectangle is a visual cue that will go away when the operation is ended.
dragging is a state variable used to ignore MouseEvents that are not involved in the move operation.
moveTrackingRect is an outline showing what the move operation might look like. moveTrackingPopup is the windowing container for the Rectangle.
@FXML
public void startMoveWindow(MouseEvent evt) {
startMoveX = evt.getScreenX();
startMoveY = evt.getScreenY();
dragging = true;
moveTrackingRect = new Rectangle();
moveTrackingRect.setWidth(vbox.getWidth());
moveTrackingRect.setHeight(vbox.getHeight());
moveTrackingRect.getStyleClass().add( "tracking-rect" );
moveTrackingPopup = new Popup();
moveTrackingPopup.getContent().add(moveTrackingRect);
moveTrackingPopup.show(vbox.getScene().getWindow());
moveTrackingPopup.setOnHidden( (e) -> resetMoveOperation());
}
private void resetMoveOperation() {
startMoveX = 0;
startMoveY = 0;
dragging = false;
moveTrackingRect = null;
}
startMoveWindow is registered to the On Drag Detected event on the title bar. In startMoveWindow, the starting point is set using the Screen position. Next, the dragging flag is set. The tracking rectangle, moveTrackingRect, is created as a slightly-transparent white Rectangle with a border. moveTrackingRectangle is added to a Popup, moveTrackingPopup, which displays the Rectangle on the screen.
@FXML
public void moveWindow(MouseEvent evt) {
if (dragging) {
double endMoveX = evt.getScreenX();
double endMoveY = evt.getScreenY();
Window w = vbox.getScene().getWindow();
double stageX = w.getX();
double stageY = w.getY();
moveTrackingPopup.setX(stageX + (endMoveX - startMoveX));
moveTrackingPopup.setY(stageY + (endMoveY - startMoveY));
}
}
If the app is in a dragging state, calculate an offset and move the tracking Popup moveTrackingPopup accordingly.
@FXML
public void endMoveWindow(MouseEvent evt) {
if (dragging) {
double endMoveX = evt.getScreenX();
double endMoveY = evt.getScreenY();
Window w = vbox.getScene().getWindow();
double stageX = w.getX();
double stageY = w.getY();
w.setX(stageX + (endMoveX - startMoveX));
w.setY(stageY + (endMoveY - startMoveY));
if (moveTrackingPopup != null) {
moveTrackingPopup.hide();
moveTrackingPopup = null;
}
}
resetMoveOperation();
}
Finally, the same computation is applied at the end of the move to the window being dragged as was used for the tracking Popup. The state variables (startMoveX, startMoveY, etc) are reset and the tracking Popup is closed. This is skipped if the tracking Popup was auto hidden via an ESC key press.
Save and run the program. Click-drag the title bar to reposition the window.
This example allowed the user to move the custom window. The next article will add maximize and minimize buttons to the program.
By Carl Walker
President and Principal Consultant of Bekwam, Inc