Search code examples
javauser-interfacebuttonjavafxjnativehook

How to wait for a key to be pressed?


I'm currently using jnativhook from: https://github.com/kwhat/jnativehook to listen for nativekeys. I was trying to make a button that waits for a nativekey to be pressed, and sets that input as the new hotkey. Here's what I've tried so far, but when the user clicks it, they have to type the key, and press the button again. It's supposed to allow the user to click on it once, and once there's a input, it'll change the label.

import com.github.kwhat.jnativehook.GlobalScreen;
import com.github.kwhat.jnativehook.NativeHookException;
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener;
import com.github.kwhat.jnativehook.mouse.NativeMouseInputListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.animation.AnimationTimer;

public class HotKeyWindowController implements NativeKeyListener {
    @FXML
    private Label stopHotKeyLabel;
    @FXML
    private Label startHotKeyLabel;
    @FXML
    private Button stopHotKeyButton;
    @FXML
    private Button startHotKeyButton;
    private static String startKey = "F1";
    private static String stopKey = "F3";
    private static String key;


    public void nativeKeyPressed(NativeKeyEvent e) {

        key = NativeKeyEvent.getKeyText(e.getKeyCode());
    }
    public void stopHotKeyButtonClicked(ActionEvent actionEvent) {
        GlobalScreen.removeNativeKeyListener(new HelloApplication());
        GlobalScreen.addNativeKeyListener(new HotKeyWindowController());
        stopKey = key;
        stopHotKeyLabel.setText("Stop HotKey: " + stopKey);
        GlobalScreen.addNativeKeyListener(new HelloApplication());
        GlobalScreen.removeNativeKeyListener(new HotKeyWindowController());
    }

    public void startHotKeyButtonClicked(ActionEvent actionEvent) {

    }
}


Solution

  • I don't think you need to involve native key listeners here at all. A regular JavaFX key event handler on a button will only be invoked if the button has keyboard focus, which typically only happens if it is pressed (but see note later). Thus if the functionality you want is for the label to be changed (and presumably other actions to occur) when a key is pressed after a button has been pressed, it should be enough to simply register a key event handler with the button. Here is a quick example:

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.GridPane;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class HelloApplication extends Application {
        @Override
        public void start(Stage stage) throws IOException {
            Label startHotkeyLabel = new Label("Start HotKey: F1");
            Label stopHotkeyLabel = new Label("Stop HotKey: F3");
            Button changeStartHotkey = new Button("Change start key");
            changeStartHotkey.setOnKeyReleased(e -> startHotkeyLabel.setText("Start HotKey: "+e.getCode()));
            Button changeStopHotkey = new Button("Change stop key");
            changeStopHotkey.setOnKeyReleased(e -> stopHotkeyLabel.setText("Stop HotKey: "+e.getCode()));
            GridPane grid = new GridPane();
            grid.setHgap(5);
            grid.setVgap(5);
            grid.setPadding(new Insets(10));
            grid.setAlignment(Pos.CENTER);
            TextField textField = new TextField();
            grid.add(textField, 0, 0, 2, 1);
            grid.add(changeStartHotkey, 0, 1);
            grid.add(startHotkeyLabel, 1, 1);
            grid.add(changeStopHotkey,0, 2);
            grid.add(stopHotkeyLabel, 1, 2);
    
            Scene scene = new Scene(grid);
            stage.setScene(scene);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    Note that the corresponding "hot key" will change any time the button has keyboard focus. So if you use the Tab key to navigate keyboard focus to one of the buttons and type a key, the corresponding label will change. This is probably what you want anyway; it would be what a user would expect. However, if you don't want that, it's easy enough to register the key event handler in the button's onAction() handler, and de-register it when the button loses focus.


    Other general solutions (that don't involve native key listeners) are possible too. For example: Register a key event handler with the scene, and in that handler check the state of your application (encapsulated in some appropriate variables) and change whatever needs to be changed (if anything). In the buttons' action handlers, update the state of the application so the correct thing happens when a key is pressed.