From 50c8042ead801d8fdbcf7326b987a83563a4eb8b Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 10 Jul 2026 18:10:26 +0800 Subject: [PATCH 1/6] Fixes #6354 --- .../ui/export/ModpackFileSelectionPage.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index 3feb91781b4..c40cb330486 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -29,8 +29,10 @@ import javafx.scene.layout.HBox; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; +import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel; +import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.Pair; @@ -44,6 +46,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; import static org.jackhuang.hmcl.util.Lang.mapOf; @@ -58,7 +61,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard private final WizardController controller; private final String version; private final ModAdviser adviser; - private final ModpackFileTreeItem rootNode; + private ModpackFileTreeItem rootNode; public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository repository, String version, ModAdviser adviser) { this.controller = controller; @@ -66,12 +69,21 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository this.adviser = adviser; JFXTreeView treeView = new JFXTreeView<>(); - rootNode = getTreeItem(repository.getRunDirectory(version), "minecraft", 0); - treeView.setRoot(rootNode); treeView.setSelectionModel(new NoneMultipleSelectionModel<>()); onEscPressed(treeView, () -> controller.onPrev(true)); - setMargin(treeView, new Insets(10, 10, 5, 10)); - this.setCenter(treeView); + + SpinnerPane spinnerPane = new SpinnerPane(); + spinnerPane.setContent(treeView); + setMargin(spinnerPane, new Insets(10, 10, 5, 10)); + this.setCenter(spinnerPane); + + spinnerPane.setLoading(true); + CompletableFuture + .supplyAsync(() -> getTreeItem(repository.getRunDirectory(version), "minecraft", 0), Schedulers.io()) + .thenAcceptAsync(modpackFileTreeItem -> { + treeView.setRoot(rootNode = modpackFileTreeItem); + spinnerPane.setLoading(false); + }, Schedulers.javafx()); HBox nextPane = new HBox(); nextPane.setPadding(new Insets(16, 16, 16, 0)); @@ -79,6 +91,7 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository { JFXButton btnNext = FXUtils.newRaisedButton(i18n("wizard.next")); btnNext.setPrefSize(100, 40); + btnNext.disableProperty().bind(spinnerPane.loadingProperty()); btnNext.setOnAction(e -> onNext()); nextPane.getChildren().setAll(btnNext); @@ -176,6 +189,7 @@ public void cleanup(SettingsMap settings) { } private void onNext() { + if (rootNode == null) return; ArrayList list = new ArrayList<>(); getFilesNeeded(rootNode, "minecraft", list); controller.getSettings().put(MODPACK_FILE_SELECTION, list); From e12645297e9416fcd98dcce608d2806e1f124a49 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Sat, 25 Jul 2026 21:27:26 +0800 Subject: [PATCH 2/6] update --- .../ui/export/ModpackFileSelectionPage.java | 23 ++++++++++++++----- .../resources/assets/lang/I18N.properties | 1 + .../resources/assets/lang/I18N_zh.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 1 + 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index c40cb330486..f69e83bbb10 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -78,12 +78,8 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository this.setCenter(spinnerPane); spinnerPane.setLoading(true); - CompletableFuture - .supplyAsync(() -> getTreeItem(repository.getRunDirectory(version), "minecraft", 0), Schedulers.io()) - .thenAcceptAsync(modpackFileTreeItem -> { - treeView.setRoot(rootNode = modpackFileTreeItem); - spinnerPane.setLoading(false); - }, Schedulers.javafx()); + loadRoot(repository, treeView, spinnerPane); + spinnerPane.setOnFailedAction((__) -> loadRoot(repository, treeView, spinnerPane)); HBox nextPane = new HBox(); nextPane.setPadding(new Insets(16, 16, 16, 0)); @@ -100,6 +96,21 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository this.setBottom(nextPane); } + private void loadRoot(HMCLGameRepository repository, JFXTreeView treeView, SpinnerPane spinnerPane) { + CompletableFuture + .supplyAsync(() -> getTreeItem(repository.getRunDirectory(version), "minecraft", 0), Schedulers.io()) + .whenCompleteAsync((modpackFileTreeItem, throwable) -> { + if (throwable != null) { + treeView.setRoot(rootNode = modpackFileTreeItem); + spinnerPane.setFailedReason(null); + } else { + LOG.warning("Failed to load modpack file tree. Please click here to retry."); + spinnerPane.setFailedReason(i18n("modpack.files.load_failed")); + } + spinnerPane.setLoading(false); + }, Schedulers.javafx()); + } + private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { if (Files.notExists(file)) return null; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index a2b393f421e..56695e25187 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -990,6 +990,7 @@ modpack.files.config=Mod Configuration Files modpack.files.dumps=NEI Debug Output Files modpack.files.hmclversion_cfg=Launcher Configuration File modpack.files.liteconfig=LiteLoader Related Files +modpack.files.load_failed=Failed to load modpack file tree. Please click here to retry. modpack.files.mods=Mods modpack.files.mods.voxelmods=VoxelMods Options modpack.files.options_txt=Minecraft Option File diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 0574758c5a6..51fd97001fd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -797,6 +797,7 @@ modpack.files.config=模組設定檔案 modpack.files.dumps=NEI 除錯輸出檔案 modpack.files.hmclversion_cfg=啟動器設定檔案 modpack.files.liteconfig=LiteLoader 相關檔案 +modpack.files.load_failed=載入整合包檔案樹失敗,點選此處重試 modpack.files.mods=模組 modpack.files.mods.voxelmods=VoxelMods 設定,如小地圖 modpack.files.options_txt=遊戲設定 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 52cac07405c..40cbc0f96a1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -802,6 +802,7 @@ modpack.files.config=模组配置文件 modpack.files.dumps=NEI 调试输出文件 modpack.files.hmclversion_cfg=启动器配置文件 modpack.files.liteconfig=LiteLoader 相关文件 +modpack.files.load_failed=加载整合包文件树失败,点击此处重试 modpack.files.mods=模组 modpack.files.mods.voxelmods=VoxelMods 配置,如小地图 modpack.files.options_txt=游戏设置 From 61077fae260c017f30c2e368eb18d2044cf143ea Mon Sep 17 00:00:00 2001 From: ToobLac Date: Mon, 27 Jul 2026 18:24:35 +0800 Subject: [PATCH 3/6] update --- .../org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java | 1 - 1 file changed, 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index f69e83bbb10..7bce84d5cd6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -200,7 +200,6 @@ public void cleanup(SettingsMap settings) { } private void onNext() { - if (rootNode == null) return; ArrayList list = new ArrayList<>(); getFilesNeeded(rootNode, "minecraft", list); controller.getSettings().put(MODPACK_FILE_SELECTION, list); From 6e7402b67b74eb98e25c41ca8efc7382f30419d5 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Mon, 27 Jul 2026 18:29:32 +0800 Subject: [PATCH 4/6] update --- .../org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index 7bce84d5cd6..4ce4d07843b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -118,7 +118,7 @@ private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { boolean isDirectory = Files.isDirectory(file); ModAdviser.ModSuggestion state = ModAdviser.ModSuggestion.SUGGESTED; - if (basePath.length() > "minecraft/".length()) { + if (level > 0) { state = adviser.advise(StringUtils.substringAfter(basePath, "minecraft/") + (isDirectory ? "/" : ""), isDirectory); String fileName = FileUtils.getName(file); From 6f05c5d8370762f431781ba26d4c74326198845d Mon Sep 17 00:00:00 2001 From: ToobLac Date: Mon, 27 Jul 2026 20:54:48 +0800 Subject: [PATCH 5/6] empty placeholder --- .../ui/export/ModpackFileSelectionPage.java | 28 +++++++++++++------ .../resources/assets/lang/I18N.properties | 1 + .../resources/assets/lang/I18N_zh.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 1 + 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index 4ce4d07843b..f288b4f0aaf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -27,6 +27,7 @@ import javafx.scene.control.TreeItem; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.task.Schedulers; @@ -72,14 +73,21 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository treeView.setSelectionModel(new NoneMultipleSelectionModel<>()); onEscPressed(treeView, () -> controller.onPrev(true)); + Label placeholder = new Label(i18n("modpack.files.empty")); + StackPane placeholderPane = new StackPane(placeholder); + placeholderPane.getStyleClass().add("notice-pane"); + placeholderPane.setVisible(false); + SpinnerPane spinnerPane = new SpinnerPane(); - spinnerPane.setContent(treeView); + StackPane center = new StackPane(treeView, placeholderPane); + spinnerPane.setContent(center); + setMargin(spinnerPane, new Insets(10, 10, 5, 10)); this.setCenter(spinnerPane); spinnerPane.setLoading(true); - loadRoot(repository, treeView, spinnerPane); - spinnerPane.setOnFailedAction((__) -> loadRoot(repository, treeView, spinnerPane)); + loadRoot(repository, treeView, placeholderPane, spinnerPane); + spinnerPane.setOnFailedAction((__) -> loadRoot(repository, treeView, placeholderPane, spinnerPane)); HBox nextPane = new HBox(); nextPane.setPadding(new Insets(16, 16, 16, 0)); @@ -96,15 +104,19 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository this.setBottom(nextPane); } - private void loadRoot(HMCLGameRepository repository, JFXTreeView treeView, SpinnerPane spinnerPane) { + private void loadRoot(HMCLGameRepository repository, JFXTreeView treeView, StackPane placeholderPane, SpinnerPane spinnerPane) { CompletableFuture .supplyAsync(() -> getTreeItem(repository.getRunDirectory(version), "minecraft", 0), Schedulers.io()) - .whenCompleteAsync((modpackFileTreeItem, throwable) -> { - if (throwable != null) { - treeView.setRoot(rootNode = modpackFileTreeItem); + .whenCompleteAsync((root, throwable) -> { + if (throwable == null) { + if (root != null) { + treeView.setRoot(rootNode = root); + } else { + placeholderPane.setVisible(true); + } spinnerPane.setFailedReason(null); } else { - LOG.warning("Failed to load modpack file tree. Please click here to retry."); + LOG.warning("Failed to load modpack file tree", throwable); spinnerPane.setFailedReason(i18n("modpack.files.load_failed")); } spinnerPane.setLoading(false); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 6984574669d..e58ee0bc471 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -986,6 +986,7 @@ modpack.file_api=Modpack URL Prefix modpack.files.blueprints=BuildCraft Blueprints modpack.files.config=Mod Configuration Files modpack.files.dumps=NEI Debug Output Files +modpack.files.empty=No files to export modpack.files.hmclversion_cfg=Launcher Configuration File modpack.files.liteconfig=LiteLoader Related Files modpack.files.load_failed=Failed to load modpack file tree. Please click here to retry. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index f5ab63e1c9f..95b70cc1a5b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -795,6 +795,7 @@ modpack.file_api=模組包下載連結前綴 modpack.files.blueprints=BuildCraft 藍圖 modpack.files.config=模組設定檔案 modpack.files.dumps=NEI 除錯輸出檔案 +modpack.files.empty=沒有需要匯出的檔案 modpack.files.hmclversion_cfg=啟動器設定檔案 modpack.files.liteconfig=LiteLoader 相關檔案 modpack.files.load_failed=載入整合包檔案樹失敗,點選此處重試 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 0c840892d03..632d877dbd4 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -801,6 +801,7 @@ modpack.file_api=整合包下载链接前缀 modpack.files.blueprints=BuildCraft 蓝图 modpack.files.config=模组配置文件 modpack.files.dumps=NEI 调试输出文件 +modpack.files.empty=没有需要导出的文件 modpack.files.hmclversion_cfg=启动器配置文件 modpack.files.liteconfig=LiteLoader 相关文件 modpack.files.load_failed=加载整合包文件树失败,点击此处重试 From 0a172580e296157e6c3687d16e581da8ba21dd75 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Tue, 28 Jul 2026 21:00:43 +0800 Subject: [PATCH 6/6] update --- .../ui/export/ModpackFileSelectionPage.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index f288b4f0aaf..6537265f51f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -40,6 +40,7 @@ import org.jackhuang.hmcl.util.SettingsMap; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; @@ -62,7 +63,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard private final WizardController controller; private final String version; private final ModAdviser adviser; - private ModpackFileTreeItem rootNode; + private @Nullable ModpackFileTreeItem rootNode; public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository repository, String version, ModAdviser adviser) { this.controller = controller; @@ -85,26 +86,25 @@ public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository setMargin(spinnerPane, new Insets(10, 10, 5, 10)); this.setCenter(spinnerPane); - spinnerPane.setLoading(true); - loadRoot(repository, treeView, placeholderPane, spinnerPane); - spinnerPane.setOnFailedAction((__) -> loadRoot(repository, treeView, placeholderPane, spinnerPane)); HBox nextPane = new HBox(); nextPane.setPadding(new Insets(16, 16, 16, 0)); nextPane.setAlignment(Pos.CENTER_RIGHT); - { - JFXButton btnNext = FXUtils.newRaisedButton(i18n("wizard.next")); - btnNext.setPrefSize(100, 40); - btnNext.disableProperty().bind(spinnerPane.loadingProperty()); - btnNext.setOnAction(e -> onNext()); - nextPane.getChildren().setAll(btnNext); - } + JFXButton btnNext = FXUtils.newRaisedButton(i18n("wizard.next")); + btnNext.setPrefSize(100, 40); + btnNext.setOnAction(e -> onNext()); + nextPane.getChildren().setAll(btnNext); + + loadRoot(repository, treeView, placeholderPane, spinnerPane, btnNext); + spinnerPane.setOnFailedAction((__) -> loadRoot(repository, treeView, placeholderPane, spinnerPane, btnNext)); this.setBottom(nextPane); } - private void loadRoot(HMCLGameRepository repository, JFXTreeView treeView, StackPane placeholderPane, SpinnerPane spinnerPane) { + private void loadRoot(HMCLGameRepository repository, JFXTreeView treeView, StackPane placeholderPane, SpinnerPane spinnerPane, JFXButton btnNext) { + spinnerPane.setLoading(true); + btnNext.setDisable(true); CompletableFuture .supplyAsync(() -> getTreeItem(repository.getRunDirectory(version), "minecraft", 0), Schedulers.io()) .whenCompleteAsync((root, throwable) -> { @@ -115,6 +115,7 @@ private void loadRoot(HMCLGameRepository repository, JFXTreeView treeVie placeholderPane.setVisible(true); } spinnerPane.setFailedReason(null); + btnNext.setDisable(false); } else { LOG.warning("Failed to load modpack file tree", throwable); spinnerPane.setFailedReason(i18n("modpack.files.load_failed"));