Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1423329
update
NoClassFoundError Feb 4, 2026
27eae28
update
NoClassFoundError Feb 4, 2026
af8fe52
update
NoClassFoundError Feb 4, 2026
e40d564
update
NoClassFoundError Feb 4, 2026
cc25198
update
NoClassFoundError Feb 4, 2026
7f0bbae
update
NoClassFoundError Feb 4, 2026
98b18a8
update
NoClassFoundError Feb 5, 2026
eaf13c5
update
NoClassFoundError Feb 5, 2026
f779a4f
update
NoClassFoundError Feb 5, 2026
82bd0b8
update
NoClassFoundError Feb 6, 2026
7f56b66
update
NoClassFoundError Feb 6, 2026
051ded9
update
NoClassFoundError Feb 6, 2026
72e0e05
update
NoClassFoundError Feb 6, 2026
641b025
update
NoClassFoundError Feb 6, 2026
a949cc1
update
NoClassFoundError Feb 6, 2026
699a52e
update
NoClassFoundError Feb 6, 2026
3b386ea
Merge branch 'HMCL-dev:main' into 1132backgroundtask
NoClassFoundError Feb 7, 2026
ded3ff8
update
NoClassFoundError Feb 7, 2026
bd632ce
update
NoClassFoundError Feb 7, 2026
248704f
update
NoClassFoundError Feb 7, 2026
ea23b95
update
NoClassFoundError Feb 7, 2026
08be0f4
Merge branch 'HMCL-dev:main' into 1132backgroundtask
NoClassFoundError Feb 12, 2026
0018248
Merge branch 'main' into 1132backgroundtask
NoClassFoundError Mar 21, 2026
dff9e08
优化并发保护,重制任务管理器界面,适配更多后台功能
NoClassFoundError Mar 21, 2026
7f9eb7a
修正i18n内的中文括号
NoClassFoundError Mar 21, 2026
29db229
修复 executor 双重 start 的问题,清除一部分无用代码并修复相关逻辑
NoClassFoundError Mar 21, 2026
f0d9dc7
适配i18n,修改SVG图标
NoClassFoundError Mar 21, 2026
93667fc
更改页面样式,支持任务失败后在堆栈提示页面直接导出启动器日志
NoClassFoundError Mar 21, 2026
e42f8b8
Merge branch 'HMCL-dev:main' into 1132backgroundtask
NoClassFoundError Mar 21, 2026
7007ac1
更新UI,修改重名检测逻辑,增加tooltip,增加设置项“自动移交后台任务”
NoClassFoundError Mar 21, 2026
0845063
修复重复导航,去除多余的滚动条
NoClassFoundError Mar 21, 2026
fd4df3e
移除未使用的import
NoClassFoundError Mar 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ private static CompletableFuture<JavaRuntime> downloadJava(GameJavaVersion javaV
MessageType.QUESTION)
.yesOrNo(() -> {
DownloadProvider downloadProvider = profile.getDependency().getDownloadProvider();
Controllers.taskDialog(JavaManager.getDownloadJavaTask(downloadProvider, SYSTEM_PLATFORM, javaVersion)
Controllers.downloadTaskDialog(JavaManager.getDownloadJavaTask(downloadProvider, SYSTEM_PLATFORM, javaVersion)
.whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
future.complete(result);
Expand All @@ -679,7 +679,8 @@ private static CompletableFuture<JavaRuntime> downloadJava(GameJavaVersion javaV
}
future.completeExceptionally(new CancellationException());
}
}), i18n("download.java"), new TaskCancellationAction(() -> future.completeExceptionally(new CancellationException())));
}), i18n("download.java"), new TaskCancellationAction(() -> future.completeExceptionally(new CancellationException())),
i18n("task.detail.java_download"));
}, () -> future.completeExceptionally(new CancellationException())).build());

