From 939a064d60c6bd1c591ebebe150dbe7ce08f9fba Mon Sep 17 00:00:00 2001 From: Calboot Date: Wed, 11 Mar 2026 20:55:54 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=B8=8D=E5=9C=A8=E4=B8=BB=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E6=88=96=E8=80=85=E5=B7=B2=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=A1=86=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=BC=B9=E5=87=BA=E5=90=AF=E5=8A=A8=E5=99=A8?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AF=B9=E8=AF=9D=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/ui/Controllers.java | 7 ++++ .../org/jackhuang/hmcl/ui/DialogUtils.java | 39 +++++++++++++++++++ .../ui/decorator/DecoratorController.java | 9 +++++ .../org/jackhuang/hmcl/ui/main/MainPage.java | 2 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 1 - 5 files changed, 56 insertions(+), 2 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 4993bb7019..bd93403eda 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -63,6 +63,7 @@ import org.jackhuang.hmcl.ui.versions.GameListPage; import org.jackhuang.hmcl.ui.versions.VersionPage; import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Architecture; @@ -318,6 +319,7 @@ public static void initialize(Stage stage) { stage.setOnCloseRequest(e -> Launcher.stopApplication()); decorator = new DecoratorController(stage, getRootPage()); + getRootPage().getMainPage().showUpdateProperty().bind(decorator.backableProperty().not().and(UpdateChecker.outdatedProperty())); if (config().getCommonDirType() == EnumCommonDirectory.CUSTOM && !FileUtils.canCreateDirectory(config().getCommonDirectory())) { @@ -503,6 +505,11 @@ public static void confirmWithCountdown(String text, String title, int seconds, timeline.play(); } + public static void dialogLater(Region content) { + if (decorator != null) + decorator.showDialogLater(content); + } + public static CompletableFuture prompt(String title, FutureCallback onResult) { return prompt(title, onResult, ""); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java index faae25cbdf..ddc14b2539 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java @@ -31,7 +31,9 @@ import org.jackhuang.hmcl.ui.decorator.Decorator; import org.jetbrains.annotations.Nullable; +import java.util.LinkedList; import java.util.Optional; +import java.util.Queue; import java.util.function.Consumer; public final class DialogUtils { @@ -45,6 +47,9 @@ private DialogUtils() { public static final String PROPERTY_PARENT_PANE_REF = DialogUtils.class.getName() + ".dialog.parentPaneRef"; public static final String PROPERTY_PARENT_DIALOG_REF = DialogUtils.class.getName() + ".dialog.parentDialogRef"; + public static final String PROPERTY_DIALOG_SHOW_LATER = DialogUtils.class.getName() + ".dialog.showLater"; + public static final String PROPERTY_DECORATOR_REF = DialogUtils.class.getName() + ".dialog.decoratorRef"; + public static void show(Decorator decorator, Node content) { if (decorator.getDrawerWrapper() == null) { Platform.runLater(() -> show(decorator, content)); @@ -121,6 +126,28 @@ public void changed(ObservableValue observable, Boolean oldVa } } + @SuppressWarnings("unchecked") + public static void showLater(Decorator decorator, Node content) { + if (decorator.getDrawerWrapper() == null) { + Platform.runLater(() -> showLater(decorator, content)); + return; + } + FXUtils.checkFxUserThread(); + + StackPane container = decorator.getDrawerWrapper(); + if (container.getProperties().get(PROPERTY_DIALOG_INSTANCE) == null) { + show(decorator, content); + return; + } + Queue queue = (Queue) container.getProperties().get(PROPERTY_DIALOG_SHOW_LATER); + if (queue == null) { + queue = new LinkedList<>(); + container.getProperties().put(PROPERTY_DIALOG_SHOW_LATER, queue); + } + container.getProperties().put(PROPERTY_DECORATOR_REF, decorator); + queue.add(content); + } + @SuppressWarnings("unchecked") public static void close(Node content) { FXUtils.checkFxUserThread(); @@ -131,6 +158,8 @@ public static void close(Node content) { JFXDialogPane pane = (JFXDialogPane) content.getProperties().get(PROPERTY_PARENT_PANE_REF); JFXDialog dialog = (JFXDialog) content.getProperties().get(PROPERTY_PARENT_DIALOG_REF); + Runnable later = null; + if (dialog != null && pane != null) { if (pane.size() == 1 && pane.peek().orElse(null) == content) { dialog.setOnDialogClosed(e -> pane.pop(content)); @@ -142,6 +171,14 @@ public static void close(Node content) { container.getProperties().remove(PROPERTY_DIALOG_PANE_INSTANCE); container.getProperties().remove(PROPERTY_PARENT_DIALOG_REF); container.getProperties().remove(PROPERTY_PARENT_PANE_REF); + + // Only for decorators + Queue queue = (Queue) container.getProperties().get(PROPERTY_DIALOG_SHOW_LATER); + if (queue != null && !queue.isEmpty()) { + Decorator decorator = (Decorator) container.getProperties().get(PROPERTY_DECORATOR_REF); + Node next = queue.remove(); + if (decorator != null) later = () -> show(decorator, next); + } } } else { pane.pop(content); @@ -151,5 +188,7 @@ public static void close(Node content) { dialogAware.onDialogClosed(); } } + + if (later != null) later.run(); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java index 08e6ed71b7..b8931550a2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java @@ -22,6 +22,7 @@ import javafx.animation.Interpolator; import javafx.beans.InvalidationListener; import javafx.beans.WeakInvalidationListener; +import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.image.Image; @@ -341,6 +342,10 @@ public void navigate(Node node, AnimationProducer animationProducer, Duration du navigator.navigate(node, animationProducer, duration, interpolator); } + public ReadOnlyBooleanProperty backableProperty() { + return navigator.backableProperty(); + } + private void close() { if (navigator.getCurrentPage() instanceof DecoratorPage) { DecoratorPage page = (DecoratorPage) navigator.getCurrentPage(); @@ -425,6 +430,10 @@ private void closeDialog(Node node) { DialogUtils.close(node); } + public void showDialogLater(Node node) { + DialogUtils.showLater(decorator, node); + } + // ==== Toast ==== public void showToast(String content) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 907881f7d7..3a3cf75b71 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -278,7 +278,7 @@ private void showUpdate(boolean show) { doAnimation(show); if (show && getLatestVersion() != null && !Objects.equals(config().getPromptedVersion(), getLatestVersion().getVersion())) { - Controllers.dialog(new MessageDialogPane.Builder("", i18n("update.bubble.title", getLatestVersion().getVersion()), MessageDialogPane.MessageType.INFO) + Controllers.dialogLater(new MessageDialogPane.Builder("", i18n("update.bubble.title", getLatestVersion().getVersion()), MessageDialogPane.MessageType.INFO) .addAction(i18n("button.view"), () -> { config().setPromptedVersion(getLatestVersion().getVersion()); onUpgrade(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 32e7b24ca8..9bc78247e0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -117,7 +117,6 @@ public MainPage getMainPage() { }); FXUtils.onChangeAndOperate(Profiles.selectedVersionProperty(), mainPage::setCurrentGame); - mainPage.showUpdateProperty().bind(UpdateChecker.outdatedProperty()); mainPage.latestVersionProperty().bind(UpdateChecker.latestVersionProperty()); Profiles.registerVersionsListener(profile -> { From d6fcc577e483b6beba0f00075373df52fabe6cfe Mon Sep 17 00:00:00 2001 From: Calboot Date: Wed, 11 Mar 2026 21:13:39 +0800 Subject: [PATCH 2/4] update --- .../org/jackhuang/hmcl/ui/DialogUtils.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java index ddc14b2539..b77360c3ec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java @@ -48,7 +48,6 @@ private DialogUtils() { public static final String PROPERTY_PARENT_DIALOG_REF = DialogUtils.class.getName() + ".dialog.parentDialogRef"; public static final String PROPERTY_DIALOG_SHOW_LATER = DialogUtils.class.getName() + ".dialog.showLater"; - public static final String PROPERTY_DECORATOR_REF = DialogUtils.class.getName() + ".dialog.decoratorRef"; public static void show(Decorator decorator, Node content) { if (decorator.getDrawerWrapper() == null) { @@ -126,26 +125,28 @@ public void changed(ObservableValue observable, Boolean oldVa } } - @SuppressWarnings("unchecked") public static void showLater(Decorator decorator, Node content) { if (decorator.getDrawerWrapper() == null) { Platform.runLater(() -> showLater(decorator, content)); return; } + showLater(decorator.getDrawerWrapper(), () -> show(decorator, content)); + } + + @SuppressWarnings("unchecked") + public static void showLater(StackPane container, Runnable showDialogAction) { FXUtils.checkFxUserThread(); - StackPane container = decorator.getDrawerWrapper(); if (container.getProperties().get(PROPERTY_DIALOG_INSTANCE) == null) { - show(decorator, content); + showDialogAction.run(); return; } - Queue queue = (Queue) container.getProperties().get(PROPERTY_DIALOG_SHOW_LATER); + Queue queue = (Queue) container.getProperties().get(PROPERTY_DIALOG_SHOW_LATER); if (queue == null) { queue = new LinkedList<>(); container.getProperties().put(PROPERTY_DIALOG_SHOW_LATER, queue); } - container.getProperties().put(PROPERTY_DECORATOR_REF, decorator); - queue.add(content); + queue.add(showDialogAction); } @SuppressWarnings("unchecked") @@ -158,7 +159,7 @@ public static void close(Node content) { JFXDialogPane pane = (JFXDialogPane) content.getProperties().get(PROPERTY_PARENT_PANE_REF); JFXDialog dialog = (JFXDialog) content.getProperties().get(PROPERTY_PARENT_DIALOG_REF); - Runnable later = null; + Runnable showNextDialogAction = null; if (dialog != null && pane != null) { if (pane.size() == 1 && pane.peek().orElse(null) == content) { @@ -172,12 +173,10 @@ public static void close(Node content) { container.getProperties().remove(PROPERTY_PARENT_DIALOG_REF); container.getProperties().remove(PROPERTY_PARENT_PANE_REF); - // Only for decorators - Queue queue = (Queue) container.getProperties().get(PROPERTY_DIALOG_SHOW_LATER); + Queue queue = (Queue) container.getProperties().get(PROPERTY_DIALOG_SHOW_LATER); if (queue != null && !queue.isEmpty()) { - Decorator decorator = (Decorator) container.getProperties().get(PROPERTY_DECORATOR_REF); - Node next = queue.remove(); - if (decorator != null) later = () -> show(decorator, next); + Runnable next = queue.remove(); + if (next != null) showNextDialogAction = next; } } } else { @@ -189,6 +188,6 @@ public static void close(Node content) { } } - if (later != null) later.run(); + if (showNextDialogAction != null) showNextDialogAction.run(); } } From 918516f35022cd1b1fa99ce268d3f798a8caaf92 Mon Sep 17 00:00:00 2001 From: Calboot Date: Wed, 11 Mar 2026 23:30:08 +0800 Subject: [PATCH 3/4] update --- HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java | 2 +- .../org/jackhuang/hmcl/ui/decorator/DecoratorController.java | 4 ++-- .../main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java | 2 +- 3 files changed, 4 insertions(+), 4 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 bd93403eda..e92ef2d67b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -319,7 +319,7 @@ public static void initialize(Stage stage) { stage.setOnCloseRequest(e -> Launcher.stopApplication()); decorator = new DecoratorController(stage, getRootPage()); - getRootPage().getMainPage().showUpdateProperty().bind(decorator.backableProperty().not().and(UpdateChecker.outdatedProperty())); + getRootPage().getMainPage().showUpdateProperty().bind(decorator.backableProperty().not().and(UpdateChecker.checkingUpdateProperty().not()).and(UpdateChecker.outdatedProperty())); if (config().getCommonDirType() == EnumCommonDirectory.CUSTOM && !FileUtils.canCreateDirectory(config().getCommonDirectory())) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java index b8931550a2..e0c2fba148 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorController.java @@ -22,7 +22,7 @@ import javafx.animation.Interpolator; import javafx.beans.InvalidationListener; import javafx.beans.WeakInvalidationListener; -import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.BooleanProperty; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.image.Image; @@ -342,7 +342,7 @@ public void navigate(Node node, AnimationProducer animationProducer, Duration du navigator.navigate(node, animationProducer, duration, interpolator); } - public ReadOnlyBooleanProperty backableProperty() { + public BooleanProperty backableProperty() { return navigator.backableProperty(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 7d4fa507c6..a5076d1cee 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -118,10 +118,10 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview) { RemoteVersion finalResult = result; Platform.runLater(() -> { - checkingUpdate.set(false); if (finalResult != null) { latestVersion.set(finalResult); } + checkingUpdate.set(false); }); }, "Update Checker", true); }); From bc305190745cd5d093ea112cd8daf8dba8f5a829 Mon Sep 17 00:00:00 2001 From: Calboot Date: Sun, 15 Mar 2026 14:10:48 +0800 Subject: [PATCH 4/4] #5490 --- .../org/jackhuang/hmcl/ui/Controllers.java | 7 +++- .../org/jackhuang/hmcl/ui/main/MainPage.java | 33 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 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 e92ef2d67b..8c8ba92f02 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -319,7 +319,12 @@ public static void initialize(Stage stage) { stage.setOnCloseRequest(e -> Launcher.stopApplication()); decorator = new DecoratorController(stage, getRootPage()); - getRootPage().getMainPage().showUpdateProperty().bind(decorator.backableProperty().not().and(UpdateChecker.checkingUpdateProperty().not()).and(UpdateChecker.outdatedProperty())); + getRootPage().getMainPage().showUpdateProperty().bind(UpdateChecker.checkingUpdateProperty().not().and(UpdateChecker.outdatedProperty())); + getRootPage().getMainPage().showUpdateDialogProperty().bind( + decorator.backableProperty().not() + .and(getRootPage().getMainPage().showUpdateProperty()) + .and(config().disableAutoShowUpdateDialogProperty().not()) + ); if (config().getCommonDirType() == EnumCommonDirectory.CUSTOM && !FileUtils.canCreateDirectory(config().getCommonDirectory())) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 2f4cedb587..9b721cadaa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -90,6 +90,7 @@ public final class MainPage extends StackPane implements DecoratorPage { private final StringProperty currentGame = new SimpleStringProperty(this, "currentGame"); private final BooleanProperty showUpdate = new SimpleBooleanProperty(this, "showUpdate"); + private final BooleanProperty showUpdateDialog = new SimpleBooleanProperty(this, "showUpdateDialog"); private final ObjectProperty latestVersion = new SimpleObjectProperty<>(this, "latestVersion"); private final ObservableList versions = FXCollections.observableArrayList(); private Profile profile; @@ -98,6 +99,8 @@ public final class MainPage extends StackPane implements DecoratorPage { private final StackPane updatePane; private final JFXButton menuButton; + private RemoteVersion lastShownVersion; + { HBox titleNode = new HBox(8); titleNode.setPadding(new Insets(0, 0, 0, 2)); @@ -170,7 +173,8 @@ public final class MainPage extends StackPane implements DecoratorPage { FXUtils.setLimitHeight(updatePane, 55); StackPane.setAlignment(updatePane, Pos.TOP_RIGHT); FXUtils.onClicked(updatePane, this::onUpgrade); - FXUtils.onChange(showUpdateProperty(), this::showUpdate); + FXUtils.onChange(showUpdateProperty(), this::doAnimation); + FXUtils.onChange(showUpdateDialogProperty(), this::showUpdateDialog); { HBox hBox = new HBox(); @@ -274,12 +278,11 @@ public void accept(String currentGame) { } - private void showUpdate(boolean show) { - doAnimation(show); - - if (show && !config().isDisableAutoShowUpdateDialog() - && getLatestVersion() != null - && !Objects.equals(config().getPromptedVersion(), getLatestVersion().getVersion())) { + private void showUpdateDialog(boolean show) { + if (show && getLatestVersion() != null && getLatestVersion() != lastShownVersion + && !Objects.equals(config().getPromptedVersion(), getLatestVersion().getVersion()) + ) { + lastShownVersion = getLatestVersion(); Controllers.dialogLater(new MessageDialogPane.Builder("", i18n("update.bubble.title", getLatestVersion().getVersion()), MessageDialogPane.MessageType.INFO) .addAction(i18n("button.view"), () -> { config().setPromptedVersion(getLatestVersion().getVersion()); @@ -362,8 +365,8 @@ private void onUpgrade() { } private void closeUpdateBubble() { - showUpdate.unbind(); - showUpdate.set(false); + showUpdateDialog.unbind(); + showUpdateDialog.set(false); } @Override @@ -403,6 +406,18 @@ public void setShowUpdate(boolean showUpdate) { this.showUpdate.set(showUpdate); } + public boolean isShowUpdateDialog() { + return showUpdateDialog.get(); + } + + public BooleanProperty showUpdateDialogProperty() { + return showUpdateDialog; + } + + public void setShowUpdateDialog(boolean showUpdateDialog) { + this.showUpdateDialog.set(showUpdateDialog); + } + public RemoteVersion getLatestVersion() { return latestVersion.get(); }