Submit the modification of JFXTextField after editing (#1869)

This commit is contained in:
Glavo 2022-11-25 17:20:16 +08:00 committed by GitHub
parent 6611774aa9
commit a94bb407e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 32 deletions

View File

@ -72,6 +72,7 @@ import javax.xml.parsers.ParserConfigurationException;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URI;
@ -530,20 +531,85 @@ public final class FXUtils {
}
}
public static void bindInt(JFXTextField textField, Property<Number> property) {
textField.textProperty().bindBidirectional(property, SafeStringConverter.fromInteger());
public static <T> void bind(JFXTextField textField, Property<T> property, StringConverter<T> converter) {
textField.setText(converter == null ? (String) property.getValue() : converter.toString(property.getValue()));
TextFieldBindingListener<T> listener = new TextFieldBindingListener<>(textField, property, converter);
textField.focusedProperty().addListener((ChangeListener<Boolean>) listener);
property.addListener(listener);
}
public static void unbindInt(JFXTextField textField, Property<Number> property) {
textField.textProperty().unbindBidirectional(property);
public static void bindInt(JFXTextField textField, Property<Number> property) {
bind(textField, property, SafeStringConverter.fromInteger());
}
public static void bindString(JFXTextField textField, Property<String> property) {
textField.textProperty().bindBidirectional(property);
bind(textField, property, null);
}
public static void unbindString(JFXTextField textField, Property<String> property) {
textField.textProperty().unbindBidirectional(property);
public static void unbind(JFXTextField textField, Property<?> property) {
TextFieldBindingListener<?> listener = new TextFieldBindingListener<>(textField, property, null);
textField.focusedProperty().removeListener((ChangeListener<Boolean>) listener);
property.removeListener(listener);
}
private static final class TextFieldBindingListener<T> implements ChangeListener<Boolean>, InvalidationListener {
private final int hashCode;
private final WeakReference<JFXTextField> textFieldRef;
private final WeakReference<Property<T>> propertyRef;
private final StringConverter<T> converter;
TextFieldBindingListener(JFXTextField textField, Property<T> property, StringConverter<T> converter) {
this.textFieldRef = new WeakReference<>(textField);
this.propertyRef = new WeakReference<>(property);
this.converter = converter;
this.hashCode = System.identityHashCode(textField) ^ System.identityHashCode(property);
}
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean focused) { // On TextField changed
JFXTextField textField = textFieldRef.get();
Property<T> property = this.propertyRef.get();
if (textField != null && property != null && oldValue == Boolean.TRUE && focused == Boolean.FALSE) {
if (textField.validate()) {
String newText = textField.getText();
@SuppressWarnings("unchecked")
T newValue = converter == null ? (T) newText : converter.fromString(newText);
if (!Objects.equals(newValue, property.getValue()))
property.setValue(newValue);
} else {
// Rollback to old value
invalidated(null);
}
}
}
@Override
public void invalidated(Observable observable) { // On property change
JFXTextField textField = textFieldRef.get();
Property<T> property = this.propertyRef.get();
if (textField != null && property != null) {
T value = property.getValue();
textField.setText(converter == null ? (String) value : converter.toString(value));
}
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TextFieldBindingListener))
return false;
TextFieldBindingListener<?> other = (TextFieldBindingListener<?>) obj;
return this.hashCode == other.hashCode
&& this.textFieldRef.get() == other.textFieldRef.get()
&& this.propertyRef.get() == other.propertyRef.get();
}
}
public static void bindBoolean(JFXToggleButton toggleButton, Property<Boolean> property) {

View File

@ -132,7 +132,7 @@ public class DownloadSettingsPage extends StackPane {
JFXTextField threadsField = new JFXTextField();
FXUtils.setLimitWidth(threadsField, 60);
threadsField.textProperty().bindBidirectional(config().downloadThreadsProperty(), SafeStringConverter.fromInteger());
FXUtils.bindInt(threadsField, config().downloadThreadsProperty());
AtomicBoolean changedByTextField = new AtomicBoolean(false);
FXUtils.onChangeAndOperate(config().downloadThreadsProperty(), value -> {
@ -213,7 +213,7 @@ public class DownloadSettingsPage extends StackPane {
GridPane.setRowIndex(txtProxyHost, 1);
GridPane.setColumnIndex(txtProxyHost, 1);
gridPane.getChildren().add(txtProxyHost);
txtProxyHost.textProperty().bindBidirectional(config().proxyHostProperty());
FXUtils.bindString(txtProxyHost, config().proxyHostProperty());
txtProxyHost.getValidators().setAll(new NumberValidator(i18n("input.number"), false));
}
@ -232,11 +232,10 @@ public class DownloadSettingsPage extends StackPane {
FXUtils.setValidateWhileTextChanged(txtProxyPort, true);
gridPane.getChildren().add(txtProxyPort);
txtProxyPort.textProperty().bindBidirectional(config().proxyPortProperty(),
SafeStringConverter.fromInteger()
.restrict(it -> it >= 0 && it <= 0xFFFF)
.fallbackTo(0)
.asPredicate(Validator.addTo(txtProxyPort)));
FXUtils.bind(txtProxyPort, config().proxyPortProperty(), SafeStringConverter.fromInteger()
.restrict(it -> it >= 0 && it <= 0xFFFF)
.fallbackTo(0)
.asPredicate(Validator.addTo(txtProxyPort)));
}
proxyPane.getChildren().add(gridPane);
}
@ -273,7 +272,7 @@ public class DownloadSettingsPage extends StackPane {
GridPane.setRowIndex(txtProxyUsername, 0);
GridPane.setColumnIndex(txtProxyUsername, 1);
authPane.getChildren().add(txtProxyUsername);
txtProxyUsername.textProperty().bindBidirectional(config().proxyUserProperty());
FXUtils.bindString(txtProxyUsername, config().proxyUserProperty());
}
{

View File

@ -146,12 +146,10 @@ public class PersonalizationPage extends StackPane {
JFXTextField txtLogFontSize = new JFXTextField();
FXUtils.setLimitWidth(txtLogFontSize, 50);
txtLogFontSize.textProperty().bindBidirectional(config().fontSizeProperty(),
SafeStringConverter.fromFiniteDouble()
.restrict(it -> it > 0)
.fallbackTo(12.0)
.asPredicate(Validator.addTo(txtLogFontSize)));
FXUtils.bind(txtLogFontSize, config().fontSizeProperty(), SafeStringConverter.fromFiniteDouble()
.restrict(it -> it > 0)
.fallbackTo(12.0)
.asPredicate(Validator.addTo(txtLogFontSize)));
hBox.getChildren().setAll(cboLogFont, txtLogFontSize);

View File

@ -43,7 +43,6 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@ -259,7 +258,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
JFXTextField txtMaxMemory = new JFXTextField();
FXUtils.setLimitWidth(txtMaxMemory, 60);
FXUtils.setValidateWhileTextChanged(txtMaxMemory, true);
txtMaxMemory.textProperty().bindBidirectional(maxMemory, SafeStringConverter.fromInteger());
FXUtils.bindInt(txtMaxMemory, maxMemory);
txtMaxMemory.setValidators(new NumberValidator(i18n("input.number"), false));
lowerBoundPane.getChildren().setAll(label, slider, txtMaxMemory, new Label("MB"));
@ -628,19 +627,19 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
// unbind data fields
if (lastVersionSetting != null) {
FXUtils.unbindInt(txtWidth, lastVersionSetting.widthProperty());
FXUtils.unbindInt(txtHeight, lastVersionSetting.heightProperty());
FXUtils.unbind(txtWidth, lastVersionSetting.widthProperty());
FXUtils.unbind(txtHeight, lastVersionSetting.heightProperty());
maxMemory.unbindBidirectional(lastVersionSetting.maxMemoryProperty());
javaCustomOption.valueProperty().unbindBidirectional(lastVersionSetting.javaDirProperty());
gameDirCustomOption.valueProperty().unbindBidirectional(lastVersionSetting.gameDirProperty());
nativesDirCustomOption.valueProperty().unbindBidirectional(lastVersionSetting.nativesDirProperty());
FXUtils.unbindString(txtJVMArgs, lastVersionSetting.javaArgsProperty());
FXUtils.unbindString(txtGameArgs, lastVersionSetting.minecraftArgsProperty());
FXUtils.unbindString(txtMetaspace, lastVersionSetting.permSizeProperty());
FXUtils.unbindString(txtWrapper, lastVersionSetting.wrapperProperty());
FXUtils.unbindString(txtPreLaunchCommand, lastVersionSetting.preLaunchCommandProperty());
FXUtils.unbindString(txtPostExitCommand, lastVersionSetting.postExitCommandProperty());
FXUtils.unbindString(txtServerIP, lastVersionSetting.serverIpProperty());
FXUtils.unbind(txtJVMArgs, lastVersionSetting.javaArgsProperty());
FXUtils.unbind(txtGameArgs, lastVersionSetting.minecraftArgsProperty());
FXUtils.unbind(txtMetaspace, lastVersionSetting.permSizeProperty());
FXUtils.unbind(txtWrapper, lastVersionSetting.wrapperProperty());
FXUtils.unbind(txtPreLaunchCommand, lastVersionSetting.preLaunchCommandProperty());
FXUtils.unbind(txtPostExitCommand, lastVersionSetting.postExitCommandProperty());
FXUtils.unbind(txtServerIP, lastVersionSetting.serverIpProperty());
FXUtils.unbindBoolean(chkAutoAllocate, lastVersionSetting.autoMemoryProperty());
FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty());
noGameCheckPane.selectedProperty().unbindBidirectional(lastVersionSetting.notCheckGameProperty());