Customize prefix text entry

To only allow digits and to respect the digitsPerGroup spacing
This commit is contained in:
Nathan McRae 2025-01-19 21:34:10 -08:00
parent b32807de9e
commit 40a4aefa93
3 changed files with 218 additions and 7 deletions

View File

@ -0,0 +1,145 @@
package name.nathanmcrae.numbersstation;
import java.lang.StringBuilder;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class AddPrefixController {
@FXML
private TextField prefixField;
@FXML
private Button okButton;
@FXML
private Button cancelButton;
private int digitsPerGroup;
private MainSettingsController mainSettingsController;
public void setDigitsPerGroup(int newDigitsPerGroup) {
digitsPerGroup = newDigitsPerGroup;
}
public void setMainSettingsController(MainSettingsController mainSettingsController) {
this.mainSettingsController = mainSettingsController;
}
@FXML
private void initialize() {
okButton.setOnAction(event -> handleOkButtonPress());
cancelButton.setOnAction(event -> handleCancelButtonPress());
// Set focus on the text field when the window is shown
Platform.runLater(() -> prefixField.requestFocus());
// Add event filter to handle Enter key press
prefixField.addEventFilter(KeyEvent.ANY, event -> {
if (event.getCode() == KeyCode.ENTER) {
handleOkButtonPress();
event.consume();
}
int cursorPosition = prefixField.getCaretPosition();
String character = event.getCharacter();
KeyCode code = event.getCode();
String str = event.getText();
// Consume the event to block the default behavior
if (!(code == KeyCode.DOWN ||
code == KeyCode.END ||
code == KeyCode.HOME ||
code == KeyCode.LEFT ||
code == KeyCode.PAGE_DOWN ||
code == KeyCode.PAGE_UP ||
code == KeyCode.RIGHT ||
code == KeyCode.UP)) {
event.consume();
}
if (event.getEventType() == KeyEvent.KEY_PRESSED &&
(code == KeyCode.BACK_SPACE ||
code == KeyCode.DELETE)) {
String newText = prefixField.getText();
if (code == KeyCode.BACK_SPACE) {
newText = (newText.substring(0, cursorPosition - 1) + newText.substring(cursorPosition)).replaceAll(" ", "");
}
else if (code == KeyCode.DELETE) {
newText = (newText.substring(0, cursorPosition) + newText.substring(cursorPosition + 1)).replaceAll(" ", "");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < newText.length(); i++) {
sb.append(newText.charAt(i));
if ((i + 1) % (digitsPerGroup) == 0) {
sb.append(' ');
}
}
prefixField.setText(sb.toString());
if (code == KeyCode.BACK_SPACE) {
prefixField.positionCaret(cursorPosition - 1);
} else if (code == KeyCode.DELETE) {
prefixField.positionCaret(cursorPosition);
}
}
if (event.getEventType() == KeyEvent.KEY_PRESSED &&
(code == KeyCode.DIGIT0 ||
code == KeyCode.DIGIT1 ||
code == KeyCode.DIGIT2 ||
code == KeyCode.DIGIT3 ||
code == KeyCode.DIGIT4 ||
code == KeyCode.DIGIT5 ||
code == KeyCode.DIGIT6 ||
code == KeyCode.DIGIT7 ||
code == KeyCode.DIGIT8 ||
code == KeyCode.DIGIT9)) {
String newText = prefixField.getText().substring(0, cursorPosition) + str + prefixField.getText().substring(cursorPosition);
newText = newText.replaceAll(" ", "");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < newText.length(); i++) {
sb.append(newText.charAt(i));
if ((i + 1) % (digitsPerGroup) == 0) {
sb.append(' ');
}
}
prefixField.setText(sb.toString());
if (prefixField.getText().charAt(cursorPosition) == ' ') {
cursorPosition++;
}
prefixField.positionCaret(cursorPosition + 1);
}
});
}
private void handleOkButtonPress() {
String prefix = prefixField.getText();
if (prefix != null && !prefix.isEmpty()) {
mainSettingsController.addPrefix(prefix.replaceAll(" ", ""));
closeWindow();
}
}
private void handleCancelButtonPress() {
closeWindow();
}
private void closeWindow() {
Stage stage = (Stage) okButton.getScene().getWindow();
stage.close();
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="92.0" prefWidth="312.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="name.nathanmcrae.numbersstation.AddPrefixController">
<children>
<AnchorPane prefHeight="200.0" prefWidth="312.0">
<children>
<Button fx:id="okButton" layoutX="89.0" layoutY="67.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="65.0" text="OK" AnchorPane.bottomAnchor="15.0" AnchorPane.rightAnchor="89.0" />
<Button fx:id="cancelButton" layoutX="169.0" layoutY="67.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="65.0" text="Cancel" AnchorPane.bottomAnchor="15.0" AnchorPane.rightAnchor="9.0" />
<Label layoutX="14.0" layoutY="14.0" text="Enter a message prefix / identifier:" />
<TextField fx:id="prefixField" layoutX="203.0" layoutY="10.0" prefHeight="25.0" prefWidth="105.0" AnchorPane.leftAnchor="203.0" AnchorPane.rightAnchor="10.0" />
</children>
</AnchorPane>
</children>
</VBox>

View File

@ -21,8 +21,10 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Spinner;
@ -33,7 +35,11 @@ import javafx.scene.control.ToggleGroup;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainSettingsController {
private IntegerProperty digitsPerGroup = new SimpleIntegerProperty();
@ -278,19 +284,59 @@ public class MainSettingsController {
stage.setUserData(false);
}
});
prefixListView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> listView) {
return new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
String newText = item.replaceAll(" ", "");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < newText.length(); i++) {
sb.append(newText.charAt(i));
if ((i + 1) % (digitsPerGroup.get()) == 0) {
sb.append(' ');
}
}
setText(sb.toString());
}
}
};
}
});
}
@FXML
private void handleAddPrefixButtonPress() {
TextInputDialog dialog = new TextInputDialog();
dialog.setTitle("New prefix");
dialog.setHeaderText("Add a new prefix");
dialog.setContentText("Enter a message prefix / identifier:");
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("AddPrefixView.fxml"));
Parent root = loader.load();
Optional<String> result = dialog.showAndWait();
result.ifPresent(prefix -> {
AddPrefixController controller = loader.getController();
controller.setMainSettingsController(this);
controller.setDigitsPerGroup(settings.getDigitsPerGroup());
Stage stage = new Stage();
stage.setTitle("New prefix");
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addPrefix(String prefix) {
if (!(prefix == null || prefix.isEmpty())) {
prefixListView.getItems().add(prefix);
});
}
}
@FXML