From 6a7db516635ad2b26805ae0a0acd3c3fcefa2389 Mon Sep 17 00:00:00 2001 From: Jafea7 Date: Mon, 2 Jun 2025 17:29:32 +1000 Subject: [PATCH] Add search to Ignore list --- .../java/ctbrec/ui/settings/IgnoreList.java | 87 +++++++++++++++++-- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/settings/IgnoreList.java b/client/src/main/java/ctbrec/ui/settings/IgnoreList.java index e8445c26..233ff4c9 100644 --- a/client/src/main/java/ctbrec/ui/settings/IgnoreList.java +++ b/client/src/main/java/ctbrec/ui/settings/IgnoreList.java @@ -6,9 +6,13 @@ import ctbrec.Config; import ctbrec.io.json.ObjectMapperFactory; import ctbrec.ui.AutosizeAlert; import ctbrec.ui.controls.Dialogs; +import javafx.beans.binding.Bindings; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; import javafx.geometry.Insets; -import javafx.scene.control.Alert.AlertType; import javafx.scene.control.*; +import javafx.scene.control.Alert.AlertType; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.GridPane; @@ -31,7 +35,9 @@ import static javafx.scene.control.ButtonType.YES; public class IgnoreList extends GridPane { private ListView ignoreListView; - + private TextField searchField; + private ObservableList masterList; + private FilteredList filteredList; private final ObjectMapper mapper = ObjectMapperFactory.getMapper(); public IgnoreList() { @@ -43,7 +49,12 @@ public class IgnoreList extends GridPane { setHgap(10); setVgap(10); setPadding(new Insets(20, 10, 10, 10)); + // Create search field with clear button + HBox searchBox = createSearchField(); + add(searchBox, 0, 0); + GridPane.setHgrow(searchBox, Priority.ALWAYS); + // Initialize ListView ignoreListView = new ListView<>(); ignoreListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); ignoreListView.addEventHandler(KeyEvent.KEY_PRESSED, event -> { @@ -51,9 +62,28 @@ public class IgnoreList extends GridPane { removeSelectedModels(); } }); - add(ignoreListView, 0, 0); + // Set custom cell factory for highlighting + ignoreListView.setCellFactory(listView -> new ListCell<>() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setText(null); + setStyle(""); + } else { + setText(item); + // Highlight if the item matches the search text + String searchText = searchField.getText().trim().toLowerCase(); + if (searchText.isEmpty() || !item.toLowerCase().contains(searchText)) { + setStyle(""); + } + } + } + }); + add(ignoreListView, 0, 1); GridPane.setHgrow(ignoreListView, Priority.ALWAYS); + // Buttons var remove = new Button("Remove"); remove.setOnAction(evt -> removeSelectedModels()); var exportIgnoreList = new Button("Export"); @@ -65,12 +95,52 @@ public class IgnoreList extends GridPane { buttons.setStyle("-fx-background-color: -fx-background"); // workaround so that the buttons don't shrink } + private HBox createSearchField() { + HBox searchBox = new HBox(5); + searchField = new TextField(); + searchField.setPromptText("Search ignore list..."); + HBox.setHgrow(searchField, Priority.ALWAYS); + + // Create clear button + Button clearButton = new Button("✖"); + clearButton.setStyle("-fx-font-size: 12px; -fx-padding: 2px 5px; -fx-background-radius: 50%;"); + clearButton.setVisible(false); // Initially hidden + clearButton.setOnAction(event -> { + searchField.clear(); + searchField.requestFocus(); // Return focus to the TextField + }); + + // Bind clear button visibility to text presence + clearButton.visibleProperty().bind( + Bindings.createBooleanBinding( + () -> !searchField.getText().trim().isEmpty(), + searchField.textProperty() + ) + ); + + // Bind the filter to the search field text property + searchField.textProperty().addListener((obs, oldValue, newValue) -> { + filteredList.setPredicate(item -> { + if (newValue == null || newValue.trim().isEmpty()) { + return true; // Show all items if search is empty + } + return item.toLowerCase().contains(newValue.trim().toLowerCase()); + }); + // Force ListView to refresh to update highlighting + ignoreListView.refresh(); + }); + + searchBox.getChildren().addAll(searchField, clearButton); + searchBox.setAlignment(javafx.geometry.Pos.CENTER_LEFT); + return searchBox; + } + + private void removeSelectedModels() { List selectedModels = ignoreListView.getSelectionModel().getSelectedItems(); if (!selectedModels.isEmpty()) { Config.getInstance().getSettings().ignoredModels.removeAll(selectedModels); - ignoreListView.getItems().removeAll(selectedModels); - log.debug(Config.getInstance().getSettings().ignoredModels.toString()); + masterList.removeAll(selectedModels); // Update master list try { Config.getInstance().save(); } catch (IOException e) { @@ -81,9 +151,10 @@ public class IgnoreList extends GridPane { private void loadIgnoredModels() { List ignored = Config.getInstance().getSettings().ignoredModels; - ignoreListView.getItems().clear(); - ignoreListView.getItems().addAll(ignored); - Collections.sort(ignoreListView.getItems()); + masterList = FXCollections.observableArrayList(ignored); + Collections.sort(masterList); + filteredList = new FilteredList<>(masterList, p -> true); + ignoreListView.setItems(filteredList); } public void refresh() {