From f9eca35422e408f94ad540a6c4d890c29a23e2b6 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Feb 2023 07:56:43 +0800 Subject: [PATCH] update --- .../hmcl/ui/versions/ModListPageSkin.java | 110 ++++++++++++++---- 1 file changed, 85 insertions(+), 25 deletions(-) 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 32962f953..7c4daaff8 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 @@ -23,6 +23,7 @@ import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.SelectionMode; import javafx.scene.control.SkinBase; @@ -57,18 +58,36 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.Locale; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2; -import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.wrap; import static org.jackhuang.hmcl.util.Lang.mapOf; +import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.StringUtils.isNotBlank; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; class ModListPageSkin extends SkinBase { + // FXThread + private ThreadPoolExecutor searchPool; + + private final TransitionPane toolbarPane; + private final HBox searchBar; + private final HBox toolbarNormal; + private final HBox toolbarSelecting; + + private final JFXListView listView; + private final JFXTextField searchField; + + // FXThread + private boolean isSearching = false; + ModListPageSkin(ModListPage skinnable) { super(skinnable); @@ -78,30 +97,27 @@ class ModListPageSkin extends SkinBase { ComponentList root = new ComponentList(); root.getStyleClass().add("no-padding"); - JFXListView listView = new JFXListView<>(); + listView = new JFXListView<>(); { - TransitionPane toolBarPane = new TransitionPane(); + toolbarPane = new TransitionPane(); - HBox searchBar = new HBox(); - HBox toolbarNormal = new HBox(); - HBox toolbarSelecting = new HBox(); + searchBar = new HBox(); + toolbarNormal = new HBox(); + toolbarSelecting = new HBox(); // Search Bar searchBar.setAlignment(Pos.CENTER); searchBar.setPadding(new Insets(0, 5, 0, 5)); - - JFXTextField searchField = new JFXTextField(); + searchField = new JFXTextField(); searchField.setPromptText(i18n("search")); HBox.setHgrow(searchField, Priority.ALWAYS); + searchField.setOnAction(e -> search()); - JFXButton cancel = createToolbarButton2(null, SVG::close, - () -> { - searchField.clear(); - toolBarPane.setContent(toolbarNormal, ContainerAnimations.FADE.getAnimationProducer()); - }); + JFXButton closeSearchBar = createToolbarButton2(null, SVG::close, + () -> changeToolbar(toolbarNormal)); - searchBar.getChildren().setAll(searchField, cancel); + searchBar.getChildren().setAll(searchField, closeSearchBar); // Toolbar Normal toolbarNormal.getChildren().setAll( @@ -110,8 +126,7 @@ class ModListPageSkin extends SkinBase { createToolbarButton2(i18n("folder.mod"), SVG::folderOpen, skinnable::openModFolder), createToolbarButton2(i18n("mods.check_updates"), SVG::update, skinnable::checkUpdates), createToolbarButton2(i18n("download"), SVG::downloadOutline, skinnable::download), - createToolbarButton2(i18n("search"), SVG::magnify, - () -> toolBarPane.setContent(searchBar, ContainerAnimations.FADE.getAnimationProducer())) + createToolbarButton2(i18n("search"), SVG::magnify, () -> changeToolbar(searchBar)) ); // Toolbar Selecting @@ -131,14 +146,14 @@ class ModListPageSkin extends SkinBase { listView.getSelectionModel().clearSelection()) ); - FXUtils.onChangeAndOperate(listView.getSelectionModel().selectedItemProperty(), selectedItem -> { - if (selectedItem == null) { - toolBarPane.setContent(toolbarNormal, ContainerAnimations.FADE.getAnimationProducer()); - } else { - toolBarPane.setContent(toolbarSelecting, ContainerAnimations.FADE.getAnimationProducer()); - } - }); - root.getContent().add(toolBarPane); + FXUtils.onChangeAndOperate(listView.getSelectionModel().selectedItemProperty(), + selectedItem -> { + if (selectedItem == null) + changeToolbar(isSearching ? searchBar : toolbarNormal); + else + changeToolbar(toolbarSelecting); + }); + root.getContent().add(toolbarPane); } { @@ -160,7 +175,6 @@ class ModListPageSkin extends SkinBase { label.prefWidthProperty().bind(pane.widthProperty().add(-100)); FXUtils.onChangeAndOperate(skinnable.moddedProperty(), modded -> { - if (modded) pane.getChildren().setAll(root); else pane.getChildren().setAll(label); }); @@ -168,6 +182,52 @@ class ModListPageSkin extends SkinBase { getChildren().setAll(pane); } + private void changeToolbar(HBox newToolbar) { + Node oldToolbar = toolbarPane.getCurrentNode(); + if (newToolbar != oldToolbar) { + toolbarPane.setContent(newToolbar, ContainerAnimations.FADE.getAnimationProducer()); + if (isSearching && newToolbar == toolbarNormal) { + isSearching = false; + searchField.clear(); + Bindings.bindContent(listView.getItems(), getSkinnable().getItems()); + } + } + } + + private void search() { + isSearching = true; + + Bindings.unbindContent(listView.getItems(), getSkinnable().getItems()); + + String queryString = searchField.getText(); + if (StringUtils.isBlank(queryString)) { + listView.getItems().setAll(getSkinnable().getItems()); + } else { + listView.getItems().clear(); + + Predicate predicate; + if (queryString.startsWith("regex:")) { + try { + Pattern pattern = Pattern.compile(queryString.substring("regex:".length())); + predicate = s -> pattern.matcher(s).find(); + } catch (Throwable e) { + LOG.log(Level.WARNING, "Illegal regular expression", e); + return; + } + } else { + String lowerQueryString = queryString.toLowerCase(Locale.ROOT); + predicate = s -> s.toLowerCase(Locale.ROOT).contains(lowerQueryString); + } + + // Do we need to search in the background thread? + for (ModInfoObject item : getSkinnable().getItems()) { + if (predicate.test(item.getModInfo().getFileName())) { + listView.getItems().add(item); + } + } + } + } + static class ModInfoObject extends RecursiveTreeObject implements Comparable { private final BooleanProperty active; private final LocalModFile localModFile;