1

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) {

    }
}

Leo
  • 15
  • 7
  • 1
    *"It's supposed to allow the user to click on it once, and once there's a input, it'll change the label."*. When you press a button, it will gain keyboard focus. Why do you need to mess with `jnativehook` etc? Isn't it sufficient just to register a regular JavaFX key event handler (or filter) with the button? That handler will only be invoked if the button has focus. – James_D Feb 21 '23 at 19:45
  • Yeh, but after the button gets clicked, I'm trying to wait for a keyboard key to be pressed, and thus will change the label to that specific keyboard key. Initially, the label is "Stop Hotkey: F3", and after the button is pressed, the program should wait for the user to input a key on their keyboard, and the label should change accordingly - "Stop Hotkey: (keyboard input)". I'm unsure how to do that with javafx key event handler either. – Leo Feb 21 '23 at 20:18
  • *"Yeh, but after the button gets clicked, I'm trying to wait for a keyboard key to be pressed, and thus will change the label to that specific keyboard key."* So what's wrong with what I suggested? – James_D Feb 21 '23 at 20:20
  • Probably my awful understanding of your response, my apologies. I'm quite new to javafx. Can you perhaps provide an example for your first response? – Leo Feb 21 '23 at 20:22
  • 1
    What's the ultimate goal? Your description defies standard expectations of how an interface works. Buttons don't wait for input: they are pressed and the user expects that the action described by the text of the button will be performed, if conditions allow (e.g., validation may block the action). I just ran the `NativeHookDemo` to see what it could possibly contribute beyond the facilities provided by JavaFX for listening for such events. ... – pfurbacher Feb 21 '23 at 20:35
  • ... I was surprised that I had to change System accessibility security settings (macOS) to allow the demo to control my computer! Yikes! Also noted that even when I was typing in another app (e.g., Messages), the demo was capturing keystrokes. Not good. – pfurbacher Feb 21 '23 at 20:35
  • @pfurbacher I'm not trying to code any sketchy things, was just trying make a window for my autoclicker... – Leo Feb 21 '23 at 20:46

1 Answers1

4

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.

James_D
  • 201,275
  • 16
  • 291
  • 322