feat(mod): mod upgrade detection.
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user