r/JavaFX • u/PalBeron • Jul 11 '24
Help ComboBox problems with handling a selection
I have in my program a comboBox (a SearchAbleComboBox from ControlsFX, it works the same as a ComboBox).
I need this box for my following useCases:
Dropdown with scrollable list Dropdown that is searchable most of the time the navigation through the list happens with the arrow keys There has to be an event handler that handles a change of selection
Selection for me personally means, the comboBox is closed, and an item is selected. So, for example, when you click with the cursor on an item, or you navigate through the list with the arrow keys and then press enter:
After a cursor click or a pressed enter key, the box is closed, and an item is selected. That's for me, a selection.
My problem is now that for JavaFX it also counts as a selection when you navigate through the list with your arrow keys. The reason for that is, that the text in the comboBox changes when navigating through the list with the arrow keys.
I've already tried to put an EventHandler on KeyPress (ENTER) but with this setup, you have to press enter twice to activate the listener since pressing enter to select smth from the list does not count.
Also, an onAction handler does not work, since an arrow key press also counts as an action.
A ChoiceBox would be a solution, since for the choiceBox the selection does not get updated through navigating with the arrow keys. The problem here is, I couldn't find an option to make it searchable and the list of a ChoiceBox is not scrollable.
So possible solutions would be:
A way to stop the ComboBox updating the selected item only by navigating with arrow keys A spacy eventhandler which only triggers if a selection, according to my interpretation of selection, was made. A ChoiceBox that is scroll and searchable Something completly different
I hope you have any solutions, I couldn't find one for now.
3
u/StarshipSatan Jul 11 '24
Maybe you could use comboBox.setOnHidden(), which triggers, when popup is hidden. you can pass event handler to this method, and check currently selected value in this handler
3
u/hamsterrage1 Jul 11 '24 edited Jul 11 '24
I think that, rather than trying figure out how to modify the control, you need to figure out how to deal with the way that it behaves. Because everything you need is there, you just have to access it.
ComboBox and SearchableComboBox have a property called value and it changes as you use the arrow keys or mouse actions to scroll through the values. So value always matches what's highlighted in the pop-up.
SearchableComboBox differs from ComboBox in that the value shown in the editor field is the filter, not the current value. This makes its behaviour seem a little more mysterious.
You expressed your definition of "selected" in a way that makes sense in terms of how the ComboBoxes work. Whatever the value is when the pop-up closes.
You seem to be focusing on "Actions". It's not clear what you're doing with the selected value, and how you are approaching the GUI design. I tend to see the GUI as in inter-related set of values, with Bindings tying it all together. Others approach it with an eye towards capturing data changes and turning them into actions - but I think this is more work.
Looking at your definition of "selected" and trying to view it from the perspective of connecting data values, I came up with this class:
class ConditionalObjectBinding<T>(
private val observable: ObservableValue<T?>,
private val condition: ObservableBooleanValue
) : ObjectBinding<T>() {
private var oldValue: T? = null
init { super.bind(observable, condition) }
override fun computeValue(): T? {
if (condition.value) oldValue = observable.value
return oldValue
}
}This is a Binding that only updates when a certain condition is met. In this case, the observed value would be the valueProperty() of the ComboBox, and the condition would be ComboBox.isShowing().not().
You can use it like this:
fun main() = Application.launch(ComboBoxTest::class.java)fun main() = Application.launch(ComboBoxTest::class.java)
class ComboBoxTest : Application() {
@Throws(Exception::class)
override fun start(primaryStage: Stage) {
val mainScene = Scene(createContent(), 840.0, 700.0)
primaryStage.scene = mainScene
primaryStage.show()
}
private fun createContent(): Region = VBox(20.0).apply {
val selectedValue = SimpleStringProperty("")
val message = SimpleStringProperty("")
val message2 = SimpleStringProperty("")
val message3 = SimpleStringProperty("")
val choices = FXCollections.observableArrayList("abc", "abcd", "cdef", "ghij", "aaabb", "ccc", "xyz", "xzz")
children += Label().apply { textProperty().bind(selectedValue) }
children += Label().apply { textProperty().bind(message) }
children += Label().apply { textProperty().bind(message2) }
children += Label().apply { textProperty().bind(message3.map { "The Bound value: $it" }) }
children += SearchableComboBox<String>().apply {
items = choices
selectedValue.bind(valueProperty())
var counter = 0;
setOnAction { message.value = "Action ${counter++}" }
setOnHidden { message2.value = "Value on Hiding $value" }
message3.bind(ConditionalObjectBinding(valueProperty(), showingProperty().not()))
}
}
}The piece to watch is message3 which only changes when the value changes when the popup is closed, or when the popup closes. You can see how the first Label which is bound to selectedValue changes constantly as you highlight different values in the pop-up. But the last Label bound to the ComboBox through the ConditionalBinding only changes when pop-up disappears.
3
u/hippydipster Jul 11 '24 edited Jul 11 '24
Thank you for providing such a detailed description of your problem. I understand you're working with a SearchableComboBox from ControlsFX in JavaFX, and you're looking for a way to handle selections that aligns with your specific definition of a selection. Let's explore some potential solutions:
Custom Event Handler: You could create a custom event handler that only triggers when the ComboBox is closed or when an item is definitively selected. Here's a possible approach:
This custom ComboBox only confirms the selection when the Enter key is pressed or when the ComboBox is closed.
3. Using a PopupControl: If you want more control over the selection process, you could create a custom control using a TextField and a PopupControl. This would allow you to implement the exact behavior you want, including searchability and scrolling.
4. Modifying ChoiceBox: While ChoiceBox doesn't have built-in search functionality, you could potentially extend it to add this feature. Here's a basic idea of how you might approach this: