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();

1.2. TextArea

Value<String> stringValue = Value.nullable();

JTextArea textArea =
        Components.textArea(stringValue)
                .rowsColumns(10, 20)
                .lineWrap(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();

2.3. Double

Value<Double> doubleValue = Value.nullable();

NumberField<Double> doubleField =
        Components.doubleField(doubleValue)
                .maximumFractionDigits(3)
                .decimalSeparator('.')
                .build();

2.4. BigDecimal

Value<BigDecimal> bigDecimalValue = Value.nullable();

NumberField<BigDecimal> bigDecimalField =
        Components.bigDecimalField(bigDecimalValue)
                .maximumFractionDigits(2)
                .groupingSeparator('.')
                .decimalSeparator(',')
                .build();

3. Date & Time

3.1. LocalTime

Value<LocalTime> localTimeValue = Value.nullable();

TemporalField<LocalTime> temporalField =
        Components.localTimeField(localTimeValue)
                .dateTimePattern("HH:mm:ss")
                .build();

3.2. LocalDate

Value<LocalDate> localDateValue = Value.nullable();

TemporalField<LocalDate> temporalField =
        Components.localDateField(localDateValue)
                .dateTimePattern("dd-MM-yyyy")
                .build();

3.3. LocalDateTime

Value<LocalDateTime> localDateTimeValue = Value.nullable();

TemporalField<LocalDateTime> temporalField =
        Components.localDateTimeField(localDateTimeValue)
                .dateTimePattern("dd-MM-yyyy HH:mm")
                .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();

4.2. NullableCheckBox

//nullable boolean value
Value<Boolean> booleanValue = Value.nullable();

NullableCheckBox checkBox =
        (NullableCheckBox) Components.checkBox(booleanValue)
                .text("Check")
                .nullable(true)
                .build();

4.3. ComboBox

Value<Boolean> booleanValue = Value.nullable();

JComboBox<Item<Boolean>> comboBox =
        Components.booleanComboBox(booleanValue)
                .toolTipText("Select a value")
                .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);

7. Examples