improve validation and focus handling in integer spinners

This commit is contained in:
Craig Raw
2022-05-17 10:52:43 +02:00
parent 766a8c267f
commit c51f3d9e66
9 changed files with 128 additions and 22 deletions
@@ -0,0 +1,83 @@
package com.sparrowwallet.sparrow.control;
import javafx.beans.NamedArg;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.util.converter.IntegerStringConverter;
public class IntegerSpinner extends Spinner<Integer> {
public IntegerSpinner() {
super();
setupEditor();
}
public IntegerSpinner(@NamedArg("min") int min,
@NamedArg("max") int max,
@NamedArg("initialValue") int initialValue) {
super(min, max, initialValue);
setupEditor();
}
public IntegerSpinner(@NamedArg("min") int min,
@NamedArg("max") int max,
@NamedArg("initialValue") int initialValue,
@NamedArg("amountToStepBy") int amountToStepBy) {
super(min, max, initialValue, amountToStepBy);
setupEditor();
}
private void setupEditor() {
getEditor().focusedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null && !newValue) {
commitValue();
}
});
getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue.matches("\\d*")) {
getEditor().setText(newValue.replaceAll("[^\\d]", ""));
}
});
}
public static class ValueFactory extends SpinnerValueFactory.IntegerSpinnerValueFactory {
public ValueFactory(@NamedArg("min") int min,
@NamedArg("max") int max) {
super(min, max);
setupConverter(min);
}
public ValueFactory(@NamedArg("min") int min,
@NamedArg("max") int max,
@NamedArg("initialValue") int initialValue) {
super(min, max, initialValue);
setupConverter(initialValue);
}
public ValueFactory(@NamedArg("min") int min,
@NamedArg("max") int max,
@NamedArg("initialValue") int initialValue,
@NamedArg("amountToStepBy") int amountToStepBy) {
super(min, max, initialValue, amountToStepBy);
setupConverter(initialValue);
}
private void setupConverter(Integer defaultValue) {
setConverter(new IntegerStringConverter() {
@Override
public Integer fromString(String value) {
if(value == null) {
return null;
}
value = value.trim();
if(value.length() < 1) {
return defaultValue;
}
return Integer.valueOf(value);
}
});
}
}
}
@@ -82,7 +82,7 @@ public class HeadersController extends TransactionFormController implements Init
private TransactionDiagram transactionDiagram;
@FXML
private Spinner<Integer> version;
private IntegerSpinner version;
@FXML
private CopyableLabel segwit;
@@ -112,10 +112,10 @@ public class HeadersController extends TransactionFormController implements Init
private Field locktimeDateField;
@FXML
private Spinner<Integer> locktimeNone;
private IntegerSpinner locktimeNone;
@FXML
private Spinner<Integer> locktimeBlock;
private IntegerSpinner locktimeBlock;
@FXML
private Hyperlink locktimeCurrentHeight;
@@ -243,8 +243,12 @@ public class HeadersController extends TransactionFormController implements Init
updateTxId();
version.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 2, (int)tx.getVersion()));
version.setValueFactory(new IntegerSpinner.ValueFactory(1, 2, (int)tx.getVersion()));
version.valueProperty().addListener((obs, oldValue, newValue) -> {
if(newValue == null || newValue < 1 || newValue > 2) {
return;
}
tx.setVersion(newValue);
if(oldValue != null) {
EventManager.get().post(new TransactionChangedEvent(tx));
@@ -305,9 +309,9 @@ public class HeadersController extends TransactionFormController implements Init
futureDateWarning.managedProperty().bind(futureDateWarning.visibleProperty());
futureDateWarning.setVisible(false);
locktimeNone.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, 0));
locktimeNone.setValueFactory(new IntegerSpinner.ValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, 0));
if(tx.getLocktime() < Transaction.MAX_BLOCK_LOCKTIME) {
locktimeBlock.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, (int)tx.getLocktime()));
locktimeBlock.setValueFactory(new IntegerSpinner.ValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, (int)tx.getLocktime()));
if(tx.getLocktime() == 0) {
locktimeToggleGroup.selectToggle(locktimeNoneType);
} else {
@@ -316,13 +320,17 @@ public class HeadersController extends TransactionFormController implements Init
LocalDateTime date = Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime();
locktimeDate.setDateTimeValue(date);
} else {
locktimeBlock.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1));
locktimeBlock.setValueFactory(new IntegerSpinner.ValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1));
LocalDateTime date = Instant.ofEpochSecond(tx.getLocktime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
locktimeDate.setDateTimeValue(date);
locktimeToggleGroup.selectToggle(locktimeDateType);
}
locktimeBlock.valueProperty().addListener((obs, oldValue, newValue) -> {
if(newValue == null || newValue < 0 || newValue >= Transaction.MAX_BLOCK_LOCKTIME) {
return;
}
tx.setLocktime(newValue);
locktimeCurrentHeight.setVisible(headersForm.isEditable() && AppServices.getCurrentBlockHeight() != null && newValue < AppServices.getCurrentBlockHeight());
futureBlockWarning.setVisible(AppServices.getCurrentBlockHeight() != null && newValue > AppServices.getCurrentBlockHeight());
@@ -104,7 +104,7 @@ public class InputController extends TransactionFormController implements Initia
private CopyableLabel locktimeAbsolute;
@FXML
private Spinner<Integer> locktimeRelativeBlocks;
private IntegerSpinner locktimeRelativeBlocks;
@FXML
private RelativeTimelockSpinner locktimeRelativeSeconds;
@@ -406,7 +406,7 @@ public class InputController extends TransactionFormController implements Initia
}
});
locktimeRelativeBlocks.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK, 0));
locktimeRelativeBlocks.setValueFactory(new IntegerSpinner.ValueFactory(0, (int)TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK, 0));
locktimeRelativeBlocks.managedProperty().bind(locktimeRelativeBlocks.visibleProperty());
locktimeRelativeSeconds.managedProperty().bind(locktimeRelativeSeconds.visibleProperty());
locktimeRelativeCombo.getSelectionModel().selectedItemProperty().addListener((ov, old_toggle, new_toggle) -> {
@@ -433,6 +433,10 @@ public class InputController extends TransactionFormController implements Initia
}
locktimeRelativeBlocks.valueProperty().addListener((obs, oldValue, newValue) -> {
if(newValue == null || newValue < 0 || newValue > TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK) {
return;
}
setRelativeLocktime(txInput, transaction, oldValue != null);
});
locktimeRelativeSeconds.valueProperty().addListener((obs, oldValue, newValue) -> {
@@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.wallet;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.DateStringConverter;
import com.sparrowwallet.sparrow.control.IntegerSpinner;
import com.sparrowwallet.sparrow.event.SettingsChangedEvent;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -10,8 +11,6 @@ import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.util.StringConverter;
import java.net.URL;
@@ -28,7 +27,7 @@ public class AdvancedController implements Initializable {
private DatePicker birthDate;
@FXML
private Spinner<Integer> gapLimit;
private IntegerSpinner gapLimit;
@FXML
private ComboBox<Integer> watchLast;
@@ -50,8 +49,12 @@ public class AdvancedController implements Initializable {
}
});
gapLimit.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(Wallet.DEFAULT_LOOKAHEAD, 9999, wallet.getGapLimit()));
gapLimit.setValueFactory(new IntegerSpinner.ValueFactory(Wallet.DEFAULT_LOOKAHEAD, 9999, wallet.getGapLimit()));
gapLimit.valueProperty().addListener((observable, oldValue, newValue) -> {
if(newValue == null || newValue < Wallet.DEFAULT_LOOKAHEAD || newValue > 9999) {
return;
}
wallet.setGapLimit(newValue);
if(!watchLast.getItems().equals(getWatchListItems(wallet))) {
Integer value = watchLast.getValue();
@@ -7,6 +7,7 @@ import com.sparrowwallet.drongo.wallet.StandardAccount;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.IntegerSpinner;
import com.sparrowwallet.sparrow.event.MixToConfigChangedEvent;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import javafx.collections.FXCollections;
@@ -31,7 +32,7 @@ public class MixToController implements Initializable {
private ComboBox<Wallet> mixToWallets;
@FXML
private Spinner<Integer> minMixes;
private IntegerSpinner minMixes;
@FXML
private ComboBox<IndexRange> indexRange;
@@ -94,8 +95,12 @@ public class MixToController implements Initializable {
});
int initialMinMixes = mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes();
minMixes.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 10000, initialMinMixes));
minMixes.setValueFactory(new IntegerSpinner.ValueFactory(1, 10000, initialMinMixes));
minMixes.valueProperty().addListener((observable, oldValue, newValue) -> {
if(newValue == null || newValue < 1 || newValue > 10000) {
return;
}
mixConfig.setMinMixes(newValue);
EventManager.get().post(new MixToConfigChangedEvent(wallet));
});