return future;
Expand Down
15 changes: 15 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,21 @@ public void setDisableAutoGameOptions(boolean disableAutoGameOptions) {
this.disableAutoGameOptions.set(disableAutoGameOptions);
}

@SerializedName("autoBackgroundTask")
private final BooleanProperty autoBackgroundTask = new SimpleBooleanProperty(false);

public BooleanProperty autoBackgroundTaskProperty() {
return autoBackgroundTask;
}

public boolean isAutoBackgroundTask() {
return autoBackgroundTask.get();
}

public void setAutoBackgroundTask(boolean autoBackgroundTask) {
this.autoBackgroundTask.set(autoBackgroundTask);
}

// Accounts

@SerializedName("authlibInjectorServers")
Expand Down
24 changes: 24 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
import org.jackhuang.hmcl.ui.main.LauncherSettingsPage;
import org.jackhuang.hmcl.ui.main.RootPage;
import org.jackhuang.hmcl.ui.task.TaskCenter;
import org.jackhuang.hmcl.ui.terracotta.TerracottaPage;
import org.jackhuang.hmcl.ui.versions.GameListPage;
import org.jackhuang.hmcl.ui.versions.VersionPage;
Expand Down Expand Up @@ -589,6 +590,23 @@ public static TaskExecutorDialogPane taskDialog(Task<?> task, String title, Task
return pane;
}

public static TaskExecutorDialogPane downloadTaskDialog(Task<?> task, String title, TaskCancellationAction onCancel, String detail) {
TaskExecutor executor = task.executor();
return downloadTaskDialog(executor, title, onCancel, detail);
}

public static TaskExecutorDialogPane downloadTaskDialog(TaskExecutor executor, String title, TaskCancellationAction onCancel, String detail) {
TaskExecutorDialogPane pane = taskDialog(executor, title, onCancel);

pane.setBackgroundAction(() -> {
TaskCenter.getInstance().enqueue(executor, title, detail);
pane.fireEvent(new DialogCloseEvent());
});

executor.start();
return pane;
}

public static void navigate(Node node) {
decorator.navigate(node, ContainerAnimations.NAVIGATION, Motion.SHORT4, Motion.EASE);
}
Expand Down Expand Up @@ -618,6 +636,12 @@ public static void onHyperlinkAction(String href) {
}
}

public static boolean isDialogShowing() {
if (decorator == null) return false;
return decorator.getDecorator().getDrawerWrapper() != null
&& decorator.getDecorator().getDrawerWrapper().getProperties().get(DialogUtils.PROPERTY_DIALOG_INSTANCE) != null;
}

