I can listen for scroll events:
tableView.addEventFilter(javafx.scene.input.ScrollEvent.SCROLL,
new EventHandler<javafx.scene.input.ScrollEvent>() {
#Override
public void handle(final javafx.scene.input.ScrollEvent scrollEvent) {
System.out.println("Scrolled.");
}
});
But How can I be notified if the bottom/ top of the table is reached?
A simple way of doing this is to retrieve the ScrollBar using a lookup:
ScrollBar tvScrollBar = (ScrollBar) tableView.lookup(".scroll-bar:vertical");
You can then add a listener to check if it's reached the bottom (the valueProperty of the ScrollBar is represented by the percentage it's been scrolled, so 0.0 is the top and 1.0 is the bottom):
tvScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
if ((Double) newValue == 1.0) {
System.out.println("Bottom!");
}
});
Below is a simple MCVE that demonstrates how to put it all together:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ScrollBarNotify extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Simple TableView to demonstrate
TableView<String> tableView = new TableView<>();
TableColumn<String, String> column = new TableColumn<>("Text");
column.setCellValueFactory(f -> new SimpleStringProperty(f.getValue()));
tableView.getColumns().add(column);
// Add some sample items to our TableView
for (int i = 0; i < 100; i++) {
tableView.getItems().add("Item #" + i);
}
// Now, let's add a listener to the TableView's scrollbar. We can only access the ScrollBar after the Scene is
// rendered, so we need to do schedule this to run later.
Platform.runLater(() -> {
ScrollBar tvScrollBar = (ScrollBar) tableView.lookup(".scroll-bar:vertical");
tvScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
if ((Double) newValue == 1.0) {
System.out.println("Bottom!");
}
});
});
// Finally, add the TableViewto our layout
root.getChildren().add(tableView);
// Show the Stage
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
If you're using Java 10+ you can subclass TableViewSkin and get access to the VirtualFlow. The latter class has the position property which you can use to know if the top or bottom has been reached.
Here's an example using custom events:
MyEvent.java
import javafx.event.Event;
import javafx.event.EventType;
public class MyEvent extends Event {
public static final EventType<MyEvent> ANY = new EventType<>(Event.ANY, "MY_EVENT");
public static final EventType<MyEvent> TOP_REACHED = new EventType<>(ANY, "TOP_REACHED");
public static final EventType<MyEvent> BOTTOM_REACHED = new EventType<>(ANY, "BOTTOM_REACHED");
public MyEvent(EventType<? extends MyEvent> eventType) {
super(eventType);
}
}
MyTableViewSkin.java
import javafx.scene.control.TableView;
import javafx.scene.control.skin.TableViewSkin;
public class MyTableViewSkin<T> extends TableViewSkin<T> {
public MyTableViewSkin(TableView<T> control) {
super(control);
getVirtualFlow().positionProperty().addListener((obs, oldVal, newVal) -> {
if (newVal.doubleValue() == 0.0) {
control.fireEvent(new MyEvent(MyEvent.TOP_REACHED));
} else if (newVal.doubleValue() == 1.0) {
control.fireEvent(new MyEvent(MyEvent.BOTTOM_REACHED));
}
});
}
}
App.java
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
var table = new TableView<Integer>();
for (int i = 0; i < 250; i++) {
table.getItems().add(i);
}
var column = new TableColumn<Integer, Number>("Value");
column.setCellValueFactory(features -> new SimpleIntegerProperty(features.getValue()));
table.getColumns().add(column);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setSkin(new MyTableViewSkin<>(table));
table.addEventHandler(MyEvent.ANY, event -> System.out.printf("%s%n", event.getEventType()));
primaryStage.setScene(new Scene(table, 500, 300));
primaryStage.setTitle("Example");
primaryStage.show();
}
}
In this example I manually call table.setSkin. Another option is to subclass TableView and override createDefaultSkin which returns the skin you want to use.
tableView.addEventFilter(javafx.scene.input.ScrollEvent.SCROLL,
new EventHandler<javafx.scene.input.ScrollEvent>() {
#Override
public void handle(final javafx.scene.input.ScrollEvent scrollEvent) {
Object virtualFlow = ((javafx.scene.control.SkinBase<?>) tableView.getSkin()).getChildren().get(1);
double position = -1;
try {
position = (double) virtualFlow.getClass().getMethod("getPosition").invoke(virtualFlow);
} catch (Exception ignored) { }
if(position == 0.0) {
System.out.println("scrolled to top!");
}
else if(position == 1.0) {
System.out.println("scrolled to bottom!");
}
}
});
Related
I want to ask if it is possible to make a chip in JFXChipView editable once it has been set.
You can create your own JFXChip and implement a behavior to enable editing. First, you need to have an editable label. I looked up online and I found this post: JavaFX custom control - editable label. Then, you can extend JFXChip to use that EditableLabel:
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXChip;
import com.jfoenix.controls.JFXChipView;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.layout.HBox;
public class EditableChip<T> extends JFXChip<Property<T>> {
protected final HBox root;
public EditableChip(JFXChipView<Property<T>> view, Property<T> item) {
super(view, item);
JFXButton closeButton = new JFXButton(null, new SVGGlyph());
closeButton.getStyleClass().add("close-button");
closeButton.setOnAction(event -> {
view.getChips().remove(item);
event.consume();
});
// Create the label with an initial value from the item
String initialValue = view.getConverter().toString(item);
EditableLabel label = new EditableLabel(initialValue);
label.setMaxWidth(100);
// Bind the item to the text in the label
item.bind(Bindings.createObjectBinding(() -> view.getConverter().fromString(label.getText()).getValue(), label.textProperty()));
root = new HBox(label, closeButton);
getChildren().setAll(root);
}
}
Note: I am using Property<T> instead of using the desired class T because JFXChipView stores the item the first time you add it. And in that case, you're going to get the values as you entered them the first time when calling JFXChipView#getChips().
Sample application:
import com.jfoenix.controls.JFXChipView;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class EditableChipViewApp extends Application {
#Override
public void start(Stage primaryStage) {
JFXChipView<Property<String>> chipView = new JFXChipView<>();
chipView.setChipFactory(EditableChip::new);
chipView.setConverter(new StringConverter<Property<String>>() {
#Override
public String toString(Property<String> object) {
return object == null ? null : object.getValue();
}
#Override
public Property<String> fromString(String string) {
return new SimpleStringProperty(string);
}
});
VBox container = new VBox(chipView);
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Result:
This is how you get the actual values of the chips:
List<String> chipsValues = chipView.getChips().stream().map(Property::getValue).collect(Collectors.toList());
I'm trying to display text in TextArea with delay in between each sentence, like you're having a conversation.
I tried using the sleep function but this doesn't work since the text only gets displayed when all methods stopped running.
What would be an efficiƫnt way to do this:
(Pseudo code)
textArea.appendText("Goodday sir, how are you doing?");
(0.5 second delay);
textArea.appendText("I'm fine thanks");
(1 second delay);
textArea.appendText("What can I do for you?");
getPlayerInput();
textArea.appendText("Sure, I'll take care of it.");
To clarify what I'm trying to do:
Display text in textArea with delays inbetween and be able to run functions in between.
As a variation on the timeline in the other answer, you can create a different KeyFrame for every message you want to display. This avoids the scenario of having "nested timelines", which I think would become unmanageable if you had more than two or three messages to display one after the other.
Here's a SSCCE using this idea:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Conversation extends Application {
private TextArea console ;
private TextField input ;
private BooleanProperty readyForInput ;
private Timeline createTimeline(String[] messages) {
Timeline timeline = new Timeline();
Duration delayBetweenMessages = Duration.seconds(1);
Duration frame = delayBetweenMessages ;
for (String msg : messages) {
timeline.getKeyFrames().add(new KeyFrame(frame, e -> console.appendText(msg+"\n")));
frame = frame.add(delayBetweenMessages);
}
timeline.statusProperty().addListener((obs, oldStatus, newStatus) -> {
readyForInput.set(newStatus != Animation.Status.RUNNING);
if (newStatus != Animation.Status.RUNNING) {
input.requestFocus();
}
});
return timeline ;
}
#Override
public void start(Stage primaryStage) {
readyForInput = new SimpleBooleanProperty(false);
console = new TextArea();
console.setEditable(false);
input = new TextField();
input.disableProperty().bind(readyForInput.not());
input.setOnAction(e -> {
String inputText = input.getText();
console.appendText("> "+inputText+"\n");
input.clear();
createTimeline(getMessages(inputText)).play();
});
BorderPane root = new BorderPane(console, input, null, null, null) ;
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
createTimeline(getMessages(null)).play();
}
private String[] getMessages(String input) {
if (input == null || input.isEmpty()) {
return new String[] {
"Goodday sir, how are you doing?",
"I'm fine thanks",
"What can I do for you?"
};
} else {
// AI logic here...
return new String[] { "Sure, I'll take care of it." };
}
}
public static void main(String[] args) {
launch(args);
}
}
you can use a Timeline's onFinished to make delayed actions in JavaFX
try the following code
package application;
import java.util.ArrayList;
import java.util.Iterator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
Timeline delay = new Timeline();
TextArea textArea = new TextArea();
boolean waitForInput = false;
Msg current;
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(textArea);
Scene scene = new Scene(root, 500, 500);
ArrayList<Msg> msgs = new ArrayList<Msg>();
msgs.add(new Msg("Goodday sir, how are you doing?\n", Duration.seconds(1), false));
msgs.add(new Msg("i'm fine thanks!\n", Duration.seconds(2), false));
msgs.add(new Msg("What can I do for you?\n", Duration.seconds(0.1), true));
msgs.add(new Msg("Sure, I'll take care of it.\n", Duration.seconds(1), false));
msgs.add(new Msg("....", Duration.seconds(0.5), false));
msgs.add(new Msg("are you sure it's the only thing you need?\n", Duration.seconds(0.1), true));
msgs.add(new Msg("alright bye", Duration.seconds(0), true));
Iterator<Msg> it = msgs.iterator();
delay.getKeyFrames().setAll(new KeyFrame(Duration.seconds(0)));
delay.setOnFinished(e -> {
if (it.hasNext()) {
current = it.next();
delay.getKeyFrames().setAll(new KeyFrame(current.getDuration()));
delay.playFromStart();
textArea.appendText(current.getContent());
if (current.requiresInput()) {
waitForInput = true;
delay.pause();
}
}
});
delay.playFromStart();
primaryStage.setScene(scene);
primaryStage.show();
scene.addEventFilter(KeyEvent.KEY_PRESSED, e ->
{
if (waitForInput && e.getCode().equals(KeyCode.ENTER)) {
delay.play();
waitForInput = false;
}
});
scene.addEventFilter(KeyEvent.KEY_TYPED, e -> {
if (!waitForInput) {
e.consume();
}
});
}
public static void main(String[] args) {
launch(args);
}
class Msg {
private boolean requireInput;
private String content;
private Duration duration;
public Msg(String c, Duration d, boolean b) {
content = c;
duration = d;
requireInput = b;
}
public String getContent() {
return content;
}
public Duration getDuration() {
return duration;
}
public boolean requiresInput() {
return requireInput;
}
}
}
How can I display the names of nodes with children bold in a javax TreeView? (Leave nodes should be displayed non-bold)
Use a cell factory on the tree that sets the state of a CSS pseudoclass on the tree cell it creates, according to whether the tree item displayed is a leaf or not. Then you can use an external css file that styles the leaf nodes and non-leaf nodes any way you like.
Example:
import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class BoldNonLeafNodes extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final BorderPane uiRoot = new BorderPane();
TreeItem<Integer> root = createTreeItem(1);
final TreeView<Integer> tree = new TreeView<>(root);
PseudoClass leaf = PseudoClass.getPseudoClass("leaf");
tree.setCellFactory(tv -> {
TreeCell<Integer> cell = new TreeCell<>();
cell.itemProperty().addListener((obs, oldValue, newValue) -> {
if (newValue == null) {
cell.setText("");
} else {
cell.setText(newValue.toString());
}
});
cell.treeItemProperty().addListener((obs, oldTreeItem, newTreeItem) ->
cell.pseudoClassStateChanged(leaf, newTreeItem != null && newTreeItem.isLeaf()));
return cell ;
});
uiRoot.setCenter(tree);
final Scene scene = new Scene(uiRoot);
scene.getStylesheets().add("bold-non-leaf-nodes.css");
primaryStage.setScene(scene);
primaryStage.setTitle(getClass().getSimpleName());
primaryStage.show();
}
private TreeItem<Integer> createTreeItem(int value) {
TreeItem<Integer> item = new TreeItem<>(value);
if (value < 10000) {
for (int i=0; i<10; i++) {
item.getChildren().add(createTreeItem(10*value+i));
}
}
return item ;
}
public static void main(String[] args) {
launch(args);
}
}
bold-non-leaf-nodes.css:
.tree-cell {
-fx-font-weight: bold ;
}
.tree-cell:leaf {
-fx-font-weight: normal ;
}
I have created ListView of Labels using :
ListView<Label> list = new ListView<Label>();
Image folder = new Image(getClass().getResourceAsStream("folder.png"));
ObservableList<Label> data = FXCollections.observableArrayList();
for (int i = 0; i < 6; i++) {
Label lbl = new Label();
lbl.setText("label" + i);
lbl.setGraphic(new ImageView(folder));
lbl.setContentDisplay(ContentDisplay.LEFT);
lbl.setGraphicTextGap(10.2);
data.add(lbl);
}
list.setItems(data);
I want the user to be able to double click on any of the Labels within the ListView, the selected Label should be replaced with a TextField so that the user can enter a new label name dynamically.
After the user presses Enter the TextField should turn back into a Label.
Don't use Label as the type of data for the ListView. Use String. Then you can just use the standard TextFieldListCell which has exactly the functionality you describe. Since you want a graphic in the standard cell display, just subclass TextFieldListCell and override the appropriate methods to include the graphic when the text field is not displayed:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
public class EditableListViewTest extends Application {
#Override
public void start(Stage primaryStage) {
ListView<String> list = new ListView<>();
Image testImg = new Rectangle(12, 12, Color.CORNFLOWERBLUE).snapshot(null, null);
for (int i = 0; i < 6; i++) {
list.getItems().add("label "+i);
}
StringConverter<String> identityStringConverter = new DefaultStringConverter();
list.setCellFactory(lv -> new TextFieldListCell<String>(identityStringConverter) {
private ImageView imageView = new ImageView(testImg);
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (! empty && ! isEditing()) {
setStaticGraphic();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setStaticGraphic();
}
#Override
public void commitEdit(String newValue) {
super.commitEdit(newValue);
setStaticGraphic();
}
private void setStaticGraphic() {
setGraphic(imageView);
setContentDisplay(ContentDisplay.LEFT);
setGraphicTextGap(10.2);
}
});
list.setEditable(true);
primaryStage.setScene(new Scene(new BorderPane(list), 250, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a ListView with a TextField above it. If a user enters in a search query into the textfield, the listview will update and filter itself to show relevant results.
The ListView shows items from a FilteredList, which is filled with Employee objects. Each Employee has a first and last name.
package application.ctrl;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import application.Main;
import application.objects.Employee;
import application.objects.EmployeeDatabase;
public class EmployeePickerWidget extends VBox implements Initializable {
#FXML
private TextField textField;
#FXML
private Button addNewEmployee;
#FXML
private ListView<Employee> employeeList;
private FilteredList<Employee> filteredList;
private ContextMenu cm;
private CustomMenuItem item;
private ClickedEmployeeInterface parent;
public EmployeePickerWidget(ClickedEmployeeInterface parent) {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource(
Main.EMPLOYEE_PICKER));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.parent = parent;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
setupEmployeeListView();
setupTextField();
}
private void setupEmployeeListView() {
filteredList = new FilteredList<Employee>(EmployeeDatabase.getInstance()
.getObservableList());
employeeList = new ListView<Employee>();
employeeList.setItems(filteredList);
employeeList.setOnMouseClicked(arg0 -> {
if (employeeList.getSelectionModel().getSelectedItem() != null) {
cm.hide();
parent.handleClickedEmployee();
}
});
}
private void setupTextField() {
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
filteredList.setPredicate(employee -> {
return filterHelper(employee, newValue);
});
});
textField.setText(" ");
textField.setText("");
textField.setOnMouseClicked(event -> cm
.show(textField, Side.BOTTOM, 0, 0));
cm = new ContextMenu();
item = new CustomMenuItem();
VBox container = new VBox();
container.setAlignment(Pos.CENTER_RIGHT);
container.getChildren().add(employeeList);
Button defineEmployeeBtn = new Button("Define New Employee");
defineEmployeeBtn.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
Main.DEFINE_NEW_EMPLOYEE));
Parent root = null;
try {
root = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
});
container.getChildren().add(defineEmployeeBtn);
item.setContent(container);
cm.getItems().add(item);
}
private boolean filterHelper(Employee employee, String query) {
String first = employee.getFirst().toLowerCase(), last = employee
.getLast().toLowerCase();
String[] querySplit = query.replace(",", "\\s").split("\\s+");
int length = querySplit.length;
for (int i = 0; i < length; i++)
querySplit[i] = querySplit[i].toLowerCase();
if (length == 1) {
if (first.contains(querySplit[0]) || last.contains(querySplit[0]))
return true;
else
return false;
} else if (length == 2) {
if (first.contains(querySplit[0]) || last.contains(querySplit[0]))
if (first.contains(querySplit[1]) || last.contains(querySplit[1]))
return true;
return false;
} else if (length == 3) {
return false;
}
return false;
}
public Employee getEmployee() {
return employeeList.getSelectionModel().getSelectedItem();
}
#FXML
public void addNewEmployee() {
}
}
interface ClickedEmployeeInterface {
void handleClickedEmployee();
}
If there were 3 employees named "Donald Trump", "Donald Smith", and "Donald Jackson" in the database, then the following needs to happen:
Typing up to the word "Donald" will show all 3 results.
Typing a space after Donald (resulting in "Donald ") will still show 3 results.
Typing a T after the previous query (resulting in "Donald T") should only show 1 result.
The problem is, after I enter in a space, the ListView breaks, and all of my Employees disappear from the ListView. When I click outside of the textfield and click back in again, it triggers this:
textField.setOnMouseClicked(event -> cm
.show(textField, Side.BOTTOM, 0, 0));
And my ListView suddenly works again, showing that one Employee.
How do I make the ListView filter properly without having to click out and back in?
I do not have the FXML file, so I wasn't able to replicate your problem. There are multiple problems with your code and this is the not the optimum solution, still, I have edited your answer to give you hints and help you understand the areas where you might have committed logical errors
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class DemoList extends Application {
#Override
public void start(Stage stage) throws Exception {
GridPane gridPane = new GridPane();
Label label = new Label("Name");
final TextField textField = new TextField();
textField.setFocusTraversable(false);
textField.setPromptText("Please Type Here");
final ContextMenu cm = new ContextMenu();
final ObservableList<String> employeeList = FXCollections
.observableArrayList();
employeeList.addAll("Donald Duck", "Donald Mouse", "Donald Goofy");
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> arg0,
String arg1, String arg2) {
// To clear the Context Menu so that same items are not added
// multiple times
cm.getItems().clear();
for (String employee : employeeList) {
if (filterHelper(employee, arg2)) {
cm.getItems().add(new MenuItem(employee));
}
}
}
});
textField.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
// To clear the Context Menu so that same items are not added
// multiple times
cm.getItems().clear();
//Adding the data for initial click
for (String employee : employeeList) {
if (filterHelper(employee, textField.getText())) {
cm.getItems().add(new MenuItem(employee));
}
}
cm.show(textField, Side.BOTTOM, 0, 0);
}
});
gridPane.add(label, 0, 0);
gridPane.add(textField, 0, 1);
Scene scene = new Scene(gridPane, 300, 300);
stage.setScene(scene);
stage.show();
}
private boolean filterHelper(String employee, String query) {
//Splitting Employee name to fetch first and last name
String first = employee.split(" ")[0].toLowerCase(), last = employee
.split(" ")[1].toLowerCase();
String[] querySplit = query.replace(",", "\\s").split("\\s+");
int length = querySplit.length;
for (int i = 0; i < length; i++)
querySplit[i] = querySplit[i].toLowerCase();
/**
* Avoid adding unnecessary return statement
* I have removed all the 'return false' statements
* The last return will take care of all the 'return false'
*/
//only single word
if (length == 1) {
if (first.startsWith(querySplit[0])
|| last.startsWith(querySplit[0]))
return true;
}
//two words, considering first word is first name
//and second word is last name
else if (length == 2) {
if (first.startsWith(querySplit[0])
&& last.startsWith(querySplit[1]))
return true;
}
return false;
}
public static void main(String[] args) {
launch(args);
}
}