feat(mod): mod upgrade detection.

This commit is contained in:
huanghongxun
2021-10-05 16:50:05 +08:00
parent ba0a7ddfa8
commit 23750e1222
17 changed files with 607 additions and 307 deletions

View File

@@ -111,6 +111,8 @@ public class Navigator extends TransitionPane {
Logging.LOG.info("Closed page " + from);
Node poppedNode = stack.pop();
NavigationEvent exited = new NavigationEvent(this, poppedNode, Navigation.NavigationDirection.PREVIOUS, NavigationEvent.EXITED);
poppedNode.fireEvent(exited);
if (poppedNode instanceof PageAware) ((PageAware) poppedNode).onPageHidden();
backable.set(canGoBack());

View File

@@ -39,7 +39,7 @@ import javafx.util.Duration;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
public class TabHeader extends Control implements TabControl {
public class TabHeader extends Control implements TabControl, PageAware {
public TabHeader(Tab<?>... tabs) {
getStyleClass().setAll("tab-header");
@@ -86,6 +86,26 @@ public class TabHeader extends Control implements TabControl {
getSelectionModel().select(tab);
}
@Override
public void onPageShown() {
Tab<?> tab = getSelectionModel().getSelectedItem();
if (tab != null) {
if (tab.getNode() instanceof PageAware) {
((PageAware) tab.getNode()).onPageShown();
}
}
}
@Override
public void onPageHidden() {
Tab<?> tab = getSelectionModel().getSelectedItem();
if (tab != null) {
if (tab.getNode() instanceof PageAware) {
((PageAware) tab.getNode()).onPageHidden();
}
}
}
/**
* The position to place the tabs.
*/

View File

@@ -144,7 +144,7 @@ public final class TaskListPane extends StackPane {
Platform.runLater(() -> {
ProgressListNode node = new ProgressListNode(task);
nodes.put(task, node);
StageNode stageNode = stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().orElse(null);
StageNode stageNode = stageNodes.stream().filter(x -> x.stage.equals(task.getInheritedStage())).findAny().orElse(null);
listBox.add(listBox.indexOf(stageNode) + 1, node);
});
}

View File

@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionPane;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.PageAware;
import org.jackhuang.hmcl.ui.construct.TabHeader;
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
@@ -34,7 +35,7 @@ import org.jackhuang.hmcl.ui.versions.VersionSettingsPage;
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class LauncherSettingsPage extends DecoratorAnimatedPage implements DecoratorPage {
public class LauncherSettingsPage extends DecoratorAnimatedPage implements DecoratorPage, PageAware {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("settings"), -1));
private final TabHeader tab;
private final TabHeader.Tab<VersionSettingsPage> gameTab = new TabHeader.Tab<>("versionSettingsPage");
@@ -124,6 +125,16 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
setCenter(transitionPane);
}
@Override
public void onPageShown() {
tab.onPageShown();
}
@Override
public void onPageHidden() {
tab.onPageHidden();
}
public void showGameSettings(Profile profile) {
gameTab.getNode().loadVersion(profile, null);
tab.select(gameTab);

View File

@@ -187,7 +187,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 ModUpdatesDialog(result));
Controllers.dialog(new ModUpdatesPane(result));
}
})
.withStagesHint(Collections.singletonList("mods.check_updates"))

View File

@@ -38,7 +38,7 @@ public class ModUpdateTask extends Task<List<LocalMod.ModUpdate>> {
dependents = mods.stream()
.map(mod -> Task.supplyAsync(() -> {
return mod.checkUpdates(gameVersion, CurseForgeRemoteModRepository.MODS);
}).setSignificance(TaskSignificance.MAJOR).withCounter("mods.check_updates"))
}).setSignificance(TaskSignificance.MAJOR).setName(mod.getFileName()).withCounter("mods.check_updates"))
.collect(Collectors.toList());
setStage("mods.check_updates");

View File

@@ -1,67 +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.JFXListView;
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.DialogPane;
import org.jackhuang.hmcl.ui.construct.MDListCell;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import java.util.List;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class ModUpdatesDialog extends DialogPane {
public ModUpdatesDialog(List<LocalMod.ModUpdate> updates) {
setTitle(i18n("mods.check_updates"));
JFXListView<LocalMod.ModUpdate> listView = new JFXListView<>();
listView.getItems().setAll(updates);
listView.setCellFactory(l -> new ModUpdateCell(listView));
setBody(listView);
}
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");
}
}
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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

@@ -581,6 +581,9 @@ 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.target_version=目标版本
mods.choose_mod=选择模组
mods.curseforge=CurseForge
mods.dependencies=前置 Mod