JavaFX how to check if all dynamically added textfields are empty - java

I have a TextField and two buttons in a row.
One button (the 'add button') adds another row of a TextField and another pair of add and delete button, the other button deletes the row.
The delete button is disabled while the current row is the only row, so there can't be no rows.
The add button is enabled only if the textfield of the current row is not empty and if it is the last textfield. So every row has a disabled 'add button' except for the last one.
My question now is how can I bind the 'add button' disableProperty to all textfields that exist and check if they are empty. As a matter of fact, I think I only have to check the last textfield and if it is empty I disable the last 'add button' if something is written the last 'add button' remains disabled but the current one of the row gets enabled.
I have found a workaround, in which I bind the button to the textfield, then if I add another row I unbind the button, disable it, and if I delete a row I only enable the last button and bind it againt to the textfield.
This solution seems very clunky and I was wondering if there is a more elgant solution with property binding.
My code (with the workaround, so you can see what I want to do):
public class Controller {
#FXML
private VBox VBox;
public ObservableList<TextField> oList = FXCollections.observableArrayList();
public ObservableList<Button> bList = FXCollections.observableArrayList();
public void initialize(){
createRow();
}
private void createRow(){
HBox box = new HBox(10);
TextField textField = new TextField();
Button addButton = new Button("Add row");
Button deleteButton = new Button("Delete");
box.getChildren().addAll(textField, addButton, deleteButton);
VBox.getChildren().add(box);
oList.add(textField);
bList.add(addButton);
addButton.disableProperty().bind(Bindings.isEmpty(textField.textProperty()));
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
addButton.disableProperty().unbind();
createRow();
textField.setDisable(true);
addButton.setDisable(true);
}
});
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
int idx = oList.indexOf(textField);
oList.remove(idx);
bList.remove(idx);
VBox.getChildren().remove(idx);
for(TextField tf : oList){
int i = oList.indexOf(tf);
if(oList.size()-1 == i){
tf.setDisable(false);
bList.get(i).disableProperty().bind(Bindings.isEmpty(tf.textProperty()));
}
}
}
});
}
}
And two Screenshots:
4 rows added, all 'add buttons' disabled, also the last one bc nothing is written in textfield
Here I deleted row test1 and the empty row and all 'add buttons' are still disabled except for the last one because there is text in the text field
Thanks for your help!
PS: I know in my Code ObservableLists aren't necessary, but I was trying things out and let them in because I forgot...

You can create a BooleanExpression that returns true if all TextFields in the list are empty. Note that you need to recreate that expression as the contents of the ObservableList<TextField>' change:
oList.addListener((ListChangeListener<? super TextField>) c -> {
addButton.disableProperty().unbind();
BooleanExpression allEmpty = oList.stream()
.map(tf -> BooleanExpression.booleanExpression(tf.textProperty().isEmpty()))
.reduce(new SimpleBooleanProperty(true), BooleanExpression::and);
addButton.disableProperty().bind(allEmpty);
});
Everytime a new TextField is added or removed, each TextField in the list will be mapped to a BooleanExpression of the empty property of that TextField. Then, all expressions will be anded together.
Note: The same way can also be done using a loop instead of a stream.
Note: You need to add this listener before adding any element to the list.

Related

Add buttons on a treeViewer

