-
Notifications
You must be signed in to change notification settings - Fork 780
feat: 资源包管理 #4475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: 资源包管理 #4475
Changes from 21 commits
92d175b
b99520d
3866eb1
69510ac
2f71a16
cad53a1
1d4fe22
0d1c22e
aaab964
553b499
e042b8f
1a56e85
8d5708b
ff58841
7857ad4
416e07f
e5ed6d0
f540134
0aa3097
6028071
45f243d
9f8999d
9506910
2e84869
d6cc5f9
66ff6b4
c9dc5f9
f358c17
d8c589a
67c0ef6
05ed1a8
b84b2ad
0d63ba2
53ffe6f
4a476dc
4288254
3e64bb6
dcdbcb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,229 @@ | ||||
| package org.jackhuang.hmcl.ui.versions; | ||||
|
|
||||
| import com.jfoenix.controls.JFXButton; | ||||
| import javafx.geometry.Insets; | ||||
| import javafx.geometry.Pos; | ||||
| import javafx.scene.Node; | ||||
| import javafx.scene.control.Control; | ||||
| import javafx.scene.control.Skin; | ||||
| import javafx.scene.control.SkinBase; | ||||
| import javafx.scene.image.ImageView; | ||||
| import javafx.scene.layout.BorderPane; | ||||
| import javafx.scene.layout.HBox; | ||||
| import javafx.stage.FileChooser; | ||||
| import org.jackhuang.hmcl.resourcepack.ResourcepackFile; | ||||
| import org.jackhuang.hmcl.setting.Profile; | ||||
| import org.jackhuang.hmcl.setting.Theme; | ||||
| import org.jackhuang.hmcl.task.Schedulers; | ||||
| import org.jackhuang.hmcl.task.Task; | ||||
| import org.jackhuang.hmcl.ui.*; | ||||
| import org.jackhuang.hmcl.ui.construct.MessageDialogPane; | ||||
| import org.jackhuang.hmcl.ui.construct.RipplerContainer; | ||||
| import org.jackhuang.hmcl.ui.construct.TwoLineListItem; | ||||
| import org.jackhuang.hmcl.util.io.FileUtils; | ||||
|
|
||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Arrays; | ||||
| import java.util.Comparator; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.stream.Stream; | ||||
|
|
||||
| import static org.jackhuang.hmcl.util.i18n.I18n.i18n; | ||||
| import static org.jackhuang.hmcl.util.logging.Logger.LOG; | ||||
|
|
||||
| public final class ResourcepackListPage extends ListPageBase<ResourcepackListPage.ResourcepackItem> implements VersionPage.VersionLoadable { | ||||
| private Path resourcepackDirectory; | ||||
|
|
||||
| public ResourcepackListPage() { | ||||
| FXUtils.applyDragListener(this, file -> file.getFileName().toString().endsWith(".zip"), this::addFiles); | ||||
| } | ||||
|
|
||||
| private static Node createIcon(Path img) { | ||||
| ImageView imageView = new ImageView(); | ||||
| FXUtils.limitSize(imageView, 32, 32); | ||||
|
|
||||
| if (img != null && Files.exists(img)) { | ||||
| try { | ||||
| imageView.setImage(FXUtils.loadImage(img, 32, 32, true, true)); | ||||
| } catch (Exception e) { | ||||
| LOG.warning("Failed to load image " + img, e); | ||||
| } | ||||
| } | ||||
|
|
||||
| if (imageView.getImage() == null) { | ||||
| imageView.setImage(FXUtils.newBuiltinImage("/assets/img/unknown_pack.png")); | ||||
| } | ||||
|
|
||||
| return imageView; | ||||
| } | ||||
|
|
||||
| @Override | ||||
| protected Skin<?> createDefaultSkin() { | ||||
| return new ResourcepackListPageSkin(this); | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public void loadVersion(Profile profile, String version) { | ||||
| this.resourcepackDirectory = profile.getRepository().getResourcepacksDirectory(version); | ||||
|
|
||||
| try { | ||||
| if (!Files.exists(resourcepackDirectory)) { | ||||
| Files.createDirectories(resourcepackDirectory); | ||||
| } | ||||
| } catch (IOException e) { | ||||
| LOG.warning("Failed to create resourcepack directory" + resourcepackDirectory, e); | ||||
| } | ||||
| refresh(); | ||||
| } | ||||
|
|
||||
| public void refresh() { | ||||
| if (resourcepackDirectory == null || !Files.isDirectory(resourcepackDirectory)) return; | ||||
| setLoading(true); | ||||
| Task.supplyAsync(Schedulers.io(), () -> { | ||||
| try (Stream<Path> stream = Files.list(resourcepackDirectory)) { | ||||
| return stream.sorted(Comparator.comparing(FileUtils::getName)) | ||||
| .flatMap(item -> { | ||||
| try { | ||||
| return Stream.of(ResourcepackFile.parse(item)).filter(Objects::nonNull).map(ResourcepackItem::new); | ||||
| } catch (IOException e) { | ||||
| LOG.warning("Failed to load resourcepack " + item, e); | ||||
| return Stream.empty(); | ||||
| } | ||||
| }) | ||||
| .toList(); | ||||
| } | ||||
| }).whenComplete(Schedulers.javafx(), ((result, exception) -> { | ||||
| if (exception == null) { | ||||
| itemsProperty().setAll(result); | ||||
| } else { | ||||
| LOG.warning("Failed to load resourcepacks", exception); | ||||
| } | ||||
| setLoading(false); | ||||
| })).start(); | ||||
| } | ||||
|
|
||||
| public void addFiles(List<Path> files) { | ||||
| if (resourcepackDirectory == null) return; | ||||
|
|
||||
| try { | ||||
| for (Path file : files) { | ||||
| Path target = resourcepackDirectory.resolve(file.getFileName()); | ||||
| if (!Files.exists(target)) { | ||||
| Files.copy(file, target); | ||||
| } | ||||
| } | ||||
CiiLu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
| } catch (IOException e) { | ||||
| LOG.warning("Failed to add resourcepacks", e); | ||||
| Controllers.dialog(i18n("resourcepack.add.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); | ||||
| } | ||||
|
|
||||
| refresh(); | ||||
| } | ||||
|
|
||||
| public void onAddFiles() { | ||||
| FileChooser fileChooser = new FileChooser(); | ||||
| fileChooser.setTitle(i18n("resourcepack.add")); | ||||
| fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("resourcepack"), "*.zip")); | ||||
| List<File> files = fileChooser.showOpenMultipleDialog(Controllers.getStage()); | ||||
| if (files != null && !files.isEmpty()) { | ||||
| addFiles(FileUtils.toPaths(files)); | ||||
| } | ||||
| } | ||||
|
|
||||
| private void onDownload() { | ||||
| Controllers.getDownloadPage().showResourcepackDownloads(); | ||||
| Controllers.navigate(Controllers.getDownloadPage()); | ||||
| } | ||||
|
|
||||
| private static class ResourcepackListPageSkin extends ToolbarListPageSkin<ResourcepackListPage> { | ||||
| protected ResourcepackListPageSkin(ResourcepackListPage control) { | ||||
| super(control); | ||||
| } | ||||
|
|
||||
| @Override | ||||
| protected List<Node> initializeToolbar(ResourcepackListPage skinnable) { | ||||
| return Arrays.asList(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("resourcepack.add"), SVG.ADD, skinnable::onAddFiles), createToolbarButton2(i18n("resourcepack.download"), SVG.DOWNLOAD, skinnable::onDownload)); | ||||
| } | ||||
| } | ||||
|
|
||||
| public class ResourcepackItem extends Control { | ||||
CiiLu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| private final ResourcepackFile file; | ||||
| // final JFXCheckBox checkBox = new JFXCheckBox(); | ||||
|
|
||||
| public ResourcepackItem(ResourcepackFile file) { | ||||
| this.file = file; | ||||
| } | ||||
|
|
||||
| @Override | ||||
| protected Skin<?> createDefaultSkin() { | ||||
| return new ResourcepackItemSkin(this); | ||||
| } | ||||
|
|
||||
| public void onDelete() { | ||||
| try { | ||||
| if (Files.isDirectory(file.getPath())) { | ||||
| FileUtils.deleteDirectory(file.getPath()); | ||||
| } else { | ||||
| Files.delete(file.getPath()); | ||||
| } | ||||
| ResourcepackListPage.this.refresh(); | ||||
| } catch (IOException e) { | ||||
| Controllers.dialog(i18n("resourcepack.delete.failed", e.getMessage()), i18n("message.error"), MessageDialogPane.MessageType.ERROR); | ||||
| LOG.warning("Failed to delete resourcepack", e); | ||||
| } | ||||
| } | ||||
|
|
||||
| public void onReveal() { | ||||
| FXUtils.showFileInExplorer(file.getPath()); | ||||
| } | ||||
|
|
||||
| public ResourcepackFile getFile() { | ||||
| return file; | ||||
| } | ||||
| } | ||||
|
|
||||
| private final class ResourcepackItemSkin extends SkinBase<ResourcepackItem> { | ||||
| public ResourcepackItemSkin(ResourcepackItem item) { | ||||
| super(item); | ||||
| BorderPane root = new BorderPane(); | ||||
| root.getStyleClass().add("md-list-cell"); | ||||
| root.setPadding(new Insets(8)); | ||||
|
|
||||
| HBox left = new HBox(8); | ||||
| left.setAlignment(Pos.CENTER); | ||||
| left.getChildren().addAll(createIcon(item.getFile().getIcon())); | ||||
| // left.getChildren().addAll(item.checkBox, createIcon(item.getFile().getIcon())); | ||||
| left.setPadding(new Insets(0, 8, 0, 0)); | ||||
| // FXUtils.setLimitWidth(left, 64); | ||||
|
||||
| FXUtils.setLimitWidth(left, 48); | ||||
| root.setLeft(left); | ||||
|
|
||||
| TwoLineListItem center = new TwoLineListItem(); | ||||
| // center.setPadding(new Insets(0, 0, 0, 8)); | ||||
|
||||
| // center.setPadding(new Insets(0, 0, 0, 8)); |
Uh oh!
There was an error while loading. Please reload this page.