修复 #3099 自动安装选项卡重新选择版本(Minecraft版本和模组加载器版本)的按钮消失的问题 (#3117)

* Fix #3099

* Fix: checkstyle

* Fix.

* Fix #3144

* Update

* Fix bugs.

* Change I18N

* Fix #3164

* Delete State.SEALED

* fix typo

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
This commit is contained in:
Burning_TNT 2024-07-28 02:41:08 +08:00 committed by GitHub
parent 6f7c922ac0
commit e8306ea59a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 266 additions and 182 deletions

View File

@ -337,7 +337,7 @@ public class GameCrashWindow extends Stage {
TwoLineListItem version = new TwoLineListItem(); TwoLineListItem version = new TwoLineListItem();
version.getStyleClass().setAll("two-line-item-second-large"); version.getStyleClass().setAll("two-line-item-second-large");
version.setTitle(i18n("archive.game_version")); version.setTitle(i18n("game.version"));
version.setSubtitle(GameCrashWindow.this.version.getId()); version.setSubtitle(GameCrashWindow.this.version.getId());
TwoLineListItem total_memory = new TwoLineListItem(); TwoLineListItem total_memory = new TwoLineListItem();

View File

@ -18,9 +18,10 @@
package org.jackhuang.hmcl.ui; package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import javafx.beans.InvalidationListener; import javafx.beans.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.*; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.geometry.Insets; import javafx.geometry.Insets;
@ -55,29 +56,77 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class InstallerItem extends Control { public class InstallerItem extends Control {
private final String id; private final String id;
private final VersionIconType iconType; private final VersionIconType iconType;
public final StringProperty libraryVersion = new SimpleStringProperty(); private final Style style;
public final StringProperty incompatibleLibraryName = new SimpleStringProperty(); private final ObjectProperty<InstalledState> versionProperty = new SimpleObjectProperty<>(this, "version", null);
public final StringProperty dependencyName = new SimpleStringProperty(); private final ObjectProperty<State> resolvedStateProperty = new SimpleObjectProperty<>(this, "resolvedState", InstallableState.INSTANCE);
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<EventHandler<? super MouseEvent>> removeAction = new SimpleObjectProperty<>();
public final ObjectProperty<EventHandler<? super MouseEvent>> action = new SimpleObjectProperty<>();
private Style style = Style.LIST_ITEM; private final ObjectProperty<EventHandler<? super MouseEvent>> installActionProperty = new SimpleObjectProperty<>(this, "installAction");
private final ObjectProperty<EventHandler<? super MouseEvent>> removeActionProperty = new SimpleObjectProperty<>(this, "removeAction");
public interface State {
}
public static final class InstallableState implements State {
public static final InstallableState INSTANCE = new InstallableState();
private InstallableState() {
}
}
public static final class IncompatibleState implements State {
private final String incompatibleItemName;
private final String incompatibleItemVersion;
public IncompatibleState(String incompatibleItemName, String incompatibleItemVersion) {
this.incompatibleItemName = incompatibleItemName;
this.incompatibleItemVersion = incompatibleItemVersion;
}
public String getIncompatibleItemName() {
return incompatibleItemName;
}
public String getIncompatibleItemVersion() {
return incompatibleItemVersion;
}
}
public static final class InstalledState implements State {
private final String version;
private final boolean external;
private final boolean incompatibleWithGame;
public InstalledState(String version, boolean external, boolean incompatibleWithGame) {
this.version = version;
this.external = external;
this.incompatibleWithGame = incompatibleWithGame;
}
public String getVersion() {
return version;
}
public boolean isExternal() {
return external;
}
public boolean isIncompatibleWithGame() {
return incompatibleWithGame;
}
}
public enum Style { public enum Style {
LIST_ITEM, LIST_ITEM,
CARD, CARD,
} }
public InstallerItem(LibraryAnalyzer.LibraryType id) { public InstallerItem(LibraryAnalyzer.LibraryType id, Style style) {
this(id.getPatchId()); this(id.getPatchId(), style);
} }
public InstallerItem(String id) { public InstallerItem(String id, Style style) {
this.id = id; this.id = id;
this.style = style;
switch (id) { switch (id) {
case "game": case "game":
@ -109,55 +158,51 @@ public class InstallerItem extends Control {
} }
} }
public void setStyleMode(Style style) {
this.style = style;
}
public void setState(String libraryVersion, boolean incompatibleWithGame, boolean removable) {
this.libraryVersion.set(libraryVersion);
this.incompatibleWithGame.set(incompatibleWithGame);
this.removable.set(removable);
}
public String getLibraryId() { public String getLibraryId() {
return id; return id;
} }
public ObjectProperty<InstalledState> versionProperty() {
return versionProperty;
}
public ObjectProperty<State> resolvedStateProperty() {
return resolvedStateProperty;
}
public ObjectProperty<EventHandler<? super MouseEvent>> installActionProperty() {
return installActionProperty;
}
public ObjectProperty<EventHandler<? super MouseEvent>> removeActionProperty() {
return removeActionProperty;
}
@Override @Override
protected Skin<?> createDefaultSkin() { protected Skin<?> createDefaultSkin() {
return new InstallerItemSkin(this); return new InstallerItemSkin(this);
} }
public final static class InstallerItemGroup { public final static class InstallerItemGroup {
public final InstallerItem game = new InstallerItem(MINECRAFT); private final InstallerItem game;
public final InstallerItem fabric = new InstallerItem(FABRIC);
public final InstallerItem fabricApi = new InstallerItem(FABRIC_API);
public final InstallerItem forge = new InstallerItem(FORGE);
public final InstallerItem neoForge = new InstallerItem(NEO_FORGE);
public final InstallerItem liteLoader = new InstallerItem(LITELOADER);
public final InstallerItem optiFine = new InstallerItem(OPTIFINE);
public final InstallerItem quilt = new InstallerItem(QUILT);
public final InstallerItem quiltApi = new InstallerItem(QUILT_API);
private final InstallerItem[] libraries; private final InstallerItem[] libraries;
private final HashMap<InstallerItem, Set<InstallerItem>> incompatibleMap = new HashMap<>(); private Set<InstallerItem> getIncompatibles(Map<InstallerItem, Set<InstallerItem>> incompatibleMap, InstallerItem item) {
private Set<InstallerItem> getIncompatibles(InstallerItem item) {
return incompatibleMap.computeIfAbsent(item, it -> new HashSet<>()); return incompatibleMap.computeIfAbsent(item, it -> new HashSet<>());
} }
private void addIncompatibles(InstallerItem item, InstallerItem... others) { private void addIncompatibles(Map<InstallerItem, Set<InstallerItem>> incompatibleMap, InstallerItem item, InstallerItem... others) {
Set<InstallerItem> set = getIncompatibles(item); Set<InstallerItem> set = getIncompatibles(incompatibleMap, item);
for (InstallerItem other : others) { for (InstallerItem other : others) {
set.add(other); set.add(other);
getIncompatibles(other).add(item); getIncompatibles(incompatibleMap, other).add(item);
} }
} }
private void mutualIncompatible(InstallerItem... items) { private void mutualIncompatible(Map<InstallerItem, Set<InstallerItem>> incompatibleMap, InstallerItem... items) {
for (InstallerItem item : items) { for (InstallerItem item : items) {
Set<InstallerItem> set = getIncompatibles(item); Set<InstallerItem> set = getIncompatibles(incompatibleMap, item);
for (InstallerItem item2 : items) { for (InstallerItem item2 : items) {
if (item2 != item) { if (item2 != item) {
@ -167,43 +212,65 @@ public class InstallerItem extends Control {
} }
} }
public InstallerItemGroup(String gameVersion) { public InstallerItemGroup(String gameVersion, Style style) {
mutualIncompatible(forge, fabric, quilt, neoForge, liteLoader); game = new InstallerItem(MINECRAFT, style);
addIncompatibles(optiFine, fabric, quilt, neoForge); InstallerItem fabric = new InstallerItem(FABRIC, style);
addIncompatibles(fabricApi, forge, quiltApi, neoForge, liteLoader, optiFine); InstallerItem fabricApi = new InstallerItem(FABRIC_API, style);
addIncompatibles(quiltApi, forge, fabric, fabricApi, neoForge, liteLoader, optiFine); InstallerItem forge = new InstallerItem(FORGE, style);
InstallerItem neoForge = new InstallerItem(NEO_FORGE, style);
InstallerItem liteLoader = new InstallerItem(LITELOADER, style);
InstallerItem optiFine = new InstallerItem(OPTIFINE, style);
InstallerItem quilt = new InstallerItem(QUILT, style);
InstallerItem quiltApi = new InstallerItem(QUILT_API, style);
InvalidationListener listener = o -> { Map<InstallerItem, Set<InstallerItem>> incompatibleMap = new HashMap<>();
for (Map.Entry<InstallerItem, Set<InstallerItem>> entry : incompatibleMap.entrySet()) { mutualIncompatible(incompatibleMap, forge, fabric, quilt, neoForge, liteLoader);
InstallerItem item = entry.getKey(); addIncompatibles(incompatibleMap, optiFine, fabric, quilt, neoForge);
addIncompatibles(incompatibleMap, fabricApi, forge, quiltApi, neoForge, liteLoader, optiFine);
addIncompatibles(incompatibleMap, quiltApi, forge, fabric, fabricApi, neoForge, liteLoader, optiFine);
String incompatibleId = null; for (Map.Entry<InstallerItem, Set<InstallerItem>> entry : incompatibleMap.entrySet()) {
for (InstallerItem other : entry.getValue()) { InstallerItem item = entry.getKey();
if (other.libraryVersion.get() != null) { Set<InstallerItem> incompatibleItems = entry.getValue();
incompatibleId = other.id;
break; Observable[] bindings = new Observable[incompatibleItems.size() + 1];
bindings[0] = item.versionProperty;
int i = 1;
for (InstallerItem other : incompatibleItems) {
bindings[i++] = other.versionProperty;
}
item.resolvedStateProperty.bind(Bindings.createObjectBinding(() -> {
InstalledState itemVersion = item.versionProperty.get();
if (itemVersion != null) {
return itemVersion;
}
for (InstallerItem other : incompatibleItems) {
InstalledState otherVersion = other.versionProperty.get();
if (otherVersion != null) {
return new IncompatibleState(other.id, otherVersion.version);
} }
} }
item.incompatibleLibraryName.set(incompatibleId); return InstallableState.INSTANCE;
} }, bindings));
};
for (InstallerItem item : incompatibleMap.keySet()) {
item.libraryVersion.addListener(listener);
} }
fabricApi.dependencyName.bind(Bindings.createStringBinding(() -> { if (gameVersion != null) {
if (fabric.libraryVersion.get() == null) return FABRIC.getPatchId(); game.versionProperty.set(new InstalledState(gameVersion, false, false));
else return null; }
}, fabric.libraryVersion));
quiltApi.dependencyName.bind(Bindings.createStringBinding(() -> { InstallerItem[] all = {game, forge, neoForge, liteLoader, optiFine, fabric, fabricApi, quilt, quiltApi};
if (quilt.libraryVersion.get() == null) return QUILT.getPatchId();
else return null; for (InstallerItem item : all) {
}, quilt.libraryVersion)); if (!item.resolvedStateProperty.isBound()) {
item.resolvedStateProperty.bind(item.versionProperty);
}
}
if (gameVersion == null) { if (gameVersion == null) {
this.libraries = new InstallerItem[]{game, forge, neoForge, liteLoader, optiFine, fabric, fabricApi, quilt, quiltApi}; this.libraries = all;
} else if (GameVersionNumber.compare(gameVersion, "1.13") < 0) { } else if (GameVersionNumber.compare(gameVersion, "1.13") < 0) {
this.libraries = new InstallerItem[]{game, forge, liteLoader, optiFine}; this.libraries = new InstallerItem[]{game, forge, liteLoader, optiFine};
} else { } else {
@ -211,15 +278,19 @@ public class InstallerItem extends Control {
} }
} }
public InstallerItem getGame() {
return game;
}
public InstallerItem[] getLibraries() { public InstallerItem[] getLibraries() {
return libraries; return libraries;
} }
} }
public static class InstallerItemSkin extends SkinBase<InstallerItem> { private static final class InstallerItemSkin extends SkinBase<InstallerItem> {
private static final PseudoClass LIST_ITEM = PseudoClass.getPseudoClass("list-item"); private static final PseudoClass LIST_ITEM = PseudoClass.getPseudoClass("list-item");
private static final PseudoClass CARD = PseudoClass.getPseudoClass("card"); private static final PseudoClass CARD = PseudoClass.getPseudoClass("card");
private static final WeakListenerHolder holder = new WeakListenerHolder();
InstallerItemSkin(InstallerItem control) { InstallerItemSkin(InstallerItem control) {
super(control); super(control);
@ -227,6 +298,7 @@ public class InstallerItem extends Control {
Pane pane; Pane pane;
if (control.style == Style.CARD) { if (control.style == Style.CARD) {
pane = new VBox(); pane = new VBox();
holder.add(FXUtils.onWeakChangeAndOperate(pane.widthProperty(), v -> FXUtils.setLimitHeight(pane, v.doubleValue() * 0.7)));
} else { } else {
pane = new HBox(); pane = new HBox();
} }
@ -262,20 +334,25 @@ public class InstallerItem extends Control {
pane.getChildren().add(statusLabel); pane.getChildren().add(statusLabel);
HBox.setHgrow(statusLabel, Priority.ALWAYS); HBox.setHgrow(statusLabel, Priority.ALWAYS);
statusLabel.textProperty().bind(Bindings.createStringBinding(() -> { statusLabel.textProperty().bind(Bindings.createStringBinding(() -> {
String incompatibleWith = control.incompatibleLibraryName.get(); State state = control.resolvedStateProperty.get();
String version = control.libraryVersion.get();
if (control.incompatibleWithGame.get()) { if (state instanceof InstalledState) {
return i18n("install.installer.change_version", version); InstalledState s = (InstalledState) state;
} else if (incompatibleWith != null) { if (s.incompatibleWithGame) {
return i18n("install.installer.incompatible", i18n("install.installer." + incompatibleWith)); return i18n("install.installer.change_version", s.version);
} else if (version == null) { }
if (s.external) {
return i18n("install.installer.external_version", s.version);
}
return i18n("install.installer.version", s.version);
} else if (state instanceof InstallableState) {
return i18n("install.installer.not_installed"); return i18n("install.installer.not_installed");
} else if (control.id.equals(MINECRAFT.getPatchId()) || control.removable.get() || control.upgradable.get()) { } else if (state instanceof IncompatibleState) {
return i18n("install.installer.version", version); return i18n("install.installer.incompatible", i18n("install.installer." + ((IncompatibleState) state).incompatibleItemName));
} else { } else {
return i18n("install.installer.external_version", version); throw new AssertionError("Unknown state type: " + state.getClass());
} }
}, control.incompatibleLibraryName, control.incompatibleWithGame, control.libraryVersion, control.installable, control.removable, control.upgradable)); }, control.resolvedStateProperty));
BorderPane.setMargin(statusLabel, new Insets(0, 0, 0, 8)); BorderPane.setMargin(statusLabel, new Insets(0, 0, 0, 8));
BorderPane.setAlignment(statusLabel, Pos.CENTER_LEFT); BorderPane.setAlignment(statusLabel, Pos.CENTER_LEFT);
@ -284,31 +361,48 @@ public class InstallerItem extends Control {
buttonsContainer.setAlignment(Pos.CENTER); buttonsContainer.setAlignment(Pos.CENTER);
pane.getChildren().add(buttonsContainer); pane.getChildren().add(buttonsContainer);
JFXButton closeButton = new JFXButton(); JFXButton removeButton = new JFXButton();
closeButton.setGraphic(SVG.CLOSE.createIcon(Theme.blackFill(), -1, -1)); removeButton.setGraphic(SVG.CLOSE.createIcon(Theme.blackFill(), -1, -1));
closeButton.getStyleClass().add("toggle-icon4"); removeButton.getStyleClass().add("toggle-icon4");
closeButton.visibleProperty().bind(control.removable); if (control.id.equals(MINECRAFT.getPatchId())) {
closeButton.managedProperty().bind(closeButton.visibleProperty()); removeButton.setVisible(false);
closeButton.onMouseClickedProperty().bind(control.removeAction); } else {
buttonsContainer.getChildren().add(closeButton); removeButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> control.resolvedStateProperty.get() instanceof InstalledState, control.resolvedStateProperty));
}
removeButton.managedProperty().bind(removeButton.visibleProperty());
removeButton.onMouseClickedProperty().bind(control.removeActionProperty);
buttonsContainer.getChildren().add(removeButton);
JFXButton arrowButton = new JFXButton(); JFXButton installButton = new JFXButton();
arrowButton.graphicProperty().bind(Bindings.createObjectBinding(() -> control.upgradable.get() installButton.graphicProperty().bind(Bindings.createObjectBinding(() ->
? SVG.UPDATE.createIcon(Theme.blackFill(), -1, -1) control.resolvedStateProperty.get() instanceof InstallableState ?
: SVG.ARROW_RIGHT.createIcon(Theme.blackFill(), -1, -1), SVG.ARROW_RIGHT.createIcon(Theme.blackFill(), -1, -1) :
control.upgradable)); SVG.UPDATE.createIcon(Theme.blackFill(), -1, -1),
arrowButton.getStyleClass().add("toggle-icon4"); control.resolvedStateProperty
arrowButton.visibleProperty().bind(Bindings.createBooleanBinding(
() -> control.installable.get() && control.libraryVersion.get() == null && control.incompatibleLibraryName.get() == null,
control.installable, control.libraryVersion, control.incompatibleLibraryName
)); ));
arrowButton.managedProperty().bind(arrowButton.visibleProperty()); installButton.getStyleClass().add("toggle-icon4");
arrowButton.onMouseClickedProperty().bind(control.action); installButton.visibleProperty().bind(Bindings.createBooleanBinding(() -> {
buttonsContainer.getChildren().add(arrowButton); if (control.installActionProperty.get() == null) {
return false;
}
FXUtils.onChangeAndOperate(arrowButton.visibleProperty(), clickable -> { State state = control.resolvedStateProperty.get();
if (state instanceof InstallableState) {
return true;
}
if (state instanceof InstalledState) {
return !((InstalledState) state).external;
}
return false;
}, control.resolvedStateProperty, control.installActionProperty));
installButton.managedProperty().bind(installButton.visibleProperty());
installButton.onMouseClickedProperty().bind(control.installActionProperty);
buttonsContainer.getChildren().add(installButton);
FXUtils.onChangeAndOperate(installButton.visibleProperty(), clickable -> {
if (clickable) { if (clickable) {
container.onMouseClickedProperty().bind(control.action); container.onMouseClickedProperty().bind(control.installActionProperty);
pane.setCursor(Cursor.HAND); pane.setCursor(Cursor.HAND);
} else { } else {
container.onMouseClickedProperty().unbind(); container.onMouseClickedProperty().unbind();

View File

@ -33,7 +33,7 @@ import org.jackhuang.hmcl.util.Lang;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
class AdditionalInstallersPage extends InstallersPage { class AdditionalInstallersPage extends InstallersPage {
@ -59,7 +59,7 @@ class AdditionalInstallersPage extends InstallersPage {
for (InstallerItem library : group.getLibraries()) { for (InstallerItem library : group.getLibraries()) {
String libraryId = library.getLibraryId(); String libraryId = library.getLibraryId();
if (libraryId.equals("game")) continue; if (libraryId.equals("game")) continue;
library.removeAction.set(e -> { library.removeActionProperty().set(e -> {
controller.getSettings().put(libraryId, new UpdateInstallerWizardProvider.RemoveVersionAction(libraryId)); controller.getSettings().put(libraryId, new UpdateInstallerWizardProvider.RemoveVersionAction(libraryId));
reload(); reload();
}); });
@ -99,12 +99,12 @@ class AdditionalInstallersPage extends InstallersPage {
if (!"game".equals(libraryId) && currentGameVersion != null && !currentGameVersion.equals(game) && getVersion(libraryId) == null && alreadyInstalled) { 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, // 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. // warns the user that we should update the library.
library.setState(libraryVersion, /* incompatibleWithGame */ true, /* removable */ true); library.versionProperty().set(new InstallerItem.InstalledState(libraryVersion, false, true));
compatible = false; compatible = false;
} else if (alreadyInstalled || getVersion(libraryId) != null) { } else if (alreadyInstalled || getVersion(libraryId) != null) {
library.setState(libraryVersion, /* incompatibleWithGame */ false, /* removable */ true); library.versionProperty().set(new InstallerItem.InstalledState(libraryVersion, false, false));
} else { } else {
library.setState(/* libraryVersion */ null, /* incompatibleWithGame */ false, /* removable */ false); library.versionProperty().set(null);
} }
} }

View File

@ -162,7 +162,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version).toPath() : profile.getRepository().getBaseDirectory().toPath(); Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version).toPath() : profile.getRepository().getBaseDirectory().toPath();
Controllers.prompt(i18n("archive.name"), (result, resolve, reject) -> { Controllers.prompt(i18n("archive.file.name"), (result, resolve, reject) -> {
if (!OperatingSystem.isNameValid(result)) { if (!OperatingSystem.isNameValid(result)) {
reject.accept(i18n("install.new_game.malformed")); reject.accept(i18n("install.new_game.malformed"));
return; return;

View File

@ -54,7 +54,7 @@ public class InstallersPage extends Control implements WizardPage {
public InstallersPage(WizardController controller, HMCLGameRepository repository, String gameVersion, DownloadProvider downloadProvider) { public InstallersPage(WizardController controller, HMCLGameRepository repository, String gameVersion, DownloadProvider downloadProvider) {
this.controller = controller; this.controller = controller;
this.group = new InstallerItem.InstallerItemGroup(gameVersion); this.group = new InstallerItem.InstallerItemGroup(gameVersion, getInstallerItemStyle());
txtName.getValidators().addAll( txtName.getValidators().addAll(
new RequiredValidator(), new RequiredValidator(),
@ -63,33 +63,31 @@ public class InstallersPage extends Control implements WizardPage {
installable.bind(createBooleanBinding(txtName::validate, txtName.textProperty())); installable.bind(createBooleanBinding(txtName::validate, txtName.textProperty()));
txtName.setText(gameVersion); txtName.setText(gameVersion);
group.game.installable.setValue(false);
for (InstallerItem item : group.getLibraries()) {
item.setStyleMode(InstallerItem.Style.CARD);
}
for (InstallerItem library : group.getLibraries()) { for (InstallerItem library : group.getLibraries()) {
String libraryId = library.getLibraryId(); String libraryId = library.getLibraryId();
if (libraryId.equals(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())) continue; if (libraryId.equals(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())) continue;
library.action.set(e -> { library.installActionProperty().set(e -> {
if (LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId().equals(libraryId)) { if (LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId().equals(libraryId)) {
Controllers.dialog(i18n("install.installer.fabric-api.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING); Controllers.dialog(i18n("install.installer.fabric-api.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING);
} }
if (library.incompatibleLibraryName.get() == null) if (!(library.resolvedStateProperty().get() instanceof InstallerItem.IncompatibleState))
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false))); controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false)));
}); });
library.removeAction.set(e -> { library.removeActionProperty().set(e -> {
controller.getSettings().remove(libraryId); controller.getSettings().remove(libraryId);
reload(); reload();
}); });
} }
} }
protected InstallerItem.Style getInstallerItemStyle() {
return InstallerItem.Style.CARD;
}
@Override @Override
public String getTitle() { public String getTitle() {
return i18n("install.new_game"); return group.getGame().versionProperty().get().getVersion();
} }
private String getVersion(String id) { private String getVersion(String id) {
@ -100,11 +98,9 @@ public class InstallersPage extends Control implements WizardPage {
for (InstallerItem library : group.getLibraries()) { for (InstallerItem library : group.getLibraries()) {
String libraryId = library.getLibraryId(); String libraryId = library.getLibraryId();
if (controller.getSettings().containsKey(libraryId)) { if (controller.getSettings().containsKey(libraryId)) {
library.libraryVersion.set(getVersion(libraryId)); library.versionProperty().set(new InstallerItem.InstalledState(getVersion(libraryId), false, false));
library.removable.set(true);
} else { } else {
library.libraryVersion.set(null); library.versionProperty().set(null);
library.removable.set(false);
} }
} }
} }
@ -148,7 +144,7 @@ public class InstallersPage extends Control implements WizardPage {
versionNamePane.setAlignment(Pos.CENTER_LEFT); versionNamePane.setAlignment(Pos.CENTER_LEFT);
control.txtName.setMaxWidth(300); control.txtName.setMaxWidth(300);
versionNamePane.getChildren().setAll(new Label(i18n("archive.name")), control.txtName); versionNamePane.getChildren().setAll(new Label(i18n("version.name")), control.txtName);
root.setTop(versionNamePane); root.setTop(versionNamePane);
} }

View File

@ -48,7 +48,7 @@ public abstract class ModpackPage extends SpinnerPane implements WizardPage {
BorderPane archiveNamePane = new BorderPane(); BorderPane archiveNamePane = new BorderPane();
{ {
Label label = new Label(i18n("archive.name")); Label label = new Label(i18n("archive.file.name"));
BorderPane.setAlignment(label, Pos.CENTER_LEFT); BorderPane.setAlignment(label, Pos.CENTER_LEFT);
archiveNamePane.setLeft(label); archiveNamePane.setLeft(label);

View File

@ -38,7 +38,6 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@ -73,37 +72,36 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
return LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(versionId), gameVersion); return LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(versionId), gameVersion);
}).thenAcceptAsync(analyzer -> { }).thenAcceptAsync(analyzer -> {
Function<String, Runnable> removeAction = libraryId -> () -> { itemsProperty().clear();
profile.getDependency().removeLibraryAsync(version, libraryId)
InstallerItem.InstallerItemGroup group = new InstallerItem.InstallerItemGroup(gameVersion, InstallerItem.Style.LIST_ITEM);
// Conventional libraries: game, fabric, forge, neoforge, liteloader, optifine
for (InstallerItem item : group.getLibraries()) {
String libraryId = item.getLibraryId();
String libraryVersion = analyzer.getVersion(libraryId).orElse(null);
if (libraryVersion != null) {
item.versionProperty().set(new InstallerItem.InstalledState(
libraryVersion,
analyzer.getLibraryStatus(libraryId) != LibraryAnalyzer.LibraryMark.LibraryStatus.CLEAR,
false
));
} else {
item.versionProperty().set(null);
}
item.installActionProperty().set(e -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
});
item.removeActionProperty().set(e -> profile.getDependency().removeLibraryAsync(version, libraryId)
.thenComposeAsync(profile.getRepository()::saveAsync) .thenComposeAsync(profile.getRepository()::saveAsync)
.withComposeAsync(profile.getRepository().refreshVersionsAsync()) .withComposeAsync(profile.getRepository().refreshVersionsAsync())
.withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)) .withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
.start(); .start());
};
itemsProperty().clear(); itemsProperty().add(item);
InstallerItem.InstallerItemGroup group = new InstallerItem.InstallerItemGroup(gameVersion);
// Conventional libraries: game, fabric, forge, neoforge, liteloader, optifine
for (InstallerItem installerItem : group.getLibraries()) {
String libraryId = installerItem.getLibraryId();
String libraryVersion = analyzer.getVersion(libraryId).orElse(null);
boolean libraryConfigurable = libraryVersion != null && analyzer.getLibraryStatus(libraryId) == LibraryAnalyzer.LibraryMark.LibraryStatus.CLEAR;
installerItem.libraryVersion.set(libraryVersion);
installerItem.upgradable.set(libraryConfigurable);
installerItem.installable.set(true);
installerItem.action.set(e -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
});
boolean removable = !LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(libraryId) && libraryConfigurable;
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. // other third-party libraries which are unable to manage.
@ -115,14 +113,13 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
if (LibraryAnalyzer.LibraryType.fromPatchId(libraryId) != null) if (LibraryAnalyzer.LibraryType.fromPatchId(libraryId) != null)
continue; continue;
Runnable action = removeAction.apply(libraryId); InstallerItem installerItem = new InstallerItem(libraryId, InstallerItem.Style.LIST_ITEM);
installerItem.versionProperty().set(new InstallerItem.InstalledState(libraryVersion, false, false));
InstallerItem installerItem = new InstallerItem(libraryId); installerItem.removeActionProperty().set(e -> profile.getDependency().removeLibraryAsync(version, libraryId)
installerItem.libraryVersion.set(libraryVersion); .thenComposeAsync(profile.getRepository()::saveAsync)
installerItem.installable.set(false); .withComposeAsync(profile.getRepository().refreshVersionsAsync())
installerItem.upgradable.set(false); .withRunAsync(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
installerItem.removable.set(true); .start());
installerItem.removeAction.set(e -> action.run());
itemsProperty().add(installerItem); itemsProperty().add(installerItem);
} }

View File

@ -260,7 +260,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
StringBuilder message = new StringBuilder(localModFile.getFileName()); StringBuilder message = new StringBuilder(localModFile.getFileName());
if (isNotBlank(localModFile.getGameVersion())) if (isNotBlank(localModFile.getGameVersion()))
message.append(", ").append(i18n("archive.game_version")).append(": ").append(localModFile.getGameVersion()); message.append(", ").append(i18n("game.version")).append(": ").append(localModFile.getGameVersion());
if (isNotBlank(localModFile.getAuthors())) if (isNotBlank(localModFile.getAuthors()))
message.append(", ").append(i18n("archive.author")).append(": ").append(localModFile.getAuthors()); message.append(", ").append(i18n("archive.author")).append(": ").append(localModFile.getAuthors());
this.message = message.toString(); this.message = message.toString();

View File

@ -159,8 +159,7 @@ account.username=Username
archive.author=Author(s) archive.author=Author(s)
archive.date=Publish Date archive.date=Publish Date
archive.game_version=Game Version archive.file.name=File Name
archive.name=Filename
archive.version=Version archive.version=Version
assets.download=Downloading Assets assets.download=Downloading Assets
@ -615,7 +614,7 @@ install.failed.malformed=The downloaded files are corrupted. You can try fixing
install.failed.optifine_conflict=Cannot install both Fabric, OptiFine, and Forge on Minecraft 1.13 or above. install.failed.optifine_conflict=Cannot install both Fabric, OptiFine, and Forge on Minecraft 1.13 or above.
install.failed.optifine_forge_1.17=For Minecraft version 1.17.1 or lower, Forge only supports OptiFine H1 Pre2 or newer. You can install them under the snapshot versions tab. install.failed.optifine_forge_1.17=For Minecraft version 1.17.1 or lower, Forge only supports OptiFine H1 Pre2 or newer. You can install them under the snapshot versions tab.
install.failed.version_mismatch=This library requires the game version %s, but the installed one is %s. install.failed.version_mismatch=This library requires the game version %s, but the installed one is %s.
install.installer.change_version=Version %s is not compatible with the current game version. Click here to replace it with another version or delete it. install.installer.change_version=%s Incompatible
install.installer.choose=Choose Your %s Version install.installer.choose=Choose Your %s Version
install.installer.depend=Requires %s install.installer.depend=Requires %s
install.installer.fabric=Fabric install.installer.fabric=Fabric
@ -1224,6 +1223,7 @@ update.no_browser=Cannot open in the system browser. But, we copied the link to
update.tooltip=Update update.tooltip=Update
version=Games version=Games
version.name=Instance Name
version.cannot_read=Unable to parse the game version, automatic installation cannot continue. version.cannot_read=Unable to parse the game version, automatic installation cannot continue.
version.empty=No Instances version.empty=No Instances
version.empty.add=Add an Instance version.empty.add=Add an Instance

View File

@ -138,8 +138,7 @@ account.username=Nombre de usuario
archive.author=Autor(es) archive.author=Autor(es)
archive.date=Fecha de publicación archive.date=Fecha de publicación
archive.game_version=Versión del juego archive.file.name=Nombre de archivo
archive.name=Nombre de archivo
archive.version=Versión archive.version=Versión
assets.download=Descargando assets assets.download=Descargando assets

View File

@ -125,8 +125,7 @@ account.username=ユーザー名
archive.author=作成者 archive.author=作成者
archive.date=公開日 archive.date=公開日
archive.game_version=ゲームバージョン archive.file.name=名前
archive.name=名前
archive.version=バージョン archive.version=バージョン
Assets.download=アセットのダウンロード Assets.download=アセットのダウンロード

View File

@ -133,8 +133,7 @@ account.username=Имя пользователя
archive.author=Автор(ы) archive.author=Автор(ы)
archive.date=Дата публикации archive.date=Дата публикации
archive.game_version=Версия игры archive.file.name=Имя файла
archive.name=Имя файла
archive.version=Версия archive.version=Версия
assets.download=Скачивание Assets assets.download=Скачивание Assets

View File

@ -168,8 +168,7 @@ account.username=使用者名稱
archive.author=作者 archive.author=作者
archive.date=發布日期 archive.date=發布日期
archive.game_version=遊戲版本 archive.file.name=檔案名稱
archive.name=名稱
archive.version=版本 archive.version=版本
assets.download=下載資源 assets.download=下載資源
@ -499,7 +498,7 @@ install.failed.malformed=剛才下載的檔案格式損壞。您可以切換到
install.failed.optifine_conflict=暫不支援 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上 install.failed.optifine_conflict=暫不支援 OptiFine 與 Forge 同時安裝在 Minecraft 1.13 上
install.failed.optifine_forge_1.17=Minecraft 1.17.1 下,僅 OptiFine H1 Pre2 及以上版本能相容 Forge。你可以從 OptiFine 測試版中選擇最新版本。 install.failed.optifine_forge_1.17=Minecraft 1.17.1 下,僅 OptiFine H1 Pre2 及以上版本能相容 Forge。你可以從 OptiFine 測試版中選擇最新版本。
install.failed.version_mismatch=該軟體需要的遊戲版本為 %s但實際的遊戲版本為 %s。 install.failed.version_mismatch=該軟體需要的遊戲版本為 %s但實際的遊戲版本為 %s。
install.installer.change_version=%s,該版本與當前遊戲不相容,您需要點擊此處更換版本或刪除 install.installer.change_version=%s 與當前遊戲不相容,請更換版本
install.installer.choose=選擇 %s 版本 install.installer.choose=選擇 %s 版本
install.installer.depend=需要先安裝 %s install.installer.depend=需要先安裝 %s
install.installer.fabric=Fabric install.installer.fabric=Fabric
@ -1081,6 +1080,7 @@ update.no_browser=無法開啟瀏覽器,網址已經複製到剪貼簿了,
update.tooltip=更新 update.tooltip=更新
version=遊戲 version=遊戲
version.name=遊戲版本名稱
version.cannot_read=讀取遊戲版本失敗,無法進行自動安裝 version.cannot_read=讀取遊戲版本失敗,無法進行自動安裝
version.empty=沒有遊戲版本 version.empty=沒有遊戲版本
version.empty.add=進入下載頁安裝遊戲 version.empty.add=進入下載頁安裝遊戲

View File

@ -169,8 +169,7 @@ account.username=用户名
archive.author=作者 archive.author=作者
archive.date=发布日期 archive.date=发布日期
archive.game_version=游戏版本 archive.file.name=文件名
archive.name=名称
archive.version=版本 archive.version=版本
assets.download=下载资源 assets.download=下载资源
@ -498,7 +497,7 @@ install.failed.malformed=下载的文件格式损坏。您可以在设置-下载
install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 或 OptiFine , Forge 同时安装在 Minecraft 1.13 及以上版本 install.failed.optifine_conflict=暂不支持 OptiFine, Fabric 或 OptiFine , Forge 同时安装在 Minecraft 1.13 及以上版本
install.failed.optifine_forge_1.17=Minecraft 1.17.1 下,仅 OptiFine H1 Pre2 及以上版本能兼容 Forge。你可以从 OptiFine 测试版中选择最新版本。 install.failed.optifine_forge_1.17=Minecraft 1.17.1 下,仅 OptiFine H1 Pre2 及以上版本能兼容 Forge。你可以从 OptiFine 测试版中选择最新版本。
install.failed.version_mismatch=该组件需要的游戏版本为 %s但实际的游戏版本为 %s。 install.failed.version_mismatch=该组件需要的游戏版本为 %s但实际的游戏版本为 %s。
install.installer.change_version=%s,该版本与当前游戏不兼容,您需要点击此处更换版本或删除 install.installer.change_version=%s 与当前游戏不兼容,请更换版本
install.installer.choose=选择 %s 版本 install.installer.choose=选择 %s 版本
install.installer.depend=需要先安装 %s install.installer.depend=需要先安装 %s
install.installer.fabric=Fabric install.installer.fabric=Fabric
@ -1080,6 +1079,7 @@ update.no_browser=无法打开浏览器,网址已经复制到剪贴板了,
update.tooltip=更新 update.tooltip=更新
version=游戏 version=游戏
version.name=游戏版本名称
version.cannot_read=读取游戏版本失败,无法进行自动安装 version.cannot_read=读取游戏版本失败,无法进行自动安装
version.empty=没有游戏版本 version.empty=没有游戏版本
version.empty.add=进入下载页安装游戏 version.empty.add=进入下载页安装游戏

View File

@ -296,8 +296,8 @@ public class Version implements Comparable<Version>, Validation {
} }
if (patches == null) { if (patches == null) {
// This is a version from external launcher. // This is a version from external launcher. NO need to resolve the patches.
thisVersion = thisVersion.merge(this, true); return thisVersion;
} else if (!patches.isEmpty()) { } else if (!patches.isEmpty()) {
// Assume patches themselves do not have patches recursively. // Assume patches themselves do not have patches recursively.
List<Version> sortedPatches = patches.stream() List<Version> sortedPatches = patches.stream()

View File

@ -54,7 +54,7 @@
<module name="ThrowsCount"> <!-- max 5 throws definitions per method: http://checkstyle.sourceforge.net/config_design.html#ThrowsCount --> <module name="ThrowsCount"> <!-- max 5 throws definitions per method: http://checkstyle.sourceforge.net/config_design.html#ThrowsCount -->
<property name="max" value="5"/> <property name="max" value="5"/>
</module> </module>
<module name="InterfaceIsType"/> <!-- interface must contain methods, should not be used for const only: http://checkstyle.sourceforge.net/config_design.html#InterfaceIsType --> <!--<module name="InterfaceIsType"/> interface must contain methods, should not be used for const only: http://checkstyle.sourceforge.net/config_design.html#InterfaceIsType -->
<module name="OuterTypeFilename"/> <!-- class Foo must be in Foo.java: http://checkstyle.sourceforge.net/config_misc.html#OuterTypeFilename --> <module name="OuterTypeFilename"/> <!-- class Foo must be in Foo.java: http://checkstyle.sourceforge.net/config_misc.html#OuterTypeFilename -->
<module name="HideUtilityClassConstructor"/> <!-- utility class constructor must be private: http://checkstyle.sourceforge.net/config_design.html#HideUtilityClassConstructor --> <module name="HideUtilityClassConstructor"/> <!-- utility class constructor must be private: http://checkstyle.sourceforge.net/config_design.html#HideUtilityClassConstructor -->