Reputation: 71
I’m trying to implement a caps lock alert on password field. If caps lock is ON then the bubble will appear below the password field. I’ve searched a lot but didn’t get any solution that how can I implement such bubble on input fields in JavaFX. I’ve found some source code to get the caps lock state.
boolean isOn=Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
scene.setOnKeyReleased( event -> {
if ( event.getCode() == KeyCode.CAPS ) {
System.out.println("Capslock pressed");
System.out.println("Capslock state: " + isOn);
}
});
But my problem is how to implement the bubble alert on text field. Here you can see what I have to do.
It would be helpful if you suggest me some possible ways as I’m new in JavaFX. Is there any JavaFX library to do such bubble alert on input fields?
Upvotes: 6
Views: 983
Reputation: 44345
Update: As of JavaFX 17, you can use Platform.isKeyLocked:
BooleanProperty capsLockOn = new SimpleBooleanProperty();
Animation timer = new Timeline(new KeyFrame(Duration.millis(500),
e -> capsLockOn.set(
Platform.isKeyLocked(KeyCode.CAPS).orElse(false))));
timer.setCycleCount(Animation.INDEFINITE);
timer.play();
Region message = new BorderPane(new Label("Caps Lock is on"));
message.setStyle(
"-fx-background-color: #f4f4f4;" +
"-fx-border-color: black;" +
"-fx-border-width: 1px;" +
"-fx-padding: 1em 1em 0.75em 1em;" +
"-fx-shape: 'M 0 10 h 20 l 10 -10 l 10 10 h 150 v 90 h -190 z';"
);
Popup capsLockWarning = new Popup();
capsLockWarning.getContent().add(message);
capsLockOn.addListener((o, wasOn, on) -> {
if (on) {
Point2D location =
passwordField.localToScreen(-15, passwordField.getHeight());
capsLockWarning.show(passwordField,
location.getX(), location.getY());
} else {
capsLockWarning.hide();
}
});
(The following also works, but is needlessly complicated.)
JavaFX doesn’t have any way to detect CapsLock. In theory, you could install a Scene-wide listener, but that wouldn’t catch when the state changes while other applications have focus.
Mixing AWT/Swing and JavaFX is perilous, because each has its own thread on which nearly all of its methods must be executed. Since CapsLock needs to be polled anyway, it makes sense to use javax.swing.Timer, which both executes an action regularly and ensures that action is run in the proper thread (the AWT event dispatch thread):
BooleanProperty capsLockOn = new SimpleBooleanProperty();
EventQueue.invokeLater(() -> {
Timer timer = new Timer(500, e -> {
boolean state = Toolkit.getDefaultToolkit().getLockingKeyState(
KeyEvent.VK_CAPS_LOCK);
Platform.runLater(() -> capsLockOn.set(state));
});
timer.start();
Platform.runLater(() -> {
Window window = passwordField.getScene().getWindow();
window.setOnShown(e -> EventQueue.invokeLater(timer::restart));
window.setOnHidden(e -> EventQueue.invokeLater(timer::stop));
});
});
Region message = new BorderPane(new Label("Caps Lock is on"));
message.setStyle(
"-fx-background-color: #f4f4f4;" +
"-fx-border-color: black;" +
"-fx-border-width: 1px;" +
"-fx-padding: 1em 1em 0.75em 1em;" +
"-fx-shape: 'M 0 10 h 20 l 10 -10 l 10 10 h 150 v 90 h -190 z';"
);
Popup capsLockWarning = new Popup();
capsLockWarning.getContent().add(message);
capsLockOn.addListener((o, wasOn, on) -> {
if (on) {
Point2D location =
passwordField.localToScreen(-15, passwordField.getHeight());
capsLockWarning.show(passwordField,
location.getX(), location.getY());
} else {
capsLockWarning.hide();
}
});
Upvotes: 1
Reputation: 3187
It sounds like you have figured out how to get the input state you could try something like this for the listener
public class Main extends Application {
private Label capsLabel = new Label("Caps is ON");
private boolean capsIsOn;
@Override
public void start(Stage stage) {
System.out.println(Toolkit.getDefaultToolkit().getLockingKeyState(20));
//Try adding this line to get state on startup
capsLabel.setVisible(Toolkit.getDefaultToolkit().getLockingKeyState(20));
TextField textField = new TextField();
//Also try adding this line and to check again so when the field
//is selected it will check again
textField.setOnMouseClicked(event -> capsLabel.setVisible(Toolkit.getDefaultToolkit().getLockingKeyState(20)));
textField.setOnKeyReleased(keyEvent -> {
if(keyEvent.getCode().toString().equals("CAPS")){
capsIsOn = !capsIsOn;
capsLabel.setVisible(capsIsOn);
}
});
VBox vBox = new VBox();
vBox.getChildren().addAll(textField, capsLabel);
stage = new Stage();
stage.setScene(new Scene(vBox));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Alternatively you could set this on a timer and have it constantly checking personally I don't like the idea of constant use of computer resources but its not my project.
Upvotes: 1