As I pointed out in my first answer, the virtual keyboard is built into PopupWindow , created at another stage, and is displayed on top of the current scene.
The -Dcom.sun.javafx.vk.adjustwindow=true option works by moving the main scene so that the control is visible and there is no overlap.
But when this input control is located at the bottom of the main cascade, it moves to the center of the screen, leaving a large blank space that indicates what it costs.
This second answer gives the decision to move the main scene only to the required distance without any gap, also taking into account the fade in / out animation of the virtual keyboard.
First, in our scene we will add a Button to the center, and a TextField to the bottom. With two controls, we can easily change focus and show / hide the keyboard.
To maximize the scene, I use getVisualBounds() , so the taskbar may be visible.
private PopupWindow keyboard; private final Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds(); private final Rectangle2D bounds = Screen.getPrimary().getBounds(); private final double taskbarHeight=bounds.getHeight()-visualBounds.getHeight(); @Override public void start(Stage stage) { TextField tfComment = new TextField(); tfComment.setPromptText("Enter comment"); BorderPane borderPane = new BorderPane(new Button("Click")); borderPane.setBottom(tfComment); Scene scene = new Scene(borderPane); stage.setScene(scene); stage.setX(visualBounds.getMinX()); stage.setY(visualBounds.getMinY()); stage.setWidth(visualBounds.getWidth()); stage.setHeight(visualBounds.getHeight()); stage.show(); }
We need to find a new stage when it will be shown. Just like Scenic View , we will use the legacy method to get the correct window:
private PopupWindow getPopupWindow() { @SuppressWarnings("deprecation") final Iterator<Window> windows = Window.impl_getWindows(); while (windows.hasNext()) { final Window window = windows.next(); if (window instanceof PopupWindow) { if(window.getScene()!=null && window.getScene().getRoot()!=null){ Parent root = window.getScene().getRoot(); if(root.getChildrenUnmodifiable().size()>0){ Node popup = root.getChildrenUnmodifiable().get(0); if(popup.lookup(".fxvk")!=null){ return (PopupWindow)window; } } } return null; } } return null; }
We will call this method when the text field receives focus:
... stage.show(); tfComment.focusedProperty().addListener((ob,b,b1)->{ if(keyboard==null){ keyboard=getPopupWindow(); } }); }
As soon as a window appears, we can listen to the changes in its position and accordingly move the main stage:
.... stage.show(); //findWindowExecutor.execute(new WindowTask()); tfComment.focusedProperty().addListener((ob,b,b1)->{ if(keyboard==null){ keyboard=getPopupWindow(); keyboard.yProperty().addListener(obs->{ System.out.println("wi "+keyboard.getY()); Platform.runLater(()->{ double y = bounds.getHeight()-taskbarHeight-keyboard.getY(); stage.setY(y>0?-y:0); }); }); } }); }
Note that instead of moving around the scene, another parameter will change its size (if there is enough space in the controls).
This will be the case when the text field receives focus and the virtual keyboard is fully displayed:
