Hey all I need some assistance in getting my tag app working as it should. Currently it displays values like this:
However, I am wanting it to wrap the tags instead of continuing to go horizontal. I changed all the HBox's to FlowPane's but that made it look all weird and didn't work well. I also tried doing VBox's but it still messes it up.
What I am wanting it to look like is this:
The current code:
Is there a way of doing this with the hbox?
Using the code for the FlowPane this is what the output looks like:
However, I am wanting it to wrap the tags instead of continuing to go horizontal. I changed all the HBox's to FlowPane's but that made it look all weird and didn't work well. I also tried doing VBox's but it still messes it up.
What I am wanting it to look like is this:
The current code:
Java:
package application;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
public class Main3 extends Application{
Color txtColor;
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("TagInputFx Demo");
VBox root = new VBox();
AutocompleteMultiSelectionBox tagInput = new AutocompleteMultiSelectionBox();
ObservableSet<String> sug = FXCollections.observableSet();
String[] listOfDBCalls = "Integrated Security=, user=, password=, pwd=, Database=, Encrypt=, Trusted_Connection=, Persist Security Info=, TrustServerCertificate=, User ID=, Initial Catalog=, AttachDbFileName=, Failover Partner=, Asynchronous Processing=, User Instance=, Packet Size=, Column Encryption Setting=, Network Library=, MultipleActiveResultSets=, Data Source=, Server=, Enclave Attestation Url=, Provider=, UID=, Connect Timeout=, Driver=, MARS_Connection=, Proxy Password=, Proxy User Id=, Host=, Pooling=, Max Pool Size=, Connection Lifetime=, Incr Pool Size=, Decr Pool Size=, DBA Privilege=, Load Balancing=, Dbq=, DistribTX=, OledbKey1=, OledbKey2=, ConnectString=, Version=, New=, UseUTF16Encoding=, Legacy Format=, Read Only=, DateTimeFormat=, BinaryGUID=, Cache Size=, Page Size=, Enlist=, Max Page Count=, Journal Mode=, Synchronous=, Compress=, UTF8Encoding=, Timeout=, NoTXN=, SyncPragma=, StepAPI=, LongNames=, Port=, location=, sslmode=, Protocol=, SslMode=, MinPoolSize=, MaxPoolSize=".split(",");
for (String call : listOfDBCalls) {
sug.add(call);
}
tagInput.setSuggestions(sug);
tagInput.setTextColor(Color.BLACK);
Text header = new Text("Tags:");
root.getChildren().addAll(header, tagInput);
Scene scene = new Scene(root, 600, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main3(String[] args) {
launch(args);
}
public class AutocompleteMultiSelectionBox extends HBox {
private final ObservableList<String> tags;
private final ObservableSet<String> suggestions;
private ContextMenu entriesPopup;
private static final int MAX_ENTRIES = 15;
private final TextField inputTextField;
public AutocompleteMultiSelectionBox() {
getStyleClass().setAll("tag-bar");
getStylesheets().add(getClass().getResource("application.css").toExternalForm());
tags = FXCollections.observableArrayList();
suggestions = FXCollections.observableSet();
inputTextField = new TextField();
this.entriesPopup = new ContextMenu();
setListner();
inputTextField.setOnKeyPressed(event -> {
if (event.getCode().equals(KeyCode.BACK_SPACE) && !tags.isEmpty() && inputTextField.getText().isEmpty()) {
String last = tags.get(tags.size() - 1);
String orgTag = last.split("=")[0] + "=";
if (orgTag.length() > 2) {
suggestions.add(orgTag);
tags.remove(last);
}
} else if (event.getCode().toString() == "ENTER" || event.getCode().toString() == "TAB" && !inputTextField.getText().isEmpty()) {
String newTag = inputTextField.getText();
String orgTag = newTag.split("=")[0] + "=";
if (orgTag.length() > 2) {
suggestions.add(newTag);
tags.add(newTag);
inputTextField.setText("");
suggestions.remove(orgTag);
suggestions.remove(newTag);
}
} else if (event.getCode().toString() == "ESCAPE") {
inputTextField.clear();
} else if (event.getCode().toString() == "DOWN" || event.getCode().toString() == "UP") {
entriesPopup.getSkin().getNode().lookup(".menu-item").requestFocus();
}
System.out.println(event.getCode().toString());
});
inputTextField.prefHeightProperty().bind(this.heightProperty());
HBox.setHgrow(inputTextField, Priority.ALWAYS);
inputTextField.setBackground(null);
tags.addListener((ListChangeListener.Change<? extends String> change) -> {
while (change.next()) {
if (change.wasPermutated()) {
ArrayList<Node> newSublist = new ArrayList<>(change.getTo() - change.getFrom());
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
newSublist.add(null);
}
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
newSublist.set(change.getPermutation(i), getChildren().get(i));
}
getChildren().subList(change.getFrom(), change.getTo()).clear();
getChildren().addAll(change.getFrom(), newSublist);
} else {
if (change.wasRemoved()) {
getChildren().subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
}
if (change.wasAdded()) {
getChildren().addAll(change.getFrom(), change.getAddedSubList().stream().map(Tag::new).collect(
Collectors.toList()));
}
}
}
});
getChildren().add(inputTextField);
}
private TextFlow buildTextFlow(String text, String filter) {
int filterIndex = text.toLowerCase().indexOf(filter.toLowerCase());
Text textBefore = new Text(text.substring(0, filterIndex));
Text textAfter = new Text(text.substring(filterIndex + filter.length()));
Text textFilter = new Text(text.substring(filterIndex, filterIndex + filter.length()));
textFilter.setFill(Color.ORANGE);
textFilter.setFont(Font.font("Helvetica", FontWeight.BOLD, 12));
return new TextFlow(textBefore, textFilter, textAfter);
}
private void setListner() {
inputTextField.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.isEmpty()) {
entriesPopup.hide();
} else {
List<String> filteredEntries = suggestions
.stream()
.filter(e -> e.toLowerCase().contains(newValue.toLowerCase()))
.collect(Collectors.toList());
if (!filteredEntries.isEmpty()) {
populatePopup(filteredEntries, newValue);
if (!entriesPopup.isShowing()) {
entriesPopup.show(this, Side.BOTTOM, 0, 0);
}
} else {
entriesPopup.hide();
}
}
});
focusedProperty().addListener((observableValue, oldValue, newValue) -> entriesPopup.hide());
}
private void populatePopup(List<String> searchResult, String searchRequest) {
List<CustomMenuItem> menuItems = new LinkedList<>();
searchResult.stream()
.limit(MAX_ENTRIES)
.forEach(result -> {
TextFlow textFlow = buildTextFlow(result, searchRequest);
textFlow.prefWidthProperty().bind(AutocompleteMultiSelectionBox.this.widthProperty());
CustomMenuItem item = new CustomMenuItem(textFlow, true);
menuItems.add(item);
item.setOnAction(actionEvent -> {
if (result.endsWith("=")) {
//Dont close it yet.. keep typing
inputTextField.setText(result);
inputTextField.requestFocus();
inputTextField.end();
} else {
//It has values on both sides of the =
//so lets add it to the tag list
tags.add(result);
suggestions.remove(result);
inputTextField.clear();
entriesPopup.hide();
}
});
});
entriesPopup.getItems().clear();
entriesPopup.getItems().addAll(menuItems);
}
public final ObservableList<String> getTags() {
return tags;
}
public final ObservableSet<String> getSuggestions() {
return suggestions;
}
public final void setTextColor(Color c) {
txtColor = c;
}
public final void setSuggestions(ObservableSet<String> suggestions) {
this.suggestions.clear();
this.suggestions.addAll(suggestions);
}
private class Tag extends HBox {
Tag(String tag) {
Button removeButton = new Button();
Image img = new Image(getClass().getResource("delete.png").toExternalForm());
ImageView view = new ImageView(img);
view.setFitHeight(16);
view.setPreserveRatio(true);
getStyleClass().add("tag");
removeButton.setBackground(null);
removeButton.setOnAction(event -> {
tags.remove(tag);
suggestions.add(tag);
inputTextField.requestFocus();
});
Text text = new Text(tag);
text.setFill(txtColor);
text.setFont(Font.font(text.getFont().getFamily(), FontWeight.BOLD, text.getFont().getSize()));
setAlignment(Pos.CENTER);
setSpacing(2);
setPadding(new Insets(0, 0, 0, 5));
removeButton.setGraphic(view);
getChildren().addAll(text, removeButton);
}
}
}
}
Using the code for the FlowPane this is what the output looks like: