feat(mod): WIP: mod update.

This commit is contained in:
huanghongxun
2021-10-05 22:52:14 +08:00
parent 271885a85e
commit d48591a4cd
23 changed files with 557 additions and 242 deletions

View File

@@ -0,0 +1,136 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.controls.JFXCheckBox;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeTableCell;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class JFXCheckBoxTreeTableCell<S, T> extends TreeTableCell<S, T> {
private final CheckBox checkBox;
private boolean showLabel;
private ObservableValue<Boolean> booleanProperty;
public JFXCheckBoxTreeTableCell() {
this(null, null);
}
public JFXCheckBoxTreeTableCell(
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty) {
this(getSelectedProperty, null);
}
public JFXCheckBoxTreeTableCell(
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty,
final StringConverter<T> converter) {
this.getStyleClass().add("check-box-tree-table-cell");
this.checkBox = new JFXCheckBox();
setGraphic(null);
setSelectedStateCallback(getSelectedProperty);
setConverter(converter);
}
private ObjectProperty<StringConverter<T>> converter =
new SimpleObjectProperty<StringConverter<T>>(this, "converter") {
protected void invalidated() {
updateShowLabel();
}
};
public final ObjectProperty<StringConverter<T>> converterProperty() {
return converter;
}
public final void setConverter(StringConverter<T> value) {
converterProperty().set(value);
}
public final StringConverter<T> getConverter() {
return converterProperty().get();
}
private ObjectProperty<Callback<Integer, ObservableValue<Boolean>>>
selectedStateCallback =
new SimpleObjectProperty<Callback<Integer, ObservableValue<Boolean>>>(
this, "selectedStateCallback");
public final ObjectProperty<Callback<Integer, ObservableValue<Boolean>>> selectedStateCallbackProperty() {
return selectedStateCallback;
}
public final void setSelectedStateCallback(Callback<Integer, ObservableValue<Boolean>> value) {
selectedStateCallbackProperty().set(value);
}
public final Callback<Integer, ObservableValue<Boolean>> getSelectedStateCallback() {
return selectedStateCallbackProperty().get();
}
@SuppressWarnings("unchecked")
@Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
StringConverter<T> c = getConverter();
if (showLabel) {
setText(c.toString(item));
}
setGraphic(checkBox);
if (booleanProperty instanceof BooleanProperty) {
checkBox.selectedProperty().unbindBidirectional((BooleanProperty) booleanProperty);
}
ObservableValue<?> obsValue = getSelectedProperty();
if (obsValue instanceof BooleanProperty) {
booleanProperty = (ObservableValue<Boolean>) obsValue;
checkBox.selectedProperty().bindBidirectional((BooleanProperty) booleanProperty);
}
checkBox.disableProperty().bind(Bindings.not(
getTreeTableView().editableProperty().and(
getTableColumn().editableProperty()).and(
editableProperty())
));
}
}
private void updateShowLabel() {
this.showLabel = converter != null;
this.checkBox.setAlignment(showLabel ? Pos.CENTER_LEFT : Pos.CENTER);
}
private ObservableValue<?> getSelectedProperty() {
return getSelectedStateCallback() != null ?
getSelectedStateCallback().call(getIndex()) :
getTableColumn().getCellObservableValue(getIndex());
}
}

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
import org.jackhuang.hmcl.task.Task;
@@ -25,13 +25,13 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class ModUpdateTask extends Task<List<LocalMod.ModUpdate>> {
public class ModCheckUpdatesTask extends Task<List<LocalModFile.ModUpdate>> {
private final String gameVersion;
private final Collection<LocalMod> mods;
private final Collection<Task<LocalMod.ModUpdate>> dependents;
private final Collection<LocalModFile> mods;
private final Collection<Task<LocalModFile.ModUpdate>> dependents;
public ModUpdateTask(String gameVersion, Collection<LocalMod> mods) {
public ModCheckUpdatesTask(String gameVersion, Collection<LocalModFile> mods) {
this.gameVersion = gameVersion;
this.mods = mods;
@@ -51,7 +51,7 @@ public class ModUpdateTask extends Task<List<LocalMod.ModUpdate>> {
}
@Override
public void preExecute() throws Exception {
public void preExecute() {
notifyPropertiesChanged();
}

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
@@ -87,7 +87,7 @@ public class ModDownloadListPage extends DownloadListPage {
}
@Override
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) {
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) {
throw new UnsupportedOperationException();
}

View File

@@ -24,7 +24,7 @@ import javafx.collections.ObservableList;
import javafx.scene.control.Skin;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
@@ -33,6 +33,7 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPageBase;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.PageAware;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -47,7 +48,7 @@ import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> implements VersionPage.VersionLoadable {
public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> implements VersionPage.VersionLoadable, PageAware {
private final BooleanProperty modded = new SimpleBooleanProperty(this, "modded", false);
private ModManager modManager;
@@ -149,7 +150,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
modManager.removeMods(selectedItems.stream()
.filter(Objects::nonNull)
.map(ModListPageSkin.ModInfoObject::getModInfo)
.toArray(LocalMod[]::new));
.toArray(LocalModFile[]::new));
loadMods(modManager);
} catch (IOException ignore) {
// Fail to remove mods if the game is running or the mod is absent.
@@ -179,7 +180,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
Task.composeAsync(() -> {
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
if (gameVersion.isPresent()) {
return new ModUpdateTask(gameVersion.get(), modManager.getMods());
return new ModCheckUpdatesTask(gameVersion.get(), modManager.getMods());
}
return null;
})
@@ -187,7 +188,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
if (exception != null) {
Controllers.dialog("Failed to check updates", "failed", MessageDialogPane.MessageType.ERROR);
} else {
Controllers.dialog(new ModUpdatesPane(result));
Controllers.navigate(new ModUpdatesPage(modManager, result));
}
})
.withStagesHint(Collections.singletonList("mods.check_updates"))

View File

@@ -34,7 +34,7 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.task.Schedulers;
@@ -126,34 +126,34 @@ class ModListPageSkin extends SkinBase<ModListPage> {
static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> implements Comparable<ModInfoObject> {
private final BooleanProperty active;
private final LocalMod localMod;
private final LocalModFile localModFile;
private final String message;
private final ModTranslations.Mod mod;
ModInfoObject(LocalMod localMod) {
this.localMod = localMod;
this.active = localMod.activeProperty();
StringBuilder message = new StringBuilder(localMod.getName());
if (isNotBlank(localMod.getVersion()))
message.append(", ").append(i18n("archive.version")).append(": ").append(localMod.getVersion());
if (isNotBlank(localMod.getGameVersion()))
message.append(", ").append(i18n("archive.game_version")).append(": ").append(localMod.getGameVersion());
if (isNotBlank(localMod.getAuthors()))
message.append(", ").append(i18n("archive.author")).append(": ").append(localMod.getAuthors());
ModInfoObject(LocalModFile localModFile) {
this.localModFile = localModFile;
this.active = localModFile.activeProperty();
StringBuilder message = new StringBuilder(localModFile.getName());
if (isNotBlank(localModFile.getVersion()))
message.append(", ").append(i18n("archive.version")).append(": ").append(localModFile.getVersion());
if (isNotBlank(localModFile.getGameVersion()))
message.append(", ").append(i18n("archive.game_version")).append(": ").append(localModFile.getGameVersion());
if (isNotBlank(localModFile.getAuthors()))
message.append(", ").append(i18n("archive.author")).append(": ").append(localModFile.getAuthors());
this.message = message.toString();
this.mod = ModTranslations.getModById(localMod.getId());
this.mod = ModTranslations.getModById(localModFile.getId());
}
String getTitle() {
return localMod.getFileName();
return localModFile.getFileName();
}
String getSubtitle() {
return message;
}
LocalMod getModInfo() {
return localMod;
LocalModFile getModInfo() {
return localModFile;
}
public ModTranslations.Mod getMod() {
@@ -162,7 +162,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
@Override
public int compareTo(@NotNull ModListPageSkin.ModInfoObject o) {
return localMod.getFileName().toLowerCase().compareTo(o.localMod.getFileName().toLowerCase());
return localModFile.getFileName().toLowerCase().compareTo(o.localModFile.getFileName().toLowerCase());
}
}

View File

@@ -0,0 +1,294 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.*;
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.curse.CurseAddon;
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.JFXCheckBoxTreeTableCell;
import org.jackhuang.hmcl.ui.construct.MDListCell;
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.Pair;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class ModUpdatesPage extends BorderPane implements DecoratorPage {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("mods.check_updates"), -1));
private final ModManager modManager;
private final ObservableList<ModUpdateObject> objects;
public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> updates) {
this.modManager = modManager;
JFXTreeTableColumn<ModUpdateObject, Boolean> enabledColumn = new JFXTreeTableColumn<>();
enabledColumn.setCellFactory(column -> new JFXCheckBoxTreeTableCell<>());
setupCellValueFactory(enabledColumn, ModUpdateObject::enabledProperty);
enabledColumn.setEditable(true);
enabledColumn.setMaxWidth(40);
enabledColumn.setMinWidth(40);
JFXTreeTableColumn<ModUpdateObject, String> fileNameColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.file"));
setupCellValueFactory(fileNameColumn, ModUpdateObject::fileNameProperty);
JFXTreeTableColumn<ModUpdateObject, String> currentVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.current_version"));
setupCellValueFactory(currentVersionColumn, ModUpdateObject::currentVersionProperty);
JFXTreeTableColumn<ModUpdateObject, String> targetVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.target_version"));
setupCellValueFactory(targetVersionColumn, ModUpdateObject::targetVersionProperty);
JFXTreeTableColumn<ModUpdateObject, String> sourceColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.source"));
setupCellValueFactory(sourceColumn, ModUpdateObject::sourceProperty);
objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
RecursiveTreeItem<ModUpdateObject> root = new RecursiveTreeItem<>(
objects,
RecursiveTreeObject::getChildren);
JFXTreeTableView<ModUpdateObject> table = new JFXTreeTableView<>(root);
table.setShowRoot(false);
table.setEditable(true);
table.getColumns().setAll(enabledColumn, fileNameColumn, currentVersionColumn, targetVersionColumn, sourceColumn);
setCenter(table);
HBox actions = new HBox(8);
actions.setPadding(new Insets(8));
actions.setAlignment(Pos.CENTER_RIGHT);
JFXButton nextButton = new JFXButton(i18n("mods.check_updates.update"));
nextButton.getStyleClass().add("jfx-button-raised");
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
nextButton.setOnAction(e -> updateMods());
JFXButton cancelButton = new JFXButton(i18n("button.cancel"));
cancelButton.getStyleClass().add("jfx-button-raised");
cancelButton.setButtonType(JFXButton.ButtonType.RAISED);
cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent()));
onEscPressed(this, cancelButton::fire);
actions.getChildren().setAll(nextButton, cancelButton);
setBottom(actions);
}
private <T> void setupCellValueFactory(JFXTreeTableColumn<ModUpdateObject, T> column, Function<ModUpdateObject, ObservableValue<T>> mapper) {
column.setCellValueFactory(param -> {
if (column.validateValue(param))
return mapper.apply(param.getValue().getValue());
else
return column.getComputedValue(param);
});
}
private void updateMods() {
Controllers.taskDialog(
new ModUpdateTask(
modManager,
objects.stream()
.filter(o -> o.enabled.get())
.map(object -> pair(object.data.getLocalMod(), object.data.getCandidates().get(0)))
.collect(Collectors.toList())),
i18n("mods.check_updates.update"),
t -> {
});
}
@Override
public ReadOnlyObjectWrapper<State> stateProperty() {
return state;
}
public static class ModUpdateCell extends MDListCell<LocalModFile.ModUpdate> {
TwoLineListItem content = new TwoLineListItem();
public ModUpdateCell(JFXListView<LocalModFile.ModUpdate> listView) {
super(listView);
getContainer().getChildren().setAll(content);
}
@Override
protected void updateControl(LocalModFile.ModUpdate item, boolean empty) {
if (empty) return;
ModTranslations.Mod mod = ModTranslations.getModById(item.getLocalMod().getId());
content.setTitle(mod != null ? mod.getDisplayName() : item.getCurrentVersion().getName());
content.setSubtitle(item.getLocalMod().getFileName());
content.getTags().setAll();
if (item.getCurrentVersion().getSelf() instanceof CurseAddon.LatestFile) {
content.getTags().add("Curseforge");
} else if (item.getCurrentVersion().getSelf() instanceof ModrinthRemoteModRepository.ModVersion) {
content.getTags().add("Modrinth");
}
}
}
private static class ModUpdateObject extends RecursiveTreeObject<ModUpdateObject> {
final LocalModFile.ModUpdate data;
final BooleanProperty enabled = new SimpleBooleanProperty();
final StringProperty fileName = new SimpleStringProperty();
final StringProperty currentVersion = new SimpleStringProperty();
final StringProperty targetVersion = new SimpleStringProperty();
final StringProperty source = new SimpleStringProperty();
public ModUpdateObject(LocalModFile.ModUpdate data) {
this.data = data;
enabled.set(true);
fileName.set(data.getLocalMod().getFileName());
currentVersion.set(data.getCurrentVersion().getVersion());
targetVersion.set(data.getCandidates().get(0).getVersion());
switch (data.getCurrentVersion().getSelf().getType()) {
case CURSEFORGE:
source.set(i18n("mods.curseforge"));
break;
case MODRINTH:
source.set(i18n("mods.modrinth"));
}
}
public boolean isEnabled() {
return enabled.get();
}
public BooleanProperty enabledProperty() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled.set(enabled);
}
public String getFileName() {
return fileName.get();
}
public StringProperty fileNameProperty() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName.set(fileName);
}
public String getCurrentVersion() {
return currentVersion.get();
}
public StringProperty currentVersionProperty() {
return currentVersion;
}
public void setCurrentVersion(String currentVersion) {
this.currentVersion.set(currentVersion);
}
public String getTargetVersion() {
return targetVersion.get();
}
public StringProperty targetVersionProperty() {
return targetVersion;
}
public void setTargetVersion(String targetVersion) {
this.targetVersion.set(targetVersion);
}
public String getSource() {
return source.get();
}
public StringProperty sourceProperty() {
return source;
}
public void setSource(String source) {
this.source.set(source);
}
}
public static class ModUpdateTask extends Task<Void> {
private final Collection<Task<?>> dependents;
ModUpdateTask(ModManager modManager, List<Pair<LocalModFile, RemoteMod.Version>> mods) {
setStage("mods.check_updates.update");
getProperties().put("total", mods.size());
dependents = mods.stream()
.map(mod -> {
return Task
.supplyAsync(() -> {
return null;
})
.setName(mod.getKey().getName())
.setSignificance(TaskSignificance.MAJOR)
.withCounter("mods.check_updates.update");
})
.collect(Collectors.toList());
}
@Override
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public boolean doPreExecute() {
return true;
}
@Override
public void preExecute() {
notifyPropertiesChanged();
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
@Override
public void execute() throws Exception {
}
}
}

View File

@@ -1,155 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.*;
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.curse.CurseAddon;
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
import org.jackhuang.hmcl.ui.construct.MDListCell;
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import java.util.List;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class ModUpdatesPane extends BorderPane implements DecoratorPage {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), -1));
public ModUpdatesPane(List<LocalMod.ModUpdate> updates) {
JFXTreeTableColumn<ModUpdateObject, String> fileNameColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.file"));
fileNameColumn.setCellValueFactory(data -> {
if (fileNameColumn.validateValue(data)) {
return data.getValue().getValue().fileName;
} else {
return fileNameColumn.getComputedValue(data);
}
});
JFXTreeTableColumn<ModUpdateObject, String> currentVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.current_version"));
currentVersionColumn.setCellValueFactory(data -> {
if (currentVersionColumn.validateValue(data)) {
return data.getValue().getValue().currentVersion;
} else {
return currentVersionColumn.getComputedValue(data);
}
});
JFXTreeTableColumn<ModUpdateObject, String> targetVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.target_version"));
targetVersionColumn.setCellValueFactory(data -> {
if (targetVersionColumn.validateValue(data)) {
return data.getValue().getValue().targetVersion;
} else {
return targetVersionColumn.getComputedValue(data);
}
});
ObservableList<ModUpdateObject> objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
RecursiveTreeItem<ModUpdateObject> root = new RecursiveTreeItem<>(
objects,
RecursiveTreeObject::getChildren);
JFXTreeTableView<ModUpdateObject> table = new JFXTreeTableView<>(root);
table.setShowRoot(false);
table.setEditable(true);
table.getColumns().setAll(fileNameColumn, currentVersionColumn, targetVersionColumn);
setCenter(table);
HBox actions = new HBox(8);
actions.setAlignment(Pos.CENTER_RIGHT);
JFXButton nextButton = new JFXButton();
nextButton.getStyleClass().add("jfx-button-raised");
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
nextButton.setOnAction(e -> updateMods());
JFXButton cancelButton = new JFXButton();
cancelButton.getStyleClass().add("jfx-button-raised");
cancelButton.setButtonType(JFXButton.ButtonType.RAISED);
cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent()));
onEscPressed(this, cancelButton::fire);
actions.getChildren().setAll(nextButton, cancelButton);
setBottom(actions);
}
private void updateMods() {
}
@Override
public ReadOnlyObjectWrapper<State> stateProperty() {
return state;
}
public static class ModUpdateCell extends MDListCell<LocalMod.ModUpdate> {
TwoLineListItem content = new TwoLineListItem();
public ModUpdateCell(JFXListView<LocalMod.ModUpdate> listView) {
super(listView);
getContainer().getChildren().setAll(content);
}
@Override
protected void updateControl(LocalMod.ModUpdate item, boolean empty) {
if (empty) return;
ModTranslations.Mod mod = ModTranslations.getModById(item.getLocalMod().getId());
content.setTitle(mod != null ? mod.getDisplayName() : item.getCurrentVersion().getName());
content.setSubtitle(item.getLocalMod().getFileName());
content.getTags().setAll();
if (item.getCurrentVersion().getSelf() instanceof CurseAddon.LatestFile) {
content.getTags().add("Curseforge");
} else if (item.getCurrentVersion().getSelf() instanceof ModrinthRemoteModRepository.ModVersion) {
content.getTags().add("Modrinth");
}
}
}
private static class ModUpdateObject extends RecursiveTreeObject<ModUpdateObject> {
final LocalMod.ModUpdate data;
final StringProperty fileName = new SimpleStringProperty();
final StringProperty currentVersion = new SimpleStringProperty();
final StringProperty targetVersion = new SimpleStringProperty();
public ModUpdateObject(LocalMod.ModUpdate data) {
this.data = data;
fileName.set(data.getLocalMod().getFileName());
currentVersion.set(data.getCurrentVersion().getName());
targetVersion.set(data.getCandidates().get(0).getName());
}
}
}

View File

@@ -430,6 +430,7 @@ launch.advice.newer_java=Java 8 is recommended to make the game faster. For many
launch.advice.not_enough_space=You have allocated too much memory, because the physical memory size is %dMB, your game may crash. Shall we continue launching?
launch.advice.require_newer_java_version=Minecraft %1$s requires Java %2$s or later, are you willing to download one now?
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game may crash. The maximum memory capacity for 32 bit systems is is 1024MB. Shall we continue launching?
launch.advice.vanilla_linux_java_8=For Linux x86-64, Minecraft 1.12.2 and below can only run on Java 8.\nJava 9 and above versions cannot load 32-bit native libraries like liblwjgl.so.
launch.failed=Unable to launch
launch.failed.cannot_create_jvm=Java virtual machine could not be created. Java arguments may cause issues. Please restart without JVM arguments.
launch.failed.creating_process=Failed to create process. Check your Java path.
@@ -581,6 +582,11 @@ mods.add.failed=Failed to install mods %s.
mods.add.success=Successfully installed mods %s.
mods.category=Category
mods.check_updates=Check updates
mods.check_updates.current_version=Current
mods.check_updates.file=File
mods.check_updates.source=Source
mods.check_updates.target_version=Target
mods.check_updates.update=Update
mods.choose_mod=Choose your mods
mods.curseforge=CurseForge
mods.dependencies=Dependencies

