TransitionHandler update

This commit is contained in:
huangyuhui
2018-01-25 22:41:15 +08:00
parent 056c0901f2
commit 12fa94627d
66 changed files with 646 additions and 522 deletions

View File

@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
@@ -109,15 +110,13 @@ public class HMCLGameRepository extends DefaultGameRepository {
@Override @Override
public void refreshVersions() { public void refreshVersions() {
EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this)); EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this));
Schedulers.newThread().schedule(() -> { refreshVersionsImpl();
refreshVersionsImpl(); EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this));
EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this));
});
} }
public void changeDirectory(File newDirectory) { public void changeDirectory(File newDirectory) {
setBaseDirectory(newDirectory); setBaseDirectory(newDirectory);
refreshVersions(); refreshVersionsAsync().start();
} }
private void checkModpack() { private void checkModpack() {

View File

@@ -37,13 +37,12 @@ import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener; import org.jackhuang.hmcl.task.TaskListener;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.DialogController; import org.jackhuang.hmcl.ui.DialogController;
import org.jackhuang.hmcl.ui.LaunchingStepsPane; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.ui.LogWindow; import org.jackhuang.hmcl.ui.LogWindow;
import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.*;
import java.io.*; import java.io.*;
import java.nio.Buffer;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -52,10 +51,11 @@ public final class LauncherHelper {
public static final LauncherHelper INSTANCE = new LauncherHelper(); public static final LauncherHelper INSTANCE = new LauncherHelper();
private LauncherHelper(){} private LauncherHelper(){}
private final LaunchingStepsPane launchingStepsPane = new LaunchingStepsPane(); private TaskExecutor executor;
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>(); public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
private final TaskExecutorDialogPane launchingStepsPane = new TaskExecutorDialogPane(() -> {});
public void launch(String selectedVersion, String launcherName) { public void launch(String selectedVersion, File scriptFile) {
Profile profile = Settings.INSTANCE.getSelectedProfile(); Profile profile = Settings.INSTANCE.getSelectedProfile();
GameRepository repository = profile.getRepository(); GameRepository repository = profile.getRepository();
DefaultDependencyManager dependencyManager = profile.getDependency(); DefaultDependencyManager dependencyManager = profile.getDependency();
@@ -67,12 +67,12 @@ public final class LauncherHelper {
VersionSetting setting = profile.getVersionSetting(selectedVersion); VersionSetting setting = profile.getVersionSetting(selectedVersion);
Controllers.dialog(launchingStepsPane); Controllers.dialog(launchingStepsPane);
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES)) executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
.then(dependencyManager.checkGameCompletionAsync(version)) .then(dependencyManager.checkGameCompletionAsync(version))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))) .then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
.then(new CurseCompletionTask(dependencyManager, selectedVersion)) .then(new CurseCompletionTask(dependencyManager, selectedVersion))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGIN))) .then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.then(Task.of(variables -> { .then(Task.of(Main.i18n("account.methods"), variables -> {
try { try {
variables.set("account", account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.INSTANCE.getProxy())); variables.set("account", account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.INSTANCE.getProxy()));
} catch (AuthenticationException e) { } catch (AuthenticationException e) {
@@ -87,22 +87,24 @@ public final class LauncherHelper {
)); ));
})) }))
.then(variables -> { .then(variables -> {
if (launcherName == null) { if (scriptFile == null) {
return variables.<DefaultLauncher>get("launcher").launchAsync(); return variables.<DefaultLauncher>get("launcher").launchAsync().setName("version.launch");
} else { } else {
variables.set("script", variables.<DefaultLauncher>get("launcher").makeLaunchScript(launcherName)); return variables.<DefaultLauncher>get("launcher").makeLaunchScriptAsync(scriptFile).setName("version.launch");
return null;
} }
}) })
.then(Task.of(variables -> { .then(Task.of(variables -> {
if (launcherName == null) { if (scriptFile == null) {
PROCESSES.add(variables.get(DefaultLauncher.LAUNCH_ASYNC_ID)); PROCESSES.add(variables.get(DefaultLauncher.LAUNCH_ASYNC_ID));
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE) if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
Main.stopApplication(); Main.stopApplication();
} } else
Platform.runLater(() ->
Controllers.dialog(Main.i18n("version.launch_script.success", scriptFile.getAbsolutePath())));
})) }))
.executor(); .executor();
launchingStepsPane.setExecutor(executor);
executor.addTaskListener(new TaskListener() { executor.addTaskListener(new TaskListener() {
AtomicInteger finished = new AtomicInteger(0); AtomicInteger finished = new AtomicInteger(0);
@@ -113,17 +115,6 @@ public final class LauncherHelper {
launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks()); launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks());
}); });
} }
@Override
public void onTerminate(AutoTypingMap<String> variables) {
Platform.runLater(() -> {
Controllers.closeDialog();
if (variables.containsKey("script")) {
Controllers.dialog(Main.i18n("version.launch_script.success", variables.<File>get("script").getAbsolutePath()));
}
});
}
}); });
executor.start(); executor.start();
@@ -163,8 +154,9 @@ public final class LauncherHelper {
if (state == LoadingState.DONE) if (state == LoadingState.DONE)
Controllers.closeDialog(); Controllers.closeDialog();
launchingStepsPane.setCurrentState(state.toString()); launchingStepsPane.setCurrentState(state.getLocalizedMessage());
launchingStepsPane.setSteps((state.ordinal() + 1) + " / " + LoadingState.values().length); launchingStepsPane.setSteps((state.ordinal() + 1) + " / " + LoadingState.values().length);
Controllers.dialog(launchingStepsPane);
} }
private void checkExit(LauncherVisibility v) { private void checkExit(LauncherVisibility v) {

View File

@@ -17,10 +17,22 @@
*/ */
package org.jackhuang.hmcl.game; package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.Main;
public enum LoadingState { public enum LoadingState {
DEPENDENCIES, DEPENDENCIES("launch.state.dependencies"),
MODS, MODS("launch.state.modpack"),
LOGIN, LOGGING_IN("launch.state.logging_in"),
LAUNCHING, LAUNCHING("launch.state.waiting_launching"),
DONE DONE("");
private final String key;
LoadingState(String key) {
this.key = key;
}
public String getLocalizedMessage() {
return Main.i18n(key);
}
} }

View File

@@ -428,7 +428,7 @@ public class Settings {
} }
private void onProfileChanged() { private void onProfileChanged() {
getSelectedProfile().getRepository().refreshVersions(); getSelectedProfile().getRepository().refreshVersionsAsync().start();
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile())); EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
} }

View File

@@ -77,4 +77,8 @@ public class AdvancedListBox extends ScrollPane {
public void setSpacing(double spacing) { public void setSpacing(double spacing) {
container.setSpacing(spacing); container.setSpacing(spacing);
} }
public void clear() {
container.getChildren().clear();
}
} }

View File

@@ -99,12 +99,24 @@ public final class Controllers {
stage.setTitle(Main.TITLE); stage.setTitle(Main.TITLE);
} }
public static Region getDialogContent() {
return decorator.getDialog().getContent();
}
public static JFXDialog dialog(Region content) { public static JFXDialog dialog(Region content) {
return decorator.showDialog(content); return decorator.showDialog(content);
} }
public static void dialog(String text) { public static void dialog(String text) {
dialog(new MessageDialogPane(text, decorator.getDialog())); dialog(text, null);
}
public static void dialog(String text, String title) {
dialog(text, title, null);
}
public static void dialog(String text, String title, Runnable onAccept) {
dialog(new MessageDialogPane(text, title, decorator.getDialog(), onAccept));
} }
public static void inputDialog(String text, Consumer<String> onResult) { public static void inputDialog(String text, Consumer<String> onResult) {

View File

@@ -47,13 +47,14 @@ import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.ui.animation.AnimationProducer; import org.jackhuang.hmcl.ui.animation.AnimationProducer;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionHandler; import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogWizardDisplayer;
import org.jackhuang.hmcl.ui.wizard.*; import org.jackhuang.hmcl.ui.wizard.*;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
public final class Decorator extends StackPane implements AbstractWizardDisplayer { public final class Decorator extends StackPane implements TaskExecutorDialogWizardDisplayer {
private static final SVGGlyph minus = Lang.apply(new SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE), private static final SVGGlyph minus = Lang.apply(new SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE),
glyph -> { glyph.setSize(12, 2); glyph.setTranslateY(4); }); glyph -> { glyph.setSize(12, 2); glyph.setTranslateY(4); });
private static final SVGGlyph resizeMax = Lang.apply(new SVGGlyph(0, "RESIZE_MAX", "M726 810v-596h-428v596h428zM726 44q34 0 59 25t25 59v768q0 34-25 60t-59 26h-428q-34 0-59-26t-25-60v-768q0-34 25-60t59-26z", Color.WHITE), private static final SVGGlyph resizeMax = Lang.apply(new SVGGlyph(0, "RESIZE_MAX", "M726 810v-596h-428v596h428zM726 44q34 0 59 25t25 59v768q0 34-25 60t-59 26h-428q-34 0-59-26t-25-60v-768q0-34 25-60t59-26z", Color.WHITE),
@@ -421,9 +422,11 @@ public final class Decorator extends StackPane implements AbstractWizardDisplaye
} }
public JFXDialog showDialog(Region content) { public JFXDialog showDialog(Region content) {
dialog.setContent(content); if (dialog.getContent() != content) {
if (!dialogShown) dialog.setContent(content);
dialog.show(); if (!dialogShown)
dialog.show();
}
return dialog; return dialog;
} }

View File

@@ -25,6 +25,7 @@ import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider; import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
@@ -59,8 +60,8 @@ public class InstallerController {
LinkedList<Library> newList = new LinkedList<>(version.getLibraries()); LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
newList.remove(library); newList.remove(library);
new VersionJsonSaveTask(profile.getRepository(), version.setLibraries(newList)) new VersionJsonSaveTask(profile.getRepository(), version.setLibraries(newList))
.with(Task.of(profile.getRepository()::refreshVersions)) .with(profile.getRepository().refreshVersionsAsync())
.with(Task.of(() -> loadVersion(this.profile, this.versionId))) .with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)))
.start(); .start();
}; };

View File

