Skip to content
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

Refactor SearchTextField with SearchHistorySupport interface #175

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions gemsfx/src/main/java/com/dlsc/gemsfx/SearchHistorySupport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.dlsc.gemsfx;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.Region;
import javafx.util.Callback;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.prefs.Preferences;

public interface SearchHistorySupport {

int DEFAULT_MAX_HISTORY_SIZE = 30;

boolean ENABLE_HISTORY_POPUP = true;

boolean DEFAULT_ADDING_ITEM_TO_HISTORY_ON_ENTER = true;

boolean DEFAULT_ADDING_ITEM_TO_HISTORY_ON_FOCUS_LOST = true;

Region getNode();

/**
* Returns the text input control that is associated with this history support.
*
* @return the text input control
*/
TextInputControl getTextInputControl();

/**
* Sets the history of the search text field. The given list of Strings will be processed to guarantee unique
* entries.
*
* @param history the list of strings representing the history
*/
void setHistory(List<String> history);

/**
* Adds the given item to the history. The method ensures that duplicates will not be added.
*
* @param item the item to add
*/
void addHistory(String item);

/**
* Adds the given items to the history.
*
* @param items the items to add
*/
void addHistory(List<String> items);

/**
* Removes the given item from the history.
*
* @param item the item to remove
* @return true if the item was removed, false otherwise
*/
boolean removeHistory(String item);

/**
* Removes the given items from the history.
*
* @param items the items to remove
*/
void removeHistory(List<String> items);

/**
* Clears the history.
*/
void clearHistory();

/**
* Returns an unmodifiable list of the history.
*/
ObservableList<String> getUnmodifiableHistory();

/**
* Returns the property representing the maximum history size of the search text field.
*
* @return the maximum history size property
*/
IntegerProperty maxHistorySizeProperty();

int getMaxHistorySize();

void setMaxHistorySize(int maxHistorySize);

/**
* Returns the property representing the history placeholder node.
*
* @return the property representing the history placeholder node
*/
ObjectProperty<Node> historyPlaceholderProperty();

Node getHistoryPlaceholder();

void setHistoryPlaceholder(Node historyPlaceholder);

/**
* The cell factory for the history popup list view.
*
* @return the cell factory
*/
ObjectProperty<Callback<ListView<String>, ListCell<String>>> historyCellFactoryProperty();

void setHistoryCellFactory(Callback<ListView<String>, ListCell<String>> historyCellFactory);

Callback<ListView<String>, ListCell<String>> getHistoryCellFactory();

/**
* Indicates whether the history popup should be enabled.
*
* @return true if the history popup should be enabled, false otherwise
*/
BooleanProperty enableHistoryPopupProperty();

boolean isEnableHistoryPopup();

void setEnableHistoryPopup(boolean enableHistoryPopup);

/**
* Determines whether the text of the text field should be added to the history when the user presses the Enter key.
*
* @return true if the text should be added to the history on Enter, false otherwise
*/
BooleanProperty addingItemToHistoryOnEnterProperty();

boolean isAddingItemToHistoryOnEnter();

void setAddingItemToHistoryOnEnter(boolean addingItemToHistoryOnEnter);

/**
* Determines whether the text of the text field should be added to the history when the field losses its focus.
*
* @return true if the text should be added to the history on focus lost, false otherwise
*/
BooleanProperty addingItemToHistoryOnFocusLostProperty();

boolean isAddingItemToHistoryOnFocusLost();

void setAddingItemToHistoryOnFocusLost(boolean addingItemToHistoryOnFocusLost);

/**
* Indicates whether the history popup is showing. This is a read-only property.
*
* @return true if the history popup is showing, false otherwise
*/
ReadOnlyBooleanProperty historyPopupShowingProperty();

boolean isHistoryPopupShowing();

/**
* Stores a preferences object that will be used for persisting the search history of the field.
*
* @return the preferences used for persisting the search history
*/
ObjectProperty<Preferences> preferencesProperty();

Preferences getPreferences();

void setPreferences(Preferences preferences);

/**
* Converts a given list of strings to a unique list of strings. Filters out empty strings.
*
* @param history the list of strings to convert
* @return the converted unique list of strings
*/
default List<String> convertToUniqueList(List<String> history) {
return history.stream().distinct().filter(StringUtils::isNotEmpty).limit(Math.max(0, getMaxHistorySize())).toList();
}

}
50 changes: 26 additions & 24 deletions gemsfx/src/main/java/com/dlsc/gemsfx/SearchTextField.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.dlsc.gemsfx;

