Binding model data to UI components is accomplished by linking a Value instance to an instance of its subclass ComponentValue, which represents a value based on an input component.
//a nullable integer value, initialized to 42
Value<Integer> integerValue =
Value.nullable(42);
//create a spinner linked to the value
JSpinner spinner =
Components.integerSpinner(integerValue)
.build();
//create a NumberField component value, basically doing the same as
//the above, with an extra step to expose the underlying ComponentValue
ComponentValue<Integer, NumberField<Integer>> numberFieldValue =
Components.integerField()
//linked to the same value
.link(integerValue)
.buildValue();
//fetch the input field from the component value
NumberField<Integer> numberField = numberFieldValue.component();
1. Text
1.1. TextField
Value<String> stringValue = Value.nullable();
JTextField textField =
Components.stringField(stringValue)
.preferredWidth(120)
.transferFocusOnEnter(true)
.build();
Value<Character> characterValue = Value.nullable();
JTextField textField =
Components.characterField(characterValue)
.preferredWidth(120)
.transferFocusOnEnter(true)
.build();
2. Numbers
2.1. Integer
Value<Integer> integerValue = Value.nullable();
NumberField<Integer> integerField =
Components.integerField(integerValue)
.valueRange(0, 10_000)
.groupingUsed(false)
.build();
2.2. Long
Value<Long> longValue = Value.nullable();
NumberField<Long> longField =
Components.longField(longValue)
.groupingUsed(true)
.build();
3. Date & Time
3.1. LocalTime
Value<LocalTime> localTimeValue = Value.nullable();
TemporalField<LocalTime> temporalField =
Components.localTimeField(localTimeValue)
.dateTimePattern("HH:mm:ss")
.build();
4. Boolean
4.1. CheckBox
//non-nullable so use this value instead of null
boolean nullValue = false;
Value<Boolean> booleanValue =
Value.builder()
.nonNull(nullValue)
.value(true)
.build();
JCheckBox checkBox =
Components.checkBox(booleanValue)
.text("Check")
.horizontalAlignment(SwingConstants.CENTER)
.build();
5. Selection
5.1. ComboBox
Value<String> stringValue = Value.nullable();
DefaultComboBoxModel<String> comboBoxModel =
new DefaultComboBoxModel<>(new String[] {"one", "two", "three"});
JComboBox<String> comboBox =
Components.comboBox(comboBoxModel, stringValue)
.preferredWidth(160)
.build();
5.1.1. FilterComboBoxModel
Supplier<Collection<String>> items = () ->
List.of("One", "Two", "Three");
FilterComboBoxModel<String> model =
FilterComboBoxModel.builder(items)
.nullItem("-")
.build();
JComboBox<String> comboBox =
Components.comboBox(model)
.mouseWheelScrolling(true)
.build();
// Hides the 'Two' item.
model.items().visible().predicate()
.set(item -> !item.equals("Two"));
// Prints the selected item
model.selection().item()
.addConsumer(System.out::println);
// Refreshes the items using the supplier from above
model.items().refresh();
5.1.2. Completion
Completion provides a way to enable completion for combo boxes.
The available completion modes are:
Combo boxes created via Components have completion enabled by default, with MAXIMUM_MATCH being the default completion mode.
The default completion mode is controlled via the Completion.COMPLETION_MODE configuration value.
Normalization
Strings are normalized by default during completion, that is, accents are removed, i.e. á, í and ú become a, i and u. To enable accented character sensitivity, normalization can be turned off, either globally via the Completion.NORMALIZE configuration value or individually via the combo box builder.
FilterComboBoxModel<String> model =
FilterComboBoxModel.builder(List.of("Jon", "Jón", "Jónsi"))
.nullItem("-")
.build();
JComboBox<String> comboBox =
Components.comboBox(model)
// Auto completion
.completionMode(Completion.Mode.AUTOCOMPLETE)
// Accented characters not normalized
.normalize(false)
.build();
6. Custom
6.1. TextField
In the following example we link a value based on a Person class to a component value displaying text fields for a first and last name.
class Person {
final String firstName;
final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return lastName + ", " + firstName;
}
}
class PersonPanel extends JPanel {
final JTextField firstNameField = new JTextField();
final JTextField lastNameField = new JTextField();
public PersonPanel() {
setLayout(new GridLayout(2, 2));
add(new JLabel("First name"));
add(new JLabel("Last name"));
add(firstNameField);
add(lastNameField);
}
}
class PersonPanelValue extends AbstractComponentValue<Person, PersonPanel> {
public PersonPanelValue(PersonPanel component) {
super(component);
//We must call notifyListeners() each time this value changes,
//that is, when either the first or last name changes.
component.firstNameField.getDocument()
.addDocumentListener((DocumentAdapter) e -> notifyListeners());
component.lastNameField.getDocument()
.addDocumentListener((DocumentAdapter) e -> notifyListeners());
}
@Override
protected Person getComponentValue() {
return new Person(component().firstNameField.getText(), component().lastNameField.getText());
}
@Override
protected void setComponentValue(Person value) {
component().firstNameField.setText(value == null ? null : value.firstName);
component().lastNameField.setText(value == null ? null : value.lastName);
}
}
Value<Person> personValue = Value.nullable();
PersonPanel personPanel = new PersonPanel();
Value<Person> personPanelValue = new PersonPanelValue(personPanel);
personPanelValue.link(personValue);