I would like to add a button on some rows of my TreeViewer. To do that, I’ve used a method that I saw on a forum and it was working on a TableViewer.
I‘ve implemented my own Label provider on the column where I want the button to be. So I’ve overriden the class update(ViewerCell cell) which calls my method addButton(cell):
(I have simplified the code for a better comprehension)
public class SelectVariableButtonLabelProvider extends ColumnLabelProvider {
#Override
public void update(ViewerCell cell) {
if(...){
addButton(cell);
}
}
private void addButton(ViewerCell cell) {
TreeItem item = (TreeItem) cell.getItem();
Button btn = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE);
btn.setText(" select variable ");
//action when the button is clicked
btn.addListener(SWT.Selection, new SelectVariableButtonListener(tree,
DataTypeTreeUtils.getTreeNodeDataTypeInstance(cell.getElement()), viewer));
TreeEditor editor = new TreeEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
// editor.horizontalAlignment = SWT.RIGHT;
editor.minimumWidth = btn.getSize().x + 110;
editor.setEditor(btn, item, cell.getColumnIndex());
editor.layout();
}
}
It’s almost working. Except that the buttons of the column of buttons is duplicated when I want to extend the column.
screenshot of the bug
The left “column of buttons'' : is completely working. The buttons are functional and they adapt themself to the extension of the nodes in the tree Viewer.
The right “column of buttons” : is fixed on the viewer and the buttons are not completely functional. And when I want to extend or not the nodes in the tree, the buttons are not corresponding to their rows anymore. (These are also the buttons in the foreground).
So I would like to not have the right columns which probably appeared because of a bug. I think this could be due to the composite to which the button is initialized :
Button btn = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE);
Or just because buttons are simply bugging when they are on Tree Viewers ? The same method is working on Table Viewers.
Just in case and if it helps, this is the declaration of the viewer:
viewer = new TreeViewer(treeContainer, SWT.FULL_SELECTION);
viewer.setContentProvider(new TreeNodeTreeContentProvider());
viewer.setLabelProvider(new CustomColumnLabelProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
and this is the declaration of the column where I want to add the buttons:
//column with the buttons "select variable"
TreeViewerColumn viewerSetValueColumn = new TreeViewerColumn(viewer, SWT.NONE);
viewerSetValueColumn.getColumn().setWidth(110);
viewerSetValueColumn.setLabelProvider(new SelectVariableButtonLabelProvider(viewer, getAllVariables()));
EDIT:
I would like to have buttons in some rows of a column of my treeViewer. But I want them to be always visible, so I would like to avoid using editing support.
I've used the LabelProvider to do it but it caused a bug (screenshot of the bug).
Does anyone know how to add buttons to a treeViewer using the labelProvider?

How to call a button on button click recursively?

How can I add new Button on clicking a button for the infinite or dynamic number of times? Or in simple words how can perform the functionality of add more buttons?
I have tried in Javafx, but it only adding the two buttons in list.
#FXML
void addMoreButton(ActionEvent event) {
b[count]=new Button("+");
b[count].setOnMouseClicked(event2 -> {
count++;
b[count]=new Button("+");
list.setAll(b[count]);
vboxTest.getChildren().addAll(list);
});
list.add(b[count]);
vboxTest.getChildren().addAll(list);
}
but I have to add dynamically till user wants to add/press the previous button
only the last added button should call the next one (means second button call third button , and third call to forth and so on)...
Assuming you start with exactly 1 button using the method as onAction event handler, you need to do 2 things to properly update the scene:
add a new button using the same event handler
remove the event handler from the button clicked
You can do so by accessing the source of the event:
#FXML
void addMoreButton(ActionEvent event) {
Button source = (Button) event.getSource();
source.setOnAction(null); // remove event handler
Button newButton = new Button("+");
newButton.setOnAction(this::addMoreButton);
vboxTest.getChildren().add(newButton);
}
Note: I left out the list/array on purpose since the array restricts the number of elements (ArrayIndexOutOfBoundsException) and should result in an exception for containing null/a value already in the child list, unless you set the array size to 1 which allows you to do one call. Of course you could save those Buttons in a list, if other code requires you to do this.
Not sure if removing the event handler is the desired result. Different things like inserting the new button after the one clicked could also be done:
#FXML
void addMoreButton(ActionEvent event) {
Node source = (Node) event.getSource();
Button newButton = new Button("+");
newButton.setOnAction(this::addMoreButton);
vboxTest.getChildren().add(vboxTest.getChildren().indexOf(source) + 1, newButton);
}

How to detect when a ComboBox's menu item is really selected by the user?

I'm currently facing the situation that if a user clicks on my ComboBox and moves the selection with his keys, the selection listener will keep being called, although for all purposes a choice was still not really made by the user.
How can I distinguish those "intermediate" selections from the proper, final, user selection in my ComboBox?
I tried looking at variables such as isPopupVisible or even playing with PopupMenuListener but they didn't seem to really help.
Thanks
Edit: Example of the offending code:
public class Main extends JFrame {
public Main() {
setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));
JComboBox<String> comboBox = new JComboBox<>();
comboBox.setModel(new DefaultComboBoxModel<>(new String[] { "a", "b", "c" }));
comboBox.addItemListener(e -> {
System.out.println(e.getItem());
});
add(comboBox);
}
}
I need something that only fires when the user actually clicks and the popup disappears.
comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
Set the above property so the event is only generated on a mouse released or an Enter key.
Note:
if you use an ActionListner the event will always be generated when an item is selected.
if you use an ItemListener the event is only generated if you change the selected item from its previous selection.
Edit:
You could disable key selection by using:
JComboBox comboBox = new JComboBox( model )
{
#Override
public boolean selectWithKeyChar(char keyChar)
{
return false;
}
};
Edit 2:
Or maybe as a hack you can disable your listener when you do the key search. The code might be something like:
JComboBox comboBox = new JComboBox( model )
{
#Override
public boolean selectWithKeyChar(char keyChar)
{
// remove the listener here
// This will cause the selected index to change
Boolean result = super.selectWithKeyChar(keyChar);
// add the listener back here
return result;
}
};

