Demonstrates basic input component usage.

input components

1. Application Model

Holds value instances which are bound to input components in the panel below.

Show code

demos/manual/src/main/java/is/codion/framework/demos/manual/common/demo/ApplicationModel.java

import static is.codion.common.value.Value.value;

public final class ApplicationModel {

  private final Value<String> shortStringValue = value();
  private final Value<String> longStringValue = value();
  private final Value<String> textValue = value();
  private final Value<LocalDate> localDateValue = value();
  private final Value<LocalDateTime> localDateTimeValue = value();
  private final Value<String> formattedStringValue = value();
  private final Value<Integer> integerValue = value();
  private final Value<Double> doubleValue = value();
  private final Value<Boolean> booleanValue = value();
  private final Value<Boolean> booleanSelectionValue = value();
  private final Value<Integer> integerItemValue = value();
  private final Value<String> stringSelectionValue = value();
  private final Value<Integer> integerSlideValue = value();
  private final Value<Integer> integerSpinValue = value();
  private final Value<Integer> integerSelectionValue = value();
  private final Value<String> itemSpinValue = value();
  private final ValueSet<String> stringListValue = valueSet();
  private final Value<String> messageValue = value();

  private final Collection<Value<?>> values = asList(
          shortStringValue,
          longStringValue,
          textValue,
          localDateValue,
          localDateTimeValue,
          formattedStringValue,
          integerValue,
          doubleValue,
          booleanValue,
          booleanSelectionValue,
          integerItemValue,
          stringSelectionValue,
          integerSlideValue,
          integerSpinValue,
          integerSelectionValue,
          itemSpinValue,
          stringListValue
  );

  public ApplicationModel() {
    setDefaultUncaughtExceptionHandler(this::exceptionHandler);
    values.forEach(value -> value.addDataListener(this::setMessage));
  }

  public void clear() {
    values.forEach(value -> value.set(null));
  }

  public Value<String> shortStringValue() {
    return shortStringValue;
  }

  public Value<String> longStringValue() {
    return longStringValue;
  }

  public Value<String> textValue() {
    return textValue;
  }

  public Value<LocalDate> localDateValue() {
    return localDateValue;
  }

  public Value<LocalDateTime> localDateTimeValue() {
    return localDateTimeValue;
  }

  public Value<Integer> integerValue() {
    return integerValue;
  }

  public Value<Double> doubleValue() {
    return doubleValue;
  }

  public Value<String> formattedStringValue() {
    return formattedStringValue;
  }

  public Value<Boolean> booleanValue() {
    return booleanValue;
  }

  public Value<Boolean> booleanSelectionValue() {
    return booleanSelectionValue;
  }

  public Value<Integer> integerItemValue() {
    return integerItemValue;
  }

  public Value<Integer> integerSlideValue() {
    return integerSlideValue;
  }

  public Value<Integer> integerSpinValue() {
    return integerSpinValue;
  }

  public Value<Integer> integerSelectionValue() {
    return integerSelectionValue;
  }

  public Value<String> itemSpinnerValue() {
    return itemSpinValue;
  }

  public Value<String> stringSelectionValue() {
    return stringSelectionValue;
  }

  public ValueSet<String> stringListValueSet() {
    return stringListValue;
  }

  public ValueObserver<String> messageObserver() {
    return messageValue.observer();
  }

  public ComboBoxModel<String> createStringComboBoxModel() {
    return new DefaultComboBoxModel<>(new String[] {"Hello", "Everybody", "How", "Are", "You"});
  }

  public ItemComboBoxModel<Integer> createIntegerItemComboBoxModel() {
    return itemComboBoxModel(asList(
            Item.item(1, "One"), Item.item(2, "Two"), Item.item(3, "Three"),
            Item.item(4, "Four"), Item.item(5, "Five"), Item.item(6, "Six"),
            Item.item(7, "Seven"), Item.item(8, "Eight"), Item.item(9, "Nine")
    ));
  }

  public DefaultBoundedRangeModel createIntegerSliderModel() {
    return new DefaultBoundedRangeModel(0, 0, 0, 100);
  }

