Fix value in text field might not be saved when pressing ESC

https://github.com/HMCL-dev/HMCL/pull/1869#issuecomment-2335027530
This commit is contained in:
Haowei Wen 2024-12-30 00:02:32 +08:00
parent c5b8aabc57
commit 81b3911dc9
2 changed files with 59 additions and 56 deletions

View File

@ -337,6 +337,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
return false; return false;
LOG.info("Saving version setting: " + id);
try { try {
FileUtils.writeText(file, GSON.toJson(localVersionSettings.get(id))); FileUtils.writeText(file, GSON.toJson(localVersionSettings.get(id)));
return true; return true;

View File

@ -31,6 +31,7 @@ import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Cursor; import javafx.scene.Cursor;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.control.*; import javafx.scene.control.*;
@ -521,10 +522,12 @@ public final class FXUtils {
} }
public static <T> void bind(JFXTextField textField, Property<T> property, StringConverter<T> converter) { public static <T> void bind(JFXTextField textField, Property<T> property, StringConverter<T> converter) {
textField.setText(converter == null ? (String) property.getValue() : converter.toString(property.getValue())); TextFieldBinding<T> binding = new TextFieldBinding<>(textField, property, converter);
TextFieldBindingListener<T> listener = new TextFieldBindingListener<>(textField, property, converter); binding.updateTextField();
textField.focusedProperty().addListener((ChangeListener<Boolean>) listener); textField.getProperties().put("FXUtils.bind.binding", binding);
property.addListener(listener); textField.focusedProperty().addListener(binding.focusedListener);
textField.sceneProperty().addListener(binding.sceneListener);
property.addListener(binding.propertyListener);
} }
public static void bindInt(JFXTextField textField, Property<Number> property) { public static void bindInt(JFXTextField textField, Property<Number> property) {
@ -536,71 +539,69 @@ public final class FXUtils {
} }
public static void unbind(JFXTextField textField, Property<?> property) { public static void unbind(JFXTextField textField, Property<?> property) {
TextFieldBindingListener<?> listener = new TextFieldBindingListener<>(textField, property, null); TextFieldBinding<?> binding = (TextFieldBinding<?>) textField.getProperties().remove("FXUtils.bind.binding");
textField.focusedProperty().removeListener((ChangeListener<Boolean>) listener); if (binding != null) {
property.removeListener(listener); textField.focusedProperty().removeListener(binding.focusedListener);
textField.sceneProperty().removeListener(binding.sceneListener);
property.removeListener(binding.propertyListener);
}
} }
private static final class TextFieldBindingListener<T> implements ChangeListener<Boolean>, InvalidationListener { private static final class TextFieldBinding<T> {
private final int hashCode; private final JFXTextField textField;
private final WeakReference<JFXTextField> textFieldRef; private final Property<T> property;
private final WeakReference<Property<T>> propertyRef;
private final StringConverter<T> converter; private final StringConverter<T> converter;
TextFieldBindingListener(JFXTextField textField, Property<T> property, StringConverter<T> converter) { public final ChangeListener<Boolean> focusedListener;
this.textFieldRef = new WeakReference<>(textField); public final ChangeListener<Scene> sceneListener;
this.propertyRef = new WeakReference<>(property); public final InvalidationListener propertyListener;
public TextFieldBinding(JFXTextField textField, Property<T> property, StringConverter<T> converter) {
this.textField = textField;
this.property = property;
this.converter = converter; this.converter = converter;
this.hashCode = System.identityHashCode(textField) ^ System.identityHashCode(property);
focusedListener = (observable, oldFocused, newFocused) -> {
if (oldFocused && !newFocused) {
if (textField.validate()) {
uppdateProperty();
} else {
// Rollback to old value
updateTextField();
}
}
};
sceneListener = (observable, oldScene, newScene) -> {
if (oldScene != null && newScene == null) {
// Component is being removed from scene
if (textField.validate()) {
uppdateProperty();
}
}
};
propertyListener = observable -> {
updateTextField();
};
} }
@Override public void uppdateProperty() {
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(); String newText = textField.getText();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T newValue = converter == null ? (T) newText : converter.fromString(newText); T newValue = converter == null ? (T) newText : converter.fromString(newText);
if (!Objects.equals(newValue, property.getValue())) if (!Objects.equals(newValue, property.getValue())) {
property.setValue(newValue); property.setValue(newValue);
} else {
// Rollback to old value
invalidated(null);
}
} }
} }
@Override public void updateTextField() {
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(); T value = property.getValue();
textField.setText(converter == null ? (String) value : converter.toString(value)); 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) { public static void bindBoolean(JFXToggleButton toggleButton, Property<Boolean> property) {
toggleButton.selectedProperty().bindBidirectional(property); toggleButton.selectedProperty().bindBidirectional(property);
} }