From 11d182f907846114753b0b08366e3862c1c93e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sat, 3 Jan 2026 16:41:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BE=93=E5=85=A5=E6=A1=86?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=20(#5129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Glavo --- .../org/jackhuang/hmcl/ui/Controllers.java | 10 ++++++---- .../account/AddAuthlibInjectorServerPane.java | 10 ++++++---- .../hmcl/ui/construct/InputDialogPane.java | 15 +++++++++++++-- .../hmcl/ui/download/DownloadPage.java | 8 ++------ .../ui/download/ModpackSelectionPage.java | 7 +++++-- .../jackhuang/hmcl/ui/versions/Versions.java | 19 ++++++++----------- .../hmcl/ui/versions/WorldListPage.java | 3 ++- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index a832401e7..4993bb701 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXDialogLayout; +import com.jfoenix.validation.base.ValidatorBase; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; @@ -72,9 +73,10 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; -import static org.jackhuang.hmcl.setting.ConfigHolder.*; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class Controllers { public static final String JAVA_VERSION_TIP = "javaVersion"; @@ -505,8 +507,8 @@ public final class Controllers { return prompt(title, onResult, ""); } - public static CompletableFuture prompt(String title, FutureCallback onResult, String initialValue) { - InputDialogPane pane = new InputDialogPane(title, initialValue, onResult); + public static CompletableFuture prompt(String title, FutureCallback onResult, String initialValue, ValidatorBase... validators) { + InputDialogPane pane = new InputDialogPane(title, initialValue, onResult, validators); dialog(pane); return pane.getCompletableFuture(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java index 84f94d540..78ee95e6b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java @@ -25,11 +25,10 @@ import javafx.scene.layout.*; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionPane; -import org.jackhuang.hmcl.ui.construct.DialogAware; -import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; -import org.jackhuang.hmcl.ui.construct.SpinnerPane; +import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.util.Lang; import javax.net.ssl.SSLException; @@ -88,6 +87,10 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement addServerPane.setBody(txtServerUrl); addServerPane.setActions(lblCreationWarning, actions); + + txtServerUrl.getValidators().addAll(new RequiredValidator(), new URLValidator()); + FXUtils.setValidateWhileTextChanged(txtServerUrl, true); + btnAddNext.disableProperty().bind(txtServerUrl.activeValidatorProperty().isNotNull()); } confirmServerPane = new JFXDialogLayout(); @@ -149,7 +152,6 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement this.setContent(addServerPane, ContainerAnimations.NONE); lblCreationWarning.maxWidthProperty().bind(((FlowPane) lblCreationWarning.getParent()).widthProperty()); - btnAddNext.disableProperty().bind(txtServerUrl.textProperty().isEmpty()); nextPane.hideSpinner(); onEscPressed(this, this::onAddCancel); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/InputDialogPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/InputDialogPane.java index 0def6d51a..8daab7bc5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/InputDialogPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/InputDialogPane.java @@ -20,9 +20,11 @@ package org.jackhuang.hmcl.ui.construct; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.controls.JFXTextField; +import com.jfoenix.validation.base.ValidatorBase; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.FutureCallback; import java.util.concurrent.CompletableFuture; @@ -36,6 +38,16 @@ public class InputDialogPane extends JFXDialogLayout implements DialogAware { private final JFXTextField textField; private final Label lblCreationWarning; private final SpinnerPane acceptPane; + private final JFXButton acceptButton; + + public InputDialogPane(String text, String initialValue, FutureCallback onResult, ValidatorBase... validators) { + this(text, initialValue, onResult); + if (validators != null && validators.length > 0) { + textField.getValidators().addAll(validators); + FXUtils.setValidateWhileTextChanged(textField, true); + acceptButton.disableProperty().bind(textField.activeValidatorProperty().isNotNull()); + } + } public InputDialogPane(String text, String initialValue, FutureCallback onResult) { textField = new JFXTextField(initialValue); @@ -47,7 +59,7 @@ public class InputDialogPane extends JFXDialogLayout implements DialogAware { acceptPane = new SpinnerPane(); acceptPane.getStyleClass().add("small-spinner-pane"); - JFXButton acceptButton = new JFXButton(i18n("button.ok")); + acceptButton = new JFXButton(i18n("button.ok")); acceptButton.getStyleClass().add("dialog-accept"); acceptPane.setContent(acceptButton); @@ -69,7 +81,6 @@ public class InputDialogPane extends JFXDialogLayout implements DialogAware { lblCreationWarning.setText(msg); }); }); - textField.setOnAction(event -> acceptButton.fire()); onEscPressed(this, cancelButton::fire); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 1c2fff281..b607b5584 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -39,6 +39,7 @@ import org.jackhuang.hmcl.ui.animation.TransitionPane; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.TabHeader; +import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.versions.DownloadListPage; @@ -134,10 +135,6 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version) : profile.getRepository().getBaseDirectory(); Controllers.prompt(i18n("archive.file.name"), (result, resolve, reject) -> { - if (!FileUtils.isNameValid(result)) { - reject.accept(i18n("install.new_game.malformed")); - return; - } Path dest = runDirectory.resolve(subdirectoryName).resolve(result); Controllers.taskDialog(Task.composeAsync(() -> { @@ -155,9 +152,8 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage Controllers.showToast(i18n("install.success")); } }), i18n("message.downloading"), TaskCancellationAction.NORMAL); - resolve.run(); - }, file.getFile().getFilename()); + }, file.getFile().getFilename(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValid)); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java index 5a9d22a50..fd7e13984 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java @@ -29,11 +29,14 @@ import javafx.scene.shape.SVGPath; import javafx.stage.FileChooser; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.mod.server.ServerModpackManifest; -import org.jackhuang.hmcl.task.*; +import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.GetTask; +import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; +import org.jackhuang.hmcl.ui.construct.URLValidator; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.SettingsMap; @@ -161,7 +164,7 @@ public final class ModpackSelectionPage extends VBox implements WizardPage { } catch (IOException e) { reject.accept(e.getMessage()); } - }); + }, "", new URLValidator()); } public void onChooseRepository() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 90781fd4d..a2c9b18f4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -26,7 +26,10 @@ import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.mod.RemoteMod; import org.jackhuang.hmcl.setting.*; -import org.jackhuang.hmcl.task.*; +import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.account.CreateAccountPane; @@ -50,8 +53,8 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class Versions { private Versions() { @@ -124,10 +127,6 @@ public final class Versions { public static CompletableFuture renameVersion(Profile profile, String version) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, resolve, reject) -> { - if (!HMCLGameRepository.isValidVersionId(newName)) { - reject.accept(i18n("install.new_game.malformed")); - return; - } if (profile.getRepository().renameVersion(version, newName)) { resolve.run(); profile.getRepository().refreshVersionsAsync() @@ -139,7 +138,8 @@ public final class Versions { } else { reject.accept(i18n("version.manage.rename.fail")); } - }, version); + }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName))); } public static void exportVersion(Profile profile, String version) { @@ -155,10 +155,6 @@ public final class Versions { new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, resolve, reject) -> { String newVersionName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue(); - if (!HMCLGameRepository.isValidVersionId(newVersionName)) { - reject.accept(i18n("install.new_game.malformed")); - return; - } Task.runAsync(() -> profile.getRepository().duplicateVersion(version, newVersionName, copySaves)) .thenComposeAsync(profile.getRepository().refreshVersionsAsync()) .whenComplete(Schedulers.javafx(), (result, exception) -> { @@ -174,6 +170,7 @@ public final class Versions { }) .addQuestion(new PromptDialogPane.Builder.HintQuestion(i18n("version.manage.duplicate.confirm"))) .addQuestion(new PromptDialogPane.Builder.StringQuestion(null, version, + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName)))) .addQuestion(new PromptDialogPane.Builder.BooleanQuestion(i18n("version.manage.duplicate.duplicate_save"), false))); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java index 30d3dd5f9..bbfac6382 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.*; +import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; @@ -141,7 +142,7 @@ public final class WorldListPage extends ListPageBase implements else reject.accept(i18n("world.import.failed", e.getClass().getName() + ": " + e.getLocalizedMessage())); }).start(); - }, world.getWorldName()); + }, world.getWorldName(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValid)); }, e -> { LOG.warning("Unable to parse world file " + zipFile, e); Controllers.dialog(i18n("world.import.invalid"));