Skip to content

Commit ca18472

Browse files
committed
Merge pull request #328 from ThomasJClark/spinner-bug
Fix the buggy spinner validation
2 parents 2d7b748 + 85455d0 commit ca18472

File tree

3 files changed

+51
-35
lines changed

3 files changed

+51
-35
lines changed

ui/src/main/java/edu/wpi/grip/ui/deployment/FRCDeploymentOptionsController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.io.OutputStream;
1616
import java.net.InetAddress;
17+
import java.text.NumberFormat;
1718
import java.util.function.Consumer;
1819
import java.util.function.Supplier;
1920

@@ -31,7 +32,7 @@ FRCDeploymentOptionsController create(
3132
Consumer<DeployedInstanceManager> onDeployCallback,
3233
@Assisted("stdOut") Supplier<OutputStream> stdOut,
3334
@Assisted("stdErr") Supplier<OutputStream> stdErr
34-
);
35+
);
3536
}
3637

3738
@Inject
@@ -53,14 +54,13 @@ protected void postInit() {
5354
final SpinnerValueFactory.IntegerSpinnerValueFactory spinnerValueFactory =
5455
new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, teamNumber);
5556
this.teamNumberSpinner = new Spinner(spinnerValueFactory);
56-
Spinners.makeEditableSafely(teamNumberSpinner, Integer::valueOf);
57+
Spinners.makeEditableSafely(teamNumberSpinner, NumberFormat.getIntegerInstance(), 0);
5758
label.setLabelFor(teamNumberSpinner);
5859

5960
getOptionsGrid().addRow(0, label, this.teamNumberSpinner);
6061
}
6162

6263

63-
6464
@Override
6565
protected Promise<DeployedInstanceManager, String, String> onDeploy() {
6666
final Deferred<DeployedInstanceManager, String, String> deferredDeploy = new DeferredObject<>();

ui/src/main/java/edu/wpi/grip/ui/pipeline/input/NumberSpinnerInputSocketController.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import javafx.scene.control.Spinner;
1414
import javafx.scene.control.SpinnerValueFactory;
1515

16+
import java.text.NumberFormat;
17+
1618
import static com.google.common.base.Preconditions.checkArgument;
1719

1820
/**
@@ -55,7 +57,8 @@ public interface Factory {
5557
public void initialize() {
5658
super.initialize();
5759
final Spinner<Double> spinner = new Spinner<>(this.valueFactory);
58-
Spinners.makeEditableSafely(spinner, Double::valueOf);
60+
Spinners.makeEditableSafely(spinner, NumberFormat.getNumberInstance(),
61+
getSocket().getSocketHint().createInitialValue().get().doubleValue());
5962
spinner.disableProperty().bind(this.getHandle().connectedProperty());
6063

6164
this.setContent(spinner);

ui/src/main/java/edu/wpi/grip/ui/util/Spinners.java

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33

44
import javafx.scene.control.Spinner;
55
import javafx.scene.control.SpinnerValueFactory;
6+
import javafx.scene.control.TextField;
7+
import javafx.scene.control.TextFormatter;
68
import javafx.util.StringConverter;
79

8-
import java.util.function.Function;
10+
import java.text.NumberFormat;
11+
import java.text.ParsePosition;
12+
import java.util.Optional;
913

1014
/**
1115
* Utility methods to set up spinners in a safe way.
@@ -19,43 +23,52 @@ private Spinners() {
1923
/**
2024
* Makes the spinner editable.
2125
* Ensures that the values will be committed when focus is lost.
22-
* Ensures that the only values that the spinner can accept are parable into whatever number type they represent.
23-
* @param <T> The number type of the spinner
26+
*
27+
* @param defaultValue The value to use if nothing is entered
28+
* @param <T> The number type of the spinner
2429
*/
2530
@SuppressWarnings("PMD.IfElseStmtsMustUseBraces")
26-
public static <T extends Number> void makeEditableSafely(Spinner<T> spinner, Function<String, T> parseToNumber) {
31+
public static <T extends Number> void makeEditableSafely(Spinner<T> spinner, NumberFormat format, T defaultValue) {
2732
spinner.setEditable(true);
28-
spinner.focusedProperty().addListener((s, ov, nv) -> {
29-
if (nv) return;
30-
commitEditorText(spinner);
33+
34+
TextField editor = spinner.getEditor();
35+
36+
// Override the converter used to interpret editor values. If there's an error, we want to set the default
37+
// value instead of throwing an exception.
38+
StringConverter<T> oldConverter = spinner.getValueFactory().getConverter();
39+
spinner.getValueFactory().setConverter(new StringConverter<T>() {
40+
@Override
41+
public String toString(T t) {
42+
return oldConverter.toString(t);
43+
}
44+
45+
@Override
46+
public T fromString(String s) {
47+
return Optional.ofNullable(oldConverter.fromString(s)).orElse(defaultValue);
48+
}
3149
});
32-
// Ensure the value entered is only a number
33-
spinner.getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
34-
if ("".equals(newValue)) {
35-
spinner.getEditor().setText("0");
36-
} else try {
37-
T value = parseToNumber.apply(newValue);
38-
spinner.getEditor().setText(value.toString());
39-
} catch (NumberFormatException e) {
40-
spinner.getEditor().setText(oldValue);
50+
51+
// Commit the spinner value when the editor loses focus
52+
editor.focusedProperty().addListener((s, ov, focused) -> {
53+
if (!focused) {
54+
SpinnerValueFactory<T> valueFactory = spinner.getValueFactory();
55+
StringConverter<T> converter = valueFactory.getConverter();
56+
valueFactory.setValue(converter.fromString(editor.getText()));
4157
}
4258
});
43-
}
4459

45-
/**
46-
*
47-
* @see <a href="http://stackoverflow.com/questions/32340476/manually-typing-in-text-in-javafx-spinner-is-not-updating-the-value-unless-user">Stack Overflow Post</a>
48-
*/
49-
private static <T> void commitEditorText(Spinner<T> spinner) {
50-
if (!spinner.isEditable()) return;
51-
String text = spinner.getEditor().getText();
52-
SpinnerValueFactory<T> valueFactory = spinner.getValueFactory();
53-
if (valueFactory != null) {
54-
StringConverter<T> converter = valueFactory.getConverter();
55-
if (converter != null) {
56-
T value = converter.fromString(text);
57-
valueFactory.setValue(value);
60+
// Filter out invalid editor changes
61+
editor.setTextFormatter(new TextFormatter<T>(change -> {
62+
if (change.isContentChange() && !change.getControlNewText().isEmpty()) {
63+
ParsePosition position = new ParsePosition(0);
64+
format.parse(change.getControlNewText(), position);
65+
66+
if (position.getIndex() == 0 || position.getIndex() < change.getControlNewText().length()) {
67+
return null;
68+
}
5869
}
59-
}
70+
71+
return change;
72+
}));
6073
}
6174
}

0 commit comments

Comments
 (0)