diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java index 70b80d1d0..8f919b748 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -30,6 +30,7 @@ import javafx.fxml.FXML; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.input.TransferMode; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; import javafx.util.Duration; @@ -42,16 +43,24 @@ import org.jackhuang.hmcl.ui.construct.PopupMenu; import org.jackhuang.hmcl.ui.construct.RipplerContainer; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; +import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; import org.jackhuang.hmcl.ui.versions.GameItem; import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; +import org.jackhuang.hmcl.util.Logging; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.javafx.MultiStepBinding; import org.jackhuang.hmcl.util.versioning.VersionNumber; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.logging.Level; import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -126,6 +135,29 @@ public final class MainPage extends StackPane implements DecoratorPage { }); Profiles.registerVersionsListener(this::loadVersions); + + setOnDragOver(event -> { + if (event.getGestureSource() != this && event.getDragboard().hasFiles()) { + if (event.getDragboard().getFiles().stream().anyMatch(it -> "zip".equals(FileUtils.getExtension(it)))) + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } + event.consume(); + }); + + setOnDragDropped(event -> { + List files = event.getDragboard().getFiles(); + if (files != null) { + List modpacks = files.stream() + .filter(it -> "zip".equals(FileUtils.getExtension(it))) + .collect(Collectors.toList()); + if (!modpacks.isEmpty()) { + File modpack = modpacks.get(0); + Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(modpack), i18n("install.modpack")); + event.setDropCompleted(true); + } + } + event.consume(); + }); } private void loadVersions(Profile profile) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java new file mode 100644 index 000000000..7925efd4d --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -0,0 +1,78 @@ +package org.jackhuang.hmcl.ui.download; + +import javafx.scene.Node; +import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.game.ModpackHelper; +import org.jackhuang.hmcl.mod.Modpack; +import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.wizard.WizardController; +import org.jackhuang.hmcl.ui.wizard.WizardProvider; + +import java.io.File; +import java.util.Map; + +import static org.jackhuang.hmcl.util.Lang.tryCast; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class ModpackInstallWizardProvider implements WizardProvider { + private Profile profile; + private final File file; + + public ModpackInstallWizardProvider() { + this(null); + } + + public ModpackInstallWizardProvider(File modpackFile) { + this.file = modpackFile; + } + + @Override + public void start(Map settings) { + profile = Profiles.getSelectedProfile(); + + if (file != null) + settings.put(ModpackPage.MODPACK_FILE, file); + settings.put(PROFILE, profile); + } + + private Task finishModpackInstallingAsync(Map settings) { + if (!settings.containsKey(ModpackPage.MODPACK_FILE)) + return null; + + File selected = tryCast(settings.get(ModpackPage.MODPACK_FILE), File.class).orElse(null); + Modpack modpack = tryCast(settings.get(ModpackPage.MODPACK_MANIFEST), Modpack.class).orElse(null); + String name = tryCast(settings.get(ModpackPage.MODPACK_NAME), String.class).orElse(null); + if (selected == null || modpack == null || name == null) return null; + + return ModpackHelper.getInstallTask(profile, selected, name, modpack) + .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); + } + + @Override + public Object finish(Map settings) { + settings.put("success_message", i18n("install.success")); + settings.put("failure_message", i18n("install.failed")); + + return finishModpackInstallingAsync(settings); + } + + @Override + public Node createPage(WizardController controller, int step, Map settings) { + switch (step) { + case 0: + return new ModpackPage(controller); + default: + throw new IllegalStateException("error step " + step + ", settings: " + settings + ", pages: " + controller.getPages()); + } + } + + @Override + public boolean cancel() { + return true; + } + + public static final String PROFILE = "PROFILE"; +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java index 6ce2657ac..99fefac37 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java @@ -32,6 +32,7 @@ import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.WebStage; +import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; @@ -39,7 +40,9 @@ import org.jackhuang.hmcl.util.StringUtils; import java.io.File; import java.util.Map; +import java.util.Optional; +import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class ModpackPage extends StackPane implements WizardPage { @@ -75,31 +78,41 @@ public final class ModpackPage extends StackPane implements WizardPage { Profile profile = (Profile) controller.getSettings().get("PROFILE"); - FileChooser chooser = new FileChooser(); - chooser.setTitle(i18n("modpack.choose")); - chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); - File selectedFile = chooser.showOpenDialog(Controllers.getStage()); - if (selectedFile == null) Platform.runLater(controller::onEnd); - else { + File selectedFile; + + Optional filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class); + if (filePath.isPresent()) { + selectedFile = filePath.get(); + } else { + FileChooser chooser = new FileChooser(); + chooser.setTitle(i18n("modpack.choose")); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); + selectedFile = chooser.showOpenDialog(Controllers.getStage()); + if (selectedFile == null) { + Platform.runLater(controller::onEnd); + return; + } + controller.getSettings().put(MODPACK_FILE, selectedFile); + } + + try { + manifest = ModpackHelper.readModpackManifest(selectedFile); + controller.getSettings().put(MODPACK_MANIFEST, manifest); + lblName.setText(manifest.getName()); + lblVersion.setText(manifest.getVersion()); + lblAuthor.setText(manifest.getAuthor()); + txtModpackName.setText(manifest.getName() + (StringUtils.isBlank(manifest.getVersion()) ? "" : "-" + manifest.getVersion())); + lblModpackLocation.setText(selectedFile.getAbsolutePath()); txtModpackName.getValidators().addAll( new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)), new Validator(i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str)) ); txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate())); - - try { - manifest = ModpackHelper.readModpackManifest(selectedFile); - controller.getSettings().put(MODPACK_MANIFEST, manifest); - lblName.setText(manifest.getName()); - lblVersion.setText(manifest.getVersion()); - lblAuthor.setText(manifest.getAuthor()); - txtModpackName.setText(manifest.getName() + (StringUtils.isBlank(manifest.getVersion()) ? "" : "-" + manifest.getVersion())); - } catch (UnsupportedModpackException e) { - txtModpackName.setText(i18n("modpack.task.install.error")); - btnInstall.setDisable(true); - } + } catch (UnsupportedModpackException e) { + Controllers.dialog(i18n("modpack.task.install.error"), i18n("message.error"), MessageBox.ERROR_MESSAGE); + Platform.runLater(controller::onEnd); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java similarity index 62% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java index d5067f540..8bf71e4a7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java @@ -21,8 +21,6 @@ import javafx.scene.Node; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.ModpackHelper; -import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.task.Schedulers; @@ -30,18 +28,14 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; -import java.io.File; import java.util.Map; -import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public final class DownloadWizardProvider implements WizardProvider { +public final class VanillaInstallWizardProvider implements WizardProvider { private Profile profile; - private final int type; - public DownloadWizardProvider(int type) { - this.type = type; + public VanillaInstallWizardProvider() { } @Override @@ -70,29 +64,12 @@ public final class DownloadWizardProvider implements WizardProvider { .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); } - private Task finishModpackInstallingAsync(Map settings) { - if (!settings.containsKey(ModpackPage.MODPACK_FILE)) - return null; - - File selected = tryCast(settings.get(ModpackPage.MODPACK_FILE), File.class).orElse(null); - Modpack modpack = tryCast(settings.get(ModpackPage.MODPACK_MANIFEST), Modpack.class).orElse(null); - String name = tryCast(settings.get(ModpackPage.MODPACK_NAME), String.class).orElse(null); - if (selected == null || modpack == null || name == null) return null; - - return ModpackHelper.getInstallTask(profile, selected, name, modpack) - .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); - } - @Override public Object finish(Map settings) { settings.put("success_message", i18n("install.success")); settings.put("failure_message", i18n("install.failed")); - switch (type) { - case 0: return finishVersionDownloadingAsync(settings); - case 1: return finishModpackInstallingAsync(settings); - default: return null; - } + return finishVersionDownloadingAsync(settings); } @Override @@ -100,14 +77,7 @@ public final class DownloadWizardProvider implements WizardProvider { DownloadProvider provider = profile.getDependency().getDownloadProvider(); switch (step) { case 0: - switch (type) { - case 0: - return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", provider, "game", () -> controller.onNext(new InstallersPage(controller, profile.getRepository(), provider))); - case 1: - return new ModpackPage(controller); - default: - throw new IllegalStateException("Error step " + step + ", subStep " + type + ", settings: " + settings + ", pages: " + controller.getPages()); - } + return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", provider, "game", () -> controller.onNext(new InstallersPage(controller, profile.getRepository(), provider))); default: throw new IllegalStateException("error step " + step + ", settings: " + settings + ", pages: " + controller.getPages()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java index 8f2bd35c7..d5f294b4d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java @@ -33,7 +33,8 @@ import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.WeakListenerHolder; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; +import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; +import org.jackhuang.hmcl.ui.download.VanillaInstallWizardProvider; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.versioning.VersionNumber; @@ -99,11 +100,11 @@ public class GameList extends Control implements DecoratorPage { } public void addNewGame() { - Controllers.getDecorator().startWizard(new DownloadWizardProvider(0), i18n("install.new_game")); + Controllers.getDecorator().startWizard(new VanillaInstallWizardProvider(), i18n("install.new_game")); } public void importModpack() { - Controllers.getDecorator().startWizard(new DownloadWizardProvider(1), i18n("install.modpack")); + Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(), i18n("install.modpack")); } public void refresh() { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0e06a27f8..73c5aa408 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -217,7 +217,7 @@ modpack.name=Modpack Name modpack.not_a_valid_name=Not a valid modpack name modpack.scan=Scanning this 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=This modpack file cannot be recognized. Only Curse, MultiMC modpacks are supported. modpack.task.install.will=Install the modpack: modpack.type.curse=Curse modpack.type.curse.completion=Install relative files to Curse modpack diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index f00a355f1..d54b7c25d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -217,7 +217,7 @@ modpack.name=整合包名稱 modpack.not_a_valid_name=整合包名稱無效 modpack.scan=解析整合包 modpack.task.install=匯入整合包 -modpack.task.install.error=安裝失敗,可能是整合包格式錯誤或操作檔案失敗。 +modpack.task.install.error=無法識別該整合包,目前僅支持導入 Curse、MultiMC、HMCL 整合包。 modpack.task.install.will=將會安裝整合包: modpack.type.curse=Curse modpack.type.curse.completion=下載 Curse 整合包相關檔案 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index f072e9f56..18cb38fcd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -217,7 +217,7 @@ modpack.name=整合包名称 modpack.not_a_valid_name=不是一个有效的整合包名称 modpack.scan=解析整合包 modpack.task.install=导入整合包 -modpack.task.install.error=安装失败,可能是整合包格式不正确或操作文件失败。 +modpack.task.install.error=无法识别该整合包,目前仅支持导入 Curse、MultiMC、HMCL 整合包。 modpack.task.install.will=将会安装整合包: modpack.type.curse=Curse modpack.type.curse.completion=下载 Curse 整合包相关文件