Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 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 @@ -59,6 +59,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 @@ -534,6 +535,31 @@ 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();
TaskExecutorDialogPane pane = taskDialog(executor, title, onCancel);

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

TaskCenter.getInstance().enqueue(executor, title, detail);
return pane;
}

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

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

TaskCenter.getInstance().enqueue(executor, title, detail);
return pane;
}

public static void navigate(Node node) {
decorator.navigate(node, ContainerAnimations.NAVIGATION, Motion.SHORT4, Motion.EASE);
}
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.Optional;
import java.util.function.Consumer;

Expand All @@ -48,9 +52,17 @@ 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;

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

public TaskExecutorDialogPane(@NotNull TaskCancellationAction cancel) {
this.getStyleClass().add("task-executor-dialog-layout");
cancelAction = null;

FXUtils.setLimitWidth(this, 500);
FXUtils.setLimitHeight(this, 300);
Expand All @@ -59,13 +71,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,19 +115,54 @@ public TaskExecutorDialogPane(@NotNull TaskCancellationAction cancel) {

setCancel(cancel);

btnCancel.setOnAction(e -> {
Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
if (onCancel.getCancellationAction() != null) {
onCancel.getCancellationAction().accept(this);
}
});
btnCancel.setOnAction(e -> handleCancelOrClose());

speedEventHandler = FetchTask.SPEED_EVENT.registerWeak(speedEvent -> {
String message = I18n.formatSpeed(speedEvent.getSpeed());
Platform.runLater(() -> lblProgress.setText(message));
});

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

private boolean isQueuedNotStarted() {
if (executor == null) {
return false;
}
if (executor instanceof AsyncTaskExecutor asyncExecutor) {
return !asyncExecutor.isStarted();
}
return false;
}

private void handleCancelOrClose() {
if (isQueuedNotStarted()) {
fireEvent(new DialogCloseEvent());
return;
}

if (cancelAction != null) {
cancelAction.run();
return;
}

Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
if (onCancel.getCancellationAction() != null) {
onCancel.getCancellationAction().accept(this);
}
}

private void applyQueuedStateIfNeeded() {
if (isQueuedNotStarted()) {
setCancelText("关闭"); // TODO: i18n
} else {
setCancelText(i18n("button.cancel"));
}
}

public void setExecutor(TaskExecutor executor) {
Expand All @@ -115,6 +183,7 @@ public void onStop(boolean success, TaskExecutor executor) {
}
});
}
applyQueuedStateIfNeeded();
}

public StringProperty titleProperty() {
Expand All @@ -134,4 +203,43 @@ 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();
applyQueuedStateIfNeeded();
}

private Runnable cancelAction;

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

