diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index ed7664a9da..b90c6bb76c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -33,6 +33,7 @@ import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; +import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.SettingsMap; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -57,7 +58,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard private final WizardController controller; private final String version; private final ModAdviser adviser; - private final CheckBoxTreeItem rootNode; + private final ModpackFileTreeItem rootNode; public ModpackFileSelectionPage(WizardController controller, Profile profile, String version, ModAdviser adviser) { this.controller = controller; @@ -65,7 +66,7 @@ public ModpackFileSelectionPage(WizardController controller, Profile profile, St this.adviser = adviser; JFXTreeView treeView = new JFXTreeView<>(); - rootNode = getTreeItem(profile.getRepository().getRunDirectory(version), "minecraft"); + rootNode = getTreeItem(profile.getRepository().getRunDirectory(version), "minecraft", 0); treeView.setRoot(rootNode); treeView.setSelectionModel(new NoneMultipleSelectionModel<>()); onEscPressed(treeView, () -> controller.onPrev(true)); @@ -86,7 +87,7 @@ public ModpackFileSelectionPage(WizardController controller, Profile profile, St this.setBottom(nextPane); } - private CheckBoxTreeItem getTreeItem(Path file, String basePath) { + private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { if (Files.notExists(file)) return null; @@ -110,20 +111,29 @@ private CheckBoxTreeItem getTreeItem(Path file, String basePath) { state = ModAdviser.ModSuggestion.HIDDEN; } - if (isDirectory && fileName.equals(version + "-natives")) // Ignore -natives - state = ModAdviser.ModSuggestion.HIDDEN; + if (isDirectory) { + if (fileName.equals(version + "-natives")) { // Ignore -natives + state = ModAdviser.ModSuggestion.HIDDEN; + } + if (level == 1 && fileName.startsWith("natives-")) { // Ignore natives-os-arch + state = ModAdviser.ModSuggestion.HIDDEN; + } + } if (state == ModAdviser.ModSuggestion.HIDDEN) return null; } - CheckBoxTreeItem node = new CheckBoxTreeItem<>(StringUtils.substringAfterLast(basePath, "/")); + ModpackFileTreeItem node = new ModpackFileTreeItem(level == 0 ? version : StringUtils.substringAfterLast(basePath, '/'), basePath); if (state == ModAdviser.ModSuggestion.SUGGESTED) node.setSelected(true); if (isDirectory) { try (var stream = Files.list(file)) { - stream.forEach(it -> { - CheckBoxTreeItem subNode = getTreeItem(it, basePath + "/" + FileUtils.getName(it)); + stream.map(path -> Pair.pair(path, Files.isDirectory(path))).sorted((p1, p2) -> { + if (p1.value() == p2.value()) return p1.key().compareTo(p2.key()); + return p1.value() ? -1 : 1; + }).map(Pair::key).forEach(it -> { + ModpackFileTreeItem subNode = getTreeItem(it, basePath + "/" + FileUtils.getName(it), level + 1); if (subNode != null) { node.setSelected(subNode.isSelected() || node.isSelected()); if (!subNode.isSelected()) { @@ -144,33 +154,17 @@ private CheckBoxTreeItem getTreeItem(Path file, String basePath) { } } - HBox graphic = new HBox(); - JFXCheckBox checkBox = new JFXCheckBox(); - checkBox.selectedProperty().bindBidirectional(node.selectedProperty()); - checkBox.indeterminateProperty().bindBidirectional(node.indeterminateProperty()); - graphic.getChildren().add(checkBox); - - if (TRANSLATION.containsKey(basePath)) { - Label comment = new Label(TRANSLATION.get(basePath)); - comment.setStyle("-fx-text-fill: -monet-on-surface-variant;"); - comment.setMouseTransparent(true); - graphic.getChildren().add(comment); - } - graphic.setPickOnBounds(false); - node.setExpanded("minecraft".equals(basePath)); - node.setGraphic(graphic); - return node; } - private void getFilesNeeded(CheckBoxTreeItem node, String basePath, List list) { + private void getFilesNeeded(ModpackFileTreeItem node, String basePath, List list) { if (node == null) return; if (node.isSelected() || node.isIndeterminate()) { if (basePath.length() > "minecraft/".length()) list.add(StringUtils.substringAfter(basePath, "minecraft/")); for (TreeItem child : node.getChildren()) { - if (child instanceof CheckBoxTreeItem) { - getFilesNeeded(((CheckBoxTreeItem) child), basePath + "/" + child.getValue(), list); + if (child instanceof ModpackFileTreeItem mChild) { + getFilesNeeded(mChild, basePath + "/" + mChild.getFileName(), list); } } } @@ -210,4 +204,40 @@ public String getTitle() { pair("minecraft/blueprints", i18n("modpack.files.blueprints")), pair("minecraft/scripts", i18n("modpack.files.scripts")) ); + + private static final class ModpackFileTreeItem extends CheckBoxTreeItem { + + private final String fileName; + + public ModpackFileTreeItem(String fileName, String basePath) { + this.fileName = fileName; + + HBox graphic = new HBox(); + JFXCheckBox checkBox = new JFXCheckBox(); + checkBox.selectedProperty().bindBidirectional(this.selectedProperty()); + checkBox.indeterminateProperty().bindBidirectional(this.indeterminateProperty()); + graphic.getChildren().add(checkBox); + + { + Label text = new Label(fileName); + text.setMouseTransparent(true); + graphic.getChildren().add(text); + } + if (TRANSLATION.containsKey(basePath)) { + Label comment = new Label(TRANSLATION.get(basePath)); + comment.setStyle("-fx-text-fill: -monet-on-surface-variant;"); + comment.setMouseTransparent(true); + graphic.getChildren().add(comment); + } + graphic.setPickOnBounds(false); + this.setExpanded("minecraft".equals(basePath)); + this.setValue(""); // To disable the default display of text + this.setGraphic(graphic); + } + + public String getFileName() { + return fileName; + } + + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java index 837bdbcb02..28c31d959e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java @@ -19,7 +19,6 @@ import org.jackhuang.hmcl.util.Lang; -import java.io.File; import java.util.List; /** @@ -74,7 +73,7 @@ enum ModSuggestion { "optionsof.txt" /* OptiFine */, "journeymap" /* JourneyMap */, "optionsshaders.txt", - "mods" + File.separator + "VoxelMods"); + "mods/VoxelMods"); static ModAdviser.ModSuggestion suggestMod(String fileName, boolean isDirectory) { if (match(MODPACK_BLACK_LIST, fileName, isDirectory)) @@ -85,10 +84,11 @@ static ModAdviser.ModSuggestion suggestMod(String fileName, boolean isDirectory) return ModAdviser.ModSuggestion.SUGGESTED; } + /// @param fileName "fileName/" for directories and "fileName" for files, regardless of the operating system static boolean match(List l, String fileName, boolean isDirectory) { for (String s : l) if (isDirectory) { - if (fileName.startsWith(s + File.separator)) + if (fileName.startsWith(s + '/')) return true; } else { if (s.startsWith("regex:")) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java index 07876ebf81..5fe755ee80 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java @@ -121,10 +121,23 @@ public Modpack setManifest(ModpackManifest manifest) { public abstract Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl); + private static boolean match(List l, String fileName) { + for (String s : l) { + if (s.startsWith("regex:")) { + if (fileName.matches(s.substring("regex:".length()))) + return true; + } else { + if (fileName.equals(s)) + return true; + } + } + return false; + } + public static boolean acceptFile(String path, List blackList, List whiteList) { if (path.isEmpty()) return true; - if (ModAdviser.match(blackList, path, false)) + if (match(blackList, path)) return false; if (whiteList == null || whiteList.isEmpty()) return true;