  public SpinnerNumberModel createIntegerSpinnerModel() {
    return new SpinnerNumberModel(0, 0, 100, 10);
  }

  public ComboBoxModel<Integer> createIntegerComboBoxModel() {
    return new DefaultComboBoxModel<>(new Integer[] {101, 202, 303, 404});
  }

  public SpinnerListModel createItemSpinnerModel() {
    return new SpinnerListModel(Arrays.asList(
            Item.item("Hello"),
            Item.item("Everybody"),
            Item.item("How"),
            Item.item("Are"),
            Item.item("You")
    ));
  }

  public ListModel<String> createStringListModel() {
    DefaultListModel<String> listModel = new DefaultListModel<>();
    listModel.addElement("Here");
    listModel.addElement("Are");
    listModel.addElement("A");
    listModel.addElement("Few");
    listModel.addElement("Elements");
    listModel.addElement("To");
    listModel.addElement("Select");
    listModel.addElement("From");

    return listModel;
  }

  private void exceptionHandler(Thread thread, Throwable exception) {
    messageValue.set(exception.getMessage());
  }

  private <T> void setMessage(T value) {
    messageValue.set(value == null ? " " : value.toString());
  }
}

2. Application Panel

Show code

demos/manual/src/main/java/is/codion/framework/demos/manual/common/demo/ApplicationPanel.java

import static is.codion.swing.common.ui.component.Components.*;

public final class ApplicationPanel extends JPanel {

