From a2ef477ae06d6ef7a4908e6abe9369ded853515f Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 31 May 2020 22:24:03 +0800 Subject: [PATCH] add: forbids installation buttons for incompatible libraries. Closes #720. --- .../org/jackhuang/hmcl/ui/InstallerItem.java | 240 +++++++++++++----- .../ui/download/AdditionalInstallersPage.java | 20 +- .../hmcl/ui/download/InstallersPage.java | 92 +------ .../hmcl/ui/versions/InstallerListPage.java | 54 ++-- .../resources/assets/lang/I18N.properties | 7 +- .../resources/assets/lang/I18N_es.properties | 5 +- .../resources/assets/lang/I18N_ru.properties | 7 +- .../resources/assets/lang/I18N_zh.properties | 7 +- .../assets/lang/I18N_zh_CN.properties | 7 +- .../hmcl/download/forge/ForgeInstallTask.java | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 11 files changed, 254 insertions(+), 189 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java index 8de943a90..c61bb1dab 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java @@ -19,94 +19,198 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.controls.JFXButton; import com.jfoenix.effects.JFXDepthManager; +import javafx.beans.binding.Bindings; +import javafx.beans.property.*; +import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.scene.Cursor; +import javafx.scene.control.Control; import javafx.scene.control.Label; +import javafx.scene.control.Skin; +import javafx.scene.control.SkinBase; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.util.i18n.I18n; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Consumer; +import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; /** * @author huangyuhui */ -public class InstallerItem extends BorderPane { +public class InstallerItem extends Control { + private final String id; + private final String imageUrl; + public final StringProperty libraryVersion = new SimpleStringProperty(); + public final StringProperty incompatibleLibraryName = new SimpleStringProperty(); + public final BooleanProperty incompatibleWithGame = new SimpleBooleanProperty(); + public final BooleanProperty removable = new SimpleBooleanProperty(); + public final BooleanProperty upgradable = new SimpleBooleanProperty(false); + public final BooleanProperty installable = new SimpleBooleanProperty(true); + public final ObjectProperty> removeAction = new SimpleObjectProperty<>(); + public final ObjectProperty> action = new SimpleObjectProperty<>(); - public InstallerItem(String libraryId, String libraryVersion, @Nullable Runnable upgrade, @Nullable Consumer deleteCallback) { - getStyleClass().addAll("two-line-list-item", "card"); - JFXDepthManager.setDepth(this, 1); + public InstallerItem(LibraryAnalyzer.LibraryType id) { + this(id.getPatchId()); + } - String[] urls = new String[]{"/assets/img/grass.png", "/assets/img/fabric.png", "/assets/img/forge.png", "/assets/img/chicken.png", "/assets/img/command.png"}; - String[] libraryIds = new String[]{"game", "fabric", "forge", "liteloader", "optifine"}; + public InstallerItem(String id) { + this.id = id; - boolean regularLibrary = false; - for (int i = 0; i < 5; ++i) { - if (libraryIds[i].equals(libraryId)) { - setLeft(FXUtils.limitingSize(new ImageView(new Image(urls[i], 32, 32, true, true)), 32, 32)); - Label label = new Label(); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - BorderPane.setMargin(label, new Insets(0, 0, 0, 8)); - if (libraryVersion == null) { - label.setText(i18n("install.installer.not_installed", i18n("install.installer." + libraryId))); - } else { - label.setText(i18n("install.installer.version", i18n("install.installer." + libraryId), libraryVersion)); - } - setCenter(label); - regularLibrary = true; + switch (id) { + case "game": + imageUrl = "/assets/img/grass.png"; + break; + case "fabric": + imageUrl = "/assets/img/fabric.png"; + break; + case "forge": + imageUrl = "/assets/img/forge.png"; + break; + case "liteloader": + imageUrl = "/assets/img/chicken.png"; + break; + case "optifine": + imageUrl = "/assets/img/command.png"; + break; + default: + imageUrl = null; break; - } - } - - if (!regularLibrary) { - String title = I18n.hasKey("install.installer." + libraryId) ? i18n("install.installer." + libraryId) : libraryId; - if (libraryVersion != null) { - TwoLineListItem item = new TwoLineListItem(); - item.setTitle(title); - item.setSubtitle(i18n("archive.version") + ": " + libraryVersion); - setCenter(item); - } else { - Label label = new Label(); - label.setStyle("-fx-font-size: 15px;"); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - setCenter(label); - } - } - - { - HBox hBox = new HBox(); - - if (upgrade != null) { - JFXButton upgradeButton = new JFXButton(); - if (libraryVersion == null) { - upgradeButton.setGraphic(SVG.arrowRight(Theme.blackFillBinding(), -1, -1)); - } else { - upgradeButton.setGraphic(SVG.update(Theme.blackFillBinding(), -1, -1)); - } - upgradeButton.getStyleClass().add("toggle-icon4"); - FXUtils.installFastTooltip(upgradeButton, i18n("install.change_version")); - upgradeButton.setOnMouseClicked(e -> upgrade.run()); - hBox.getChildren().add(upgradeButton); - } - - if (deleteCallback != null) { - JFXButton deleteButton = new JFXButton(); - deleteButton.setGraphic(SVG.close(Theme.blackFillBinding(), -1, -1)); - deleteButton.getStyleClass().add("toggle-icon4"); - deleteButton.setOnMouseClicked(e -> deleteCallback.accept(this)); - hBox.getChildren().add(deleteButton); - } - - hBox.setAlignment(Pos.CENTER_RIGHT); - setRight(hBox); } } + public void setState(String libraryVersion, boolean incompatibleWithGame, boolean removable) { + this.libraryVersion.set(libraryVersion); + this.incompatibleWithGame.set(incompatibleWithGame); + this.removable.set(removable); + } + + public String getLibraryId() { + return id; + } + + @Override + protected Skin createDefaultSkin() { + return new InstallerItemSkin(this); + } + + public static class InstallerItemGroup { + public InstallerItem game = new InstallerItem(MINECRAFT); + public InstallerItem fabric = new InstallerItem(FABRIC); + public InstallerItem forge = new InstallerItem(FORGE); + public InstallerItem liteLoader = new InstallerItem(LITELOADER); + public InstallerItem optiFine = new InstallerItem(OPTIFINE); + + public InstallerItemGroup() { + forge.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> { + if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId(); + return null; + }, fabric.libraryVersion)); + + liteLoader.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> { + if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId(); + return null; + }, fabric.libraryVersion)); + + optiFine.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> { + if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId(); + return null; + }, fabric.libraryVersion)); + + fabric.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> { + if (liteLoader.libraryVersion.get() != null) return LITELOADER.getPatchId(); + if (optiFine.libraryVersion.get() != null) return OPTIFINE.getPatchId(); + if (forge.libraryVersion.get() != null) return FORGE.getPatchId(); + return null; + }, optiFine.libraryVersion, forge.libraryVersion)); + } + + public InstallerItem[] getLibraries() { + return new InstallerItem[]{game, fabric, forge, liteLoader, optiFine}; + } + } + + public static class InstallerItemSkin extends SkinBase { + + InstallerItemSkin(InstallerItem control) { + super(control); + + HBox hbox = new HBox(); + getChildren().setAll(hbox); + JFXDepthManager.setDepth(hbox, 1); + + hbox.getStyleClass().add("card"); + + hbox.setAlignment(Pos.CENTER_LEFT); + + if (control.imageUrl != null) { + hbox.getChildren().add(FXUtils.limitingSize(new ImageView(new Image(control.imageUrl, 32, 32, true, true)), 32, 32)); + } + + Label nameLabel = new Label(); + hbox.getChildren().add(nameLabel); + nameLabel.setPrefWidth(80); + nameLabel.textProperty().set(I18n.hasKey("install.installer." + control.id) ? i18n("install.installer." + control.id) : control.id); + HBox.setMargin(nameLabel, new Insets(0, 4, 0, 4)); + + Label label = new Label(); + hbox.getChildren().add(label); + label.setMaxWidth(Double.MAX_VALUE); + HBox.setHgrow(label, Priority.ALWAYS); + label.textProperty().bind(Bindings.createStringBinding(() -> { + String incompatibleWith = control.incompatibleLibraryName.get(); + String version = control.libraryVersion.get(); + if (control.incompatibleWithGame.get()) { + return i18n("install.installer.change_version", version); + } else if (incompatibleWith != null) { + return i18n("install.installer.incompatible", i18n("install.installer." + incompatibleWith)); + } else if (version == null) { + return i18n("install.installer.not_installed"); + } else { + return i18n("install.installer.version", version); + } + }, control.incompatibleLibraryName, control.incompatibleWithGame, control.libraryVersion)); + BorderPane.setMargin(label, new Insets(0, 0, 0, 8)); + BorderPane.setAlignment(label, Pos.CENTER_LEFT); + + JFXButton closeButton = new JFXButton(); + closeButton.setGraphic(SVG.close(Theme.blackFillBinding(), -1, -1)); + closeButton.getStyleClass().add("toggle-icon4"); + closeButton.visibleProperty().bind(control.removable); + closeButton.managedProperty().bind(closeButton.visibleProperty()); + closeButton.onMouseClickedProperty().bind(control.removeAction); + hbox.getChildren().add(closeButton); + + JFXButton arrowButton = new JFXButton(); + arrowButton.graphicProperty().bind(Bindings.createObjectBinding(() -> control.upgradable.get() + ? SVG.update(Theme.blackFillBinding(), -1, -1) + : SVG.arrowRight(Theme.blackFillBinding(), -1, -1), + control.upgradable)); + arrowButton.getStyleClass().add("toggle-icon4"); + arrowButton.visibleProperty().bind(Bindings.createBooleanBinding( + () -> control.installable.get() && control.incompatibleLibraryName.get() == null, + control.installable, control.incompatibleLibraryName)); + arrowButton.managedProperty().bind(arrowButton.visibleProperty()); + arrowButton.onMouseClickedProperty().bind(control.action); + hbox.getChildren().add(arrowButton); + + FXUtils.onChangeAndOperate(arrowButton.visibleProperty(), clickable -> { + if (clickable) { + hbox.onMouseClickedProperty().bind(control.action); + hbox.setCursor(Cursor.HAND); + } else { + hbox.onMouseClickedProperty().unbind(); + hbox.onMouseClickedProperty().set(null); + hbox.setCursor(Cursor.DEFAULT); + } + }); + } + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java index 2799bc285..c780ab917 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java @@ -24,6 +24,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.ui.InstallerItem; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.util.Lang; @@ -53,10 +54,8 @@ class AdditionalInstallersPage extends InstallersPage { () -> compatible.get() && txtName.validate(), txtName.textProperty(), compatible)); - InstallerPageItem[] libraries = new InstallerPageItem[]{game, fabric, forge, liteLoader, optiFine}; - - for (InstallerPageItem library : libraries) { - String libraryId = library.id; + for (InstallerItem library : group.getLibraries()) { + String libraryId = library.getLibraryId(); if (libraryId.equals("game")) continue; library.removeAction.set(e -> { controller.getSettings().put(libraryId, new UpdateInstallerWizardProvider.RemoveVersionAction(libraryId)); @@ -90,28 +89,25 @@ class AdditionalInstallersPage extends InstallersPage { String liteLoader = analyzer.getVersion(LITELOADER).orElse(null); String optiFine = analyzer.getVersion(OPTIFINE).orElse(null); - InstallerPageItem[] libraries = new InstallerPageItem[]{this.game, this.fabric, this.forge, this.liteLoader, this.optiFine}; + InstallerItem[] libraries = group.getLibraries(); String[] versions = new String[]{game, fabric, forge, liteLoader, optiFine}; String currentGameVersion = Lang.nonNull(getVersion("game"), game); boolean compatible = true; for (int i = 0; i < libraries.length; ++i) { - String libraryId = libraries[i].id; + String libraryId = libraries[i].getLibraryId(); String libraryVersion = Lang.nonNull(getVersion(libraryId), versions[i]); boolean alreadyInstalled = versions[i] != null && !(controller.getSettings().get(libraryId) instanceof UpdateInstallerWizardProvider.RemoveVersionAction); if (!"game".equals(libraryId) && currentGameVersion != null && !currentGameVersion.equals(game) && getVersion(libraryId) == null && alreadyInstalled) { // For third-party libraries, if game version is being changed, and the library is not being reinstalled, // warns the user that we should update the library. - libraries[i].label.set(i18n("install.installer.change_version", i18n("install.installer." + libraryId), libraryVersion)); - libraries[i].removable.set(true); + libraries[i].setState(libraryVersion, /* incompatibleWithGame */ true, /* removable */ true); compatible = false; } else if (alreadyInstalled || getVersion(libraryId) != null) { - libraries[i].label.set(i18n("install.installer.version", i18n("install.installer." + libraryId), libraryVersion)); - libraries[i].removable.set(true); + libraries[i].setState(libraryVersion, /* incompatibleWithGame */ false, /* removable */ true); } else { - libraries[i].label.set(i18n("install.installer.not_installed", i18n("install.installer." + libraryId))); - libraries[i].removable.set(false); + libraries[i].setState(/* libraryVersion */ null, /* incompatibleWithGame */ false, /* removable */ false); } } this.compatible.set(compatible); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java index 62b4fedb6..0dd7bfd6b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java @@ -19,29 +19,21 @@ package org.jackhuang.hmcl.ui.download; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXTextField; -import com.jfoenix.effects.JFXDepthManager; import javafx.beans.binding.Bindings; import javafx.beans.property.*; -import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.geometry.Pos; -import javafx.scene.Cursor; import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.game.GameRepository; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.InstallerItem; import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; @@ -55,11 +47,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class InstallersPage extends Control implements WizardPage { protected final WizardController controller; - protected InstallerPageItem game = new InstallerPageItem("game"); - protected InstallerPageItem fabric = new InstallerPageItem("fabric"); - protected InstallerPageItem forge = new InstallerPageItem("forge"); - protected InstallerPageItem liteLoader = new InstallerPageItem("liteloader"); - protected InstallerPageItem optiFine = new InstallerPageItem("optifine"); + protected InstallerItem.InstallerItemGroup group = new InstallerItem.InstallerItemGroup(); protected JFXTextField txtName = new JFXTextField(); protected BooleanProperty installable = new SimpleBooleanProperty(); @@ -75,13 +63,15 @@ public class InstallersPage extends Control implements WizardPage { txtName.textProperty())); txtName.setText(gameVersion); - InstallerPageItem[] libraries = new InstallerPageItem[]{game, fabric, forge, liteLoader, optiFine}; + group.game.installable.setValue(false); - for (InstallerPageItem library : libraries) { - String libraryId = library.id; + for (InstallerItem library : group.getLibraries()) { + String libraryId = library.getLibraryId(); if (libraryId.equals("game")) continue; - library.action.set(e -> - controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false)))); + library.action.set(e -> { + if (library.incompatibleLibraryName.get() == null) + controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false))); + }); library.removeAction.set(e -> { controller.getSettings().remove(libraryId); reload(); @@ -99,15 +89,13 @@ public class InstallersPage extends Control implements WizardPage { } protected void reload() { - InstallerPageItem[] libraries = new InstallerPageItem[]{game, fabric, forge, liteLoader, optiFine}; - - for (InstallerPageItem library : libraries) { - String libraryId = library.id; + for (InstallerItem library : group.getLibraries()) { + String libraryId = library.getLibraryId(); if (controller.getSettings().containsKey(libraryId)) { - library.label.set(i18n("install.installer.version", i18n("install.installer." + libraryId), getVersion(libraryId))); + library.libraryVersion.set(getVersion(libraryId)); library.removable.set(true); } else { - library.label.setValue(i18n("install.installer.not_installed", i18n("install.installer." + libraryId))); + library.libraryVersion.set(null); library.removable.set(false); } } @@ -133,54 +121,8 @@ public class InstallersPage extends Control implements WizardPage { return new InstallersPageSkin(this); } - protected static class InstallerPageItem { - String id; - StringProperty label = new SimpleStringProperty(); - BooleanProperty removable = new SimpleBooleanProperty(); - ObjectProperty> removeAction = new SimpleObjectProperty<>(); - ObjectProperty> action = new SimpleObjectProperty<>(); - - public InstallerPageItem(String id) { - this.id = id; - } - } - protected static class InstallersPageSkin extends SkinBase { - protected static class InstallersPageItemSkin extends BorderPane { - final ImageView imageView; - final Label label; - - InstallersPageItemSkin(String imageUrl, InstallerPageItem item, boolean clickable) { - getStyleClass().add("card"); - - setLeft(FXUtils.limitingSize(imageView = new ImageView(new Image(imageUrl, 32, 32, true, true)), 32, 32)); - setCenter(label = new Label()); - label.textProperty().bind(item.label); - BorderPane.setMargin(label, new Insets(0, 0, 0, 8)); - BorderPane.setAlignment(label, Pos.CENTER_LEFT); - - if (clickable) { - HBox right = new HBox(); - right.setAlignment(Pos.CENTER_RIGHT); - setRight(right); - JFXButton closeButton = new JFXButton(); - closeButton.setGraphic(SVG.close(Theme.blackFillBinding(), -1, -1)); - right.getChildren().add(closeButton); - closeButton.getStyleClass().add("toggle-icon4"); - closeButton.visibleProperty().bind(item.removable); - closeButton.onMouseClickedProperty().bind(item.removeAction); - onMouseClickedProperty().bind(item.action); - JFXButton arrowButton = new JFXButton(); - arrowButton.setGraphic(SVG.arrowRight(Theme.blackFillBinding(), -1, -1)); - arrowButton.onMouseClickedProperty().bind(item.action); - arrowButton.getStyleClass().add("toggle-icon4"); - right.getChildren().add(arrowButton); - setCursor(Cursor.HAND); - } - } - } - /** * Constructor for all SkinBase instances. * @@ -207,13 +149,7 @@ public class InstallersPage extends Control implements WizardPage { list.getChildren().add(versionNamePane); } - InstallersPageItemSkin game = new InstallersPageItemSkin("/assets/img/grass.png", control.game, false); - InstallersPageItemSkin fabric = new InstallersPageItemSkin("/assets/img/fabric.png", control.fabric, true); - InstallersPageItemSkin forge = new InstallersPageItemSkin("/assets/img/forge.png", control.forge, true); - InstallersPageItemSkin liteLoader = new InstallersPageItemSkin("/assets/img/chicken.png", control.liteLoader, true); - InstallersPageItemSkin optiFine = new InstallersPageItemSkin("/assets/img/command.png", control.optiFine, true); - list.getChildren().addAll(game, fabric, forge, liteLoader, optiFine); - list.getChildren().forEach(node -> JFXDepthManager.setDepth(node, 1)); + list.getChildren().addAll(control.group.getLibraries()); { JFXButton installButton = new JFXButton(i18n("button.install")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java index c2976bb73..4fe4249bb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui.versions; import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.scene.Node; import javafx.scene.control.Skin; import javafx.stage.FileChooser; @@ -42,6 +43,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; +import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -74,7 +76,7 @@ public class InstallerListPage extends ListPageBase { return LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(versionId)); }).thenAcceptAsync(analyzer -> { - Function> removeAction = libraryId -> x -> { + Function removeAction = libraryId -> () -> { profile.getDependency().removeLibraryAsync(version, libraryId) .thenComposeAsync(profile.getRepository()::saveAsync) .withComposeAsync(profile.getRepository().refreshVersionsAsync()) @@ -84,15 +86,28 @@ public class InstallerListPage extends ListPageBase { itemsProperty().clear(); - for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) { - String libraryId = type.getPatchId(); - String libraryVersion = analyzer.getVersion(type).orElse(null); - Consumer action = "game".equals(libraryId) || libraryVersion == null ? null : removeAction.apply(libraryId); - itemsProperty().add(new InstallerItem(libraryId, libraryVersion, () -> { + InstallerItem.InstallerItemGroup group = new InstallerItem.InstallerItemGroup(); + + // Conventional libraries: game, fabric, forge, liteloader, optifine + for (InstallerItem installerItem : group.getLibraries()) { + String libraryId = installerItem.getLibraryId(); + String libraryVersion = analyzer.getVersion(libraryId).orElse(null); + installerItem.libraryVersion.set(libraryVersion); + installerItem.upgradable.set(libraryVersion != null); + installerItem.installable.set(true); + installerItem.action.set(e -> { Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion)); - }, action)); + }); + boolean removable = !"game".equals(libraryId) && libraryVersion != null; + installerItem.removable.set(removable); + if (removable) { + Runnable action = removeAction.apply(libraryId); + installerItem.removeAction.set(e -> action.run()); + } + itemsProperty().add(installerItem); } + // other third-party libraries which are unable to manage. for (LibraryAnalyzer.LibraryMark mark : analyzer) { String libraryId = mark.getLibraryId(); String libraryVersion = mark.getLibraryVersion(); @@ -101,14 +116,23 @@ public class InstallerListPage extends ListPageBase { if (LibraryAnalyzer.LibraryType.fromPatchId(libraryId) != null) continue; - Consumer action = removeAction.apply(libraryId); - if (libraryVersion != null && Lang.test(() -> profile.getDependency().getVersionList(libraryId))) - itemsProperty().add( - new InstallerItem(libraryId, libraryVersion, () -> { - Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion)); - }, action)); - else - itemsProperty().add(new InstallerItem(libraryId, libraryVersion, null, action)); + Runnable action = removeAction.apply(libraryId); + + InstallerItem installerItem = new InstallerItem(libraryId); + installerItem.libraryVersion.set(libraryVersion); + installerItem.installable.set(false); + installerItem.upgradable.bind(installerItem.installable); + installerItem.removable.set(true); + installerItem.removeAction.set(e -> action.run()); + + if (libraryVersion != null && Lang.test(() -> profile.getDependency().getVersionList(libraryId))) { + installerItem.installable.set(true); + installerItem.action.set(e -> { + Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion)); + }); + } + + itemsProperty().add(installerItem); } }, Platform::runLater); } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 706e0ba9e..0b478dfb8 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -139,11 +139,12 @@ install.failed.install_online=Unable to recognize the provided installer file install.failed.malformed=The files just downloaded a moment ago is malformed. You may switch to other download provider to resolve this problem. install.failed.optifine_conflict=Fabric, OptiFine and Forge are installed simultaneously on Minecraft 1.13 install.failed.version_mismatch=The library requires the game version %s, but the actual version is %s. -install.installer.change_version=%s version: %s, this version is not compatible with current game version. Click here to choose another one. +install.installer.change_version=%s, this version is not compatible with current game version. Click here to choose another one. install.installer.choose=Choose a %s version install.installer.fabric=Fabric install.installer.forge=Forge install.installer.game=Minecraft +install.installer.incompatible=Incompatible with %s install.installer.install=Install %s install.installer.install_offline=Install/Upgrade from file install.installer.install_offline.extension=Forge/OptiFine installer @@ -151,9 +152,9 @@ install.installer.install_offline.tooltip=Supports import of Forge/OptiFine inst install.installer.install_online=Install Online install.installer.install_online.tooltip=Support installation of Fabric, Forge, OptiFine, LiteLoader. install.installer.liteloader=LiteLoader -install.installer.not_installed=%s Version: not installed +install.installer.not_installed=Not installed install.installer.optifine=OptiFine -install.installer.version=%s Version: %s +install.installer.version=%s install.modpack=Install a modpack install.new_game=Install a New Game install.new_game.already_exists=This version already exists. diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 7e6452fe1..95bcd5841 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -140,6 +140,7 @@ install.installer.choose=Escoja una versión de %s install.installer.fabric=Fabric install.installer.forge=Forge install.installer.game=Minecraft +install.installer.incompatible=Incompatible with %s install.installer.install=Instalar %s install.installer.install_offline=Instalar/actualizar con archivo local install.installer.install_offline.extension=Instalador de Forge/OptiFine @@ -147,9 +148,9 @@ install.installer.install_offline.tooltip=Soporta importación de archivo instal install.installer.install_online=Instalar en línea install.installer.install_online.tooltip=Soporta instalación de Fabric, Forge, OptiFine, LiteLoader install.installer.liteloader=LiteLoader -install.installer.not_installed=%s no está instalado +install.installer.not_installed=no está instalado install.installer.optifine=OptiFine -install.installer.version=Versión %s: %s +install.installer.version=%s install.modpack=Instalar un modpack install.new_game=Instalar un juego nuevo install.new_game.already_exists=Esta versión ya existe. diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 122945d36..a60483b56 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -139,11 +139,12 @@ install.failed.install_online=Невозможно распознать пред install.failed.malformed=Файлы, скачаные минуту назад, неправильно сформированы. Для решения этой проблемы можно переключиться на другого поставщика скачивания. install.failed.optifine_conflict=Fabric, OptiFine и Forge установлены одновременно на Minecraft 1.13 install.failed.version_mismatch=Библиотека требует версию игры %s, но фактическая версия %s. -install.installer.change_version=Версия %s: %s, эта версия не совместима с текущей версией игры. Что бы выбрать другую, нажмите здесь. +install.installer.change_version=%s, эта версия не совместима с текущей версией игры. Что бы выбрать другую, нажмите здесь. install.installer.choose=Выберите версию %s install.installer.fabric=Fabric install.installer.forge=Forge install.installer.game=Minecraft +install.installer.incompatible=Incompatible with %s install.installer.install=Установка %s install.installer.install_offline=Установить/обновить из файла install.installer.install_offline.extension=Установщик Forge/OptiFine @@ -151,9 +152,9 @@ install.installer.install_offline.tooltip=Поддерживает импорт install.installer.install_online=Установить из сети install.installer.install_online.tooltip=Поддерживается установка Fabric, Forge, OptiFine, LiteLoader. install.installer.liteloader=LiteLoader -install.installer.not_installed=%s не установлен +install.installer.not_installed=не установлен install.installer.optifine=OptiFine -install.installer.version=%s - версия: %s +install.installer.version=%s install.modpack=Установить модпак install.new_game=Установка новой игры install.new_game.already_exists=Эта версия уже существует. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 034b1f0a3..2eaa6113e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -139,11 +139,12 @@ install.failed.install_online=無法識別要安裝的軟體 install.failed.malformed=剛才下載的檔案格式損壞。您可以切換到其他下載來源以解決此問題。 install.failed.optifine_conflict=暫不支援 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上 install.failed.version_mismatch=該軟體需要的遊戲版本為 %s,但實際的遊戲版本為 %s。 -install.installer.change_version=%s 版本: %s,該版本與當前遊戲不相容,您需要點擊此處更換版本或刪除 +install.installer.change_version=%s,該版本與當前遊戲不相容,您需要點擊此處更換版本或刪除 install.installer.choose=選擇 %s 版本 install.installer.fabric=Fabric install.installer.forge=Forge install.installer.game=Minecraft +install.installer.incompatible=與 %s 不相容 install.installer.install=安裝%s install.installer.install_offline=從本機檔案安裝或升級 install.installer.install_offline.extension=Forge/OptiFine 安裝器 @@ -151,9 +152,9 @@ install.installer.install_offline.tooltip=支援匯入已經下載好的 Forge/O install.installer.install_online=線上安裝 install.installer.install_online.tooltip=支援安裝 Fabric、Forge、OptiFine、LiteLoader install.installer.liteloader=LiteLoader -install.installer.not_installed=%s 版本: 不安裝 +install.installer.not_installed=不安裝 install.installer.optifine=OptiFine -install.installer.version=%s 版本: %s +install.installer.version=%s install.modpack=安裝整合包 install.new_game=安裝新遊戲版本 install.new_game.already_exists=此版本已經存在,請重新命名 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 f33e891f1..c9d17e02a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -139,11 +139,12 @@ install.failed.install_online=无法识别要安装的软件 install.failed.malformed=刚才下载的文件格式损坏。您可以切换到其他下载源来解决此问题。 install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 与 Forge 同时安装在 Minecraft 1.13 及以上版本 install.failed.version_mismatch=该软件需要的游戏版本为 %s,但实际的游戏版本为 %s。 -install.installer.change_version=%s 版本: %s,该版本与当前游戏不兼容,您需要点击此处更换版本或删除 +install.installer.change_version=%s,该版本与当前游戏不兼容,您需要点击此处更换版本或删除 install.installer.choose=选择 %s 版本 install.installer.fabric=Fabric install.installer.forge=Forge install.installer.game=Minecraft +install.installer.incompatible=与 %s 不兼容 install.installer.install=安装 %s install.installer.install_offline=从本地文件安装/升级 install.installer.install_offline.extension=Forge/OptiFine 安装器 @@ -151,9 +152,9 @@ install.installer.install_offline.tooltip=支持导入已经下载好的 Forge/O install.installer.install_online=在线安装 install.installer.install_online.tooltip=支持安装 Fabric、Forge、OptiFine、LiteLoader install.installer.liteloader=LiteLoader -install.installer.not_installed=%s 版本: 不安装 +install.installer.not_installed=不安装 install.installer.optifine=OptiFine -install.installer.version=%s 版本: %s +install.installer.version=%s install.modpack=安装整合包 install.new_game=安装新游戏版本 install.new_game.already_exists=此版本已经存在,请换一个名字 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java index 943dd7e83..dcb18ec26 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java @@ -104,7 +104,7 @@ public final class ForgeInstallTask extends Task { public void execute() throws IOException, VersionMismatchException, OptiFineInstallTask.UnsupportedOptiFineInstallationException { String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0) { - // Forge 1.13 is not compatible with any other libraries. + // Forge 1.13 is not compatible with fabric. if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass)) throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException(); } else { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6713fe9cd..42a37a618 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip