Skip to content

为游戏崩溃界面导出和启动器日志导出添加加载指示器#5694

Open
CiiLu wants to merge 7 commits intoHMCL-dev:mainfrom
CiiLu:ed
Open

为游戏崩溃界面导出和启动器日志导出添加加载指示器#5694
CiiLu wants to merge 7 commits intoHMCL-dev:mainfrom
CiiLu:ed

Conversation

@CiiLu
Copy link
Contributor

@CiiLu CiiLu commented Mar 2, 2026

前置:#5693

来自崩溃群

这两个地方涉及到文件压缩,如果待压缩文件比较大耗时会比较久,目前点击没有任何反馈用户可能以为没点到导致重复导出

@CiiLu CiiLu changed the title update 为游戏崩溃界面导出和启动器日志导出添加加载指示器 Mar 2, 2026
LOG.warning("Failed to export logs", e);
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.failed") + "\n" + StringUtils.getStackTrace(e), null, MessageType.ERROR));
Platform.runLater(() -> {
spinnerPane.showSpinner();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么是 showSpinner


Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.success", outputFile)));
Platform.runLater(() -> {
spinnerPane.showSpinner();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么是 showSpinner

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds loading spinner indicators to two export operations in the HMCL launcher: the game crash info export in GameCrashWindow and the launcher log export in SettingsPage. Both operations involve file compression that can take significant time, and previously had no visual feedback, causing users to think the button click was not registered and leading to repeated exports.

Changes:

  • Wraps the export buttons in both GameCrashWindow and SettingsPage with SpinnerPane controls that show a loading spinner during the export operation.
  • Shows the spinner before starting the async export task and hides it in all completion paths (success and error).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java Adds a SpinnerPane wrapping the crash info export button, shows spinner on click, hides on completion. Adds small-spinner-pane CSS class.
HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java Adds a SpinnerPane wrapping the log export button, shows spinner on click, hides on completion in both success and error paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@SuppressWarnings("FieldCanBeLocal")
private final InvalidationListener updateListener;

private final SpinnerPane spinnerPane = new SpinnerPane();
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The small-spinner-pane style class is missing on the spinnerPane in SettingsPage. Every other usage of SpinnerPane wrapping a button in the codebase adds spinnerPane.getStyleClass().add("small-spinner-pane") to reduce the spinner size (radius 10, stroke 3) to match the button. Without it, the default (larger) spinner will appear when loading, which will look out of place next to the small border button.

See examples: GameCrashWindow.java:119, AccountListItemSkin.java:95,130,165,170, CreateAccountPane.java:135, DialogPane.java:58, InputDialogPane.java:61, MicrosoftAccountLoginPane.java:108, AddAuthlibInjectorServerPane.java:79.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +280 to 283
return CompletableFuture.supplyAsync(() ->
logs.stream().map(Log::getLog).collect(Collectors.joining("\n")))
.thenComposeAsync(logs -> {
long processStartTime = managedProcess.getProcess().info()
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In exportGameCrashInfo(), the lambda parameter name logs in thenComposeAsync(logs -> { ... }) shadows the logs field. Renaming the parameter (e.g., logsText/logText) would make the code easier to follow and avoid accidental confusion between the list and the joined string.

Copilot uses AI. Check for mistakes.
Comment on lines +305 to +309
SpinnerPane exportLogPane = new SpinnerPane();

JFXButton logButton = FXUtils.newBorderButton(i18n("settings.launcher.launcher_log.export"));
logButton.setOnAction(e -> onExportLogs());
exportLogPane.setContent(logButton);
logButton.setOnAction(e -> {
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpinnerPane wrapping a button should include the small-spinner-pane style class; otherwise the spinner tends to be oversized and can shift the layout compared to other spinner-wrapped buttons in the UI (e.g., LogWindow export dump). Add the style class to exportLogPane for consistent sizing.

Copilot uses AI. Check for mistakes.
Comment on lines +401 to 403
private CompletableFuture<Path> onExportLogs() {
return CompletableFuture.supplyAsync(Lang.wrap(() -> {
String nameBase = "hmcl-exported-logs-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss"));
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onExportLogs() performs file I/O and compression but uses CompletableFuture.supplyAsync(...) with the default executor (ForkJoin common pool). Use Schedulers.io() (and pass it as the executor argument to supplyAsync) so this work runs on the project’s I/O pool/virtual threads instead of competing with compute tasks.

Copilot uses AI. Check for mistakes.
Comment on lines +277 to 283
private CompletableFuture<Path> exportGameCrashInfo() {
Path logFile = Paths.get("minecraft-exported-crash-info-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".zip").toAbsolutePath();

CompletableFuture.supplyAsync(() ->
return CompletableFuture.supplyAsync(() ->
logs.stream().map(Log::getLog).collect(Collectors.joining("\n")))
.thenComposeAsync(logs -> {
long processStartTime = managedProcess.getProcess().info()
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exportGameCrashInfo() ultimately does log export/compression work, but the initial CompletableFuture.supplyAsync(...) uses the default executor (common pool). Consider switching to Schedulers.io() for these async stages to keep I/O work off the compute pool and take advantage of the project’s I/O executor/virtual threads.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants