mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-12 13:26:53 -04:00
Refactor ModListPage and InstallerListPage
This commit is contained in:
parent
d8a34aac1c
commit
daa1a38a63
@ -17,7 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ListProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.Node;
|
||||
@ -26,6 +28,7 @@ import javafx.scene.control.Skin;
|
||||
|
||||
public abstract class ListPage<T extends Node> extends Control {
|
||||
private final ListProperty<T> items = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||
private final BooleanProperty loading = new SimpleBooleanProperty(false);
|
||||
|
||||
public abstract void add();
|
||||
|
||||
@ -33,6 +36,10 @@ public abstract class ListPage<T extends Node> extends Control {
|
||||
return items;
|
||||
}
|
||||
|
||||
public BooleanProperty loadingProperty() {
|
||||
return loading;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new ListPageSkin(this);
|
||||
|
@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXScrollPane;
|
||||
import com.jfoenix.controls.JFXSpinner;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
@ -33,8 +34,14 @@ public class ListPageSkin extends SkinBase<ListPage> {
|
||||
public ListPageSkin(ListPage<?> skinnable) {
|
||||
super(skinnable);
|
||||
|
||||
StackPane root = new StackPane();
|
||||
StackPane rootPane = new StackPane();
|
||||
|
||||
JFXSpinner spinner = new JFXSpinner();
|
||||
spinner.setRadius(16);
|
||||
spinner.getStyleClass().setAll("materialDesign-purple", "first-spinner");
|
||||
|
||||
StackPane contentPane = new StackPane();
|
||||
{
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
{
|
||||
scrollPane.setFitToWidth(true);
|
||||
@ -68,8 +75,16 @@ public class ListPageSkin extends SkinBase<ListPage> {
|
||||
vBox.getChildren().setAll(btnAdd);
|
||||
}
|
||||
|
||||
root.getChildren().setAll(scrollPane, vBox);
|
||||
contentPane.getChildren().setAll(scrollPane, vBox);
|
||||
}
|
||||
|
||||
getChildren().setAll(root);
|
||||
rootPane.getChildren().setAll(contentPane);
|
||||
|
||||
skinnable.loadingProperty().addListener((a, b, newValue) -> {
|
||||
if (newValue) rootPane.getChildren().setAll(spinner);
|
||||
else rootPane.getChildren().setAll(contentPane);
|
||||
});
|
||||
|
||||
getChildren().setAll(rootPane);
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.versions;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.MaintainTask;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||
@ -30,8 +27,8 @@ import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.InstallerItem;
|
||||
import org.jackhuang.hmcl.ui.ListPage;
|
||||
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@ -41,26 +38,16 @@ import java.util.function.Function;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class InstallerController {
|
||||
public class InstallerListPage extends ListPage<InstallerItem> {
|
||||
private Profile profile;
|
||||
private String versionId;
|
||||
private Version version;
|
||||
@FXML
|
||||
private ScrollPane scrollPane;
|
||||
@FXML private VBox contentPane;
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
}
|
||||
|
||||
public void loadVersion(Profile profile, String versionId) {
|
||||
this.profile = profile;
|
||||
this.versionId = versionId;
|
||||
this.version = profile.getRepository().getResolvedVersion(versionId);
|
||||
|
||||
contentPane.getChildren().clear();
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
|
||||
|
||||
Function<Library, Consumer<InstallerItem>> removeAction = library -> x -> {
|
||||
@ -73,13 +60,13 @@ public class InstallerController {
|
||||
.start();
|
||||
};
|
||||
|
||||
analyzer.getForge().ifPresent(library -> contentPane.getChildren().add(new InstallerItem("Forge", library.getVersion(), removeAction.apply(library))));
|
||||
analyzer.getLiteLoader().ifPresent(library -> contentPane.getChildren().add(new InstallerItem("LiteLoader", library.getVersion(), removeAction.apply(library))));
|
||||
analyzer.getOptiFine().ifPresent(library -> contentPane.getChildren().add(new InstallerItem("OptiFine", library.getVersion(), removeAction.apply(library))));
|
||||
analyzer.getForge().ifPresent(library -> itemsProperty().add(new InstallerItem("Forge", library.getVersion(), removeAction.apply(library))));
|
||||
analyzer.getLiteLoader().ifPresent(library -> itemsProperty().add(new InstallerItem("LiteLoader", library.getVersion(), removeAction.apply(library))));
|
||||
analyzer.getOptiFine().ifPresent(library -> itemsProperty().add(new InstallerItem("OptiFine", library.getVersion(), removeAction.apply(library))));
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAdd() {
|
||||
@Override
|
||||
public void add() {
|
||||
Optional<String> gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version));
|
||||
|
||||
if (!gameVersion.isPresent())
|
@ -18,13 +18,8 @@
|
||||
package org.jackhuang.hmcl.ui.versions;
|
||||
|
||||
import com.jfoenix.concurrency.JFXUtilities;
|
||||
import com.jfoenix.controls.JFXSpinner;
|
||||
import com.jfoenix.controls.JFXTabPane;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.mod.ModInfo;
|
||||
import org.jackhuang.hmcl.mod.ModManager;
|
||||
@ -32,6 +27,7 @@ import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.ListPage;
|
||||
import org.jackhuang.hmcl.ui.ModItem;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
@ -47,32 +43,20 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class ModController {
|
||||
@FXML
|
||||
private ScrollPane scrollPane;
|
||||
|
||||
@FXML private StackPane rootPane;
|
||||
|
||||
@FXML private VBox modPane;
|
||||
|
||||
@FXML private StackPane contentPane;
|
||||
@FXML private JFXSpinner spinner;
|
||||
public final class ModListPage extends ListPage<ModItem> {
|
||||
|
||||
private JFXTabPane parentTab;
|
||||
private ModManager modManager;
|
||||
private String versionId;
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
|
||||
rootPane.setOnDragOver(event -> {
|
||||
if (event.getGestureSource() != rootPane && event.getDragboard().hasFiles())
|
||||
public ModListPage() {
|
||||
setOnDragOver(event -> {
|
||||
if (event.getGestureSource() != this && event.getDragboard().hasFiles())
|
||||
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
|
||||
event.consume();
|
||||
});
|
||||
|
||||
rootPane.setOnDragDropped(event -> {
|
||||
setOnDragDropped(event -> {
|
||||
List<File> files = event.getDragboard().getFiles();
|
||||
if (files != null) {
|
||||
Collection<File> mods = files.stream()
|
||||
@ -98,11 +82,8 @@ public final class ModController {
|
||||
this.modManager = modManager;
|
||||
this.versionId = versionId;
|
||||
Task.of(variables -> {
|
||||
synchronized (ModController.this) {
|
||||
JFXUtilities.runInFX(() -> {
|
||||
rootPane.getChildren().remove(contentPane);
|
||||
spinner.setVisible(true);
|
||||
});
|
||||
synchronized (ModListPage.this) {
|
||||
JFXUtilities.runInFX(() -> loadingProperty().set(true));
|
||||
|
||||
modManager.refreshMods(versionId);
|
||||
|
||||
@ -130,18 +111,17 @@ public final class ModController {
|
||||
variables.set("list", list);
|
||||
}
|
||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||
rootPane.getChildren().add(contentPane);
|
||||
spinner.setVisible(false);
|
||||
loadingProperty().set(false);
|
||||
if (isDependentsSucceeded)
|
||||
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
if (newValue != null && newValue.getUserData() == ModController.this)
|
||||
modPane.getChildren().setAll(variables.<List<ModItem>>get("list"));
|
||||
if (newValue != null && newValue.getUserData() == ModListPage.this)
|
||||
itemsProperty().setAll(variables.<List<ModItem>>get("list"));
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAdd() {
|
||||
@Override
|
||||
public void add() {
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle(i18n("mods.choose_mod"));
|
||||
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
|
@ -44,9 +44,9 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
@FXML
|
||||
private Tab modTab;
|
||||
@FXML
|
||||
private ModController modController;
|
||||
private ModListPage mod;
|
||||
@FXML
|
||||
private InstallerController installerController;
|
||||
private InstallerListPage installer;
|
||||
@FXML
|
||||
private JFXListView<?> browseList;
|
||||
@FXML
|
||||
@ -93,10 +93,10 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
title.set(i18n("settings.game") + " - " + id);
|
||||
|
||||
versionSettings.loadVersionSetting(profile, id);
|
||||
modController.setParentTab(tabPane);
|
||||
modTab.setUserData(modController);
|
||||
modController.loadMods(profile.getModManager(), id);
|
||||
installerController.loadVersion(profile, id);
|
||||
mod.setParentTab(tabPane);
|
||||
modTab.setUserData(mod);
|
||||
mod.loadMods(profile.getModManager(), id);
|
||||
installer.loadVersion(profile, id);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<StackPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.jackhuang.hmcl.ui.versions.InstallerController">
|
||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
||||
<VBox fx:id="contentPane" spacing="10" style="-fx-padding: 20;">
|
||||
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
|
||||
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" onMouseClicked="#onAdd" styleClass="jfx-button-raised-round">
|
||||
<graphic>
|
||||
<fx:include source="/assets/svg/plus.fxml" />
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
</VBox>
|
||||
</StackPane>
|
@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXSpinner?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<StackPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:id="rootPane"
|
||||
fx:controller="org.jackhuang.hmcl.ui.versions.ModController">
|
||||
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
|
||||
<StackPane fx:id="contentPane">
|
||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
||||
<VBox fx:id="modPane" spacing="10" style="-fx-padding: 20 20 70 20;">
|
||||
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
|
||||
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" onMouseClicked="#onAdd" styleClass="jfx-button-raised-round">
|
||||
<graphic>
|
||||
<fx:include source="/assets/svg/plus.fxml"/>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
</VBox>
|
||||
</StackPane>
|
||||
</StackPane>
|
@ -5,6 +5,8 @@
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import org.jackhuang.hmcl.ui.versions.VersionSettingsPage?>
|
||||
<?import org.jackhuang.hmcl.ui.versions.ModListPage?>
|
||||
<?import org.jackhuang.hmcl.ui.versions.InstallerListPage?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:id="rootPane"
|
||||
@ -16,10 +18,10 @@
|
||||
<VersionSettingsPage fx:id="versionSettings" />
|
||||
</Tab>
|
||||
<Tab fx:id="modTab" text="%mods">
|
||||
<fx:include source="mod.fxml" fx:id="mod"/>
|
||||
<ModListPage fx:id="mod" />
|
||||
</Tab>
|
||||
<Tab text="%settings.tabs.installers">
|
||||
<fx:include source="installer.fxml" fx:id="installer"/>
|
||||
<InstallerListPage fx:id="installer" />
|
||||
</Tab>
|
||||
</JFXTabPane>
|
||||
|
||||
|
61
HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java
Normal file
61
HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java
Normal file
@ -0,0 +1,61 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Unzipper;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class World {
|
||||
|
||||
private final Path file;
|
||||
private final String name;
|
||||
|
||||
public World(Path file) throws IOException {
|
||||
this.file = file;
|
||||
|
||||
if (Files.isDirectory(file))
|
||||
name = loadFromDirectory();
|
||||
else if (Files.isRegularFile(file))
|
||||
name = loadFromZip();
|
||||
else
|
||||
throw new IOException("Path " + file + " cannot be recognized as a Minecraft world");
|
||||
}
|
||||
|
||||
private String loadFromDirectory() {
|
||||
return FileUtils.getName(file);
|
||||
}
|
||||
|
||||
private String loadFromZip() throws IOException {
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(file)) {
|
||||
Path root = Files.list(fs.getPath("/")).filter(Files::isDirectory).findAny()
|
||||
.orElseThrow(() -> new IOException("Not a valid world zip file"));
|
||||
|
||||
Path levelDat = root.resolve("level.dat");
|
||||
if (!Files.exists(levelDat))
|
||||
throw new FileNotFoundException("Not a valid world zip file since level.dat cannot be found.");
|
||||
|
||||
return FileUtils.getName(root);
|
||||
}
|
||||
}
|
||||
|
||||
public void install(Path savesDir) throws IOException {
|
||||
Path worldDir = savesDir.resolve(name);
|
||||
if (Files.isDirectory(worldDir)) {
|
||||
throw new FileAlreadyExistsException("World already exists");
|
||||
}
|
||||
|
||||
if (Files.isRegularFile(file)) {
|
||||
new Unzipper(file, savesDir)
|
||||
.setSubDirectory("/" + name + "/")
|
||||
.unzip();
|
||||
} else if (Files.isDirectory(file)) {
|
||||
FileUtils.copyDirectory(file, worldDir);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,8 +22,8 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -54,6 +54,10 @@ public final class FileUtils {
|
||||
return StringUtils.addPrefix(StringUtils.removeSuffix(path, "/", "\\"), "/");
|
||||
}
|
||||
|
||||
public static String getName(Path path) {
|
||||
return StringUtils.removeSuffix(path.getFileName().toString(), "/", "\\");
|
||||
}
|
||||
|
||||
public static String readText(File file) throws IOException {
|
||||
return readText(file, UTF_8);
|
||||
}
|
||||
@ -97,6 +101,26 @@ public final class FileUtils {
|
||||
return Lang.test(() -> deleteDirectory(directory));
|
||||
}
|
||||
|
||||
public static void copyDirectory(Path src, Path dest) throws IOException {
|
||||
Files.walkFileTree(src, new SimpleFileVisitor<Path>(){
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Path destFile = dest.resolve(src.relativize(file));
|
||||
Files.copy(file, destFile);
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
Path destDir = dest.resolve(src.relativize(dir));
|
||||
Files.createDirectory(destDir);
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean moveToTrash(File file) {
|
||||
try {
|
||||
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
|
||||
|
Loading…
x
Reference in New Issue
Block a user