View File

@@ -430,6 +430,7 @@ launch.advice.newer_java=偵測到您未使用 Java 8 及更新版本Java 8
launch.advice.not_enough_space=您設定的記憶體大小過大,由於超過了系統記憶體大小 %dMB所以可能影響遊戲體驗或無法啟動遊戲。是否繼續啟動
launch.advice.require_newer_java_version=Minecraft %1$s 僅能運行在 Java %2$s 或更高版本上,是否下載?
launch.advice.too_large_memory_for_32bit=您設定的記憶體大小過大,由於可能超過了 32 位元 Java 的記憶體分配限制,所以可能無法啟動遊戲,請將記憶體調至低於 1024MB 的值。是否繼續啟動?
launch.advice.vanilla_linux_java_8=對於 Linux x86-64 平台Minecraft 1.12.2 及以下版本僅支持 Java 8。\nJava 9+ 版本會無法載入 32 位的 liblwjgl.so。
launch.failed=啟動失敗
launch.failed.cannot_create_jvm=偵測到無法建立 Java 虛擬機,可能是 Java 參數有問題。可以在設定中開啟無參數模式啟動。
launch.failed.creating_process=啟動失敗,在建立新處理程式時發生錯誤。可能是 Java 路徑錯誤。
@@ -581,6 +582,11 @@ mods.add.failed=新增模組 %s 失敗。
mods.add.success=成功新增模組 %s。
mods.category=類別
mods.check_updates=檢查模組更新
mods.check_updates.current_version=當前版本
mods.check_updates.file=文件
mods.check_updates.source=來源
mods.check_updates.target_version=目標版本
mods.check_updates.update=更新
mods.choose_mod=選擇模組
mods.curseforge=CurseForge
mods.dependencies=前置 Mod