How can I trigger code in JavaFX when the text of a ComboBox changes?

I have a ComboBox in JavaFX. I want to trigger an event whenever the user types or deletes a character in the ComboBox, so that I can call ComboBox.show() and populate the dropdown with custom text, to make a sort of "predictive text" feature.
However, I cannot find any way to trigger an event upon text change. Does any such methodology exist? If not, can you recommend any other way to accomplish what I am trying to do?
Here's my ComboBox:
ObservableList<String> options =
FXCollections.observableArrayList(
"Option 1",
"Option 2",
"Option 3"
);
ComboBox myComboBox = new ComboBox(options);
myComboBox.setEditable(true);
and here's the code that I've tried to use to trigger an event:
myComboBox.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Event triggered.");
myComboBox.show();
}
});
The problem with this code, however, is that it only goes off when the user presses "Enter" in the ComboBox, or when they select an item from the dropdown.
Any ideas?
You can retrieve the TextField that is used to edit the content of the combo box with
myComboBox.getEditor();
Like any text input component, the text in it is represented by an observable StringProperty that you can retrieve with textProperty(). You can add a listener to this property that is notified if the text changes:
myComboBox.getEditor().textProperty().addListener((obs, oldText, newText) -> {
// do whatever you need with newText (or oldText too if you need)
});

JavaFx Combobox drop down list width is less on first click on Combo box

when I click on the ComboBox the first time and the popup menu list that is shown has its width very short. The second time I click on the ComboBox and the list is shown again, the width is now correct as the width of the list now aligns itself with the Combbox.
i tried to alter the width of the drop down on mouse click on the combo box. But it did not work,
final ComboBox<String> combo = new ComboBox<String>();
combo.getStyleClass().add("combo-border");
combo.setMinWidth(100.0);
combo.setEditable(true);
combo.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
combo.setMinWidth(100.0); // Did not work
//csectCombo.setPrefWidth(100.0); // Did not work
}
});
I am using Javafx 2.2. Is there any workaround for this?
From the below post, it says that is the known bug in JavaFx 2 and has been fixed in JavaFx 8.
http://tech.chitgoks.com/2013/09/20/width-of-combobox-popup-list-is-too-small-in-java-fx-2/
Try this
final ComboBox<String> combo = new ComboBox<String>();
combo.getStyleClass().add("combo-border");
combo.setMinWidth(100.0);
combo.setEditable(true);
combo.setPrefWidth(combo.getMinWidth());

Categories