import com.dlsc.gemsfx.skins.SearchTextFieldHistoryPopup;
import com.dlsc.gemsfx.skins.SearchHistoryPopup;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
Expand All @@ -21,6 +21,7 @@
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextInputControl;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
Expand Down Expand Up @@ -50,20 +51,16 @@
* from a ListView or TableView that displays results, or through other interactions, by calling the {@link #addHistory}
* method to add the current text to the history.
*/
public class SearchTextField extends CustomTextField {
public class SearchTextField extends CustomTextField implements SearchHistorySupport {

private static final Logger LOG = Logger.getLogger(SearchTextField.class.getName());

private static final int DEFAULT_MAX_HISTORY_SIZE = 30;
private static final boolean 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 PseudoClass DISABLED_POPUP_PSEUDO_CLASS = PseudoClass.getPseudoClass("disabled-popup");
private static final PseudoClass HISTORY_POPUP_SHOWING_PSEUDO_CLASS = PseudoClass.getPseudoClass("history-popup-showing");

private SearchTextFieldHistoryPopup historyPopup;
private final boolean round;
private final StackPane searchIconWrapper;
private SearchHistoryPopup searchHistoryPopup;

/**
* Constructs a new text field customized for search operations.
Expand All @@ -82,6 +79,7 @@ public SearchTextField(boolean round) {
if (round) {
getStyleClass().add("round");
}
this.round = round;

getStyleClass().add("search-text-field");

Expand Down Expand Up @@ -219,23 +217,26 @@ private void clickIconWrapperHandler(MouseEvent event) {
return;
}

if (historyPopup == null) {
historyPopup = new SearchTextFieldHistoryPopup(this);
historyPopupShowing.bind(historyPopup.showingProperty());
if (searchHistoryPopup == null) {
searchHistoryPopup = new SearchHistoryPopup(this);
if (round) {
searchHistoryPopup.getStyleClass().add("round");
}
historyPopupShowing.bind(searchHistoryPopup.showingProperty());
}

if (historyPopup.isShowing()) {
historyPopup.hide();
if (searchHistoryPopup.isShowing()) {
searchHistoryPopup.hide();
} else {
historyPopup.show(this);
searchHistoryPopup.show(this);
}

positionCaret(textProperty().getValueSafe().length());
}

private void hideHistoryPopup() {
if (historyPopup != null && historyPopup.isShowing()) {
historyPopup.hide();
if (searchHistoryPopup != null && searchHistoryPopup.isShowing()) {
searchHistoryPopup.hide();
}
}

Expand Down Expand Up @@ -500,13 +501,14 @@ public final void setPreferences(Preferences preferences) {
this.preferences.set(preferences);
}

/**
* Converts a given list of strings to a unique list of strings. Filters out empty strings.
*
* @param history the list of strings to convert
* @return the converted unique list of strings
*/
private List<String> convertToUniqueList(List<String> history) {
return history.stream().distinct().filter(StringUtils::isNotEmpty).limit(Math.max(0, getMaxHistorySize())).toList();
@Override
public final TextInputControl getTextInputControl() {
return this;
}

@Override
public Region getNode() {
return this;
}

}
35 changes: 35 additions & 0 deletions gemsfx/src/main/java/com/dlsc/gemsfx/skins/SearchHistoryPopup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.dlsc.gemsfx.skins;

import com.dlsc.gemsfx.CustomPopupControl;
import com.dlsc.gemsfx.SearchHistorySupport;
import javafx.scene.control.Skin;

import java.util.Objects;

public class SearchHistoryPopup extends CustomPopupControl {

public static final String DEFAULT_STYLE_CLASS = "search-history-popup";

private final SearchHistorySupport historySupport;

public SearchHistoryPopup(SearchHistorySupport historySupport) {
this.historySupport = Objects.requireNonNull(historySupport);

getStyleClass().add(DEFAULT_STYLE_CLASS);

maxWidthProperty().bind(this.historySupport.getNode().widthProperty());

setAutoFix(true);
setAutoHide(true);
setHideOnEscape(true);
}

protected Skin<?> createDefaultSkin() {
return new SearchHistoryPopupSkin(this);
}

public final SearchHistorySupport getHistorySupport() {
return historySupport;
}

}
Loading
Loading