public static boolean isStopped() {
return decorator == null;
}
Expand Down
1 change: 1 addition & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum SVG {
CHAT("M6 14H14V12H6V14ZM6 11H18V9H6V11ZM6 8H18V6H6V8ZM2 22V4Q2 3.175 2.5875 2.5875T4 2H20Q20.825 2 21.4125 2.5875T22 4V16Q22 16.825 21.4125 17.4125T20 18H6L2 22ZM5.15 16H20V4H4V17.125L5.15 16ZM4 16V4 16Z"),
CHECK("M9.55 18 3.85 12.3 5.275 10.875 9.55 15.15 18.725 5.975 20.15 7.4 9.55 18Z"),
CHECKROOM("M3 20Q2.575 20 2.2875 19.7125T2 19Q2 18.75 2.1 18.5375T2.4 18.2L11 11.75V10Q11 9.575 11.3 9.2875T12.025 9Q12.65 9 13.075 8.55T13.5 7.475Q13.5 6.85 13.0625 6.425T12 6Q11.375 6 10.9375 6.4375T10.5 7.5H8.5Q8.5 6.05 9.525 5.025T12 4Q13.45 4 14.475 5.0125T15.5 7.475Q15.5 8.65 14.8125 9.575T13 10.85V11.75L21.6 18.2Q21.8 18.325 21.9 18.5375T22 19Q22 19.425 21.7125 19.7125T21 20H3ZM6 18H18L12 13.5 6 18Z"),
CHECKLIST("M5.55 19.5 1.85 15.8 3.275 14.35 5.55 16.625 10.15 12.025 11.575 13.475 5.55 19.5ZM5.55 11.5 1.85 7.8 3.275 6.35 5.55 8.625 10.15 4.025 11.575 5.475 5.55 11.5ZM13 17V15H22V17H13ZM13 9V7H22V9H13Z"),
CHECK_CIRCLE("M10.6 16.6 17.65 9.55 16.25 8.15 10.6 13.8 7.75 10.95 6.35 12.35 10.6 16.6ZM12 22Q9.925 22 8.1 21.2125T4.925 19.075Q3.575 17.725 2.7875 15.9T2 12Q2 9.925 2.7875 8.1T4.925 4.925Q6.275 3.575 8.1 2.7875T12 2Q14.075 2 15.9 2.7875T19.075 4.925Q20.425 6.275 21.2125 8.1T22 12Q22 14.075 21.2125 15.9T19.075 19.075Q17.725 20.425 15.9 21.2125T12 22ZM12 20Q15.35 20 17.675 17.675T20 12Q20 8.65 17.675 6.325T12 4Q8.65 4 6.325 6.325T4 12Q4 15.35 6.325 17.675T12 20ZM12 12Z"),
CLOSE("M6.4 19 5 17.6 10.6 12 5 6.4 6.4 5 12 10.6 17.6 5 19 6.4 13.4 12 19 17.6 17.6 19 12 13.4 6.4 19Z"),
CONTENT_CUT("M19 21l-7-7-2.35 2.35q.2.375.275.8T10 18q0 1.65-1.175 2.825T6 22q-1.65 0-2.825-1.175T2 18t1.175-2.825T6 14q.425 0 .85.075t.8.275L10 12 7.65 9.65q-.375.2-.8.275T6 10q-1.65 0-2.825-1.175T2 6q0-1.65 1.175-2.825T6 2q1.65 0 2.825 1.175T10 6q0 .425-.075.85t-.275.8L22 20v1H19Zm-4-10-2-2 6-6h3v1l-7 7ZM7.4125 7.4125Q8 6.825 8 6t-.5875-1.4125Q6.825 4 6 4t-1.4125.5875Q4 5.175 4 6t.5875 1.4125T6 8t1.4125-.5875ZM12.35 12.35q.15-.15.15-.35t-.15-.35-.35-.15-.35.15-.15.35.15.35.35.15.35-.15ZM7.4125 19.4125Q8 18.825 8 18t-.5875-1.4125Q6.825 16 6 16t-1.4125.5875Q4 17.175 4 18t.5875 1.4125Q5.175 20 6 20t1.4125-.5875Z"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
Expand All @@ -46,13 +45,6 @@ public class AdvancedListBox extends ScrollPane {
setVbarPolicy(ScrollBarPolicy.NEVER);

container.getStyleClass().add("advanced-list-box-content");

this.addEventFilter(MouseEvent.MOUSE_ENTERED, event -> {
if (container.getHeight() > getHeight())
setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
});
this.addEventFilter(MouseEvent.MOUSE_EXITED,
event -> setVbarPolicy(ScrollBarPolicy.NEVER));
}

public AdvancedListBox add(Node child) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@
import javafx.application.Platform;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.task.*;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.ui.SVG;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
Expand All @@ -47,6 +51,14 @@ public class TaskExecutorDialogPane extends BorderPane {
private final Label lblProgress;
private final JFXButton btnCancel;
private final TaskListPane taskListPane;
private final JFXButton btnBackground;
private Runnable onBackground;
private Runnable escAction;
private Runnable cancelAction;

public void setEscAction(Runnable action) {
this.escAction = action;
}

public TaskExecutorDialogPane(@NotNull TaskCancellationAction cancel) {
this.getStyleClass().add("task-executor-dialog-layout");
Expand All @@ -58,13 +70,34 @@ public TaskExecutorDialogPane(@NotNull TaskCancellationAction cancel) {
this.setCenter(center);
center.setPadding(new Insets(16));
{
HBox titleBar = new HBox();
titleBar.setAlignment(Pos.CENTER_LEFT);
titleBar.setSpacing(8);

lblTitle = new Label();
lblTitle.setStyle("-fx-font-size: 14px; -fx-font-weight: BOLD;");

btnBackground = new JFXButton();
btnBackground.setGraphic(SVG.DOWNLOAD.createIcon(16)); // TODO: 可替换为更合适的后台图标
btnBackground.getStyleClass().add("toggle-icon4");
FXUtils.installFastTooltip(btnBackground, i18n("task.move_to_background"));
btnBackground.setOnAction(e -> {
if (onBackground != null) {
onBackground.run();
}
});
btnBackground.setVisible(false);
btnBackground.setManaged(false);

HBox spacer = new HBox();
HBox.setHgrow(spacer, Priority.ALWAYS);

titleBar.getChildren().setAll(lblTitle, spacer, btnBackground);

taskListPane = new TaskListPane();
VBox.setVgrow(taskListPane, Priority.ALWAYS);

center.getChildren().setAll(lblTitle, taskListPane);
center.getChildren().setAll(titleBar, taskListPane);
}

BorderPane bottom = new BorderPane();
Expand All @@ -82,6 +115,10 @@ public TaskExecutorDialogPane(@NotNull TaskCancellationAction cancel) {
setCancel(cancel);

btnCancel.setOnAction(e -> {
if (cancelAction != null) {
cancelAction.run();
return;
}
if (onCancel.getCancellationAction() != null) {
if (executor != null)
executor.cancel();
Expand All @@ -94,7 +131,12 @@ public TaskExecutorDialogPane(@NotNull TaskCancellationAction cancel) {
Platform.runLater(() -> lblProgress.setText(message));
});

onEscPressed(this, btnCancel::fire);
escAction = btnCancel::fire;
onEscPressed(this, () -> {
if (escAction != null) {
escAction.run();
}
});
}

public void setExecutor(TaskExecutor executor) {
Expand Down Expand Up @@ -134,4 +176,66 @@ public void setCancel(TaskCancellationAction onCancel) {

runInFX(() -> btnCancel.setDisable(onCancel == null));
}

private final AtomicBoolean background = new AtomicBoolean(false);

public void setBackgroundAction(Runnable action) {
this.onBackground = action;
background.set(false);

btnBackground.setVisible(action != null);
btnBackground.setManaged(action != null);

if (action != null) {
btnBackground.setDisable(false);
btnBackground.setOnAction(e -> {
if (!background.compareAndSet(false, true)) {
return;
}
btnBackground.setDisable(true);
onBackground.run();
});
} else {
btnBackground.setDisable(false);
btnBackground.setOnAction(null);
}
}

public void refreshTaskList() {
taskListPane.refresh();
}

public void setCancelAction(Runnable action) {
this.cancelAction = action;
}

public void setCancelText(String text) {
btnCancel.setText(text);
}

private Label lblWaiting;

public void setWaitingForBackground(boolean waiting) {
if (waiting) {
if (lblWaiting == null) {
lblWaiting = new Label(i18n("task.waiting_for_background"));
lblWaiting.setStyle("-fx-text-fill: -fx-secondary-text-color; -fx-font-size: 13px;");
lblWaiting.setWrapText(true);
}
taskListPane.setVisible(false);
taskListPane.setManaged(false);
lblProgress.setVisible(false);
lblProgress.setManaged(false);
((VBox) getCenter()).getChildren().add(lblWaiting);
} else {
taskListPane.setVisible(true);
taskListPane.setManaged(true);
lblProgress.setVisible(true);
lblProgress.setManaged(true);
lblProgress.setText("");
if (lblWaiting != null) {
((VBox) getCenter()).getChildren().remove(lblWaiting);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ public void onReady(Task<?> task) {
public void onRunning(Task<?> task) {
if (!task.getSignificance().shouldShow() || task.getName() == null)
return;
if (task.getName() == null) {
task.setName(i18n("task.unnamed"));
}

if (task instanceof GameAssetDownloadTask) {
task.setName(i18n("assets.download_all"));
Expand Down Expand Up @@ -522,4 +525,14 @@ public void setThrowable(Throwable throwable) {
progress.set(0.);
}
}

public void refresh() {
if (executor == null) return;
Platform.runLater(() -> {
stageNodes.clear();
listView.getItems().clear();
addStagesHints(executor.getHints());
updateProgressNodePadding();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,19 @@
import javafx.util.Duration;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.theme.Themes;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.Motion;
import org.jackhuang.hmcl.ui.animation.TransitionPane;
import org.jackhuang.hmcl.ui.task.TaskCenter;
import org.jackhuang.hmcl.ui.task.TaskCenterPage;
import org.jackhuang.hmcl.ui.wizard.Navigation;
import org.jackhuang.hmcl.util.platform.OperatingSystem;

import static org.jackhuang.hmcl.util.i18n.I18n.i18n;

public class DecoratorSkin extends SkinBase<Decorator> {
private final StackPane root, parent;
private final StackPane titleContainer;
Expand Down Expand Up @@ -231,6 +236,35 @@ public DecoratorSkin(Decorator control) {
buttonsContainer.setAlignment(Pos.TOP_RIGHT);
buttonsContainer.setMaxHeight(40);
{
// Background task indicator
JFXButton btnTask = new JFXButton();
btnTask.setFocusTraversable(false);
btnTask.getStyleClass().add("jfx-decorator-button");
btnTask.setOnAction(e -> Controllers.navigate(new TaskCenterPage()));
FXUtils.installFastTooltip(btnTask, i18n("task.manage"));

Label taskBadge = new Label();
taskBadge.setStyle("-fx-font-size: 10px; -fx-text-fill: white; -fx-background-color: #F44336; " +
"-fx-background-radius: 8; -fx-min-width: 16; -fx-min-height: 16; " +
"-fx-alignment: center; -fx-padding: 0 3 0 3;");

StackPane taskIconPane = new StackPane();
taskIconPane.getChildren().add(SVG.CHECKLIST.createIcon(Themes.titleFillProperty()));
taskIconPane.getChildren().add(taskBadge);
StackPane.setAlignment(taskBadge, Pos.TOP_RIGHT);
StackPane.setMargin(taskBadge, new Insets(-4, -6, 0, 0));
btnTask.setGraphic(taskIconPane);

Runnable updateTaskIndicator = () -> {
int count = TaskCenter.getInstance().getEntries().size();
btnTask.setVisible(count > 0);
btnTask.setManaged(count > 0);
taskBadge.setText(String.valueOf(count));
};
updateTaskIndicator.run();
TaskCenter.getInstance().getEntries().addListener(
(ListChangeListener<TaskCenter.Entry>) change -> updateTaskIndicator.run());

JFXButton btnHelp = new JFXButton();
btnHelp.setFocusTraversable(false);
btnHelp.setGraphic(SVG.HELP.createIcon(Themes.titleFillProperty()));
Expand All @@ -249,7 +283,7 @@ public DecoratorSkin(Decorator control) {
btnClose.getStyleClass().add("jfx-decorator-button");
btnClose.setOnAction(e -> skinnable.close());

buttonsContainer.getChildren().setAll(btnHelp, btnMin, btnClose);
buttonsContainer.getChildren().setAll(btnTask, btnHelp, btnMin, btnClose);
}
AnchorPane layer = new AnchorPane();
layer.setPickOnBounds(false);
Expand Down
Loading