@@ -21,6 +21,7 @@ import com.jfoenix.effects.JFXDepthManager;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.Main;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -43,7 +44,7 @@ public class InstallerItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"); setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1); JFXDepthManager.setDepth(this, 1);
lblInstallerArtifact.setText(artifact); lblInstallerArtifact.setText(artifact);
lblInstallerVersion.setText(version); lblInstallerVersion.setText(Main.i18n("archive.version") + ": " + version);
} }
public void onDelete() { public void onDelete() {

View File

@@ -24,8 +24,10 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main; import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent; import org.jackhuang.hmcl.event.ProfileChangedEvent;
@@ -39,6 +41,7 @@ import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.OperatingSystem;
import java.io.File; import java.io.File;
import java.util.LinkedList; import java.util.LinkedList;
@@ -65,7 +68,9 @@ public final class MainPage extends StackPane implements DecoratorPage {
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged); EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged);
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install"))); btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install")));
btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersions()); FXUtils.installTooltip(btnAdd, 0, 5000, 0, new Tooltip(Main.i18n("install")));
btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start());
FXUtils.installTooltip(btnRefresh, 0, 5000, 0, new Tooltip(Main.i18n("button.refresh")));
} }
private Node buildNode(Profile profile, String version, String game) { private Node buildNode(Profile profile, String version, String game) {
@@ -82,9 +87,15 @@ public final class MainPage extends StackPane implements DecoratorPage {
if (Settings.INSTANCE.getSelectedAccount() == null) if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.no_Player007")); Controllers.dialog(Main.i18n("login.no_Player007"));
else { else {
Controllers.inputDialog(Main.i18n("mainwindow.enter_script_name"), file -> { FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(profile.getRepository().getRunDirectory(version));
chooser.setTitle(Main.i18n("version.launch_script.save"));
chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
? new FileChooser.ExtensionFilter(Main.i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(Main.i18n("extension.sh"), "*.sh"));
File file = chooser.showSaveDialog(Controllers.getStage());
if (file != null)
LauncherHelper.INSTANCE.launch(version, file); LauncherHelper.INSTANCE.launch(version, file);
});
} }
}); });
item.setOnSettingsButtonClicked(e -> { item.setOnSettingsButtonClicked(e -> {

View File

@@ -138,7 +138,7 @@ public final class ModController {
public void onAdd() { public void onAdd() {
FileChooser chooser = new FileChooser(); FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("mods.choose_mod")); chooser.setTitle(Main.i18n("mods.choose_mod"));
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter("Mod", "*.jar", "*.zip", "*.litemod")); chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(Main.i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
File res = chooser.showOpenDialog(Controllers.getStage()); File res = chooser.showOpenDialog(Controllers.getStage());
if (res == null) return; if (res == null) return;
Task.of(() -> modManager.addMod(versionId, res)) Task.of(() -> modManager.addMod(versionId, res))

View File

@@ -24,6 +24,7 @@ import javafx.geometry.Pos;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.mod.ModInfo; import org.jackhuang.hmcl.mod.ModInfo;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -54,7 +55,7 @@ public final class ModItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"); setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1); JFXDepthManager.setDepth(this, 1);
lblModFileName.setText(info.getFileName()); lblModFileName.setText(info.getFileName());
lblModAuthor.setText(info.getName() + ", Version: " + info.getVersion() + ", Game: " + info.getGameVersion() + ", Authors: " + info.getAuthors()); lblModAuthor.setText(info.getName() + ", " + Main.i18n("archive.version") + ": " + info.getVersion() + ", " + Main.i18n("archive.game_version") + ": " + info.getGameVersion() + ", " + Main.i18n("archive.author") + ": " + info.getAuthors());
chkEnabled.setSelected(info.isActive()); chkEnabled.setSelected(info.isActive());
chkEnabled.selectedProperty().addListener((a, b, newValue) -> { chkEnabled.selectedProperty().addListener((a, b, newValue) -> {
info.activeProperty().set(newValue); info.activeProperty().set(newValue);

View File

@@ -52,7 +52,7 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
this.profile = profile; this.profile = profile;
title = new SimpleStringProperty(this, "title", title = new SimpleStringProperty(this, "title",
profile == null ? Main.i18n("ui.newProfileWindow.title") : Main.i18n("profile") + " - " + profile.getName()); profile == null ? Main.i18n("profile.new") : Main.i18n("profile") + " - " + profile.getName());
location = new SimpleStringProperty(this, "location", location = new SimpleStringProperty(this, "location",
Optional.ofNullable(profile).map(Profile::getGameDir).map(File::getAbsolutePath).orElse("")); Optional.ofNullable(profile).map(Profile::getGameDir).map(File::getAbsolutePath).orElse(""));

View File

@@ -22,6 +22,7 @@ import javafx.beans.binding.Bindings;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.effect.BlurType; import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow; import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image; import javafx.scene.image.Image;
@@ -32,6 +33,7 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.jackhuang.hmcl.Main;
import java.util.Optional; import java.util.Optional;
@@ -68,6 +70,10 @@ public final class VersionItem extends StackPane {
btnLaunch.setGraphic(SVG.launch("black", 15, 15)); btnLaunch.setGraphic(SVG.launch("black", 15, 15));
btnScript.setGraphic(SVG.script("black", 15, 15)); btnScript.setGraphic(SVG.script("black", 15, 15));
FXUtils.installTooltip(btnSettings, 0, 5000, 0, new Tooltip(Main.i18n("version.manage.settings")));
FXUtils.installTooltip(btnLaunch, 0, 5000, 0, new Tooltip(Main.i18n("version.launch")));
FXUtils.installTooltip(btnScript, 0, 5000, 0, new Tooltip(Main.i18n("version.launch_script")));
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty())); icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
FXUtils.limitSize(iconView, 32, 32); FXUtils.limitSize(iconView, 32, 32);

View File

@@ -56,6 +56,8 @@ public final class VersionPage extends StackPane implements DecoratorPage {
@FXML @FXML
private JFXButton btnBrowseMenu; private JFXButton btnBrowseMenu;
@FXML @FXML
private JFXButton btnDelete;
@FXML
private JFXButton btnManagementMenu; private JFXButton btnManagementMenu;
@FXML @FXML
private JFXButton btnExport; private JFXButton btnExport;
@@ -80,6 +82,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
browsePopup = new JFXPopup(browseList); browsePopup = new JFXPopup(browseList);
managementPopup = new JFXPopup(managementList); managementPopup = new JFXPopup(managementList);
FXUtils.installTooltip(btnDelete, 0, 5000, 0, new Tooltip(Main.i18n("version.manage.remove")));
FXUtils.installTooltip(btnBrowseMenu, 0, 5000, 0, new Tooltip(Main.i18n("game_settings.exploration"))); FXUtils.installTooltip(btnBrowseMenu, 0, 5000, 0, new Tooltip(Main.i18n("game_settings.exploration")));
FXUtils.installTooltip(btnManagementMenu, 0, 5000, 0, new Tooltip(Main.i18n("game_settings.management"))); FXUtils.installTooltip(btnManagementMenu, 0, 5000, 0, new Tooltip(Main.i18n("game_settings.management")));
FXUtils.installTooltip(btnExport, 0, 5000, 0, new Tooltip(Main.i18n("modpack.export"))); FXUtils.installTooltip(btnExport, 0, 5000, 0, new Tooltip(Main.i18n("modpack.export")));
@@ -109,9 +112,12 @@ public final class VersionPage extends StackPane implements DecoratorPage {
} }
public void onDelete() { public void onDelete() {
profile.getRepository().removeVersionFromDisk(version); if (FXUtils.alert(Alert.AlertType.CONFIRMATION, "Confirm", Main.i18n("version.manage.remove.confirm") + version)) {
profile.getRepository().refreshVersions(); if (profile.getRepository().removeVersionFromDisk(version)) {
Controllers.navigate(null); profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
}
}
} }
public void onExport() { public void onExport() {
@@ -154,18 +160,13 @@ public final class VersionPage extends StackPane implements DecoratorPage {
Optional<String> res = FXUtils.inputDialog("Input", Main.i18n("version.manage.rename.message"), null, version); Optional<String> res = FXUtils.inputDialog("Input", Main.i18n("version.manage.rename.message"), null, version);
if (res.isPresent()) { if (res.isPresent()) {
if (profile.getRepository().renameVersion(version, res.get())) { if (profile.getRepository().renameVersion(version, res.get())) {
profile.getRepository().refreshVersions(); profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null); Controllers.navigate(null);
} }
} }
break; break;
case 1: // remove a version case 1: // remove a version
if (FXUtils.alert(Alert.AlertType.CONFIRMATION, "Confirm", Main.i18n("version.manage.remove.confirm") + version)) { onDelete();
if (profile.getRepository().removeVersionFromDisk(version)) {
profile.getRepository().refreshVersions();
Controllers.navigate(null);
}
}
break; break;
case 2: // redownload asset index case 2: // redownload asset index
new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version).resolve(profile.getRepository())).start(); new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version).resolve(profile.getRepository())).start();

View File

@@ -24,6 +24,7 @@ import javafx.scene.Node;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.control.Toggle; import javafx.scene.control.Toggle;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
@@ -73,6 +74,7 @@ public final class VersionSettingsController {
@FXML private MultiFileItem javaItem; @FXML private MultiFileItem javaItem;
@FXML private MultiFileItem gameDirItem; @FXML private MultiFileItem gameDirItem;
@FXML private JFXToggleButton chkShowLogs; @FXML private JFXToggleButton chkShowLogs;
@FXML private JFXButton btnIconSelection;
@FXML private ImageView iconView; @FXML private ImageView iconView;
public void initialize() { public void initialize() {
@@ -114,6 +116,8 @@ public final class VersionSettingsController {
gameDirItem.createChildren(Main.i18n("advancedsettings.game_dir.default"), EnumGameDirectory.ROOT_FOLDER), gameDirItem.createChildren(Main.i18n("advancedsettings.game_dir.default"), EnumGameDirectory.ROOT_FOLDER),
gameDirItem.createChildren(Main.i18n("advancedsettings.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER) gameDirItem.createChildren(Main.i18n("advancedsettings.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER)
)); ));
FXUtils.installTooltip(btnIconSelection, 0, 5000, 0, new Tooltip(Main.i18n("button.edit")));
} }
public void loadVersionSetting(Profile profile, String versionId, VersionSetting versionSetting) { public void loadVersionSetting(Profile profile, String versionId, VersionSetting versionSetting) {
@@ -245,7 +249,7 @@ public final class VersionSettingsController {
public void onExploreIcon() { public void onExploreIcon() {
FileChooser chooser = new FileChooser(); FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Image", "*.png")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("extension.png"), "*.png"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage()); File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile != null) { if (selectedFile != null) {
File iconFile = profile.getRepository().getVersionIcon(versionId); File iconFile = profile.getRepository().getVersionIcon(versionId);

View File

@@ -22,7 +22,8 @@ import javafx.scene.layout.Pane;
import javafx.util.Duration; import javafx.util.Duration;
public interface AnimationHandler { public interface AnimationHandler {
Node getSnapshot();
Duration getDuration(); Duration getDuration();
Pane getCurrentRoot(); Pane getCurrentRoot();
Node getPreviousNode();
Node getCurrentNode();
} }

View File

@@ -31,53 +31,57 @@ public enum ContainerAnimations {
* A fade between the old and new view * A fade between the old and new view
*/ */
FADE(c -> FADE(c ->
Arrays.asList(new KeyFrame(Duration.ZERO, new KeyValue(c.getSnapshot().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)), Arrays.asList(new KeyFrame(Duration.ZERO,
new KeyFrame(c.getDuration(), new KeyValue(c.getSnapshot().opacityProperty(), 0.0D, Interpolator.EASE_BOTH)))), new KeyValue(c.getPreviousNode().opacityProperty(), 1.0D, Interpolator.EASE_BOTH),
new KeyValue(c.getCurrentNode().opacityProperty(), 0.0D, Interpolator.EASE_BOTH)),
new KeyFrame(c.getDuration(),
new KeyValue(c.getPreviousNode().opacityProperty(), 0.0D, Interpolator.EASE_BOTH),
new KeyValue(c.getCurrentNode().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)))),
/** /**
* A zoom effect * A zoom effect
*/ */
ZOOM_IN(c -> ZOOM_IN(c ->
Arrays.asList(new KeyFrame(Duration.ZERO, Arrays.asList(new KeyFrame(Duration.ZERO,
new KeyValue(c.getSnapshot().scaleXProperty(), 1, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleXProperty(), 1, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().scaleYProperty(), 1, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleYProperty(), 1, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)), new KeyValue(c.getPreviousNode().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)),
new KeyFrame(c.getDuration(), new KeyFrame(c.getDuration(),
new KeyValue(c.getSnapshot().scaleXProperty(), 4, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleXProperty(), 4, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().scaleYProperty(), 4, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleYProperty(), 4, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().opacityProperty(), 0, Interpolator.EASE_BOTH)))), new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH)))),
/** /**
* A zoom effect * A zoom effect
*/ */
ZOOM_OUT(c -> ZOOM_OUT(c ->
(Arrays.asList(new KeyFrame(Duration.ZERO, (Arrays.asList(new KeyFrame(Duration.ZERO,
new KeyValue(c.getSnapshot().scaleXProperty(), 1, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleXProperty(), 1, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().scaleYProperty(), 1, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleYProperty(), 1, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)), new KeyValue(c.getPreviousNode().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)),
new KeyFrame(c.getDuration(), new KeyFrame(c.getDuration(),
new KeyValue(c.getSnapshot().scaleXProperty(), 0, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleXProperty(), 0, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().scaleYProperty(), 0, Interpolator.EASE_BOTH), new KeyValue(c.getPreviousNode().scaleYProperty(), 0, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().opacityProperty(), 0, Interpolator.EASE_BOTH))))), new KeyValue(c.getPreviousNode().opacityProperty(), 0, Interpolator.EASE_BOTH))))),
/** /**
* A swipe effect * A swipe effect
*/ */
SWIPE_LEFT(c -> SWIPE_LEFT(c ->
Arrays.asList(new KeyFrame(Duration.ZERO, Arrays.asList(new KeyFrame(Duration.ZERO,
new KeyValue(c.getCurrentRoot().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH), new KeyValue(c.getCurrentNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)), new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH)),
new KeyFrame(c.getDuration(), new KeyFrame(c.getDuration(),
new KeyValue(c.getCurrentRoot().translateXProperty(), 0, Interpolator.EASE_BOTH), new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)))), new KeyValue(c.getPreviousNode().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)))),
/** /**
* A swipe effect * A swipe effect
*/ */
SWIPE_RIGHT(c -> SWIPE_RIGHT(c ->
Arrays.asList(new KeyFrame(Duration.ZERO, Arrays.asList(new KeyFrame(Duration.ZERO,
new KeyValue(c.getCurrentRoot().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH), new KeyValue(c.getCurrentNode().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)), new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH)),
new KeyFrame(c.getDuration(), new KeyFrame(c.getDuration(),
new KeyValue(c.getCurrentRoot().translateXProperty(), 0, Interpolator.EASE_BOTH), new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
new KeyValue(c.getSnapshot().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)))); new KeyValue(c.getPreviousNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH))));
private AnimationProducer animationProducer; private AnimationProducer animationProducer;

View File

@@ -32,22 +32,24 @@ public final class TransitionHandler implements AnimationHandler {
private final StackPane view; private final StackPane view;
private Timeline animation; private Timeline animation;
private Duration duration; private Duration duration;
private final ImageView snapshot; private Node previousNode, currentNode;
/** /**
* @param view A stack pane that contains another control that is [Parent] * @param view A stack pane that contains another control that is {@link Parent}
*/ */
public TransitionHandler(StackPane view) { public TransitionHandler(StackPane view) {
this.view = view; this.view = view;
currentNode = view.getChildren().stream().findFirst().orElse(null);
snapshot = new ImageView();
snapshot.setPreserveRatio(true);
snapshot.setSmooth(true);
} }
@Override @Override
public Node getSnapshot() { public Node getPreviousNode() {
return snapshot; return previousNode;
}
@Override
public Node getCurrentNode() {
return currentNode;
} }
@Override @Override
@@ -76,10 +78,7 @@ public final class TransitionHandler implements AnimationHandler {
Timeline nowAnimation = new Timeline(); Timeline nowAnimation = new Timeline();
nowAnimation.getKeyFrames().addAll(transition.animate(this)); nowAnimation.getKeyFrames().addAll(transition.animate(this));
nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> { nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
snapshot.setImage(null); view.getChildren().remove(previousNode);
snapshot.setX(0);
snapshot.setY(0);
snapshot.setVisible(false);
})); }));
nowAnimation.play(); nowAnimation.play();
animation = nowAnimation; animation = nowAnimation;
@@ -87,23 +86,16 @@ public final class TransitionHandler implements AnimationHandler {
private void updateContent(Node newView) { private void updateContent(Node newView) {
if (view.getWidth() > 0 && view.getHeight() > 0) { if (view.getWidth() > 0 && view.getHeight() > 0) {
Node content = view.getChildren().stream().findFirst().orElse(null); previousNode = currentNode;
WritableImage image; if (previousNode == null)
if (content != null && content instanceof Parent) { previousNode = NULL;
view.getChildren().setAll();
image = FXUtils.takeSnapshot((Parent) content, view.getWidth(), view.getHeight());
view.getChildren().setAll(content);
} else
image = view.snapshot(new SnapshotParameters(), new WritableImage((int) view.getWidth(), (int) view.getHeight()));
snapshot.setImage(image);
snapshot.setFitWidth(view.getWidth());
snapshot.setFitHeight(view.getHeight());
} else } else
snapshot.setImage(null); previousNode = NULL;
snapshot.setVisible(true); currentNode = newView;
snapshot.setOpacity(1.0);
view.getChildren().setAll(snapshot, newView); view.getChildren().setAll(previousNode, currentNode);
snapshot.toFront();
} }
private static final StackPane NULL = new StackPane();
} }

View File

@@ -26,7 +26,9 @@ import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser; import javafx.stage.DirectoryChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import java.io.File; import java.io.File;
@@ -51,6 +53,7 @@ public class FileItem extends BorderPane {
right.setGraphic(SVG.pencil("black", 15, 15)); right.setGraphic(SVG.pencil("black", 15, 15));
right.getStyleClass().add("toggle-icon4"); right.getStyleClass().add("toggle-icon4");
right.setOnMouseClicked(e -> onExplore()); right.setOnMouseClicked(e -> onExplore());
FXUtils.installTooltip(right, 0, 5000, 0, new Tooltip(Main.i18n("button.edit")));
setRight(right); setRight(right);
Tooltip tip = new Tooltip(); Tooltip tip = new Tooltip();

View File

@@ -24,6 +24,8 @@ import javafx.scene.control.Label;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import java.util.Optional;
public final class MessageDialogPane extends StackPane { public final class MessageDialogPane extends StackPane {
private final String text; private final String text;
private final JFXDialog dialog; private final JFXDialog dialog;
@@ -32,13 +34,22 @@ public final class MessageDialogPane extends StackPane {
private JFXButton acceptButton; private JFXButton acceptButton;
@FXML @FXML
private Label content; private Label content;
@FXML
private Label title;
public MessageDialogPane(String text, JFXDialog dialog) { public MessageDialogPane(String text, String title, JFXDialog dialog, Runnable onAccept) {
this.text = text; this.text = text;
this.dialog = dialog; this.dialog = dialog;
FXUtils.loadFXML(this, "/assets/fxml/message-dialog.fxml"); FXUtils.loadFXML(this, "/assets/fxml/message-dialog.fxml");
if (title != null)
this.title.setText(title);
content.setText(text); content.setText(text);
acceptButton.setOnMouseClicked(e -> dialog.close()); acceptButton.setOnMouseClicked(e -> {
dialog.close();
Optional.ofNullable(onAccept).ifPresent(Runnable::run);
});
} }
} }

View File

@@ -15,37 +15,80 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}. * along with this program. If not, see {http://www.gnu.org/licenses/}.
*/ */
package org.jackhuang.hmcl.ui; package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXProgressBar; import com.jfoenix.controls.JFXProgressBar;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.TaskListPane;
import java.util.Optional;
public class TaskExecutorDialogPane extends StackPane {
private TaskExecutor executor;
public class LaunchingStepsPane extends StackPane {
@FXML @FXML
private JFXProgressBar pgsTasks; private JFXProgressBar pgsTasks;
@FXML @FXML
private Label lblCurrentState; private Label lblCurrentState;
@FXML @FXML
private Label lblSteps; private Label lblSteps;
@FXML
private JFXButton btnCancel;
@FXML
private TaskListPane taskListPane;
public LaunchingStepsPane() { public TaskExecutorDialogPane(Runnable cancel) {
FXUtils.loadFXML(this, "/assets/fxml/launching-steps.fxml"); FXUtils.loadFXML(this, "/assets/fxml/launching-steps.fxml");
FXUtils.limitHeight(this, 200); FXUtils.limitHeight(this, 200);
FXUtils.limitWidth(this, 400); FXUtils.limitWidth(this, 400);
btnCancel.setOnMouseClicked(e -> {
Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
cancel.run();
});
}
public void setExecutor(TaskExecutor executor) {
this.executor = executor;
taskListPane.setExecutor(executor);
}
public StringProperty currentStateProperty() {
return lblCurrentState.textProperty();
}
public String getCurrentState() {
return lblCurrentState.getText();
} }
public void setCurrentState(String currentState) { public void setCurrentState(String currentState) {
lblCurrentState.setText(currentState); lblCurrentState.setText(currentState);
} }
public StringProperty stepsProperty() {
return lblSteps.textProperty();
}
public String getSteps() {
return lblSteps.getText();
}
public void setSteps(String steps) { public void setSteps(String steps) {
lblSteps.setText(steps); lblSteps.setText(steps);
} }
public void setProgress(double progress) { public void setProgress(double progress) {
pgsTasks.setProgress(progress); if (progress == Double.MAX_VALUE)
pgsTasks.setVisible(false);
else
pgsTasks.setProgress(progress);
} }
} }

View File

@@ -0,0 +1,84 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.beans.property.StringProperty;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.wizard.AbstractWizardDisplayer;
import org.jackhuang.hmcl.util.OperatingSystem;
import org.jackhuang.hmcl.util.StringUtils;
import java.util.Map;
public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer {
@Override
default void handleTask(Map<String, Object> settings, Task task) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(() -> {
Controllers.closeDialog();
Controllers.navigate(null);
});
pane.setCurrentState(Main.i18n("message.doing"));
pane.setProgress(Double.MAX_VALUE);
if (settings.containsKey("title")) {
Object title = settings.get("title");
if (title instanceof StringProperty)
pane.currentStateProperty().bind((StringProperty) title);
else if (title instanceof String)
pane.setCurrentState((String) title);
}
if (settings.containsKey("subtitle")) {
Object subtitle = settings.get("subtitle");
if (subtitle instanceof StringProperty)
pane.stepsProperty().bind((StringProperty) subtitle);
else if (subtitle instanceof String)
pane.setSteps((String) subtitle);
}
JFXUtilities.runInFX(() -> {
TaskExecutor executor = task.executor(e -> new TaskListener() {
@Override
public void onSucceed() {
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
JFXUtilities.runInFX(() -> Controllers.dialog((String) settings.get("success_message"), null, () -> Controllers.navigate(null)));
else if (!settings.containsKey("forbid_success_message"))
JFXUtilities.runInFX(() -> Controllers.dialog(Main.i18n("message.success"), null, () -> Controllers.navigate(null)));
}
@Override
public void onTerminate() {
String appendix = StringUtils.getStackTrace(e.getLastException());
if (settings.containsKey("failure_message") && settings.get("failure_message") instanceof String)
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, (String) settings.get("failure_message"), () -> Controllers.navigate(null)));
else if (!settings.containsKey("forbid_failure_message"))
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Main.i18n("wizard.failed"), () -> Controllers.navigate(null)));
}
});
pane.setExecutor(executor);
Controllers.dialog(pane);
executor.start();
});
}
}

View File

@@ -23,6 +23,7 @@ import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main; import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask; import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
@@ -45,22 +46,26 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
public final class TaskListPane extends StackPane { public final class TaskListPane extends StackPane {
private final TaskExecutor executor;
private final AdvancedListBox listBox = new AdvancedListBox(); private final AdvancedListBox listBox = new AdvancedListBox();
private final Map<Task, ProgressListNode> nodes = new HashMap<>(); private final Map<Task, ProgressListNode> nodes = new HashMap<>();
public TaskListPane(TaskExecutor executor, Runnable onTerminate) { public TaskListPane() {
this.executor = executor;
listBox.setSpacing(0); listBox.setSpacing(0);
getChildren().setAll(listBox);
}
public void setExecutor(TaskExecutor executor) {
executor.addTaskListener(new TaskListener() { executor.addTaskListener(new TaskListener() {
@Override
public void onStart() {
Platform.runLater(listBox::clear);
}
@Override @Override
public void onReady(Task task) { public void onReady(Task task) {
if (!task.getSignificance().shouldShow()) if (!task.getSignificance().shouldShow())
return; return;
ProgressListNode node = new ProgressListNode(task);
nodes.put(task, node);
Platform.runLater(() -> listBox.add(node));
if (task instanceof GameAssetRefreshTask) { if (task instanceof GameAssetRefreshTask) {
task.setName(Main.i18n("assets.download")); task.setName(Main.i18n("assets.download"));
@@ -83,6 +88,11 @@ public final class TaskListPane extends StackPane {
} else if (task instanceof HMCLModpackExportTask) { } else if (task instanceof HMCLModpackExportTask) {
task.setName(Main.i18n("modpack.export")); task.setName(Main.i18n("modpack.export"));
} }
ProgressListNode node = new ProgressListNode(task);
nodes.put(task, node);
Platform.runLater(() -> listBox.add(node));
} }
@Override @Override
@@ -101,17 +111,10 @@ public final class TaskListPane extends StackPane {
return; return;
Platform.runLater(() -> node.setThrowable(throwable)); Platform.runLater(() -> node.setThrowable(throwable));
} }
@Override
public void onTerminate() {
Optional.ofNullable(onTerminate).ifPresent(Runnable::run);
}
}); });
getChildren().setAll(listBox);
} }
private static class ProgressListNode extends StackPane { private static class ProgressListNode extends VBox {
private final JFXProgressBar bar = new JFXProgressBar(); private final JFXProgressBar bar = new JFXProgressBar();
private final Label title = new Label(); private final Label title = new Label();
private final Label state = new Label(); private final Label state = new Label();
@@ -124,9 +127,8 @@ public final class TaskListPane extends StackPane {
BorderPane borderPane = new BorderPane(); BorderPane borderPane = new BorderPane();
borderPane.setLeft(title); borderPane.setLeft(title);
borderPane.setRight(state); borderPane.setRight(state);
getChildren().addAll(bar, borderPane); getChildren().addAll(borderPane, bar);
bar.setMinHeight(20);
bar.minWidthProperty().bind(widthProperty()); bar.minWidthProperty().bind(widthProperty());
bar.prefWidthProperty().bind(widthProperty()); bar.prefWidthProperty().bind(widthProperty());
bar.maxWidthProperty().bind(widthProperty()); bar.maxWidthProperty().bind(widthProperty());

View File

@@ -22,6 +22,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
@@ -36,26 +37,31 @@ import static org.jackhuang.hmcl.Main.i18n;
class AdditionalInstallersPage extends StackPane implements WizardPage { class AdditionalInstallersPage extends StackPane implements WizardPage {
private final InstallerWizardProvider provider; private final InstallerWizardProvider provider;
private final WizardController controller; private final WizardController controller;
private final GameRepository repository;
private final DownloadProvider downloadProvider;
@FXML private VBox list; @FXML
@FXML private JFXButton btnForge; private VBox list;
@FXML private JFXButton btnLiteLoader; @FXML
@FXML private JFXButton btnOptiFine; private JFXButton btnForge;
@FXML private Label lblGameVersion; @FXML
@FXML private Label lblVersionName; private JFXButton btnLiteLoader;
@FXML private Label lblForge; @FXML
@FXML private Label lblLiteLoader; private JFXButton btnOptiFine;
@FXML
private Label lblGameVersion;
@FXML
private Label lblVersionName;
@FXML
private Label lblForge;
@FXML
private Label lblLiteLoader;
@FXML @FXML
private Label lblOptiFine; private Label lblOptiFine;
@FXML private JFXButton btnInstall; @FXML
private JFXButton btnInstall;
public AdditionalInstallersPage(InstallerWizardProvider provider, WizardController controller, GameRepository repository, DownloadProvider downloadProvider) { public AdditionalInstallersPage(InstallerWizardProvider provider, WizardController controller, GameRepository repository, DownloadProvider downloadProvider) {
this.provider = provider; this.provider = provider;
this.controller = controller; this.controller = controller;
this.repository = repository;
this.downloadProvider = downloadProvider;
FXUtils.loadFXML(this, "/assets/fxml/download/additional-installers.fxml"); FXUtils.loadFXML(this, "/assets/fxml/download/additional-installers.fxml");
@@ -64,55 +70,64 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
btnForge.setOnMouseClicked(e -> { btnForge.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 0); controller.getSettings().put(INSTALLER_TYPE, 0);
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.forge")), provider.getGameVersion(), downloadProvider, "forge", () -> { controller.onPrev(false); })); controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.forge")), provider.getGameVersion(), downloadProvider, "forge", () -> {
controller.onPrev(false);
}));
}); });
btnLiteLoader.setOnMouseClicked(e -> { btnLiteLoader.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 1); controller.getSettings().put(INSTALLER_TYPE, 1);
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.liteloader")), provider.getGameVersion(), downloadProvider, "liteloader", () -> { controller.onPrev(false); })); controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.liteloader")), provider.getGameVersion(), downloadProvider, "liteloader", () -> {
controller.onPrev(false);
}));
}); });
btnOptiFine.setOnMouseClicked(e -> { btnOptiFine.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 2); controller.getSettings().put(INSTALLER_TYPE, 2);
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.optifine")), provider.getGameVersion(), downloadProvider, "optifine", () -> { controller.onPrev(false); })); controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.optifine")), provider.getGameVersion(), downloadProvider, "optifine", () -> {
controller.onPrev(false);
}));
}); });
btnInstall.setOnMouseClicked(e -> onInstall());
}
private void onInstall() {
controller.onFinish();
} }
@Override @Override
public String getTitle() { public String getTitle() {
return "Choose a game version"; return Main.i18n("settings.tabs.installers");
} }
@Override @Override
public void onNavigate(Map<String, Object> settings) { public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText("Current Game Version: " + provider.getGameVersion()); lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
btnForge.setDisable(provider.getForge() != null); btnForge.setDisable(provider.getForge() != null);
if (provider.getForge() != null || controller.getSettings().containsKey("forge")) if (provider.getForge() != null || controller.getSettings().containsKey("forge"))
lblForge.setText("Forge Versoin: " + Lang.nonNull(provider.getForge(), controller.getSettings().get("forge"))); lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), controller.getSettings().get("forge")));
else else
lblForge.setText("Forge not installed"); lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
btnLiteLoader.setDisable(provider.getLiteLoader() != null); btnLiteLoader.setDisable(provider.getLiteLoader() != null);
if (provider.getLiteLoader() != null || controller.getSettings().containsKey("liteloader")) if (provider.getLiteLoader() != null || controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText("LiteLoader Versoin: " + Lang.nonNull(provider.getLiteLoader(), controller.getSettings().get("liteloader"))); lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), controller.getSettings().get("liteloader")));
else else
lblLiteLoader.setText("LiteLoader not installed"); lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
btnOptiFine.setDisable(provider.getOptiFine() != null); btnOptiFine.setDisable(provider.getOptiFine() != null);
if (provider.getOptiFine() != null || controller.getSettings().containsKey("optifine")) if (provider.getOptiFine() != null || controller.getSettings().containsKey("optifine"))
lblOptiFine.setText("OptiFine Versoin: " + Lang.nonNull(provider.getOptiFine(), controller.getSettings().get("optifine"))); lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), controller.getSettings().get("optifine")));
else else
lblOptiFine.setText("OptiFine not installed"); lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
} }
@Override public void cleanup(Map<String, Object> settings) { @Override
settings.remove(INSTALLER_TYPE); public void cleanup(Map<String, Object> settings) {
} settings.remove(INSTALLER_TYPE);
}
public void onInstall() { public static final String INSTALLER_TYPE = "INSTALLER_TYPE";
controller.onFinish();
}
public static final String INSTALLER_TYPE = "INSTALLER_TYPE";
} }

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui.download; package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node; import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.HMCLModpackInstallTask; import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
@@ -77,7 +78,7 @@ public final class DownloadWizardProvider implements WizardProvider {
profile.getRepository().markVersionAsModpack(name); profile.getRepository().markVersionAsModpack(name);
Task finalizeTask = Task.of(() -> { Task finalizeTask = Task.of(() -> {
profile.getRepository().refreshVersions(); profile.getRepository().refreshVersionsAsync().start();
VersionSetting vs = profile.specializeVersionSetting(name); VersionSetting vs = profile.specializeVersionSetting(name);
profile.getRepository().undoMark(name); profile.getRepository().undoMark(name);
if (vs != null) if (vs != null)
@@ -99,6 +100,9 @@ public final class DownloadWizardProvider implements WizardProvider {
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(Map<String, Object> settings) {
settings.put("success_message", Main.i18n("install.success"));
settings.put("failure_message", Main.i18n("install.failed"));
switch (Lang.parseInt(settings.get(InstallTypePage.INSTALL_TYPE), -1)) { switch (Lang.parseInt(settings.get(InstallTypePage.INSTALL_TYPE), -1)) {
case 0: return finishVersionDownloadingAsync(settings); case 0: return finishVersionDownloadingAsync(settings);
case 1: return finishModpackInstallingAsync(settings); case 1: return finishModpackInstallingAsync(settings);

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui.download; package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node; import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider; import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
@@ -78,18 +79,21 @@ public final class InstallerWizardProvider implements WizardProvider {
@Override @Override
public Object finish(Map<String, Object> settings) { public Object finish(Map<String, Object> settings) {
settings.put("success_message", Main.i18n("install.success"));
settings.put("failure_message", Main.i18n("install.failed"));
Task ret = Task.empty(); Task ret = Task.empty();
if (settings.containsKey("forge")) if (settings.containsKey("forge"))
ret = ret.with(profile.getDependency().installLibraryAsync(gameVersion, version, "forge", (String) settings.get("forge"))); ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "forge", (String) settings.get("forge")));
if (settings.containsKey("liteloader")) if (settings.containsKey("liteloader"))
ret = ret.with(profile.getDependency().installLibraryAsync(gameVersion, version, "liteloader", (String) settings.get("liteloader"))); ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "liteloader", (String) settings.get("liteloader")));
if (settings.containsKey("optifine")) if (settings.containsKey("optifine"))
ret = ret.with(profile.getDependency().installLibraryAsync(gameVersion, version, "optifine", (String) settings.get("optifine"))); ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "optifine", (String) settings.get("optifine")));
return ret.with(Task.of(profile.getRepository()::refreshVersions)); return ret.then(profile.getRepository().refreshVersionsAsync());
} }
@Override @Override

View File

@@ -104,7 +104,7 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
public static final String MODPACK_NAME = "modpack.name"; public static final String MODPACK_NAME = "modpack.name";
public static final String MODPACK_VERSION = "modpack.version"; public static final String MODPACK_VERSION = "modpack.version";
public static final String MODPACK_AUTHOR = "modpack.author"; public static final String MODPACK_AUTHOR = "archive.author";
public static final String MODPACK_DESCRIPTION = "modpack.description"; public static final String MODPACK_DESCRIPTION = "modpack.description";
public static final String MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher"; public static final String MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher";
public static final String MODPACK_FILE = "modpack.file"; public static final String MODPACK_FILE = "modpack.file";

View File

@@ -38,62 +38,11 @@ public interface AbstractWizardDisplayer extends WizardDisplayer {
Queue<Object> getCancelQueue(); Queue<Object> getCancelQueue();
@Override
default void handleDeferredWizardResult(Map<String, Object> settings, DeferredWizardResult deferredWizardResult) {
VBox vbox = new VBox();
JFXProgressBar progressBar = new JFXProgressBar();
Label label = new Label();
progressBar.setMaxHeight(10);
vbox.getChildren().addAll(progressBar, label);
StackPane root = new StackPane();
root.getChildren().add(vbox);
navigateTo(root, Navigation.NavigationDirection.FINISH);
getCancelQueue().add(Lang.thread(() -> {
deferredWizardResult.start(settings, new ResultProgressHandle() {
private boolean running = true;
@Override
public void setProgress(int currentStep, int totalSteps) {
progressBar.setProgress(1.0 * currentStep / totalSteps);
}
@Override
public void setProgress(String description, int currentStep, int totalSteps) {
label.setText(description);
progressBar.setProgress(1.0 * currentStep / totalSteps);
}
@Override
public void setBusy(String description) {
progressBar.setProgress(JFXProgressBar.INDETERMINATE_PROGRESS);
}
@Override
public void finished(Object result) {
running = false;
}
@Override
public void failed(String message, boolean canNavigateBack) {
running = false;
}
@Override
public boolean isRunning() {
return running;
}
});
Platform.runLater(this::navigateToSuccess);
}));
}
@Override @Override
default void handleTask(Map<String, Object> settings, Task task) { default void handleTask(Map<String, Object> settings, Task task) {
TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor(); TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor();
TaskListPane pane = new TaskListPane(executor, () -> Platform.runLater(AbstractWizardDisplayer.this::navigateToSuccess)); TaskListPane pane = new TaskListPane();
pane.setExecutor(executor);
navigateTo(pane, Navigation.NavigationDirection.FINISH); navigateTo(pane, Navigation.NavigationDirection.FINISH);
getCancelQueue().add(executor); getCancelQueue().add(executor);
executor.start(); executor.start();

View File

@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.animation.TransitionHandler; import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;

View File

@@ -1,104 +0,0 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui.wizard;
/**
* A controller for the progress bar shown in the user interface. Used in
* conjunction then `DeferredWizardResult` for cases where at
* the conclusion of the wizard, the work to create the final wizard result
* will take a while and needs to happen on a background thread.
* @author Tim Boudreau
*/
public interface ResultProgressHandle {
/**
* Set the current position and total number of steps. Note it is
* inadvisable to be holding any locks when calling this method, as it
* may immediately update the GUI using
* `EventQueue.invokeAndWait()`.
*
* @param currentStep the current step in the progress of computing the
* * result.
* *
* @param totalSteps the total number of steps. Must be greater than
* * or equal to currentStep.
*/
void setProgress(int currentStep, int totalSteps);
/**
* Set the current position and total number of steps, and description
* of what the computation is doing. Note it is
* inadvisable to be holding any locks when calling this method, as it
* may immediately update the GUI using
* `EventQueue.invokeAndWait()`.
* @param description Text to describe what is being done, which can
* * be displayed in the UI.
* *
* @param currentStep the current step in the progress of computing the
* * result.
* *
* @param totalSteps the total number of steps. Must be greater than
* * or equal to currentStep.
*/
void setProgress(String description, int currentStep, int totalSteps);
/**
* Set the status as "busy" - a rotating icon will be displayed instead
* of a percent complete progress bar.
* Note it is inadvisable to be holding any locks when calling this method, as it
* may immediately update the GUI using
* `EventQueue.invokeAndWait()`.
* @param description Text to describe what is being done, which can
* * be displayed in the UI.
*/
void setBusy(String description);
/**
* Call this method when the computation is complete, and pass in the
* final result of the computation. The method doing the computation
* (`DeferredWizardResult.start()` or something it
* called) should exit immediately after calling this method. If the
* `failed()` method is called after this method has been
* called, a runtime exception may be thrown.
* @param result the Object which was computed, if any.
*/
void finished(Object result);
/**
* Call this method if computation fails. The message may be some text
* describing what went wrong, or null if no description.
* @param message The text to display to the user. The method
* * doing the computation (`DeferredWizardResult.start()` or something it
* * called). If the `finished()` method is called after this
* * method has been called, a runtime exception may be thrown.
* * should exit immediately after calling this method.
* * It is A description of what went wrong, or null.
* *
* @param canNavigateBack whether or not the Prev button should be
* * enabled.
*/
void failed(String message, boolean canNavigateBack);
/**
* Returns true if the computation is still running, i.e., if neither finished or failed have been called.
*
* @return true if there is no result yet.
*/
boolean isRunning();
}

View File

@@ -44,6 +44,11 @@ public final class Summary {
this.result = result; this.result = result;
} }
public Summary(Node component, Object result) {
this.component = component;
this.result = result;
}
/** /**
* The component that will display the summary information * The component that will display the summary information
*/ */

View File

@@ -100,8 +100,7 @@ public class WizardController implements Navigation {
@Override @Override
public void onFinish() { public void onFinish() {
Object result = provider.finish(settings); Object result = provider.finish(settings);
if (result instanceof DeferredWizardResult) displayer.handleDeferredWizardResult(settings, ((DeferredWizardResult) result)); if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT);
else if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT);
else if (result instanceof Task) displayer.handleTask(settings, ((Task) result)); else if (result instanceof Task) displayer.handleTask(settings, ((Task) result));
else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result); else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result);
} }

View File

@@ -27,6 +27,5 @@ public interface WizardDisplayer {
void onEnd(); void onEnd();
void onCancel(); void onCancel();
void navigateTo(Node page, Navigation.NavigationDirection nav); void navigateTo(Node page, Navigation.NavigationDirection nav);
void handleDeferredWizardResult(Map<String, Object> settings, DeferredWizardResult deferredWizardResult);
void handleTask(Map<String, Object> settings, Task task); void handleTask(Map<String, Object> settings, Task task);
} }

View File

@@ -51,7 +51,7 @@
</RequiredFieldValidator> </RequiredFieldValidator>
</validators> </validators>
</JFXTextField> </JFXTextField>
<JFXPasswordField fx:id="txtPassword" promptText="%ui.label.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2"> <JFXPasswordField fx:id="txtPassword" promptText="%login.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2">
<validators> <validators>
<RequiredFieldValidator message="%input.not_empty"> <RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator> </RequiredFieldValidator>

View File

@@ -55,8 +55,8 @@
</center> </center>
<bottom> <bottom>
<HBox alignment="CENTER"> <HBox alignment="CENTER">
<JFXButton fx:id="btnInstall" onMouseClicked="#onInstall" prefWidth="100" prefHeight="40" <JFXButton fx:id="btnInstall" prefWidth="100" prefHeight="40"
buttonType="RAISED" text="%ui.button.install" styleClass="jfx-button-raised"/> buttonType="RAISED" text="%button.install" styleClass="jfx-button-raised"/>
</HBox> </HBox>
</bottom> </bottom>
</BorderPane> </BorderPane>

View File

@@ -56,7 +56,7 @@
<bottom> <bottom>
<HBox alignment="CENTER"> <HBox alignment="CENTER">
<JFXButton fx:id="btnInstall" onMouseClicked="#onInstall" prefWidth="100" prefHeight="40" <JFXButton fx:id="btnInstall" onMouseClicked="#onInstall" prefWidth="100" prefHeight="40"
buttonType="RAISED" text="%ui.button.install" styleClass="jfx-button-raised"/> buttonType="RAISED" text="%button.install" styleClass="jfx-button-raised"/>
</HBox> </HBox>
</bottom> </bottom>
</BorderPane> </BorderPane>

View File

@@ -18,11 +18,11 @@
<BorderPane><left><Label text="%modpack.task.install.will" /></left><right><Label fx:id="lblModpackLocation" /></right></BorderPane> <BorderPane><left><Label text="%modpack.task.install.will" /></left><right><Label fx:id="lblModpackLocation" /></right></BorderPane>
<JFXTextField fx:id="txtModpackName" labelFloat="true" promptText="%modpack.enter_name" StackPane.margin="$insets" /> <JFXTextField fx:id="txtModpackName" labelFloat="true" promptText="%modpack.enter_name" StackPane.margin="$insets" />
<BorderPane><left><Label text="%modpack.name"/></left><right><Label fx:id="lblName" /></right></BorderPane> <BorderPane><left><Label text="%modpack.name"/></left><right><Label fx:id="lblName" /></right></BorderPane>
<BorderPane><left><Label text="%ui.label.version"/></left><right><Label fx:id="lblVersion" /></right></BorderPane> <BorderPane><left><Label text="%archive.version"/></left><right><Label fx:id="lblVersion" /></right></BorderPane>
<BorderPane><left><Label text="Author"/></left><right><Label fx:id="lblAuthor" /></right></BorderPane> <BorderPane><left><Label text="Author"/></left><right><Label fx:id="lblAuthor" /></right></BorderPane>
<BorderPane> <BorderPane>
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.wizard.step.3" styleClass="jfx-button" /></left> <left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.wizard.step.3" styleClass="jfx-button" /></left>
<right><JFXButton buttonType="RAISED" fx:id="btnInstall" onMouseClicked="#onInstall" text="%ui.button.install" styleClass="jfx-button-raised" /></right> <right><JFXButton buttonType="RAISED" fx:id="btnInstall" onMouseClicked="#onInstall" text="%button.install" styleClass="jfx-button-raised" /></right>
</BorderPane> </BorderPane>
</ComponentList> </ComponentList>
</VBox> </VBox>

View File

@@ -10,7 +10,7 @@
type="StackPane"> type="StackPane">
<JFXDialogLayout> <JFXDialogLayout>
<heading> <heading>
<Label id="content" /> <Label fx:id="content" />
</heading> </heading>
<body> <body>
<JFXTextField fx:id="textField" /> <JFXTextField fx:id="textField" />

View File

@@ -4,13 +4,19 @@
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import com.jfoenix.controls.JFXButton?>
<?import org.jackhuang.hmcl.ui.construct.TaskListPane?>
<fx:root xmlns="http://javafx.com/javafx" <fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
type="StackPane"> type="StackPane">
<JFXProgressBar fx:id="pgsTasks" StackPane.alignment="TOP_CENTER" /> <JFXProgressBar fx:id="pgsTasks" StackPane.alignment="TOP_CENTER" />
<VBox alignment="CENTER"> <VBox alignment="TOP_CENTER" style="-fx-padding: 16px;">
<Label fx:id="lblCurrentState" style="-fx-font-size: 20px;" /> <Label fx:id="lblCurrentState" style="-fx-font-size: 20px;" />
<Label fx:id="lblSteps" style="-fx-font-size: 14px;" /> <Label fx:id="lblSteps" style="-fx-font-size: 14px;" />
<TaskListPane fx:id="taskListPane" />
</VBox> </VBox>
<StackPane pickOnBounds="false" style="-fx-padding: 8px;">
<JFXButton StackPane.alignment="BOTTOM_RIGHT" fx:id="btnCancel" text="%button.cancel" />
</StackPane>
</fx:root> </fx:root>

View File

@@ -52,7 +52,7 @@
</StackPane> </StackPane>
<HBox alignment="CENTER_RIGHT" style="-fx-padding: 0 3 0 3;" spacing="3"> <HBox alignment="CENTER_RIGHT" style="-fx-padding: 0 3 0 3;" spacing="3">
<JFXButton onMouseClicked="#onTerminateGame" text="%logwindow.terminate_game" /> <JFXButton onMouseClicked="#onTerminateGame" text="%logwindow.terminate_game" />
<JFXButton onMouseClicked="#onClear" text="%ui.button.clear" /> <JFXButton onMouseClicked="#onClear" text="%button.clear" />
</HBox> </HBox>
</VBox> </VBox>
</fx:root> </fx:root>

View File

@@ -9,7 +9,7 @@
type="StackPane"> type="StackPane">
<JFXDialogLayout> <JFXDialogLayout>
<heading> <heading>
<Label text="%message.info" /> <Label fx:id="title" text="%message.info" />
</heading> </heading>
<body> <body>
<Label fx:id="content" textAlignment="JUSTIFY" wrapText="true" /> <Label fx:id="content" textAlignment="JUSTIFY" wrapText="true" />

View File

@@ -34,13 +34,13 @@
<RequiredFieldValidator message="%modpack.not_a_valid_name"/> <RequiredFieldValidator message="%modpack.not_a_valid_name"/>
</validators> </validators>
</JFXTextField> </JFXTextField>
<JFXTextField labelFloat="true" promptText="%modpack.author" fx:id="txtModpackAuthor" <JFXTextField labelFloat="true" promptText="%archive.author" fx:id="txtModpackAuthor"
StackPane.margin="$insets"> StackPane.margin="$insets">
<validators> <validators>
<RequiredFieldValidator/> <RequiredFieldValidator/>
</validators> </validators>
</JFXTextField> </JFXTextField>
<JFXTextField labelFloat="true" promptText="%ui.label.version" fx:id="txtModpackVersion" text="1.0" <JFXTextField labelFloat="true" promptText="%archive.version" fx:id="txtModpackVersion" text="1.0"
StackPane.margin="$insets"> StackPane.margin="$insets">
<validators> <validators>
<RequiredFieldValidator/> <RequiredFieldValidator/>

View File

@@ -39,11 +39,11 @@
<BorderPane pickOnBounds="false" style="-fx-padding: 20;"> <BorderPane pickOnBounds="false" style="-fx-padding: 20;">
<left> <left>
<JFXButton BorderPane.alignment="BOTTOM_LEFT" fx:id="btnDelete" onMouseClicked="#onDelete" prefWidth="100" prefHeight="40" <JFXButton BorderPane.alignment="BOTTOM_LEFT" fx:id="btnDelete" onMouseClicked="#onDelete" prefWidth="100" prefHeight="40"
buttonType="RAISED" text="%ui.button.delete" styleClass="jfx-button-raised" /> buttonType="RAISED" text="%button.delete" styleClass="jfx-button-raised" />
</left> </left>
<right> <right>
<JFXButton BorderPane.alignment="BOTTOM_RIGHT" fx:id="btnSave" onMouseClicked="#onSave" prefWidth="100" prefHeight="40" <JFXButton BorderPane.alignment="BOTTOM_RIGHT" fx:id="btnSave" onMouseClicked="#onSave" prefWidth="100" prefHeight="40"
buttonType="RAISED" text="%ui.button.save" styleClass="jfx-button-raised"/> buttonType="RAISED" text="%button.save" styleClass="jfx-button-raised"/>
</right> </right>
</BorderPane> </BorderPane>
</fx:root> </fx:root>

View File

@@ -29,25 +29,13 @@
<BorderPane> <BorderPane>
<left> <left>
<HBox spacing="8"> <HBox spacing="8">
<JFXButton fx:id="btnSettings" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30"> <JFXButton fx:id="btnSettings" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
<tooltip>
<Tooltip text="%version.manage.settings" />
</tooltip>
</JFXButton>
</HBox> </HBox>
</left> </left>
<right> <right>
<HBox spacing="8"> <HBox spacing="8">
<JFXButton fx:id="btnScript" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30"> <JFXButton fx:id="btnScript" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
<tooltip> <JFXButton fx:id="btnLaunch" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minHeight="30" minWidth="30" prefWidth="30" prefHeight="30" />
<Tooltip text="%version.launch_script" />
</tooltip>
</JFXButton>
<JFXButton fx:id="btnLaunch" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minHeight="30" minWidth="30" prefWidth="30" prefHeight="30">
<tooltip>
<Tooltip text="%version.launch" />
</tooltip>
</JFXButton>
</HBox> </HBox>
</right> </right>
</BorderPane> </BorderPane>

View File

@@ -19,7 +19,9 @@
<BorderPane> <!-- Icon --> <BorderPane> <!-- Icon -->
<left> <left>
<Label text="settings.icon" /> <VBox alignment="CENTER_LEFT">
<Label text="%settings.icon" />
</VBox>
</left> </left>
<right> <right>
<HBox alignment="CENTER_RIGHT" spacing="8"> <HBox alignment="CENTER_RIGHT" spacing="8">

View File

@@ -10,12 +10,12 @@
type="StackPane"> type="StackPane">
<JFXDialogLayout> <JFXDialogLayout>
<heading> <heading>
<Label text="%ui.message.enter_password" /> <Label text="%login.enter_password" />
</heading> </heading>
<body> <body>
<VBox spacing="15" style="-fx-padding: 15 0 0 0;"> <VBox spacing="15" style="-fx-padding: 15 0 0 0;">
<Label fx:id="lblUsername" /> <Label fx:id="lblUsername" />
<JFXPasswordField fx:id="txtPassword" promptText="%ui.label.password" labelFloat="true"> <JFXPasswordField fx:id="txtPassword" promptText="%login.password" labelFloat="true">
<validators> <validators>
<RequiredFieldValidator message="Input Required!"> <RequiredFieldValidator message="Input Required!">
</RequiredFieldValidator> </RequiredFieldValidator>

View File

@@ -30,17 +30,16 @@ launch.wrong_javadir=Incorrect Java directory, will reset to default Java direct
launch.failed.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help. launch.failed.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help.
launch.state.logging_in=Logging In launch.state.logging_in=Logging In
launch.state.generating_launching_codes=Generating Launching Codes launch.state.modpack=Preparing for modpack
launch.state.downloading_libraries=Downloading dependencies launch.state.dependencies=Decompressing natives
launch.state.decompressing_natives=Decompressing natives
launch.state.waiting_launching=Waiting for game launching launch.state.waiting_launching=Waiting for game launching
install.no_version=The version is not found. install.no_version=The version is not found.
install.no_version_if_intall=The required version is not found, do you wish to install the version automatically? install.no_version_if_intall=The required version is not found, do you wish to install the version automatically?
install.failed=Failed to install install.failed=Failed to install
install.success=Install successfully install.success=Installed successfully
install.installer.not_installed=%s not Installed install.installer.not_installed=%s not Installed
install.version=Version archive.version=Version
install.installer.version=%s Version install.installer.version=%s Version
install.time=Time install.time=Time
install.release_time=Release Time install.release_time=Release Time
@@ -81,8 +80,7 @@ minecraft.wrong_path=Wrong Minecraft path, the launcher could not find the path.
operation.stopped=The operation was aborted. operation.stopped=The operation was aborted.
operation.confirm_stop=Terminate the operations? operation.confirm_stop=Terminate the operations?
ui.login.password=Password button.more=More
ui.more=More
crash.advice.UnsupportedClassVersionError=Maybe your java is too old, try to update the java. crash.advice.UnsupportedClassVersionError=Maybe your java is too old, try to update the java.
crash.advice.ConcurrentModificationException=Maybe your Java is newer than 1.8.0_11, you could downgrade to Java 7. crash.advice.ConcurrentModificationException=Maybe your Java is newer than 1.8.0_11, you could downgrade to Java 7.
@@ -106,50 +104,44 @@ crash.error=Minecraft has crashed.
crash.main_class_not_found=Main Class is not found, may be your mc has been broken. crash.main_class_not_found=Main Class is not found, may be your mc has been broken.
crash.class_path_wrong=Maybe the launch script is malformed. crash.class_path_wrong=Maybe the launch script is malformed.
ui.label.newProfileWindow.new_profile_name=New Profile Name: profile.new_name=New Profile Name:
ui.label.newProfileWindow.copy_from=Copy From: profile.copy_from=Copy From:
ui.newProfileWindow.title=New Config profile.new=New Config
ui.button.ok=OK button.refresh=Refresh
ui.button.refresh=Refresh
version.launch=Play version.launch=Play
version.manage.settings=Settings version.manage.settings=Settings
ui.button.about=About button.about=About
ui.button.others=Others button.others=Others
ui.button.logout=Log Out button.download=Download
ui.button.download=Download button.retry=Retry
ui.button.retry=Retry button.delete=Delete
ui.button.delete=Delete button.install=Install
ui.button.install=Install button.info=Info
ui.button.info=Info button.save=Save
ui.button.save=Save button.copy=Copy
ui.button.copy=Copy button.clear=Clear
ui.button.clear=Clear button.close=Close
ui.button.close=Close button.explore=Explore
ui.button.explore=Explore button.test=Test
ui.button.test=Test button.preview=Preview
ui.button.preview=Preview
button.cancel=Cancel button.cancel=Cancel
button.ok=OK button.ok=OK
button.yes=Yes button.yes=Yes
button.no=No button.no=No
ui.label.version=Version login.password=Password
ui.label.password=Password
profile=Profile profile=Profile
ui.message.first_load=Please enter your name. login.enter_username=Please enter your name.
ui.message.enter_password=Please enter your password. login.enter_password=Please enter your password.
ui.message.launching=Launching...
ui.message.making=Generating...
ui.message.downloading=Downloading... ui.message.downloading=Downloading...
ui.message.sure_remove=Sure to remove profile %s? profile.remove=Sure to remove profile %s?
ui.message.update_java=Please upgrade your Java. launcher.update_java=Please upgrade your Java.
ui.message.open_jdk=We have found that you started this application using OpenJDK, which will cause so many troubles drawing the UI. We suggest you using Oracle JDK instead. launcher.open_jdk=We have found that you started this application using OpenJDK, which will cause so many troubles drawing the UI. We suggest you using Oracle JDK instead.
ui.label.settings=Settings launcher.crash=Hello Minecraft! Launcher has crashed!
ui.label.crashing=Hello Minecraft! Launcher has crashed! launcher.crash_out_dated=Hello Minecraft! Launcher has crashed! Your launcher is outdated. Update it!
ui.label.crashing_out_dated=Hello Minecraft! Launcher has crashed! Your launcher is outdated. Update it!
ui.label.failed_set=Failed to set: ui.label.failed_set=Failed to set:
download=Download download=Download
@@ -208,7 +200,7 @@ modpack.export_finished=Exporting the modpack finished. See
modpack.included_launcher=The modpack is included the launcher, you can publish it directly. modpack.included_launcher=The modpack is included the launcher, you can publish it directly.
modpack.not_included_launcher=You can use the modpack by clicking the "Import Modpack" button. modpack.not_included_launcher=You can use the modpack by clicking the "Import Modpack" button.
modpack.enter_name=Enter your desired name for this game. modpack.enter_name=Enter your desired name for this game.
modpack.author=Author archive.author=Authors
modpack.export=Export Modpack modpack.export=Export Modpack
modpack.task.install=Import Modpack modpack.task.install=Import Modpack
modpack.task.install.error=Failed to install the modpack. Maybe the files is incorrect, or a management issue occurred. modpack.task.install.error=Failed to install the modpack. Maybe the files is incorrect, or a management issue occurred.
@@ -276,7 +268,7 @@ advancedsettings.dont_check_game_completeness=Don't check game completeness
mainwindow.show_log=Show Logs mainwindow.show_log=Show Logs
version.launch_script=Make Launching Script. version.launch_script=Make Launching Script.
version.launch_script.failed=Failed to make script. version.launch_script.failed=Failed to make script.
mainwindow.enter_script_name=Enter the script name. version.launch_script.save=Save the launch script
version.launch_script.success=Finished script creation, %s. version.launch_script.success=Finished script creation, %s.
mainwindow.no_version=No version found. Switch to Game Downloads Tab? mainwindow.no_version=No version found. Switch to Game Downloads Tab?
@@ -332,7 +324,6 @@ assets.failed_download=Failed to download assets, may cause no sounds and langua
taskwindow.title=Tasks taskwindow.title=Tasks
taskwindow.single_progress=Single progress taskwindow.single_progress=Single progress
taskwindow.total_progress=Total progress taskwindow.total_progress=Total progress
taskwindow.cancel=Cancel
taskwindow.no_more_instance=Maybe you opened more than one task window, don't open it again! taskwindow.no_more_instance=Maybe you opened more than one task window, don't open it again!
taskwindow.file_name=Task taskwindow.file_name=Task
taskwindow.download_progress=Pgs. taskwindow.download_progress=Pgs.
@@ -413,3 +404,11 @@ modpack.type.curse=Curse
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.hmcl=HMCL modpack.type.hmcl=HMCL
modpack.type.curse.completion=Install relative files to Curse modpack modpack.type.curse.completion=Install relative files to Curse modpack
archive.game_version=Game
button.edit=Edit
extension.sh=Bash shell
extension.bat=Windows Bat file
extension.mod=Mod file
extension.png=Image file
message.success=Tasks succeeded
message.doing=Please wait

View File

@@ -30,9 +30,8 @@ launch.wrong_javadir=错误的Java路径将自动重置为默认Java路径。
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。 launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
launch.state.logging_in=登录中 launch.state.logging_in=登录中
launch.state.generating_launching_codes=正在生成启动代码 launch.state.modpack=正在下载必要文件
launch.state.downloading_libraries=正在下载必要文件 launch.state.dependencies=正在处理游戏依赖
launch.state.decompressing_natives=正在释放本地文件
launch.state.waiting_launching=等待游戏启动 launch.state.waiting_launching=等待游戏启动
install.no_version=未找到要安装的对应MC版本 install.no_version=未找到要安装的对应MC版本
@@ -40,7 +39,7 @@ install.no_version_if_intall=未找到要安装的对应MC版本是否自动
install.failed=安装失败 install.failed=安装失败
install.success=安装成功 install.success=安装成功
install.installer.not_installed=不安装%s install.installer.not_installed=不安装%s
install.version=版本 archive.version=版本
install.installer.version=%s版本 install.installer.version=%s版本
install.time=时间 install.time=时间
install.release_time=发布时间 install.release_time=发布时间
@@ -81,8 +80,7 @@ minecraft.wrong_path=错误的Minecraft路径启动器未找到设定的Minec
operation.stopped=操作被强行终止 operation.stopped=操作被强行终止
operation.confirm_stop=真的要终止操作吗? operation.confirm_stop=真的要终止操作吗?
ui.login.password=密码 button.more=更多
ui.more=更多
crash.advice.UnsupportedClassVersionError=这可能是因为您的Java版本过于老旧可以尝试更换最新Java并在版本设置的Java路径中设置. crash.advice.UnsupportedClassVersionError=这可能是因为您的Java版本过于老旧可以尝试更换最新Java并在版本设置的Java路径中设置.
crash.advice.ConcurrentModificationException=这可能是因为您的Java版本高于Java 1.8.0_11导致的,可以尝试卸载Java8安装Java7。 crash.advice.ConcurrentModificationException=这可能是因为您的Java版本高于Java 1.8.0_11导致的,可以尝试卸载Java8安装Java7。
@@ -106,49 +104,43 @@ crash.error=您的Minecraft崩溃了。
crash.main_class_not_found=找不到主类可能是您的JSON文件填写错误。无法启动游戏。可以通过下载整合包解决问题。 crash.main_class_not_found=找不到主类可能是您的JSON文件填写错误。无法启动游戏。可以通过下载整合包解决问题。
crash.class_path_wrong=解析Class Path时出现错误此错误本不应该发生。可能是启动脚本错误请仔细检查启动脚本。 crash.class_path_wrong=解析Class Path时出现错误此错误本不应该发生。可能是启动脚本错误请仔细检查启动脚本。
ui.label.newProfileWindow.new_profile_name=新配置名: profile.new_name=新配置名:
ui.label.newProfileWindow.copy_from=复制配置: profile.copy_from=复制配置:
ui.newProfileWindow.title=新建配置 profile.new=新建配置
ui.button.ok=确认 button.refresh=刷新
ui.button.refresh=刷新
version.launch=启动游戏 version.launch=启动游戏
version.manage.settings=游戏设置 version.manage.settings=游戏设置
ui.button.about=关于 button.about=关于
ui.button.others=其他 button.others=其他
ui.button.logout=登出 button.download=下载
ui.button.download=下载 button.retry=重试
ui.button.retry=重试 button.delete=删除
ui.button.delete=删除 button.install=安装
ui.button.install=安装 button.info=信息
ui.button.info=信息 button.save=保存
ui.button.save=保存 button.copy=复制
ui.button.copy=复制 button.clear=清除
ui.button.clear=清除 button.close=关闭
ui.button.close=关闭 button.explore=浏览
ui.button.explore=浏览 button.test=测试
ui.button.test=测试 button.preview=预览
ui.button.preview=预览
button.cancel=取消 button.cancel=取消
button.ok=确定 button.ok=确定
button.yes= button.yes=
button.no= button.no=
ui.label.version=版本 login.password=密码
ui.label.password=密码
profile=配置 profile=配置
ui.message.first_load=请输入您的账号 login.enter_username=请输入您的账号
ui.message.enter_password=请输入您的密码 login.enter_password=请输入您的密码
ui.message.launching=启动中 profile.remove=真的要删除配置%s吗
ui.message.making=生成中 launcher.update_java=请更新您的Java
ui.message.sure_remove=真的要删除配置%s吗 launcher.open_jdk=我们发现您正在使用OpenJDK这会导致很多界面问题我们建议您更换Oracle JDK。
ui.message.update_java=请更新您的Java
ui.message.open_jdk=我们发现您正在使用OpenJDK这会导致很多界面问题我们建议您更换Oracle JDK。
ui.label.settings=选项 launcher.crash=Hello Minecraft!遇到了无法处理的错误请复制下列内容并通过mcbbs、贴吧、Github或Minecraft Forum反馈bug。
ui.label.crashing=Hello Minecraft!遇到了无法处理的错误,请复制下列内容并通过mcbbs、贴吧、Github或Minecraft Forum反馈bug。 launcher.crash_out_dated=Hello Minecraft! Launcher遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试!
ui.label.crashing_out_dated=Hello Minecraft! Launcher遇到了无法处理的错误已检测到您的启动器不是最新版本请更新后再试
ui.label.failed_set=设置失败: ui.label.failed_set=设置失败:
download=下载 download=下载
@@ -207,7 +199,7 @@ modpack.export_finished=整合包导出完成,参见
modpack.included_launcher=整合包已包含启动器,可直接发布 modpack.included_launcher=整合包已包含启动器,可直接发布
modpack.not_included_launcher=整合包未包含启动器,可使用本软件的导入整合包功能导入整合包 modpack.not_included_launcher=整合包未包含启动器,可使用本软件的导入整合包功能导入整合包
modpack.enter_name=给游戏起个你喜欢的名字 modpack.enter_name=给游戏起个你喜欢的名字
modpack.author=作者 archive.author=作者
modpack.export=导出整合包 modpack.export=导出整合包
modpack.task.install=导入整合包 modpack.task.install=导入整合包
@@ -276,7 +268,7 @@ advancedsettings.dont_check_game_completeness=不检查游戏完整性
mainwindow.show_log=查看日志 mainwindow.show_log=查看日志
version.launch_script=生成启动脚本 version.launch_script=生成启动脚本
version.launch_script.failed=生成启动脚本失败 version.launch_script.failed=生成启动脚本失败
mainwindow.enter_script_name=输入要生成脚本的文件名 version.launch_script.save=保存启动脚本
version.launch_script.success=启动脚本已生成完毕: %s. version.launch_script.success=启动脚本已生成完毕: %s.
mainwindow.no_version=未找到任何版本,是否进入游戏下载? mainwindow.no_version=未找到任何版本,是否进入游戏下载?
@@ -332,7 +324,6 @@ assets.failed_download=下载资源文件失败,可能导致没有中文和声
taskwindow.title=任务 taskwindow.title=任务
taskwindow.single_progress=单项进度 taskwindow.single_progress=单项进度
taskwindow.total_progress=总进度 taskwindow.total_progress=总进度
taskwindow.cancel=取消
taskwindow.no_more_instance=可能同时打开了多个任务窗口,请不要多次打开! taskwindow.no_more_instance=可能同时打开了多个任务窗口,请不要多次打开!
taskwindow.file_name=任务 taskwindow.file_name=任务
taskwindow.download_progress=进度 taskwindow.download_progress=进度
@@ -413,3 +404,11 @@ modpack.type.curse=Curse
modpack.type.multimc=MultiMC modpack.type.multimc=MultiMC
modpack.type.hmcl=HMCL modpack.type.hmcl=HMCL
modpack.type.curse.completion=下载Curse整合包相关文件 modpack.type.curse.completion=下载Curse整合包相关文件
archive.game_version=游戏版本
button.edit=修改
extension.sh=Bash 脚本
extension.bat=Windows 脚本
extension.mod=模组文件
extension.png=图片文件
message.success=已完成
message.doing=请耐心等待

View File

@@ -125,8 +125,8 @@ public final class ForgeInstallTask extends TaskResult<Version> {
setResult(installProfile.getVersionInfo() setResult(installProfile.getVersionInfo()
.setInheritsFrom(version.getId()) .setInheritsFrom(version.getId())
.resolve(provider) .resolve(provider).setJar(null)
.setId(version.getId()).setLogging(Collections.EMPTY_MAP)); .setId(version.getId()).setLogging(Collections.emptyMap()));
dependencies.add(new GameLibrariesTask(dependencyManager, installProfile.getVersionInfo())); dependencies.add(new GameLibrariesTask(dependencyManager, installProfile.getVersionInfo()));
} }

View File

@@ -71,9 +71,13 @@ public final class GameAssetDownloadTask extends Task {
} }
@Override @Override
public void execute() { public void execute() throws Exception {
int size = refreshTask.getResult().size(); int size = refreshTask.getResult().size();
int downloaded = 0;
for (Map.Entry<File, AssetObject> entry : refreshTask.getResult()) { for (Map.Entry<File, AssetObject> entry : refreshTask.getResult()) {
if (Thread.interrupted())
throw new InterruptedException();
File file = entry.getKey(); File file = entry.getKey();
AssetObject assetObject = entry.getValue(); AssetObject assetObject = entry.getValue();
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation(); String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
@@ -84,7 +88,6 @@ public final class GameAssetDownloadTask extends Task {
if (file.isDirectory()) if (file.isDirectory())
continue; continue;
boolean flag = true; boolean flag = true;
int downloaded = 0;
try { try {
// check the checksum of file to ensure that the file is not need to re-download. // check the checksum of file to ensure that the file is not need to re-download.
if (file.exists()) { if (file.exists()) {

View File

@@ -79,6 +79,9 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
int progress = 0; int progress = 0;
if (index != null) if (index != null)
for (AssetObject assetObject : index.getObjects().values()) { for (AssetObject assetObject : index.getObjects().values()) {
if (Thread.interrupted())
throw new InterruptedException();
res.add(new Pair<>(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject)); res.add(new Pair<>(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject));
updateProgress(++progress, index.getObjects().size()); updateProgress(++progress, index.getObjects().size());
} }

View File

@@ -17,6 +17,8 @@
*/ */
package org.jackhuang.hmcl.game; package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.task.Task;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@@ -69,6 +71,10 @@ public interface GameRepository extends VersionProvider {
*/ */
void refreshVersions(); void refreshVersions();
default Task refreshVersionsAsync() {
return Task.of(this::refreshVersions);
}
/** /**
* Gets the root folder of specific version. * Gets the root folder of specific version.
* The root folders the versions must be unique. * The root folders the versions must be unique.

View File

@@ -19,6 +19,8 @@ package org.jackhuang.hmcl.launch;
import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.SimpleTaskResult;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.*;
@@ -274,23 +276,18 @@ public class DefaultLauncher extends Launcher {
} }
public final TaskResult<ManagedProcess> launchAsync() { public final TaskResult<ManagedProcess> launchAsync() {
return new TaskResult<ManagedProcess>() { return new SimpleTaskResult<>(LAUNCH_ASYNC_ID, this::launch);
@Override
public String getId() {
return LAUNCH_ASYNC_ID;
}
@Override
public void execute() throws Exception {
setResult(launch());
}
};
} }
@Override @Override
public File makeLaunchScript(String file) throws IOException { public void makeLaunchScript(File scriptFile) throws IOException {
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS; boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
File scriptFile = new File(file + (isWindows ? ".bat" : ".sh"));
if (isWindows && !FileUtils.getExtension(scriptFile).equals("bat"))
throw new IOException("The extension of " + scriptFile + " is not 'bat' in Windows");
else if (!isWindows && !FileUtils.getExtension(scriptFile).equals("sh"))
throw new IOException("The extension of " + scriptFile + " is not 'sh' in macOS/Linux");
if (!FileUtils.makeFile(scriptFile)) if (!FileUtils.makeFile(scriptFile))
throw new IOException("Script file: " + scriptFile + " cannot be created."); throw new IOException("Script file: " + scriptFile + " cannot be created.");
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(scriptFile)))) { try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(scriptFile)))) {
@@ -310,7 +307,10 @@ public class DefaultLauncher extends Launcher {
} }
if (!scriptFile.setExecutable(true)) if (!scriptFile.setExecutable(true))
throw new IOException("Cannot make script file '" + scriptFile + "' executable."); throw new IOException("Cannot make script file '" + scriptFile + "' executable.");
return scriptFile; }
public final Task makeLaunchScriptAsync(File file) {
return Task.of(() -> makeLaunchScript(file));
} }
private void startMonitors(ManagedProcess managedProcess) { private void startMonitors(ManagedProcess managedProcess) {
@@ -367,4 +367,5 @@ public class DefaultLauncher extends Launcher {
} }
public static final String LAUNCH_ASYNC_ID = "process"; public static final String LAUNCH_ASYNC_ID = "process";
public static final String LAUNCH_SCRIPT_ASYNC_ID = "script";
} }

View File

@@ -61,10 +61,9 @@ public abstract class Launcher {
} }
/** /**
* @param file The file path without extension * @param file the file path.
* @return the actual file with extension sh or bat.
*/ */
public abstract File makeLaunchScript(String file) throws IOException; public abstract void makeLaunchScript(File file) throws IOException;
public abstract ManagedProcess launch() throws IOException, InterruptedException; public abstract ManagedProcess launch() throws IOException, InterruptedException;

View File

@@ -212,6 +212,7 @@ public class FileDownloadTask extends Task {
if (temp != null) if (temp != null)
temp.delete(); temp.delete();
Logging.LOG.log(Level.WARNING, "Unable to download file " + currentURL, e); Logging.LOG.log(Level.WARNING, "Unable to download file " + currentURL, e);
exception = e;
} finally { } finally {
closeFiles(); closeFiles();
} }

View File

@@ -51,6 +51,7 @@ class SchedulerImpl extends Scheduler {
} finally { } finally {
latch.countDown(); latch.countDown();
} }
Thread.interrupted(); // clear the `interrupted` flag to prevent from interrupting EventDispatch thread.
}); });
return new Future<Void>() { return new Future<Void>() {

View File

@@ -21,7 +21,7 @@ import java.util.concurrent.*;
/** /**
* *
* @author huang * @author huangyuhui
*/ */
public final class Schedulers { public final class Schedulers {
@@ -33,7 +33,10 @@ public final class Schedulers {
private static synchronized ExecutorService getCachedExecutorService() { private static synchronized ExecutorService getCachedExecutorService() {
if (CACHED_EXECUTOR == null) if (CACHED_EXECUTOR == null)
CACHED_EXECUTOR = new ThreadPoolExecutor(0, Integer.MAX_VALUE, CACHED_EXECUTOR = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60, TimeUnit.SECONDS, new SynchronousQueue<>()); 60, TimeUnit.SECONDS, new SynchronousQueue<>(), runnable -> {
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
return thread;
});
return CACHED_EXECUTOR; return CACHED_EXECUTOR;
} }
@@ -42,8 +45,8 @@ public final class Schedulers {
private static synchronized ExecutorService getIOExecutorService() { private static synchronized ExecutorService getIOExecutorService() {
if (IO_EXECUTOR == null) if (IO_EXECUTOR == null)
IO_EXECUTOR = Executors.newFixedThreadPool(6, r -> { IO_EXECUTOR = Executors.newFixedThreadPool(6, runnable -> {
Thread thread = Executors.defaultThreadFactory().newThread(r); Thread thread = Executors.defaultThreadFactory().newThread(runnable);
thread.setDaemon(true); thread.setDaemon(true);
return thread; return thread;
}); });
@@ -55,8 +58,8 @@ public final class Schedulers {
private static synchronized ExecutorService getSingleExecutorService() { private static synchronized ExecutorService getSingleExecutorService() {
if (SINGLE_EXECUTOR == null) if (SINGLE_EXECUTOR == null)
SINGLE_EXECUTOR = Executors.newSingleThreadExecutor(r -> { SINGLE_EXECUTOR = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = Executors.defaultThreadFactory().newThread(r); Thread thread = Executors.defaultThreadFactory().newThread(runnable);
thread.setDaemon(true); thread.setDaemon(true);
return thread; return thread;
}); });

View File

@@ -29,15 +29,18 @@ class SimpleTask extends Task {
private final ExceptionalConsumer<AutoTypingMap<String>, ?> consumer; private final ExceptionalConsumer<AutoTypingMap<String>, ?> consumer;
private final Scheduler scheduler; private final Scheduler scheduler;
public SimpleTask(ExceptionalConsumer<AutoTypingMap<String>, ?> consumer) { public SimpleTask(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> consumer) {
this(consumer, Schedulers.defaultScheduler()); this(name, consumer, Schedulers.defaultScheduler());
} }
public SimpleTask(ExceptionalConsumer<AutoTypingMap<String>, ?> consumer, Scheduler scheduler) { public SimpleTask(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> consumer, Scheduler scheduler) {
this.consumer = consumer; this.consumer = consumer;
this.scheduler = scheduler; this.scheduler = scheduler;
setName(consumer.toString()); if (name == null)
setSignificance(TaskSignificance.MINOR);
else
setName(name);
} }
@Override @Override

View File

@@ -15,26 +15,26 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}. * along with this program. If not, see {http://www.gnu.org/licenses/}.
*/ */
package org.jackhuang.hmcl.ui.wizard; package org.jackhuang.hmcl.task;
import java.util.Map; import org.jackhuang.hmcl.util.ExceptionalSupplier;
/** public final class SimpleTaskResult<V> extends TaskResult<V> {
* @author huangyuhui private final String id;
*/ private final ExceptionalSupplier<V, ?> supplier;
public abstract class DeferredWizardResult {
private final boolean canAbort;
public DeferredWizardResult() { public SimpleTaskResult(String id, ExceptionalSupplier<V, ?> supplier) {
this(false); this.id = id;
this.supplier = supplier;
} }
public DeferredWizardResult(boolean canAbort) { @Override
this.canAbort = canAbort; public void execute() throws Exception {
setResult(supplier.get());
} }
public abstract void start(Map<String, Object> settings, ResultProgressHandle progressHandle); @Override
public String getId() {
public void abort() { return id;
} }
} }

View File

@@ -87,8 +87,9 @@ public abstract class Task {
return name; return name;
} }
public void setName(String name) { public Task setName(String name) {
this.name = name; this.name = name;
return this;
} }
private AutoTypingMap<String> variables = null; private AutoTypingMap<String> variables = null;
@@ -131,7 +132,7 @@ public abstract class Task {
private long lastTime = Long.MIN_VALUE; private long lastTime = Long.MIN_VALUE;
private final AtomicReference<Double> progressUpdate = new AtomicReference<>(); private final AtomicReference<Double> progressUpdate = new AtomicReference<>();
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", 0); private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", -1);
public ReadOnlyDoubleProperty progressProperty() { public ReadOnlyDoubleProperty progressProperty() {
return progress.getReadOnlyProperty(); return progress.getReadOnlyProperty();
@@ -244,20 +245,36 @@ public abstract class Task {
}); });
} }
public static Task of(String name, ExceptionalRunnable<?> runnable) {
return of(name, s -> runnable.run());
}
public static Task of(ExceptionalRunnable<?> runnable) { public static Task of(ExceptionalRunnable<?> runnable) {
return of(s -> runnable.run()); return of(s -> runnable.run());
} }
public static Task of(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return of(name, Schedulers.defaultScheduler(), closure);
}
public static Task of(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) { public static Task of(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return of(Schedulers.defaultScheduler(), closure); return of(Schedulers.defaultScheduler(), closure);
} }
public static Task of(String name, Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return new SimpleTask(name, closure, scheduler);
}
public static Task of(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) { public static Task of(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
return new SimpleTask(closure, scheduler); return of(null, scheduler, closure);
}
public static Task of(String name, Scheduler scheduler, ExceptionalRunnable<?> closure) {
return new SimpleTask(name, i -> closure.run(), scheduler);
} }
public static Task of(Scheduler scheduler, ExceptionalRunnable<?> closure) { public static Task of(Scheduler scheduler, ExceptionalRunnable<?> closure) {
return new SimpleTask(i -> closure.run(), scheduler); return of(null, scheduler, closure);
} }
public static <V> TaskResult<V> ofResult(String id, Callable<V> callable) { public static <V> TaskResult<V> ofResult(String id, Callable<V> callable) {

View File

@@ -23,10 +23,7 @@ import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
@@ -66,17 +63,22 @@ public final class TaskExecutor {
this.scheduler = Objects.requireNonNull(scheduler); this.scheduler = Objects.requireNonNull(scheduler);
} }
public void start() { public TaskExecutor start() {
taskListeners.forEach(TaskListener::onStart);
workerQueue.add(scheduler.schedule(() -> { workerQueue.add(scheduler.schedule(() -> {
if (!executeTasks(Collections.singleton(firstTask))) if (executeTasks(Collections.singleton(firstTask)))
taskListeners.forEach(TaskListener::onSucceed);
else
taskListeners.forEach(it -> { taskListeners.forEach(it -> {
it.onTerminate(); it.onTerminate();
it.onTerminate(variables); it.onTerminate(variables);
}); });
})); }));
return this;
} }
public boolean test() { public boolean test() {
taskListeners.forEach(TaskListener::onStart);
AtomicBoolean flag = new AtomicBoolean(true); AtomicBoolean flag = new AtomicBoolean(true);
Future<?> future = scheduler.schedule(() -> { Future<?> future = scheduler.schedule(() -> {
if (!executeTasks(Collections.singleton(firstTask))) { if (!executeTasks(Collections.singleton(firstTask))) {
@@ -85,7 +87,8 @@ public final class TaskExecutor {
it.onTerminate(variables); it.onTerminate(variables);
}); });
flag.set(false); flag.set(false);
} } else
taskListeners.forEach(TaskListener::onSucceed);
}); });
workerQueue.add(future); workerQueue.add(future);
Lang.invoke(() -> future.get()); Lang.invoke(() -> future.get());
@@ -128,7 +131,11 @@ public final class TaskExecutor {
if (canceled) if (canceled)
return false; return false;
latch.await(); try {
latch.await();
} catch (InterruptedException e) {
return false;
}
return success.get() && !canceled; return success.get() && !canceled;
} }
@@ -156,8 +163,10 @@ public final class TaskExecutor {
variables.set(taskResult.getId(), taskResult.getResult()); variables.set(taskResult.getId(), taskResult.getResult());
} }
if (!executeTasks(task.getDependencies()) && task.isRelyingOnDependencies()) if (!executeTasks(task.getDependencies()) && task.isRelyingOnDependencies()) {
throw new IllegalStateException("Subtasks failed for " + task.getName()); Logging.LOG.severe("Subtasks failed for " + task.getName());
return false;
}
flag = true; flag = true;
if (task.getSignificance().shouldLog()) { if (task.getSignificance().shouldLog()) {
@@ -169,7 +178,7 @@ public final class TaskExecutor {
} catch (InterruptedException e) { } catch (InterruptedException e) {
if (task.getSignificance().shouldLog()) { if (task.getSignificance().shouldLog()) {
lastException = e; lastException = e;
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName(), e); Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName());
task.onDone().fireEvent(new TaskEvent(this, task, true)); task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e)); taskListeners.forEach(it -> it.onFailed(task, e));
} }
@@ -178,12 +187,10 @@ public final class TaskExecutor {
} catch (RejectedExecutionException e) { } catch (RejectedExecutionException e) {
return false; return false;
} catch (Exception e) { } catch (Exception e) {
if (task.getSignificance().shouldLog()) { lastException = e;
lastException = e; Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e); task.onDone().fireEvent(new TaskEvent(this, task, true));
task.onDone().fireEvent(new TaskEvent(this, task, true)); taskListeners.forEach(it -> it.onFailed(task, e));
taskListeners.forEach(it -> it.onFailed(task, e));
}
} finally { } finally {
task.setVariables(null); task.setVariables(null);
} }
@@ -209,7 +216,8 @@ public final class TaskExecutor {
@Override @Override
public void run() { public void run() {
try { try {
Thread.currentThread().setName(task.getName()); if (Thread.currentThread().getName().contains("pool"))
Thread.currentThread().setName(task.getName());
if (!executeTask(task)) if (!executeTask(task))
success.set(false); success.set(false);
} finally { } finally {

View File

@@ -27,6 +27,9 @@ import java.util.EventListener;
*/ */
public abstract class TaskListener implements EventListener { public abstract class TaskListener implements EventListener {
public void onStart() {
}
public void onReady(Task task) { public void onReady(Task task) {
} }
@@ -42,6 +45,9 @@ public abstract class TaskListener implements EventListener {
public void onTerminate(AutoTypingMap<String> variables) { public void onTerminate(AutoTypingMap<String> variables) {
} }
public void onSucceed() {
}
public static class DefaultTaskListener extends TaskListener { public static class DefaultTaskListener extends TaskListener {
public static final DefaultTaskListener INSTANCE = new DefaultTaskListener(); public static final DefaultTaskListener INSTANCE = new DefaultTaskListener();

View File

@@ -165,7 +165,7 @@ public final class JavaVersion implements Serializable {
return JAVAS; return JAVAS;
} }
public static synchronized void initialize() throws IOException, InterruptedException { public static synchronized void initialize() throws IOException {
if (JAVAS != null) if (JAVAS != null)
throw new IllegalStateException("JavaVersions have already been initialized."); throw new IllegalStateException("JavaVersions have already been initialized.");
HashMap<String, JavaVersion> temp = new HashMap<>(); HashMap<String, JavaVersion> temp = new HashMap<>();

View File

@@ -219,16 +219,21 @@ public final class Lang {
public static <T> List<T> merge(Collection<T> a, Collection<T> b) { public static <T> List<T> merge(Collection<T> a, Collection<T> b) {
LinkedList<T> result = new LinkedList<>(); LinkedList<T> result = new LinkedList<>();
result.addAll(a); if (a != null)
result.addAll(b); result.addAll(a);
if (b != null)
result.addAll(b);
return result; return result;
} }
public static <T> List<T> merge(Collection<T> a, Collection<T> b, Collection<T> c) { public static <T> List<T> merge(Collection<T> a, Collection<T> b, Collection<T> c) {
LinkedList<T> result = new LinkedList<>(); LinkedList<T> result = new LinkedList<>();
result.addAll(a); if (a != null)
result.addAll(b); result.addAll(a);
result.addAll(c); if (b != null)
result.addAll(b);
if (c != null)
result.addAll(c);
return result; return result;
} }

View File

@@ -17,6 +17,8 @@
*/ */
package org.jackhuang.hmcl.util; package org.jackhuang.hmcl.util;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -62,6 +64,19 @@ public final class StringUtils {
return str; return str;
} }
public static String getStackTrace(Throwable throwable) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
throwable.printStackTrace(new PrintStream(stream));
return stream.toString();
}
public static String getStackTrace(StackTraceElement[] elements) {
StringBuilder builder = new StringBuilder();
for (StackTraceElement element : elements)
builder.append("\tat ").append(element).append(OperatingSystem.LINE_SEPARATOR);
return builder.toString();
}
public static boolean isBlank(String str) { public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty(); return str == null || str.trim().isEmpty();
} }