[Bugfix] 修复导出整合包文件选择界面UI线程阻塞 (#6363)

Fixes #6354
This commit is contained in:
ToobLac authored and GitHub committed 2026-07-29 20:02:24 +08:00
1 parent f1b861eb21
commit d0fc46f930
4 files changed
+55 -12

No files matched your search

@@ -27,17 +27,21 @@ import javafx.scene.control.Label;
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.GameInstanceID;
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;
@@ -45,6 +49,7 @@ import java.nio.file.Path;
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;
@@ -59,7 +64,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard
private final WizardController controller;
private final GameInstanceID instanceId;
private final ModAdviser adviser;
private final ModpackFileTreeItem rootNode;
private @Nullable ModpackFileTreeItem rootNode;
public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository repository, GameInstanceID instanceId, ModAdviser adviser) {
this.controller = controller;
@@ -67,27 +72,59 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard
this.adviser = adviser;
JFXTreeView<String> treeView = new JFXTreeView<>();
rootNode = getTreeItem(repository.getRunDirectory(instanceId), "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<String> treeView, StackPane placeholderPane, SpinnerPane spinnerPane, JFXButton btnNext) {
spinnerPane.setLoading(true);
btnNext.setDisable(true);
CompletableFuture
.supplyAsync(() -> getTreeItem(repository.getRunDirectory(instanceId), "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;
@@ -95,7 +132,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard
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);
@@ -1038,8 +1038,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
@@ -847,8 +847,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=遊戲設定
@@ -853,8 +853,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=游戏设置