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..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 @@ -27,16 +27,20 @@ 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; 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; 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; @@ -44,6 +48,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 +63,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 @Nullable ModpackFileTreeItem rootNode; public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository repository, String version, ModAdviser adviser) { this.controller = controller; @@ -66,27 +71,59 @@ 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); + + 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(); + StackPane center = new StackPane(treeView, placeholderPane); + spinnerPane.setContent(center); + + setMargin(spinnerPane, new Insets(10, 10, 5, 10)); + this.setCenter(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.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, JFXButton btnNext) { + spinnerPane.setLoading(true); + btnNext.setDisable(true); + CompletableFuture + .supplyAsync(() -> getTreeItem(repository.getRunDirectory(version), "minecraft", 0), Schedulers.io()) + .whenCompleteAsync((root, throwable) -> { + if (throwable == null) { + if (root != null) { + treeView.setRoot(rootNode = root); + } else { + 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")); + } + spinnerPane.setLoading(false); + }, Schedulers.javafx()); + } + private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { if (Files.notExists(file)) return null; @@ -94,7 +131,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); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 16964d04b1b..e58ee0bc471 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -986,8 +986,10 @@ 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. 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 de6bde9270d..95b70cc1a5b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -795,8 +795,10 @@ 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=載入整合包檔案樹失敗,點選此處重試 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 f78404b6d92..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,8 +801,10 @@ 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=加载整合包文件树失败,点击此处重试 modpack.files.mods=模组 modpack.files.mods.voxelmods=VoxelMods 配置,如小地图 modpack.files.options_txt=游戏设置