public void setCancelText(String text) {
btnCancel.setText(text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,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 @@ -504,4 +507,14 @@ public void setThrowable(Throwable throwable) {
progress.set(0.);
}
}

public void refresh() {
if (executor == null) return;
Platform.runLater(() -> {
stageNodes.clear();
listView.getItems().clear();
addStages(executor.getStages());
updateProgressNodePadding();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.ui.task.TaskCenter;
import org.jackhuang.hmcl.ui.versions.DownloadListPage;
import org.jackhuang.hmcl.ui.versions.HMCLLocalizedDownloadListPage;
import org.jackhuang.hmcl.ui.versions.VersionPage;
Expand Down Expand Up @@ -134,10 +135,29 @@ public static void download(Profile profile, @Nullable String version, RemoteMod

Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version) : profile.getRepository().getBaseDirectory();

String detailPrefix;
switch (subdirectoryName) {
case "mods":
detailPrefix = "安装模组";//TODO i18n
break;
case "resourcepacks":
detailPrefix = "安装资源包";//TODO i18n
break;
case "shaderpacks":
detailPrefix = "安装光影";//TODO i18n
break;
case "saves":
detailPrefix = "安装世界";//TODO i18n
break;
default:
detailPrefix = "下载";//TODO i18n
break;
}

Controllers.prompt(i18n("archive.file.name"), (result, handler) -> {
Path dest = runDirectory.resolve(subdirectoryName).resolve(result);

Controllers.taskDialog(Task.composeAsync(() -> {
Controllers.downloadTaskDialog(Task.composeAsync(() -> {
var task = new FileDownloadTask(file.getFile().getUrl(), dest);
task.setName(file.getName());
return task;
Expand All @@ -148,10 +168,12 @@ public static void download(Profile profile, @Nullable String version, RemoteMod
} else {
Controllers.dialog(DownloadProviders.localizeErrorMessage(exception), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR);
}
} else {
}
else {
Controllers.showToast(i18n("install.success"));
}
}), i18n("message.downloading"), TaskCancellationAction.NORMAL);
}), i18n("message.downloading"), TaskCancellationAction.NORMAL,
detailPrefix + "-[" + file.getName() + "]");
handler.resolve();
}, file.getFile().getFilename(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValid));

Expand Down Expand Up @@ -301,6 +323,12 @@ public Object finish(SettingsMap settings) {
settings.put("success_message", i18n("install.success"));
settings.put(FailureCallback.KEY, (settings1, exception, next) -> UpdateInstallerWizardProvider.alertFailureMessage(exception, next));

settings.put("task_detail", "安装游戏-[" + settings.get("name") + "]");//TODO i18n
settings.put("backgroundable", true);
settings.put("return_to_download_list", true);
settings.put("task_kind", TaskCenter.TaskKind.GAME_INSTALL);
settings.put("task_name", settings.get("name"));

return finishVersionDownloadingAsync(settings);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.task.TaskCenter;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.util.SettingsMap;

Expand All @@ -41,7 +42,8 @@ public InstallersPage(WizardController controller, HMCLGameRepository repository

txtName.getValidators().addAll(
new RequiredValidator(),
new Validator(i18n("install.new_game.already_exists"), str -> !repository.versionIdConflicts(str)),
new Validator(i18n("install.new_game.already_exists"), str ->
!repository.versionIdConflicts(str) && !TaskCenter.getInstance().hasQueuedInstallName(TaskCenter.TaskKind.GAME_INSTALL, str)),
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
installable.bind(createBooleanBinding(txtName::validate, txtName.textProperty()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.task.TaskCenter;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.util.SettingsMap;
import org.jackhuang.hmcl.util.StringUtils;
Expand Down Expand Up @@ -67,12 +68,13 @@ public LocalModpackPage(WizardController controller) {
if (installAsVersion) {
txtModpackName.getValidators().setAll(
new RequiredValidator(),
new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().versionIdConflicts(str)),
new Validator(i18n("install.new_game.already_exists"), str ->
!profile.getRepository().versionIdConflicts(str) && !TaskCenter.getInstance().hasQueuedInstallName(TaskCenter.TaskKind.MODPACK_INSTALL, str)),
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
} else {
txtModpackName.getValidators().setAll(
new RequiredValidator(),
new Validator(i18n("install.new_game.already_exists"), str -> !ModpackHelper.isExternalGameNameConflicts(str) && Profiles.getProfiles().stream().noneMatch(p -> p.getName().equals(str))),
new Validator(i18n("install.new_game.already_exists"), str -> !ModpackHelper.isExternalGameNameConflicts(str) && Profiles.getProfiles().stream().noneMatch(p -> p.getName().equals(str)) && !TaskCenter.getInstance().hasQueuedInstallName(TaskCenter.TaskKind.MODPACK_INSTALL, str)),
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.task.TaskCenter;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.SettingsMap;
Expand Down Expand Up @@ -142,6 +143,13 @@ public Object finish(SettingsMap settings) {
}
});

String taskName = settings.get(LocalModpackPage.MODPACK_NAME);
if (taskName != null) {
settings.put("task_detail", "安装整合包-[" + taskName + "]");//TODO: i18n
}
settings.put("task_kind", TaskCenter.TaskKind.MODPACK_INSTALL);
settings.put("task_name", taskName);

return finishModpackInstallingAsync(settings);
}

Expand Down
Loading