diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/HistoryManagerApp.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/HistoryManagerApp.java index d0f3d221..75564759 100644 --- a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/HistoryManagerApp.java +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/HistoryManagerApp.java @@ -53,10 +53,21 @@ public void start(Stage primaryStage) throws Exception { private Node basicDemo() { TextField textField = new TextField(); + + HistoryButton historyButton = new HistoryButton<>(textField); + + // Tips: We can set the delimiter and preferencesKey when creating, otherwise use the default value. + // StringHistoryManager historyManager = new StringHistoryManager(";", "history-records", + // Preferences.userNodeForPackage(HistoryManagerApp.class).node("simpleTextField")); + StringHistoryManager historyManager = new StringHistoryManager(); + + // Tips: If we want to persist the history after the application restarts, we need to set the preferences. // historyManager.setPreferences(Preferences.userNodeForPackage(HistoryManagerApp.class).node("simpleTextField")); - HistoryButton historyButton = new HistoryButton<>(textField, historyManager); + // Tips: If we want to enable the history function, we need to set the history manager. + historyButton.setHistoryManager(historyManager); + historyButton.setConfigureHistoryPopup(historyPopup -> { // When choosing a history item, replace the text in the text field. historyPopup.setOnHistoryItemConfirmed(item -> { @@ -108,7 +119,8 @@ private Node advancedDemo() { historyManager.set(List.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten")); } - HistoryButton historyButton = new HistoryButton<>(textField, historyManager); + HistoryButton historyButton = new HistoryButton<>(textField); + historyButton.setHistoryManager(historyManager); // add history item to the history when the enter key is pressed. textField.setOnKeyPressed(e -> { @@ -214,7 +226,8 @@ public Student fromString(String string) { historyManager.setPreferences(Preferences.userNodeForPackage(HistoryManagerApp.class).node("list")); - HistoryButton historyButton = new HistoryButton<>(null, historyManager); + HistoryButton historyButton = new HistoryButton<>(null); + historyButton.setHistoryManager(historyManager); historyButton.setText("History"); historyButton.setConfigureHistoryPopup(historyPopup -> { diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchFieldApp.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchFieldApp.java index f8ba5f02..8a57cb96 100644 --- a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchFieldApp.java +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchFieldApp.java @@ -1,6 +1,8 @@ package com.dlsc.gemsfx.demo; import com.dlsc.gemsfx.SearchField; +import com.dlsc.gemsfx.util.HistoryManager; +import com.dlsc.gemsfx.util.StringHistoryManager; import fr.brouillard.oss.cssfx.CSSFX; import javafx.application.Application; import javafx.beans.binding.Bindings; @@ -8,6 +10,7 @@ import javafx.scene.Scene; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; +import javafx.scene.control.Separator; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; @@ -21,8 +24,15 @@ import java.util.prefs.Preferences; import java.util.stream.Collectors; +/** + * This demo shows how to use the {@link SearchField} control. + *

+ * About the HistoryManager, you can refer to: {@link HistoryManager} {@link SearchTextFieldApp}, {@link HistoryManagerApp} + */ public class SearchFieldApp extends Application { + private StringHistoryManager historyManager; + private final List countries = new ArrayList<>(); @Override @@ -81,10 +91,45 @@ public void start(Stage primaryStage) throws Exception { CheckBox autoCommitOnFocusLostBox = new CheckBox("Auto commit on field lost focus."); autoCommitOnFocusLostBox.selectedProperty().bindBidirectional(field.autoCommitOnFocusLostProperty()); + CheckBox enableHistoryBox = new CheckBox("Enable History"); + enableHistoryBox.selectedProperty().addListener((obs, oldVal, newVal) -> { + if (newVal) { + if (field.getHistoryManager() == null) { + historyManager = new StringHistoryManager(Preferences.userNodeForPackage(SearchFieldApp.class).node("field")); + // Optional: Set the maximum history size. default is 30. + historyManager.setMaxHistorySize(20); + // Optional: If the history items is empty, we can set a default history list. + if (historyManager.getAll().isEmpty()) { + historyManager.set(List.of("United Kingdom", "Switzerland")); + } + } + // If the history manager is not null, the search field will have a history feature. + field.setHistoryManager(historyManager); + } else { + // If the history manager is null, the search field will not have a history feature. + field.setHistoryManager(null); + } + primaryStage.sizeToScene(); + }); + enableHistoryBox.setSelected(true); + + CheckBox addHistoryOnActionBox = new CheckBox("Add History on Enter"); + addHistoryOnActionBox.setSelected(true); + field.addingItemToHistoryOnEnterProperty().bind(addHistoryOnActionBox.selectedProperty()); + + CheckBox addHistoryOnFocusLossBox = new CheckBox("Add History on Focus Loss"); + addHistoryOnFocusLossBox.setSelected(true); + field.addingItemToHistoryOnFocusLostProperty().bind(addHistoryOnFocusLossBox.selectedProperty()); + + VBox historyControls = new VBox(10, new Separator(), addHistoryOnActionBox, addHistoryOnFocusLossBox); + historyControls.managedProperty().bind(enableHistoryBox.selectedProperty()); + historyControls.visibleProperty().bind(enableHistoryBox.selectedProperty()); + field.leftProperty().bind(Bindings.createObjectBinding(() -> showLeftRightNodes.isSelected() ? regionLeft : null, showLeftRightNodes.selectedProperty())); field.rightProperty().bind(Bindings.createObjectBinding(() -> showLeftRightNodes.isSelected() ? regionRight : null, showLeftRightNodes.selectedProperty())); - VBox vbox = new VBox(20, createNewItemBox, showPromptText, usePlaceholder, hideWithSingleChoiceBox, hideWithNoChoiceBox, showSearchIconBox, showLeftRightNodes, autoCommitOnFocusLostBox, hBox, hBox2, field); + VBox vbox = new VBox(20, createNewItemBox, showPromptText, usePlaceholder, hideWithSingleChoiceBox, hideWithNoChoiceBox, showSearchIconBox, showLeftRightNodes, + autoCommitOnFocusLostBox, hBox, hBox2, enableHistoryBox, historyControls, field); vbox.setPadding(new Insets(20)); Scene scene = new Scene(vbox); @@ -123,8 +168,8 @@ public Country fromString(String string) { setMatcher((broker, searchText) -> broker.getName().toLowerCase().startsWith(searchText.toLowerCase())); setComparator(Comparator.comparing(Country::getName)); getEditor().setPromptText("Start typing country name ..."); - // If not setPreferences() history records are only stored temporarily in memory and are not persisted locally. - getHistoryManager().setPreferences(Preferences.userNodeForPackage(SearchFieldApp.class).node("field")); + // Tips: If we don't set a HistoryManager, the search field will not have a history feature. + // setHistoryManager(new StringHistoryManager(Preferences.userNodeForPackage(SearchFieldApp.class).node("field"))); } } diff --git a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchTextFieldApp.java b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchTextFieldApp.java index 1487cb9e..c57bac2c 100644 --- a/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchTextFieldApp.java +++ b/gemsfx-demo/src/main/java/com/dlsc/gemsfx/demo/SearchTextFieldApp.java @@ -1,9 +1,10 @@ package com.dlsc.gemsfx.demo; import com.dlsc.gemsfx.SearchTextField; +import com.dlsc.gemsfx.Spacer; +import com.dlsc.gemsfx.util.HistoryManager; import com.dlsc.gemsfx.util.StringHistoryManager; import javafx.application.Application; -import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; @@ -18,72 +19,85 @@ import java.time.LocalTime; import java.util.List; +import java.util.Optional; import java.util.prefs.Preferences; public class SearchTextFieldApp extends Application { + private StringHistoryManager stringHistoryManager; + @Override public void start(Stage primaryStage) throws Exception { - SearchTextField field1 = new SearchTextField(); - StringHistoryManager historyManager1 = field1.getHistoryManager(); - historyManager1.setPreferences(Preferences.userNodeForPackage(SearchTextFieldApp.class).node("field1")); - - SearchTextField field2 = new SearchTextField(true); - StringHistoryManager historyManager2 = field2.getHistoryManager(); - historyManager2.setPreferences(Preferences.userNodeForPackage(SearchTextFieldApp.class).node("field2")); + SearchTextField field = new SearchTextField(); + + CheckBox roundBox = new CheckBox("Round"); + field.roundProperty().bind(roundBox.selectedProperty()); + + CheckBox enableHistoryBox = new CheckBox("Enable History"); + enableHistoryBox.selectedProperty().addListener((obs, oldVal, newVal) -> { + if (newVal) { + if (stringHistoryManager == null) { + Preferences preferences = Preferences.userNodeForPackage(SearchTextFieldApp.class).node("field1"); + stringHistoryManager = new StringHistoryManager(preferences); + } + field.setHistoryManager(stringHistoryManager); + } else { + field.setHistoryManager(null); + } + primaryStage.sizeToScene(); + }); + enableHistoryBox.setSelected(true); Label label = new Label("Max History Size:"); - Spinner maxHistorySizeSpinner = new Spinner<>(5, 50, 10, 5); - historyManager1.maxHistorySizeProperty().bind(maxHistorySizeSpinner.valueProperty()); - historyManager2.maxHistorySizeProperty().bind(maxHistorySizeSpinner.valueProperty()); - maxHistorySizeSpinner.setMaxWidth(Double.MAX_VALUE); - HBox maxHistorySizeBox = new HBox(5, label, maxHistorySizeSpinner); - maxHistorySizeBox.setAlignment(Pos.CENTER_LEFT); + Spinner maxHistorySizeSpinner = new Spinner<>(5, 50, 30, 5); + maxHistorySizeSpinner.valueProperty().addListener((obs, oldVal, newVal) -> { + HistoryManager historyManager = field.getHistoryManager(); + if (newVal != null && historyManager != null) { + historyManager.setMaxHistorySize(newVal); + } + }); - CheckBox enableHistoryPopupBox = new CheckBox("Enable History Popup"); - enableHistoryPopupBox.setSelected(true); - field1.enableHistoryPopupProperty().bindBidirectional(enableHistoryPopupBox.selectedProperty()); - field2.enableHistoryPopupProperty().bindBidirectional(enableHistoryPopupBox.selectedProperty()); + maxHistorySizeSpinner.setMaxWidth(140); + HBox maxHistorySizeBox = new HBox(5, label, new Spacer(), maxHistorySizeSpinner); + maxHistorySizeBox.setAlignment(Pos.CENTER_LEFT); CheckBox addHistoryOnActionBox = new CheckBox("Add History on Enter"); addHistoryOnActionBox.setSelected(true); - field1.addingItemToHistoryOnEnterProperty().bind(addHistoryOnActionBox.selectedProperty()); - field2.addingItemToHistoryOnEnterProperty().bind(addHistoryOnActionBox.selectedProperty()); + field.addingItemToHistoryOnEnterProperty().bind(addHistoryOnActionBox.selectedProperty()); CheckBox addHistoryOnFocusLossBox = new CheckBox("Add History on Focus Loss"); addHistoryOnFocusLossBox.setSelected(true); - field1.addingItemToHistoryOnFocusLostProperty().bind(addHistoryOnFocusLossBox.selectedProperty()); - field2.addingItemToHistoryOnFocusLostProperty().bind(addHistoryOnFocusLossBox.selectedProperty()); + field.addingItemToHistoryOnFocusLostProperty().bind(addHistoryOnFocusLossBox.selectedProperty()); Button setHistoryButton = new Button("Set History"); setHistoryButton.setMaxWidth(Double.MAX_VALUE); setHistoryButton.setOnAction(e -> { List list = List.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"); - historyManager1.set(list); - historyManager2.set(list); + Optional.ofNullable(field.getHistoryManager()).ifPresent(historyManager -> { + historyManager.set(list); + System.out.println("History set to: " + list); + }); }); Button addHistoryButton = new Button("Add History"); addHistoryButton.setMaxWidth(Double.MAX_VALUE); - addHistoryButton.setOnAction(e -> { - historyManager1.add("New " + LocalTime.now()); - historyManager2.add("New" + LocalTime.now()); - }); + addHistoryButton.setOnAction(e -> Optional.ofNullable(field.getHistoryManager()).ifPresent(historyManager -> historyManager.add("New " + LocalTime.now()))); - Button removeStandardHistoryButton = createRemoveHistoryButton("Standard Field Remove First History Item", historyManager1); - Button removeRoundHistoryButton = createRemoveHistoryButton("Round Field Remove First History Item", historyManager2); + Button removeHistoryButton = new Button("Remove First History Item"); + removeHistoryButton.setMaxWidth(Double.MAX_VALUE); + removeHistoryButton.setOnAction(e -> Optional.ofNullable(field.getHistoryManager()).ifPresent(historyManager -> historyManager.remove(historyManager.getAll().get(0)))); Button clearButton = new Button("Clear History"); clearButton.setMaxWidth(Double.MAX_VALUE); - clearButton.setOnAction(e -> { - historyManager1.clear(); - historyManager2.clear(); - }); + clearButton.setOnAction(e -> Optional.ofNullable(field.getHistoryManager()).ifPresent(HistoryManager::clear)); + + VBox historyControls = new VBox(5, new Separator(), maxHistorySizeBox, addHistoryOnActionBox, addHistoryOnFocusLossBox, + setHistoryButton, addHistoryButton, clearButton); + historyControls.managedProperty().bind(enableHistoryBox.selectedProperty()); + historyControls.visibleProperty().bind(enableHistoryBox.selectedProperty()); - VBox vbox = new VBox(20, new Label("Standard"), field1, new Label("Round"), field2, - new Separator(), maxHistorySizeBox, enableHistoryPopupBox, addHistoryOnActionBox, addHistoryOnFocusLossBox, - setHistoryButton, addHistoryButton, removeStandardHistoryButton, removeRoundHistoryButton, clearButton); + VBox vbox = new VBox(20, new Label("Standard"), field, roundBox, enableHistoryBox, historyControls); vbox.setPadding(new Insets(20)); Scene scene = new Scene(vbox); @@ -94,14 +108,6 @@ public void start(Stage primaryStage) throws Exception { primaryStage.show(); } - private Button createRemoveHistoryButton(String text, StringHistoryManager historyManager) { - Button removeHistoryButton2 = new Button(text); - removeHistoryButton2.disableProperty().bind(Bindings.createObjectBinding(() -> historyManager.getAll().isEmpty(), historyManager.getAll())); - removeHistoryButton2.setMaxWidth(Double.MAX_VALUE); - removeHistoryButton2.setOnAction(e -> historyManager.remove(historyManager.getAll().get(0))); - return removeHistoryButton2; - } - public static void main(String[] args) { launch(args); } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/HistoryButton.java b/gemsfx/src/main/java/com/dlsc/gemsfx/HistoryButton.java index b20f2a86..24a5f1dc 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/HistoryButton.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/HistoryButton.java @@ -6,7 +6,6 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; -import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.css.CssMetaData; import javafx.css.PseudoClass; @@ -15,6 +14,7 @@ import javafx.css.StyleableProperty; import javafx.css.converter.BooleanConverter; import javafx.event.ActionEvent; +import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.layout.Region; import org.kordamp.ikonli.javafx.FontIcon; @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; /** @@ -55,48 +56,55 @@ public class HistoryButton extends Button { private static final String DEFAULT_STYLE_CLASS = "history-button"; - private static final boolean ENABLE_HISTORY_POPUP = true; private static final boolean DEFAULT_FOCUS_POPUP_OWNER_ON_OPEN = false; private static final PseudoClass DISABLED_POPUP_PSEUDO_CLASS = PseudoClass.getPseudoClass("disabled-popup"); private static final PseudoClass HISTORY_POPUP_SHOWING_PSEUDO_CLASS = PseudoClass.getPseudoClass("history-popup-showing"); - private final Region popupOwner; - private final HistoryManager historyManager; private HistoryPopup historyPopup; /** * Creates a new instance of the history button. - * - * @param popupOwner The owner of the popup. can be null. if null, the button will be the popup owner. - * @param historyManager The history manager. */ - public HistoryButton(Region popupOwner, HistoryManager historyManager) { + public HistoryButton() { getStyleClass().addAll(DEFAULT_STYLE_CLASS); - this.popupOwner = popupOwner; - this.historyManager = historyManager; setGraphic(new FontIcon(MaterialDesign.MDI_HISTORY)); setOnAction(this::onActionHandler); } + /** + * Creates a new instance of the history button. + * + * @param popupOwner The owner of the popup. can be null. if null, the button will be the popup owner. + */ + public HistoryButton(Region popupOwner) { + this(); + setPopupOwner(popupOwner); + } + protected void onActionHandler(ActionEvent event) { - Region popupOwner = getPopupOwner(); - HistoryManager historyManager = getHistoryManager(); + Node popupOwner = getPopupOwner(); if (popupOwner != null && popupOwner != this && !popupOwner.isFocused() && getFocusPopupOwnerOnOpen()) { popupOwner.requestFocus(); } - if (!isEnableHistoryPopup()) { + if (getHistoryManager() == null) { return; } if (historyPopup == null) { - historyPopup = new HistoryPopup<>(historyManager); + historyPopup = new HistoryPopup<>(); // basic settings - historyPopup.setHistoryCellFactory(view -> new RemovableListCell<>((listView, item) -> historyManager.remove(item))); + historyPopup.historyManagerProperty().bind(historyManagerProperty()); historyPopupShowing.bind(historyPopup.showingProperty()); + historyPopup.setHistoryCellFactory(view -> new RemovableListCell<>((listView, item) -> { + HistoryManager historyManager = getHistoryManager(); + if (historyManager != null) { + historyManager.remove(item); + } + })); // Set up the popup if (getConfigureHistoryPopup() != null) { @@ -170,42 +178,63 @@ public final void setConfigureHistoryPopup(Consumer> configureHi configureHistoryPopupProperty().set(configureHistoryPopup); } - private BooleanProperty enableHistoryPopup; + private final ReadOnlyBooleanWrapper historyPopupShowing = new ReadOnlyBooleanWrapper(this, "historyPopupShowing") { + @Override + protected void invalidated() { + pseudoClassStateChanged(HISTORY_POPUP_SHOWING_PSEUDO_CLASS, get()); + } + }; + + public final boolean isHistoryPopupShowing() { + return historyPopupShowing.get(); + } + + private ObjectProperty> historyManager; /** - * Indicates whether the history popup should be enabled. + * The history manager that is used to manage the history of the HistoryButton. + *

+ * If its value is null, clicking the button will not display the history popup. + *

+ * If its value is not null, clicking the button will display the history popup. * - * @return true if the history popup should be enabled, false otherwise + * @return the property representing the history manager */ - public final BooleanProperty enableHistoryPopupProperty() { - if (enableHistoryPopup == null) { - enableHistoryPopup = new SimpleBooleanProperty(this, "enableHistoryPopup", ENABLE_HISTORY_POPUP) { + public final ObjectProperty> historyManagerProperty() { + if (historyManager == null) { + historyManager = new SimpleObjectProperty<>(this, "historyManager") { @Override protected void invalidated() { - pseudoClassStateChanged(DISABLED_POPUP_PSEUDO_CLASS, !get()); + pseudoClassStateChanged(DISABLED_POPUP_PSEUDO_CLASS, get() == null); } }; } - return enableHistoryPopup; + return historyManager; } - public final boolean isEnableHistoryPopup() { - return enableHistoryPopup == null ? ENABLE_HISTORY_POPUP : enableHistoryPopup.get(); + public final void setHistoryManager(HistoryManager historyManager) { + historyManagerProperty().set(historyManager); } - public final void setEnableHistoryPopup(boolean enableHistoryPopup) { - enableHistoryPopupProperty().set(enableHistoryPopup); + public final HistoryManager getHistoryManager() { + return historyManager == null ? null : historyManager.get(); } - private final ReadOnlyBooleanWrapper historyPopupShowing = new ReadOnlyBooleanWrapper(this, "historyPopupShowing") { - @Override - protected void invalidated() { - pseudoClassStateChanged(HISTORY_POPUP_SHOWING_PSEUDO_CLASS, get()); + private ObjectProperty popupOwner; + + public final ObjectProperty popupOwnerProperty() { + if (popupOwner == null) { + popupOwner = new SimpleObjectProperty<>(this, "popupOwner"); } - }; + return popupOwner; + } - public final boolean isHistoryPopupShowing() { - return historyPopupShowing.get(); + public final Node getPopupOwner() { + return popupOwner == null ? null : popupOwner.get(); + } + + public final void setPopupOwner(Node popupOwner) { + popupOwnerProperty().set(popupOwner); } /** @@ -251,24 +280,6 @@ public boolean isSettable(HistoryButton control) { return HistoryButton.StyleableProperties.STYLEABLES; } - /** - * Returns the HistoryManager instance used by this control. - * - * @return the history manager - */ - public final HistoryManager getHistoryManager() { - return historyManager; - } - - /** - * Returns the popup owner node. - * - * @return the popup owner node - */ - public final Region getPopupOwner() { - return popupOwner; - } - /** * Returns the history popup. */ @@ -290,7 +301,7 @@ public final void hideHistoryPopup() { */ public final void showHistoryPopup() { if (historyPopup != null) { - historyPopup.show(popupOwner == null ? this : popupOwner); + historyPopup.show(Optional.ofNullable(getPopupOwner()).orElse(this)); } } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/SearchField.java b/gemsfx/src/main/java/com/dlsc/gemsfx/SearchField.java index 179889ca..2e80ba7d 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/SearchField.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/SearchField.java @@ -2,6 +2,7 @@ import com.dlsc.gemsfx.skins.SearchFieldPopup; import com.dlsc.gemsfx.skins.SearchFieldSkin; +import com.dlsc.gemsfx.util.HistoryManager; import com.dlsc.gemsfx.util.StringHistoryManager; import javafx.animation.Animation; import javafx.animation.RotateTransition; @@ -84,11 +85,11 @@ * * *

- * History management is enabled by default and can be accessed and controlled through a history button integrated into the search field. - * Users can interact with their search history, revisit previous queries, or clear historical entries. The history functionality is managed by - * a {@link StringHistoryManager}, accessible via {@code getHistoryManager()}, allowing programmatic manipulation of history records. - * If {@code setPreferences(Preferences preferences)} is not set on the {@link StringHistoryManager}, history records are only stored temporarily in memory - * and are not persisted locally. This means that history data will not be retained after the application is closed. + * The history manager is disabled by default, but it can be enabled using the {@link #setHistoryManager(HistoryManager)} method. + * We have implemented a local history manager, {@link StringHistoryManager}, which uses the Java Preferences API to store history records. + * You can enable it via the {@link #setHistoryManager(HistoryManager)} method. + * If you want to persistently store history records, you also need to provide a {@link java.util.prefs.Preferences} instance to the {@link StringHistoryManager}. + * Otherwise, the history records will be lost after the application restarts. *

* * @param the type of objects to work on @@ -111,7 +112,6 @@ public class SearchField extends Control { private static final PseudoClass DISABLED_POPUP_PSEUDO_CLASS = PseudoClass.getPseudoClass("disabled-popup"); private final SearchService searchService = new SearchService(); - private final StringHistoryManager historyManager; private final TextField editor = new TextField(); @@ -128,22 +128,8 @@ public class SearchField extends Control { * @see #setNewItemProducer(Callback) */ public SearchField() { - this(new StringHistoryManager()); - } - - /** - * Constructs a new spotlight field. The field will set defaults for the - * matcher, the converter, the cell factory, and the comparator. It will - * not set a default for the "new item" producer. - *

- * The history manager is initialized with the given preferences. - * - * @see #setNewItemProducer(Callback) - */ - public SearchField(StringHistoryManager historyManager) { getStyleClass().add(DEFAULT_STYLE_CLASS); - this.historyManager = historyManager; historyButton = createHistorySupportedButton(); setGraphic(historyButton); @@ -153,7 +139,12 @@ public SearchField(StringHistoryManager historyManager) { editor.promptTextProperty().bindBidirectional(promptTextProperty()); // history listCell factory - setHistoryCellFactory(view -> new RemovableListCell<>((listView, item) -> historyManager.remove(item))); + setHistoryCellFactory(view -> new RemovableListCell<>((listView, item) -> { + HistoryManager historyManager = getHistoryManager(); + if (historyManager != null) { + historyManager.remove(item); + } + })); // history listView placeholder Label placeholder = new Label("No history available."); @@ -180,7 +171,7 @@ public SearchField(StringHistoryManager historyManager) { if (!editor.isFocused()) { // Add the current text to the history if the editor lost focus. if (isAddingItemToHistoryOnFocusLost()) { - historyManager.add(editor.getText()); + addToHistory(editor.getText()); } commit(); if (getSelectedItem() == null) { @@ -205,7 +196,7 @@ public SearchField(StringHistoryManager historyManager) { boolean releasedEnter = keyCode.equals(KeyCode.ENTER); // Add the current text to the history if the user pressed the ENTER key. if (releasedEnter && isAddingItemToHistoryOnEnter() && !lastHistoryPopupShowing) { - historyManager.add(editor.getText()); + addToHistory(editor.getText()); } if ((keyCode.equals(KeyCode.RIGHT) || releasedEnter) && !lastHistoryPopupShowing) { @@ -359,7 +350,8 @@ private void onHistoryItemConfirmed(String historyItem) { } private HistoryButton createHistorySupportedButton() { - HistoryButton historyButton = new HistoryButton(this, historyManager); + HistoryButton historyButton = new HistoryButton<>(this); + historyButton.historyManagerProperty().bind(historyManagerProperty()); // Create the graphic Region graphic = new Region(); @@ -369,7 +361,6 @@ private HistoryButton createHistorySupportedButton() { // Configure the history button historyButton.setFocusTraversable(false); historyButton.setFocusPopupOwnerOnOpen(true); - historyButton.enableHistoryPopupProperty().bind(enableHistoryPopupProperty()); historyButton.setConfigureHistoryPopup(historyPopup -> { @@ -420,7 +411,7 @@ public void commit() { // add on commit if (isAddingItemToHistoryOnCommit()) { - historyManager.add(text); + addToHistory(text); } } else { clear(); @@ -435,6 +426,13 @@ public void commit() { } } + private void addToHistory(String text) { + HistoryManager historyManager = getHistoryManager(); + if (historyManager != null) { + historyManager.add(text); + } + } + private final ObjectProperty> onCommit = new SimpleObjectProperty<>(this, "onCommit"); public final Consumer getOnCommit() { @@ -1307,31 +1305,48 @@ public final void setAddingItemToHistoryOnCommit(boolean addingItemToHistoryOnCo addingItemToHistoryOnCommitProperty().set(addingItemToHistoryOnCommit); } - private BooleanProperty enableHistoryPopup; + private ObjectProperty> historyManager; /** - * Indicates whether the history popup should be enabled. + * The history manager that is used to manage the history of the SearchField. + *

+ * If its value is null, the history feature will not be enabled, which means only + * the magnifying glass icon will be displayed, and the dropdown arrow next to the + * magnifying glass will not be shown. + *

+ * If its value is not null, the history feature will be enabled, meaning that both + * the magnifying glass icon and the dropdown arrow will be displayed. Clicking the + * magnifying glass icon button will display the history popup. + *

+ * To enable the history feature, you need to set an instance of {@link HistoryManager}. + * Typically, you would use an instance of {@link StringHistoryManager}, which is an + * implementation of {@link HistoryManager} that manages string-type history records. + *

+ * Please note that if you do not set the {@code StringHistoryManager#setPreferences(Preferences)} + * method for the {@link StringHistoryManager} instance, the history records will only be saved + * in memory and will not be persisted locally. This means that the history data will not be + * retained after the application is closed. * - * @return true if the history popup should be enabled, false otherwise + * @return the property representing the history manager */ - public final BooleanProperty enableHistoryPopupProperty() { - if (enableHistoryPopup == null) { - enableHistoryPopup = new SimpleBooleanProperty(this, "enableHistoryPopup", DEFAULT_ENABLE_HISTORY_POPUP) { + public final ObjectProperty> historyManagerProperty() { + if (historyManager == null) { + historyManager = new SimpleObjectProperty<>(this, "historyManager") { @Override protected void invalidated() { - pseudoClassStateChanged(DISABLED_POPUP_PSEUDO_CLASS, !get()); + pseudoClassStateChanged(DISABLED_POPUP_PSEUDO_CLASS, get() == null); } }; } - return enableHistoryPopup; + return historyManager; } - public final boolean isEnableHistoryPopup() { - return enableHistoryPopup == null ? DEFAULT_ENABLE_HISTORY_POPUP : enableHistoryPopup.get(); + public final HistoryManager getHistoryManager() { + return historyManager == null ? null : historyManager.get(); } - public final void setEnableHistoryPopup(boolean enableHistoryPopup) { - enableHistoryPopupProperty().set(enableHistoryPopup); + public final void setHistoryManager(HistoryManager historyManager) { + historyManagerProperty().set(historyManager); } /** @@ -1396,13 +1411,4 @@ public final SearchFieldPopup getPopup() { return popup; } - /** - * If we want to manually add history records, delete history records, clear history records, then please get the HistoryManager object through this method. - * - * @return the history manager - */ - public final StringHistoryManager getHistoryManager() { - return historyManager; - } - } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/SearchTextField.java b/gemsfx/src/main/java/com/dlsc/gemsfx/SearchTextField.java index 0a329a24..b60a1916 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/SearchTextField.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/SearchTextField.java @@ -1,6 +1,8 @@ package com.dlsc.gemsfx; +import com.dlsc.gemsfx.util.HistoryManager; import com.dlsc.gemsfx.util.StringHistoryManager; +import com.dlsc.gemsfx.util.UIUtil; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -27,6 +29,12 @@ * A custom text field specifically designed for search functionality. This class enhances a text field with features * such as a history of search terms, an optional history popup, and custom icons for search and clear operations. *

+ * The history manager is disabled by default, but it can be enabled using the {@link #setHistoryManager(HistoryManager)} method. + * We have implemented a local history manager, {@link StringHistoryManager}, which uses the Java Preferences API to store history records. + * You can enable it via the {@link #setHistoryManager(HistoryManager)} method. + * If you want to persistently store history records, you also need to provide a {@link java.util.prefs.Preferences} instance to the {@link StringHistoryManager}. + * Otherwise, the history records will be lost after the application restarts. + *

* By default, when the field loses its focus or the user presses the "enter" key (triggering the onAction event), the * text is added to the history. This behavior can be disabled by setting the {@link #addingItemToHistoryOnEnterProperty()} * and / or the {@link #addingItemToHistoryOnEnterProperty()} to false. @@ -34,25 +42,15 @@ * Additionally, history can be manually added based on user actions, such as after typing text and selecting an item * from a ListView or TableView that displays results, or through other interactions, by calling the {@link #getHistoryManager()} * method to access the {@link StringHistoryManager} instance. then calling the {@link StringHistoryManager#add(String)} method. - * - *

- * History management is enabled by default and can be accessed and controlled through a history button integrated into the search text field. - * Users can interact with their search history, revisit previous queries, or clear historical entries. The history functionality is managed by - * a {@link StringHistoryManager}, accessible via {@code getHistoryManager()}, allowing programmatic manipulation of history records. - * If {@code setPreferences(Preferences preferences)} is not set on the {@link StringHistoryManager}, history records are only stored temporarily in memory - * and are not persisted locally. This means that history data will not be retained after the application is closed. - *

- * */ public class SearchTextField extends CustomTextField { - private static final boolean DEFAULT_ENABLE_HISTORY_POPUP = true; private static final boolean DEFAULT_ADDING_ITEM_TO_HISTORY_ON_ENTER = true; private static final boolean DEFAULT_ADDING_ITEM_TO_HISTORY_ON_FOCUS_LOST = true; + private static final boolean DEFAULT_ROUND = false; private static final PseudoClass DISABLED_POPUP_PSEUDO_CLASS = PseudoClass.getPseudoClass("disabled-popup"); - private final StringHistoryManager historyManager; private final HistoryButton historyButton; /** @@ -61,40 +59,8 @@ public class SearchTextField extends CustomTextField { * The history manager is initialized with default values. */ public SearchTextField() { - this(false, new StringHistoryManager()); - } - - /** - * Constructs a new text field customized for search operations. - */ - public SearchTextField(StringHistoryManager historyManager) { - this(false, historyManager); - } - - /** - * Constructs a new text field customized for search operations. The look and feel can be - * adjusted to feature rounded corners / sides. - *

- * The history manager is initialized with default values. - * - * @param round if true the sides of the field will be round - */ - public SearchTextField(boolean round) { - this(round, new StringHistoryManager()); - } - - /** - * Constructs a new text field customized for search operations. The look and feel can be - * adjusted to feature rounded corners / sides. - * - * @param round if true the sides of the field will be round - */ - public SearchTextField(boolean round, StringHistoryManager historyManager) { - if (round) { - getStyleClass().add("round"); - } - getStyleClass().add("search-text-field"); + UIUtil.toggleClassBasedOnObservable(this, "round", roundProperty()); setPromptText("Search..."); @@ -102,10 +68,14 @@ public SearchTextField(boolean round, StringHistoryManager historyManager) { placeholder.getStyleClass().add("default-placeholder"); setHistoryPlaceholder(placeholder); - this.historyManager = historyManager; - setHistoryCellFactory(view -> new RemovableListCell<>((listView, item) -> historyManager.remove(item))); + setHistoryCellFactory(view -> new RemovableListCell<>((listView, item) -> { + HistoryManager historyManager = getHistoryManager(); + if (historyManager != null) { + historyManager.remove(item); + } + })); - historyButton = createLeftNode(round); + historyButton = createLeftNode(); setLeft(historyButton); setRight(createRightNode()); @@ -114,14 +84,22 @@ public SearchTextField(boolean round, StringHistoryManager historyManager) { focusedProperty().addListener(it -> { if (!isFocused() && isAddingItemToHistoryOnFocusLost()) { - historyManager.add(getText()); + addToHistory(); } historyButton.hideHistoryPopup(); }); } - private HistoryButton createLeftNode(boolean round) { - HistoryButton historyButton = new HistoryButton<>(this, historyManager); + private void addToHistory() { + HistoryManager historyManager = getHistoryManager(); + if (historyManager != null) { + historyManager.add(getText()); + } + } + + private HistoryButton createLeftNode() { + HistoryButton historyButton = new HistoryButton<>(this); + historyButton.historyManagerProperty().bind(historyManagerProperty()); // Create the graphic Region graphic = new Region(); @@ -131,11 +109,8 @@ private HistoryButton createLeftNode(boolean round) { // Configure the history button historyButton.setFocusTraversable(false); historyButton.setFocusPopupOwnerOnOpen(true); - historyButton.enableHistoryPopupProperty().bind(enableHistoryPopupProperty()); historyButton.setConfigureHistoryPopup(historyPopup -> { - if (round) { - historyPopup.getStyleClass().add("round"); - } + UIUtil.toggleClassBasedOnObservable(historyPopup, "round", roundProperty()); historyPopup.historyPlaceholderProperty().bind(historyPlaceholderProperty()); historyPopup.historyCellFactoryProperty().bind(historyCellFactoryProperty()); @@ -159,7 +134,7 @@ private void addEventHandlers() { // On Action event, add the text to the history addEventHandler(ActionEvent.ANY, e -> { if (isAddingItemToHistoryOnEnter()) { - historyManager.add(getText()); + addToHistory(); } }); @@ -235,33 +210,6 @@ public final void setHistoryCellFactory(Callback, ListCell> historyManager; + + /** + * The history manager that is used to manage the history of the search text field. + *

+ * If its value is null, the history feature will not be enabled, which means only + * the magnifying glass icon will be displayed, and the dropdown arrow next to the + * magnifying glass will not be shown. + *

+ * If its value is not null, the history feature will be enabled, meaning that both + * the magnifying glass icon and the dropdown arrow will be displayed. Clicking the + * magnifying glass icon button will display the history popup. + *

+ * To enable the history feature, you need to set an instance of {@link HistoryManager}. + * Typically, you would use an instance of {@link StringHistoryManager}, which is an + * implementation of {@link HistoryManager} that manages string-type history records. + *

+ * Please note that if you do not set the {@code StringHistoryManager#setPreferences(Preferences)} + * method for the {@link StringHistoryManager} instance, the history records will only be saved + * in memory and will not be persisted locally. This means that the history data will not be + * retained after the application is closed. + * + * @return the property representing the history manager + */ + public final ObjectProperty> historyManagerProperty() { + if (historyManager == null) { + historyManager = new SimpleObjectProperty<>(this, "historyManager") { + @Override + protected void invalidated() { + pseudoClassStateChanged(DISABLED_POPUP_PSEUDO_CLASS, get() == null); + } + }; + } return historyManager; } + public final HistoryManager getHistoryManager() { + return historyManager == null ? null : historyManager.get(); + } + + public final void setHistoryManager(HistoryManager historyManager) { + historyManagerProperty().set(historyManager); + } + } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopup.java b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopup.java index 9607e94e..0d370fb7 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopup.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopup.java @@ -35,10 +35,8 @@ public class HistoryPopup extends CustomPopupControl { public static final String DEFAULT_STYLE_CLASS = "history-popup"; - private final HistoryManager historyManager; - public HistoryPopup(HistoryManager historyManager) { - this.historyManager = historyManager; + public HistoryPopup() { getStyleClass().addAll(DEFAULT_STYLE_CLASS); setAutoFix(true); @@ -52,10 +50,6 @@ protected Skin createDefaultSkin() { return new HistoryPopupSkin<>(this); } - public final HistoryManager getHistoryManager() { - return historyManager; - } - private final ObjectProperty left = new SimpleObjectProperty<>(this, "left"); public final ObjectProperty leftProperty() { @@ -186,7 +180,6 @@ public final void setOnHistoryItemConfirmed(Consumer onHistoryItemConfirmed) onHistoryItemConfirmedProperty().set(onHistoryItemConfirmed); } - private ObjectProperty> onHistoryItemSelected; public final Consumer getOnHistoryItemSelected() { @@ -212,4 +205,21 @@ public final void setOnHistoryItemSelected(Consumer onHistoryItemSelected) { onHistoryItemSelectedProperty().set(onHistoryItemSelected); } + private ObjectProperty> historyManager; + + public final HistoryManager getHistoryManager() { + return historyManager == null ? null : historyManager.get(); + } + + public final ObjectProperty> historyManagerProperty() { + if (historyManager == null) { + historyManager = new SimpleObjectProperty<>(this, "historyManager"); + } + return historyManager; + } + + public final void setHistoryManager(HistoryManager historyManager) { + historyManagerProperty().set(historyManager); + } + } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopupSkin.java b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopupSkin.java index 37e861d7..db12b3cd 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopupSkin.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/skins/HistoryPopupSkin.java @@ -1,6 +1,7 @@ package com.dlsc.gemsfx.skins; import com.dlsc.gemsfx.SearchField; +import com.dlsc.gemsfx.util.HistoryManager; import javafx.beans.binding.Bindings; import javafx.scene.Node; import javafx.scene.control.ListView; @@ -63,7 +64,19 @@ private ListView createHistoryListView() { ListView listView = new ListView<>(); listView.getStyleClass().add("history-list-view"); - Bindings.bindContent(listView.getItems(), control.getHistoryManager().getAll()); + HistoryManager historyManager = control.getHistoryManager(); + if (historyManager != null) { + Bindings.bindContent(listView.getItems(), historyManager.getAll()); + } + + control.historyManagerProperty().addListener((observable, oldValue, newValue) -> { + if (oldValue != null) { + Bindings.unbindContent(listView.getItems(), oldValue.getAll()); + } + if (newValue != null) { + Bindings.bindContent(listView.getItems(), newValue.getAll()); + } + }); listView.cellFactoryProperty().bind(control.historyCellFactoryProperty()); listView.placeholderProperty().bind(control.historyPlaceholderProperty()); @@ -116,7 +129,10 @@ public HistoryPopup getSkinnable() { } public void dispose() { - Bindings.unbindContent(listView.getItems(), control.getHistoryManager().getAll()); + HistoryManager historyManager = control.getHistoryManager(); + if (historyManager != null) { + Bindings.unbindContent(listView.getItems(), historyManager.getAll()); + } listView.prefWidthProperty().unbind(); listView.maxWidthProperty().unbind(); diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/HistoryManager.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/HistoryManager.java index a3248ff0..37a622ad 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/util/HistoryManager.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/HistoryManager.java @@ -31,6 +31,14 @@ public interface HistoryManager { */ void add(List items); + /** + * Sets the history of the HistoryManager with the provided list of items. + * The method ensures that duplicates are removed from the list. + * + * @param items the list of items representing the history + */ + void set(List items); + /** * Removes a single item from the history storage. * diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/PreferencesHistoryManager.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/PreferencesHistoryManager.java index 6869401f..4ae465b0 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/util/PreferencesHistoryManager.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/PreferencesHistoryManager.java @@ -47,12 +47,12 @@ public class PreferencesHistoryManager implements HistoryManager { * Using Unicode Record Separator as delimiter. * This character is not likely to be used in the history items. */ - private static final String DEFAULT_DELIMITER = "␞"; + public static final String DEFAULT_DELIMITER = "␞"; /** * Default preferences key used to store history items. */ - private static final String DEFAULT_PREFERENCES_KEY = "history-items"; + public static final String DEFAULT_PREFERENCES_KEY = "history-items"; private final String delimiter; private final String preferencesKey; diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/StringHistoryManager.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/StringHistoryManager.java index 5cfd6c22..eae92523 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/util/StringHistoryManager.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/StringHistoryManager.java @@ -3,6 +3,8 @@ import javafx.util.StringConverter; import org.apache.commons.lang3.StringUtils; +import java.util.prefs.Preferences; + /** * Manages a history of string records using the Java Preferences API. This class specializes * the generic {@link PreferencesHistoryManager} for strings, providing simple and efficient @@ -47,9 +49,19 @@ public StringHistoryManager() { setFilter(StringUtils::isNotEmpty); } + public StringHistoryManager(Preferences preferences) { + this(); + setPreferences(preferences); + } + public StringHistoryManager(String delimiter, String preferencesKey) { super(delimiter, preferencesKey, DEFAULT_STRING_CONVERTER); setFilter(StringUtils::isNotEmpty); } + public StringHistoryManager(String delimiter, String preferencesKey, Preferences preferences) { + this(delimiter, preferencesKey); + setPreferences(preferences); + } + } diff --git a/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java b/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java index f4247575..b9a43456 100644 --- a/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java +++ b/gemsfx/src/main/java/com/dlsc/gemsfx/util/UIUtil.java @@ -1,8 +1,8 @@ package com.dlsc.gemsfx.util; - +import javafx.beans.value.ObservableValue; +import javafx.css.Styleable; import javafx.geometry.Insets; -import javafx.scene.Node; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.MouseButton; @@ -24,7 +24,7 @@ private UIUtil() { * @param node The node to add the style class to. * @param styleClass The style class to add. */ - public static void addClassIfAbsent(Node node, String styleClass) { + public static void addClassIfAbsent(Styleable node, String styleClass) { Optional.ofNullable(node).ifPresent(n -> { if (!n.getStyleClass().contains(styleClass)) { n.getStyleClass().add(styleClass); @@ -38,7 +38,7 @@ public static void addClassIfAbsent(Node node, String styleClass) { * @param node The node to add the style classes to. * @param styleClasses The style classes to add. */ - public static void addClassesIfAbsent(Node node, String... styleClasses) { + public static void addClassesIfAbsent(Styleable node, String... styleClasses) { List list = Arrays.stream(styleClasses) .filter(styleClass -> !node.getStyleClass().contains(styleClass)) .toList(); @@ -53,7 +53,7 @@ public static void addClassesIfAbsent(Node node, String... styleClasses) { * @param node The node to toggle the style on. * @param styleClass The style class to add or remove. */ - public static void toggleClass(Node node, String styleClass) { + public static void toggleClass(Styleable node, String styleClass) { if (node.getStyleClass().contains(styleClass)) { node.getStyleClass().remove(styleClass); } else { @@ -70,7 +70,7 @@ public static void toggleClass(Node node, String styleClass) { * @param styleClass The style class to add or remove. * @param condition The condition that determines whether to add or remove the style. */ - public static void toggleClassOnCondition(Node node, String styleClass, boolean condition) { + public static void toggleClassOnCondition(Styleable node, String styleClass, boolean condition) { if (condition) { addClassIfAbsent(node, styleClass); } else { @@ -78,6 +78,19 @@ public static void toggleClassOnCondition(Node node, String styleClass, boolean } } + /** + * Toggles a style class on a node based on an observable value. + * If the observable value is true, the style class is added. + * If the observable value is false, the style class is removed. + * + * @param node The node to toggle the style on. + * @param styleClass The style class to add or remove. + * @param booleanObservableValue The observable value that determines whether to add or remove the style. + */ + public static void toggleClassBasedOnObservable(Styleable node, String styleClass, ObservableValue booleanObservableValue) { + toggleClassOnCondition(node, styleClass, booleanObservableValue.getValue()); + booleanObservableValue.addListener((obs, oldVal, newVal) -> toggleClassOnCondition(node, styleClass, newVal)); + } /** * Optimizes style updates for a given node by first adding a specified style to ensure it's present, @@ -88,7 +101,7 @@ public static void toggleClassOnCondition(Node node, String styleClass, boolean * @param stylesToRemove A list of styles to be removed from the node, except for the styleToAdd. * @param styleToAdd The style to be added to the node, if it's not already present. */ - public static void updateStyles(Node node, List stylesToRemove, String styleToAdd) { + public static void updateStyles(Styleable node, List stylesToRemove, String styleToAdd) { // Add the style if it's not already present addClassIfAbsent(node, styleToAdd); @@ -107,7 +120,7 @@ public static void updateStyles(Node node, List stylesToRemove, String s * @param stylesToRemove An array of styles to be removed from the node, except for the styleToAdd. * @param styleToAdd The style to be added to the node, if it's not already present. */ - public static void updateStyles(Node node, String[] stylesToRemove, String styleToAdd) { + public static void updateStyles(Styleable node, String[] stylesToRemove, String styleToAdd) { updateStyles(node, Arrays.asList(stylesToRemove), styleToAdd); } @@ -118,7 +131,7 @@ public static void updateStyles(Node node, String[] stylesToRemove, String style * @param enumValue The enum value determining the style to apply. *

Example If Dir.UP is passed, add "up" style and removes {"down", "left", "right"} styles. */ - public static > void updateStyleFromEnum(Node node, T enumValue) { + public static > void updateStyleFromEnum(Styleable node, T enumValue) { updateStyles(node, EnumUtil.convertAllToStylesClassName(enumValue.getClass()), EnumUtil.convertToStyleClassName(enumValue)); } @@ -129,7 +142,7 @@ public static > void updateStyleFromEnum(Node node, T enumValu * @param enumClass The enum class whose associated styles will be removed. *

Example If Dir.class is passed, removes all styles {"up","down","left", "right"}. */ - public static > void clearStylesByEnum(Node node, Class enumClass) { + public static > void clearStylesByEnum(Styleable node, Class enumClass) { node.getStyleClass().removeAll(EnumUtil.convertAllToStylesClassName(enumClass)); }