  public ApplicationPanel(ApplicationModel model) {
    super(borderLayout());

    setBorder(emptyBorder());

    JPanel settingsPanel = new JPanel(borderLayout());

    State inputEnabledState = State.state(true);

    checkBox(inputEnabledState)
            .text("Enabled")
            .mnemonic('N')
            .transferFocusOnEnter(true)
            .build(checkBox -> settingsPanel.add(checkBox, BorderLayout.WEST));

    button(Control.builder(model::clear)
            .enabled(inputEnabledState)
            .name("Clear")
            .mnemonic('L'))
            .transferFocusOnEnter(true)
            .build(button -> settingsPanel.add(button, BorderLayout.EAST));

    JPanel inputPanel = flexibleGridLayoutPanel(0, 2).build();

    Components.stringField(model.shortStringValue())
            .columns(20)
            .lowerCase(true)
            .maximumLength(20)
            .selectAllOnFocusGained(true)
            .transferFocusOnEnter(true)
            .validator(new PGValidator())
            .selector(new StringSelector())
            .label(label("Short String (1)")
                    .displayedMnemonic('1')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    textFieldPanel(model.longStringValue())
            .columns(20)
            .maximumLength(400)
            .buttonFocusable(true)
            .selectAllOnFocusGained(true)
            .transferFocusOnEnter(true)
            .label(label("Long String (2)")
                    .displayedMnemonic('2')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    textArea(model.textValue())
            .rowsColumns(4, 20)
            .lineWrap(true)
            .wrapStyleWord(true)
            .transferFocusOnEnter(true)
            .dragEnabled(true)
            .transferHandler(new FilePathTransferHandler())
            .keyEvent(KeyEvents.builder(VK_SPACE)
                    .modifiers(CTRL_DOWN_MASK)
                    .action(Control.actionControl(actionEvent ->
                            ((JTextArea) actionEvent.getSource()).append("SPACE"))))
            .label(label("Text (3)")
                    .displayedMnemonic('3')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .scrollPane()
            .build(inputPanel::add);

    maskedTextField(model.formattedStringValue())
            .mask("(##) ##-##")
            .placeholderCharacter('_')
            .placeholder("(00) 00-00")
            .emptyStringToNullValue(true)
            .invalidStringToNullValue(true)
            .valueContainsLiteralCharacters(true)
            .commitsOnValidEdit(true)
            .focusLostBehaviour(JFormattedTextField.COMMIT)
            .transferFocusOnEnter(true)
            .label(label("Formatted String (4)")
                    .displayedMnemonic('4')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    comboBox(model.createStringComboBoxModel(), model.stringSelectionValue())
            .editable(true)
            .mouseWheelScrolling(true)
            .transferFocusOnEnter(true)
            .label(label("String Selection (5)")
                    .displayedMnemonic('5')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    localDateField(LocaleDateTimePattern.builder()
            .delimiterDash()
            .yearFourDigits()
            .build()
            .dateTimePattern(), model.localDateValue())
            .transferFocusOnEnter(true)
            .label(label("Date (6)")
                    .displayedMnemonic('6')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    localDateTimeFieldPanel(LocaleDateTimePattern.builder()
            .delimiterDot()
            .yearTwoDigits()
            .hoursMinutes()
            .build()
            .dateTimePattern(), model.localDateTimeValue())
            .transferFocusOnEnter(true)
            .label(label("Date Time (7)")
                    .displayedMnemonic('7')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    integerField(model.integerValue())
            .valueRange(0, 10_000)
            .groupingUsed(true)
            .groupingSeparator('.')
            .transferFocusOnEnter(true)
            .label(label("Integer (8)")
                    .displayedMnemonic('8')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    doubleField(model.doubleValue())
            .nullable(false)
            .valueRange(0, 1_000_000)
            .groupingUsed(true)
            .maximumFractionDigits(2)
            .decimalSeparator(',')
            .groupingSeparator('.')
            .convertGroupingToDecimalSeparator(true)
            .transferFocusOnEnter(true)
            .label(label("Double (9)")
                    .displayedMnemonic('9')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    ItemComboBoxModel<Integer> integerItemComboBoxModel = model.createIntegerItemComboBoxModel();
    Value<Integer> integerItemSelectorValue = integerItemComboBoxModel.createSelectorValue(new IntegerItemFinder());
    NumberField<Integer> integerItemSelectorField = integerField(integerItemSelectorValue)
            .columns(2)
            .horizontalAlignment(SwingConstants.CENTER)
            .selectAllOnFocusGained(true)
            .transferFocusOnEnter(true)
            .label(label("Integer Item (A)")
                    .displayedMnemonic('A')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build();
    JComboBox<Item<Integer>> integerItemComboBox = itemComboBox(integerItemComboBoxModel, model.integerItemValue())
            .completionMode(Completion.Mode.AUTOCOMPLETE)
            .popupMenuControl(comboBox -> createSelectRandomItemControl(integerItemComboBoxModel))
            .mouseWheelScrollingWithWrapAround(true)
            .transferFocusOnEnter(true)
            .enabled(inputEnabledState)
            .build();
    borderLayoutPanel()
            .westComponent(integerItemSelectorField)
            .centerComponent(integerItemComboBox)
            .build(inputPanel::add);

    slider(model.createIntegerSliderModel(), model.integerSlideValue())
            .paintTicks(true)
            .paintTrack(true)
            .minorTickSpacing(5)
            .majorTickSpacing(20)
            .mouseWheelScrolling(true)
            .transferFocusOnEnter(true)
            .label(label("Integer Slide (B)")
                    .displayedMnemonic('B')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    integerSpinner(model.createIntegerSpinnerModel(), model.integerSpinValue())
            .columns(4)
            .mouseWheelScrolling(true)
            .transferFocusOnEnter(true)
            .label(label("Integer Spin (C)")
                    .displayedMnemonic('C')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    comboBox(model.createIntegerComboBoxModel(), model.integerSelectionValue())
            .editable(true)
            .mouseWheelScrolling(true)
            .transferFocusOnEnter(true)
            .label(label("Integer Selection (D)")
                    .displayedMnemonic('D')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    itemSpinner(model.createItemSpinnerModel(), model.itemSpinnerValue())
            .columns(20)
            .horizontalAlignment(SwingConstants.CENTER)
            .mouseWheelScrolling(true)
            .transferFocusOnEnter(true)
            .label(label("Item Spin (E)")
                    .displayedMnemonic('E')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    checkBox(model.booleanValue())
            .horizontalAlignment(SwingConstants.CENTER)
            .transferFocusOnEnter(true)
            .label(label("Boolean (F)")
                    .displayedMnemonic('F')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    booleanComboBox(model.booleanSelectionValue())
            .mouseWheelScrolling(true)
            .transferFocusOnEnter(true)
            .enabled(inputEnabledState)
            .label(label("Boolean Selection (G)")
                    .displayedMnemonic('G')
                    .build(inputPanel::add))
            .build(inputPanel::add);

    Components.list(model.createStringListModel(), model.stringListValueSet())
            .visibleRowCount(4)
            .layoutOrientation(JList.HORIZONTAL_WRAP)
            .transferFocusOnEnter(true)
            .label(label("Text List Selection (H)")
                    .displayedMnemonic('H')
                    .build(inputPanel::add))
            .enabled(inputEnabledState)
            .build(inputPanel::add);

    add(settingsPanel, BorderLayout.NORTH);
    add(inputPanel, BorderLayout.CENTER);

    flexibleGridLayoutPanel(2, 1)
            .add(stringField()
                    .columns(20)
                    .editable(false)
                    .focusable(false)
                    .border(createTitledBorder("Message"))
                    .enabled(inputEnabledState)
                    .linkedValue(model.messageObserver())
                    .build(component -> add(component, BorderLayout.SOUTH)))
            .add(lookAndFeelComboBox(true))
            .build(southPanel -> add(southPanel, BorderLayout.SOUTH));

    Sizes.setPreferredWidth(this, 400);
  }

  private static class PGValidator implements Value.Validator<String> {

    private final List<String> swearWords = asList("fuck", "shit");

    @Override
    public void validate(String value) {
      if (value != null) {
        String lowerCaseValue = value.toLowerCase();
        swearWords.forEach(swearWord -> {
          if (lowerCaseValue.contains(swearWord)) {
            throw new IllegalArgumentException("No swearing please");
          }
        });
      }
    }
  }

  private static class StringSelector implements SingleSelector<String> {

    private static final String DEFAULT_SELECTION = "strings";

    private final List<String> stringsToSelectFrom = Arrays.asList("a", "few", "short", "strings", "to", "choose", "from");

    @Override
    public Optional<String> select(JComponent dialogOwner) {
      return Dialogs.selectionDialog(stringsToSelectFrom)
              .owner(dialogOwner)
              .defaultSelection(DEFAULT_SELECTION)
              .selectSingle();
    }
  }

  private static class FilePathTransferHandler extends TransferHandler {

    @Override
    public boolean canImport(TransferSupport support) {
      return Arrays.stream(support.getDataFlavors())
              .anyMatch(DataFlavor::isFlavorJavaFileListType);
    }

    @Override
    public boolean importData(TransferSupport support) {
      try {
        ((JTextArea) support.getComponent()).setText(support.getTransferable()
                .getTransferData(DataFlavor.javaFileListFlavor).toString());

        return true;
      }
      catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  private static final class IntegerItemFinder implements ItemFinder<Item<Integer>, Integer> {

    @Override
    public Integer value(Item<Integer> item) {
      return item.get();
    }

    @Override
    public Predicate<Item<Integer>> createPredicate(Integer value) {
      return item -> Objects.equals(item.get(), value);
    }
  }

  private static Control createSelectRandomItemControl(ItemComboBoxModel<Integer> integerItemComboBoxModel) {
    Random random = new Random();
    return Control.builder(() ->
                    integerItemComboBoxModel.setSelectedItem(random.nextInt(integerItemComboBoxModel.getSize()) + 1))
            .name("Select Random Item")
            .build();
  }

  public static void main(String[] args) {
    Arrays.stream(FlatAllIJThemes.INFOS)
            .forEach(LookAndFeelProvider::addLookAndFeelProvider);

    findLookAndFeelProvider("com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMonokaiProIJTheme")
            .ifPresent(LookAndFeelProvider::enable);

    ApplicationModel applicationModel = new ApplicationModel();

    ApplicationPanel applicationPanel = new ApplicationPanel(applicationModel);

    Dialogs.componentDialog(applicationPanel)
            .title("Codion Input Components Demo")
            .icon(Logos.logoTransparent())
            .show();
  }
}