From 5953cc641121c9c91b2ed1a43fee5950721320e9 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Tue, 19 Mar 2019 00:04:52 +0800 Subject: [PATCH] Add filter to hide world that version mismatch --- .../main/java/org/jackhuang/hmcl/ui/SVG.java | 8 ++ .../hmcl/ui/versions/ModListPageSkin.java | 8 +- .../hmcl/ui/versions/WorldListPage.java | 101 +++++++++++++++--- .../hmcl/ui/versions/WorldListPageSkin.java | 99 +++++++++++++++++ HMCL/src/main/resources/assets/css/root.css | 2 + .../resources/assets/lang/I18N.properties | 2 + .../resources/assets/lang/I18N_zh.properties | 2 + .../assets/lang/I18N_zh_CN.properties | 2 + 8 files changed, 205 insertions(+), 19 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPageSkin.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index fdd64a175..9fde2a673 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui; import javafx.beans.binding.ObjectBinding; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Node; @@ -29,6 +30,13 @@ public final class SVG { private SVG() { } + public static Node wrap(Node node) { + StackPane stackPane = new StackPane(); + stackPane.setPadding(new Insets(0, 5, 0, 2)); + stackPane.getChildren().setAll(node); + return stackPane; + } + private static Node createSVGPath(String d, ObjectBinding fill, double width, double height) { SVGPath path = new SVGPath(); path.getStyleClass().add("svg"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index d69c67c23..dc1ad0a04 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -41,17 +41,11 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane; import java.util.function.Function; +import static org.jackhuang.hmcl.ui.SVG.wrap; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class ModListPageSkin extends SkinBase { - private static Node wrap(Node node) { - StackPane stackPane = new StackPane(); - stackPane.setPadding(new Insets(0, 5, 0, 2)); - stackPane.getChildren().setAll(node); - return stackPane; - } - public ModListPageSkin(ModListPage skinnable) { super(skinnable); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java index 53095e6d0..d1cfb0ef2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java @@ -17,7 +17,16 @@ */ package org.jackhuang.hmcl.ui.versions; +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.collections.ObservableList; +import javafx.scene.control.Control; +import javafx.scene.control.Skin; import javafx.stage.FileChooser; +import org.jackhuang.hmcl.game.GameVersion; import org.jackhuang.hmcl.game.World; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; @@ -37,28 +46,60 @@ import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class WorldListPage extends ListPage { +public class WorldListPage extends Control { + private final ListProperty items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList()); + private final BooleanProperty loading = new SimpleBooleanProperty(this, "loading", false); + private final BooleanProperty showAll = new SimpleBooleanProperty(this, "showAll", false); + private Path savesDir; + private List worlds; + private Profile profile; + private String id; + private String gameVersion; public WorldListPage() { FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> { installWorld(modpacks.get(0)); }); - } - public void loadVersion(Profile profile, String id) { - this.savesDir = profile.getRepository().getRunDirectory(id).toPath().resolve("saves"); - - setLoading(true); - Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList())) - .whenComplete(Schedulers.javafx(), (result, isDependentSucceeded, exception) -> { - setLoading(false); - if (isDependentSucceeded) - itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList())); - }).start(); + showAll.addListener(e -> { + if (worlds != null) + itemsProperty().setAll(worlds.stream() + .filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion)) + .map(WorldListItem::new).collect(Collectors.toList())); + }); } @Override + protected Skin createDefaultSkin() { + return new WorldListPageSkin(this); + } + + public void loadVersion(Profile profile, String id) { + this.profile = profile; + this.id = id; + this.savesDir = profile.getRepository().getRunDirectory(id).toPath().resolve("saves"); + refresh(); + } + + public void refresh() { + if (profile == null || id == null) + return; + + setLoading(true); + Task + .of(() -> gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse(null)) + .thenSupply(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList())) + .whenComplete(Schedulers.javafx(), (result, isDependentSucceeded, exception) -> { + worlds = result; + setLoading(false); + if (isDependentSucceeded) + itemsProperty().setAll(result.stream() + .filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion)) + .map(WorldListItem::new).collect(Collectors.toList())); + }).start(); + } + public void add() { FileChooser chooser = new FileChooser(); chooser.setTitle(i18n("world.import.choose")); @@ -91,4 +132,40 @@ public class WorldListPage extends ListPage { Controllers.dialog(i18n("world.import.invalid")); }).start(); } + + public boolean isShowAll() { + return showAll.get(); + } + + public BooleanProperty showAllProperty() { + return showAll; + } + + public void setShowAll(boolean showAll) { + this.showAll.set(showAll); + } + + public ObservableList getItems() { + return items.get(); + } + + public ListProperty itemsProperty() { + return items; + } + + public void setItems(ObservableList items) { + this.items.set(items); + } + + public boolean isLoading() { + return loading.get(); + } + + public BooleanProperty loadingProperty() { + return loading; + } + + public void setLoading(boolean loading) { + this.loading.set(loading); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPageSkin.java new file mode 100644 index 000000000..43dcbe3f0 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPageSkin.java @@ -0,0 +1,99 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2019 huangyuhui 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 . + */ +package org.jackhuang.hmcl.ui.versions; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXCheckBox; +import com.jfoenix.controls.JFXScrollPane; +import com.jfoenix.effects.JFXDepthManager; +import javafx.beans.binding.Bindings; +import javafx.geometry.Insets; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.SkinBase; +import javafx.scene.layout.*; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.construct.SpinnerPane; + +import static org.jackhuang.hmcl.ui.SVG.wrap; +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class WorldListPageSkin extends SkinBase { + + public WorldListPageSkin(WorldListPage skinnable) { + super(skinnable); + + SpinnerPane spinnerPane = new SpinnerPane(); + spinnerPane.getStyleClass().add("large-spinner-pane"); + + BorderPane contentPane = new BorderPane(); + + { + HBox toolbar = new HBox(); + toolbar.getStyleClass().add("jfx-tool-bar-second"); + JFXDepthManager.setDepth(toolbar, 1); + toolbar.setPickOnBounds(false); + + JFXCheckBox chkShowAll = new JFXCheckBox(); + chkShowAll.getStyleClass().add("jfx-tool-bar-checkbox"); + chkShowAll.textFillProperty().bind(Theme.foregroundFillBinding()); + chkShowAll.setText(i18n("world.show_all")); + chkShowAll.selectedProperty().bindBidirectional(skinnable.showAllProperty()); + toolbar.getChildren().add(chkShowAll); + + JFXButton btnRefresh = new JFXButton(); + btnRefresh.getStyleClass().add("jfx-tool-bar-button"); + btnRefresh.textFillProperty().bind(Theme.foregroundFillBinding()); + btnRefresh.setGraphic(wrap(SVG.refresh(Theme.foregroundFillBinding(), -1, -1))); + btnRefresh.setText(i18n("button.refresh")); + btnRefresh.setOnMouseClicked(e -> skinnable.refresh()); + toolbar.getChildren().add(btnRefresh); + + JFXButton btnAdd = new JFXButton(); + btnAdd.getStyleClass().add("jfx-tool-bar-button"); + btnAdd.textFillProperty().bind(Theme.foregroundFillBinding()); + btnAdd.setGraphic(wrap(SVG.plus(Theme.foregroundFillBinding(), -1, -1))); + btnAdd.setText(i18n("world.add")); + btnAdd.setOnMouseClicked(e -> skinnable.add()); + toolbar.getChildren().add(btnAdd); + + contentPane.setTop(toolbar); + } + + { + ScrollPane scrollPane = new ScrollPane(); + scrollPane.setFitToWidth(true); + + VBox content = new VBox(); + content.setSpacing(10); + content.setPadding(new Insets(10)); + + Bindings.bindContent(content.getChildren(), skinnable.itemsProperty()); + + scrollPane.setContent(content); + JFXScrollPane.smoothScrolling(scrollPane); + + contentPane.setCenter(scrollPane); + } + + spinnerPane.loadingProperty().bind(skinnable.loadingProperty()); + spinnerPane.setContent(contentPane); + + getChildren().setAll(spinnerPane); + } +} diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index d3ac305c8..15168e2bc 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -382,6 +382,8 @@ -fx-pref-height: 42; -fx-padding: 2 2 2 2; -fx-background-color: -fx-base-check-color; + -fx-alignment: CENTER-LEFT; + -fx-spacing: 8; } .jfx-tool-bar-second .jfx-rippler { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0589d09d6..f45977fff 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -271,6 +271,7 @@ datapack.title=World %s - Datapacks datapack.remove=Remove world=Worlds/Datapacks +world.add=Add world(.zip) world.datapack=Manage data packs world.datapack.1_13=Only Minecraft 1.13 and later versions support data packs. world.description=%s. Last played time: %s. Game version: %s. @@ -287,6 +288,7 @@ world.import.invalid=Not a valid world zip world.name=World Name world.name.enter=Enter the world name world.reveal=Reveal in Explorer +world.show_all=Show all world.time=EEE, MMM d, yyyy HH:mm:ss profile=Game Directories diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 08f064d89..4746d4dc2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -269,6 +269,7 @@ datapack.title=世界 %s - 資料包 datapack.remove=刪除 world=世界/資料包 +world.add=添加世界 world.datapack=管理資料包 world.datapack.1_13=僅 Minecraft 1.13 及之後的版本支持資料包 world.description=%s. 上一次遊戲時間: %s. 遊戲版本: %s @@ -285,6 +286,7 @@ world.game_version=遊戲版本 world.name=世界名稱 world.name.enter=輸入世界名稱 world.reveal=打開資料夾 +world.show_all=顯示全部 world.time=yyyy 年 MM 月 dd 日 HH:mm:ss profile=遊戲目錄 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 5fcbfe4e6..afb5e958f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -269,6 +269,7 @@ datapack.title=世界 %s - 数据包 datapack.remove=删除 world=世界/数据包 +world.add=添加世界 world.datapack=管理数据包 world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包 world.description=%s. 上一次游戏时间: %s. 游戏版本: %s @@ -285,6 +286,7 @@ world.import.invalid=无法识别的存档压缩包 world.name=世界名称 world.name.enter=输入世界名称 world.reveal=打开文件夹 +world.show_all=显示全部 world.time=yyyy 年 MM 月 dd 日 HH:mm:ss profile=游戏目录