Skip to content

Commit

Permalink
Merge pull request #82 from leewyatt/feature-suggestion-and-fix-selec…
Browse files Browse the repository at this point in the history
…ted-event

Introduce SUGGESTION_SELECTED event and fix multiple commit calls
  • Loading branch information
dlemmermann authored Aug 22, 2023
2 parents b230318 + 762883f commit bcfe0bb
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public void start(Stage primaryStage) throws Exception {

CountriesSearchField field = new CountriesSearchField();
field.getEditor().setPrefColumnCount(30);
field.addEventHandler(SearchField.SearchEvent.SUGGESTION_SELECTED, event -> {
System.out.println("A suggestion was selected! => " + event.getSelectedSuggestion());
});

Region regionLeft = new Region();
regionLeft.setPrefWidth(30);
Expand Down
60 changes: 42 additions & 18 deletions gemsfx/src/main/java/com/dlsc/gemsfx/SearchField.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public class SearchField<T> extends Control {

private final SearchFieldPopup<T> popup;

private BooleanProperty shouldCommitProperty = new SimpleBooleanProperty(this, "shouldCommit", false);

/**
* Constructs a new spotlight field. The field will set defaults for the
* matcher, the converter, the cell factory, and the comparator. It will
Expand All @@ -78,7 +80,7 @@ public class SearchField<T> extends Control {
public SearchField() {
getStyleClass().add(DEFAULT_STYLE_CLASS);

popup = new SearchFieldPopup<>(this);
popup = new SearchFieldPopup<>(this, shouldCommitProperty);

editor.textProperty().bindBidirectional(textProperty());
editor.promptTextProperty().bindBidirectional(promptTextProperty());
Expand Down Expand Up @@ -211,11 +213,11 @@ public T fromString(String s) {
}
});

searchService.setOnRunning(evt -> fireEvent(new SearchEvent(SearchEvent.SEARCH_STARTED, searchService.getText())));
searchService.setOnRunning(evt -> fireEvent(SearchEvent.createEventForText(SearchEvent.SEARCH_STARTED, searchService.getText())));

searchService.setOnSucceeded(evt -> {
update(searchService.getValue());
fireEvent(new SearchEvent(SearchEvent.SEARCH_FINISHED, searchService.getText()));
fireEvent(SearchEvent.createEventForText(SearchEvent.SEARCH_FINISHED, searchService.getText()));
});

searching.bind(searchService.runningProperty());
Expand All @@ -229,22 +231,25 @@ public T fromString(String s) {
* item.
*/
public void commit() {
committing = true;
try {
T selectedItem = getSelectedItem();
if (selectedItem != null) {
String text = getConverter().toString(selectedItem);
if (text != null) {
editor.setText(text);
editor.positionCaret(text.length());
if (shouldCommitProperty.get()) {
committing = true;
try {
T selectedItem = getSelectedItem();
if (selectedItem != null) {
String text = getConverter().toString(selectedItem);
if (text != null) {
editor.setText(text);
editor.positionCaret(text.length());
} else {
clear();
}
} else {
clear();
}
} else {
clear();
} finally {
committing = false;
}
} finally {
committing = false;
shouldCommitProperty.set(false);
}
}

Expand Down Expand Up @@ -290,7 +295,6 @@ public final EventHandler<SearchEvent> getOnSearchStarted() {

private SearchEventHandlerProperty onSearchFinished;


/**
* An event handler that can be used to get informed whenever the field finishes a search.
*
Expand Down Expand Up @@ -850,24 +854,44 @@ public static class SearchEvent extends Event {
*/
public static final EventType<SearchEvent> SEARCH_FINISHED = new EventType<>(Event.ANY, "SEARCH_FINISHED");

private String text;
/**
* An event that gets fired when the user selects a suggestion.
*/
public static final EventType<SearchEvent> SUGGESTION_SELECTED = new EventType<>(Event.ANY, "SUGGESTION_SELECTED");

private final Object selectedSuggestion;
private final String text;

public static SearchEvent createEventForText(EventType<? extends SearchEvent> eventType, String text) {
return new SearchEvent(eventType, text, null);
}

public SearchEvent(EventType<? extends SearchEvent> eventType, String text) {
public static SearchEvent createEventForSuggestion(Object suggestion) {
return new SearchEvent(SUGGESTION_SELECTED, null, suggestion);
}

private SearchEvent(EventType<? extends SearchEvent> eventType, String text, Object suggestion) {
super(eventType);
this.text = text;
this.selectedSuggestion = suggestion;
}

public String getText() {
return text;
}

public Object getSelectedSuggestion() {
return selectedSuggestion;
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("eventType", eventType)
.append("target", target)
.append("consumed", consumed)
.append("text", text)
.append("selectedSuggestion", selectedSuggestion)
.toString();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package com.dlsc.gemsfx.skins;

import com.dlsc.gemsfx.SearchField;
import javafx.beans.property.BooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation;
Expand All @@ -25,8 +26,11 @@ public class SearchFieldPopup<T> extends PopupControl {

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

public SearchFieldPopup(SearchField<T> searchField) {
private final BooleanProperty shouldCommitProperty;

public SearchFieldPopup(SearchField<T> searchField, BooleanProperty shouldCommitProperty) {
this.searchField = Objects.requireNonNull(searchField);
this.shouldCommitProperty = shouldCommitProperty;

minWidthProperty().bind(searchField.widthProperty());

Expand Down Expand Up @@ -104,6 +108,6 @@ private void selectFirstSuggestion() {
}

protected Skin<?> createDefaultSkin() {
return new SearchFieldPopupSkin(this);
return new SearchFieldPopupSkin(this, shouldCommitProperty);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
package com.dlsc.gemsfx.skins;

import com.dlsc.gemsfx.SearchField;
import javafx.beans.property.BooleanProperty;
import javafx.collections.transformation.SortedList;
import javafx.scene.Node;
import javafx.scene.control.ListView;
import javafx.scene.control.Skin;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.util.StringConverter;

Expand All @@ -20,9 +23,10 @@ public class SearchFieldPopupSkin<T> implements Skin<SearchFieldPopup<T>> {
private final SearchFieldPopup<T> control;
private final ListView<T> suggestionList;
private final SearchField<T> searchField;

public SearchFieldPopupSkin(SearchFieldPopup<T> control) {
private BooleanProperty shouldCommit;
public SearchFieldPopupSkin(SearchFieldPopup<T> control, BooleanProperty shouldCommit) {
this.control = control;
this.shouldCommit = shouldCommit;

searchField = control.getSearchField();

Expand Down Expand Up @@ -88,9 +92,23 @@ private Comparator<T> createInnerComparator() {
}

private void registerEventListener() {
suggestionList.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
shouldCommit.set(true);
if (event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB) {
fireSuggestionSelected();
}
});

suggestionList.setOnMouseClicked((me) -> {
if (me.getButton() == MouseButton.PRIMARY) {
selectItem();
if (me.getClickCount() == 2) {
// hide the popup on double click
control.hide();
} else if (me.getClickCount() == 1) {
shouldCommit.set(true);
selectItem();
fireSuggestionSelected();
}
}
});

Expand All @@ -116,6 +134,14 @@ private void registerEventListener() {

}

private void fireSuggestionSelected() {
Object selectedSuggestion = suggestionList.getSelectionModel().getSelectedItem();
if (selectedSuggestion != null) {
SearchField.SearchEvent searchEvent = SearchField.SearchEvent.createEventForSuggestion(selectedSuggestion);
searchField.fireEvent(searchEvent);
}
}

private void selectItem() {
T selectedItem = suggestionList.getSelectionModel().getSelectedItem();
if (selectedItem != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.dlsc.gemsfx.skins;

import com.dlsc.gemsfx.SearchField;
import com.dlsc.gemsfx.SearchField.SearchFieldListCell;
import javafx.scene.control.SkinBase;

public class SearchFieldSkin<T> extends SkinBase<SearchField<T>> {
Expand Down

0 comments on commit bcfe0bb

Please sign in to comment.