View File

@@ -430,6 +430,7 @@ launch.advice.newer_java=检测到您未使用 Java 8 及更新版本Java 8
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小 %dMB所以可能影响游戏体验或无法启动游戏。是否继续启动
launch.advice.require_newer_java_version=Minecraft %1$s 仅能运行在 Java %2$s 或更高版本上,是否下载?
launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于可能超过了 32 位 Java 的内存分配限制,所以可能无法启动游戏,请将内存调至 1024MB 或更小。是否继续启动?
launch.advice.vanilla_linux_java_8=对于 Linux x86-64 平台Minecraft 1.12.2 及以下版本仅支持 Java 8。\nJava 9+ 版本会无法加载 32 位的 liblwjgl.so。
launch.failed=启动失败
launch.failed.cannot_create_jvm=截获到无法创建 Java 虚拟机,可能是 Java 参数有问题,可以在设置中开启无参数模式启动。
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误。
@@ -583,7 +584,9 @@ mods.category=类别
mods.check_updates=检查模组更新
mods.check_updates.current_version=当前版本
mods.check_updates.file=文件
mods.check_updates.source=来源
mods.check_updates.target_version=目标版本
mods.check_updates.update=更新
mods.choose_mod=选择模组
mods.curseforge=CurseForge
mods.dependencies=前置 Mod