Editable table

To create an editable table for a list of Person class objects, the following code can be used:

List<Person> people = new ArrayList<>();
EditableTable<Person> peopleTable = Tables
  .editable(people)
  .parent(parent)
  .create();

IObservableList<Car> carsObservable = new WritableList<>(cars, Car.class);

Click here to see the definition of the Person class in the example project at GitHub.

In this code, the carsObservable variable is also created to facilitate editing the person’s car, as we will see in the upcoming sections.

IObservableList class from the Eclipse Core library is a special type of List that can trigger custom events when its contents changes.

1. Text editor

1.1. String field type

Let’s create two columns with a simple text editor. Both accept String values, and value changes are committed based on the validation strategy.

In the first column, we configure the name field to reject empty values: if an empty string is entered, the value update is discarded.

The second column uses ValidationStrategies.stringAny() and thus accepts any string input (including empty values).

Multiple validation strategies exist for various data types: numbers, colors, dates and times. For instance, you can restrict input to only positive numbers, only negative numbers, or specific value ranges, as demonstrated below (See "Double field type" section). A more custom validation logic can be added by extending the ValidationStrategy class.

Additionally, any field can be marked as non-editable when needed.

peopleTable
  .column(Person::getName)
  .name("Name")
  .textEditor()
    .strategy(ValidationStrategies.stringIsNotEmpty())
    .handler((person, name) -> person.setName(name))
  .build();

peopleTable
  .column(Person::getCountry)
  .name("Country")
  .textEditor()
    .strategy(ValidationStrategies.stringAny()) // data update strategy
    .handler((person, country) -> person.setCountry(country)) // send cell value to certain field of object
  .build();
Text string editor

1.2. Integer field type

We can configure integer values to accept only positive numbers.

peopleTable
  .column(Person::getAge)
  .name("Age")
  .textEditor()
    .strategy(ValidationStrategies.integerPositive())
    .handler((person, age) -> person.setAge(age))
  .build();
Text integer numbers editor

1.3. Double field type

There are also multiple ways to handle double values. Consider the middle column as an example — we can assign a specific interval of valid values (this works for integers too). Additionally, there’s a dedicated method for percentage values, restricting input to the 0–100 range (see the right column for implementation).

Both columns correspond to the same field of the Person object, but have different ranges of acceptable inputs. Consider what happens if the value of 50 is typed in these columns. For the left column, the value will be discarded, as 50 is outside the acceptable range 5..20. However, when typed into the right column, it will be saved and displayed in both columns because constraints work only with data that is being input in the cell of a certain column, not with the field of the object in general.
peopleTable
  .column(Person::getDiscount)
  .name("Discount")
  .textEditor()
    .strategy(ValidationStrategies.doubleBetweenValues(5, 20))
    .handler((person, discount) -> person.setDiscount(discount))
  .build();

peopleTable
  .column(Person::getDiscount)
  .name("Discount, %")
  .textEditor()
    .strategy(ValidationStrategies.doublePercentage())
    .handler((person, discount) -> person.setDiscount(discount))
  .build();
Text double numbers editor

2. Combo editor

A simple comboEditor:

peopleTable
  .column(Person::getCar)
  .name("Car")
  .format(car -> String.valueOf(car.number()))
  .comboEditor()
    .elements(carsObservable) // observable list of cars to choose from
    .format(car -> String.valueOf(car.number())) // show car as car number
    .handler((person, car) -> person.setCar(car)) // update person's car with the selected one
  .build();
Combo editor

autoCompleteComboEditor allows selecting combo box elements using the "↓ Down" and "↑ Up" keys or pressing Ctrl+Space to open the dropdown list.

Auto complete combo editor

The dynamicAutoCompleteComboEditor provides functionality similar to autoCompleteComboEditor, but with one key difference: when the actual list size exceeds the maxComboBoxElementsCount(int) argument value, the dynamic auto complete combo-box becomes hidden, while other types of combo-box remain visible.

In this case, the dynamicAutoCompleteComboEditor behaves exactly as a regular text editor that only accepts values from the combo list. This behavior is demonstrated in the image below:

  • the left column shows standard behavior without maxComboBoxElementsCount(int) applied,

  • the right column uses the maxComboBoxElementsCount(int) method with an argument value of 2 for a list of 3 elements, causing the combo box to remain hidden.

In other aspects, the behavior of dynamicAutoCompleteComboEditor repeats that of autoCompleteComboEditor.

peopleTable
  .column(Person::getCar)
  .name("Car with max combo box elements count")
  .format(car -> String.valueOf(car.number()))
  .dynamicAutoCompleteComboEditor()
    .elements(carsObservable)
    .format(car -> String.valueOf(car.number()))
    .handler((person, car) -> person.setCar(car))
    .maxComboBoxElementsCount(2) // set max elements count
  .build();
Dynamic auto complete combo editor

The enum editor works similarly to other combo editor types. Like with any other editor, a field can be marked as non-editable when needed.

peopleTable
  .column(Person::getTown)
  .name("Town")
  .format(Town::getName)
  .enumComboEditor()
    .elements(Town.values()) // set combo list as enum values
    .format(Town::getName) // set format of showing enum values in combo list
    .handler((person, town) -> person.setTown(town)) // handle chosen enum value
  .build();
Enumeration combo editor

2.1. Combo settings

We will now discuss setup methods that are common to all combo editor types.

We can include a null element in the combo list and define its display text.

Additionally, we can specify a custom comparator to control the sorting of combo list items.

This code demonstrates inclusion of a null element and combo elements sorting:

peopleTable
  .column(Person::getCar)
  .name("Car")
  .format(car -> String.valueOf(car.number()))
  .comboEditor()
    .elements(carsObservable)
    .format(car -> String.valueOf(car.number()))
    .handler((person, car) -> person.setCar(car))
    .enableNullElement("NONE") // add null element in combo list and specify its value
    .comparator((p1, p2) -> p1.number() - p2.number()) // add comparator for combo list
  .build();
Combo editor with null value and comparator

The maximum combo list size can be set using maxComboBoxElementsCount(int). When the actual list exceeds this limit, excess elements will be hidden from view.

peopleTable
  .column(Person::getCar)
  .name("Car")
  .format(car -> String.valueOf(car.number()))
  .comboEditor()
    .elements(carsObservable)
    .format(car -> String.valueOf(car.number()))
    .handler((person, car) -> person.setCar(car))
    .enableNullElement("NONE")
    .comparator((p1, p2) -> p1.number() - p2.number())
    .maxComboBoxElementsCount(2) // max combo list size
  .build();
Combo editor with max combo list size

Combo elements can be filtered:

peopleTable
  .column(Person::getCar)
  .name("Car")
  .format(car -> String.valueOf(car.number()))
  .comboEditor()
    .elements(carsObservable)
    .format(car -> String.valueOf(car.number()))
    .handler((person, car) -> person.setCar(car))
    .enableNullElement("NONE")
    .comparator((p1, p2) -> p1.number() - p2.number())
    .maxComboBoxElementsCount(2)
    .filter(car -> car.number() > 50) // add filter
  .build();
Combo editor with filter

Also, a field can be marked non-editable.

3. Auto complete text editor

For text input that only accepts predefined values (similar to a text editor but with strict validation), use autoCompleteTextEditor. The GIF below demonstrates how only country names existing in the source list are accepted for updates. Note that this editor also supports marking fields as non-editable.

peopleTable
  .column(Person::getCountry)
  .name("Country")
  .autoCompleteTextEditor()
    .elements(new WritableList<>(List.of("Spain", "Italy", "France"), String.class)) // set IObservableList of possible values
    .format(string -> string) // in this case observable values are string by itself
    .handler((person, string) -> person.setCountry(string)) // set chosen value as certain field
  .build();
Auto complete text editor

4. Checkbox editor

Create a column with checkbox editor for boolean field values:

peopleTable
  .column(Person::isPreferential)
  .name("Privilege")
  .checkBoxEditor()
    .handler((person, value) -> person.setPreferential(value)) // set action after checkbox press
    .canEdit(person -> person.getAge() > 25) // some cells can be uneditable depends on table item value
  .build();
Simple checkbox

Also we can change checkbox view.

peopleTable
  .column(Person::isPreferential)
  .name("Privilege")
  .checkBoxEditor()
    .handler((person, value) -> person.setPreferential(value))
    .canEdit(person -> person.getAge() > 25)
    .format(value -> value ? "YES" : "NO") // change checkbox view
  .build();
Checkbox with certain values

5. Non-editable cells

All editor types include the canEdit() method, which dynamically controls cell ability to being edited based on row values. For example, in the previous section, we prevented 'privilege' field edits when the person’s age is under 25. The demonstration below shows this logic in action:

Non-editable cell

6. Button action editor

Cell buttons can trigger custom actions when clicked. Like all editors, these button cells support the non-editable flag to disable interactions when needed.

peopleTable
  .column(Person::getCountry)
  .name("Country")
  .buttonActionEditor()
    .handler(person -> person.setCountry("Russia")) // action to do after button pressing
    .buttonText("Change country") // set custom button text
    .buttonToolTip("Change country to \"Russia\"")
  .build();
Action button editor

7. Button-on-top editor

The buttonOnTop editor functions like buttonActionEditor, but displays buttons persistently (without requiring cell focus).

You can use an icon instead of text for the button.

Like all editors, fields can be marked as non-editable.

peopleTable
  .column(Person::getCountry)
  .name("Country")
  .buttonOnTop()
    .handler(person -> person.setCountry("Germany")) // action to do after button pressing
    .buttonWidth(20) // button width
    .buttonToolTip("Change country to \"Germany\"") // button tool tip
    .icon(homeImage) // set icon instead of default button text
  .build();
Button on top editor

8. Date and time editor

Create a column with a date-time editor that works exclusively with the LocalDateTime class. First, let’s examine date-time display formatting.

Amalgama Platform provides the Formats class. Calling Formats.getDefaultFormats() enables access to standard date-time formats, currency, and other unit measurements based on the current locale.

Additionally, you can mark fields as non-editable and customize the button appearance.

peopleTable
  .column(Person::getPurchaseDate)
  .name("Purchase date")
  .format(Formats.getDefaultFormats()::dayMonthLongYearHoursMinutes) // instead of using simple toString view of LocalDateTime object
  .localDateTimeEditor()
    .handler((person, date) -> person.setPurchaseDate(date)) // handle date assigning
  .build();
Table view with simple date editor

Use the clearExistButton() method to add a 'Clear' button in the dialog window to reset the date to its default value (which is the current date-time by default).

The default date-time value can be configured via the newDateCreator() method.

Note that cells can be restricted to date-only or time-only editing - in this case, we’ve selected date-only editing, which is why all cells in the following image show the default time of 12 a.m. (0:00).

peopleTable
  .column(Person::getPurchaseDate)
  .name("Purchase date")
  .format(Formats.getDefaultFormats()::dayMonthLongYearHoursMinutes)
  .localDateTimeEditor()
    .handler((person, date) -> person.setPurchaseDate(date))
    .clearExistButton() // add clear button to dialog window
    .newDateCreator(() -> LocalDateTime.of(2025, 1, 1, 10, 10)) // set default value
    .typeDialog().onlyDate() // only date without time
  .build();
Date editor with extra settings

Editor of LocalTime has the same methods as localDateTimeEditor. A local time editor can be created with the localTimeEditor() method.

A field can be marked non-editable. Button view can be customized with an icon.

peopleTable
  .column(Person::getArriveTime)
  .name("Arrive time")
  .format(Formats.getDefaultFormats()::hoursMinutes) // set format
  .localTimeEditor()
    .handler((person, time) -> person.setArriveTime(time)) // handle time setting
  .build();
Time editor

9. Color editor

A color editor column works with the Color class.

A field can be marked non-editable.

peopleTable
  .column(Person::getFavouriteColor)
  .name("Favourite color")
  .backgroundColor(Person::getFavouriteColor)
  .colorEditor()
    .handler((person, color) -> person.setFavouriteColor(color)) // handle color editing
  .build();
Color editor

10. Single object selection dialog editor

An alternative to combo editors is the object selection dialog editor.

A field can be marked non-editable. Button view can be customized with an icon.

peopleTable
  .column(Person::getCar)
  .name("Car")
  .format(car -> String.valueOf(car.number()))
  .objectSelectionDialogEditor()
    .elements(carsObservable) // observable list of cars to choose from
    .columns(dialogTable -> dialogTable.column(Car::number).name("Number")) // create dialog table of cars
    .handler((person, car) -> {
      person.setCar(car);
      MessageBox messageBox = new MessageBox(parent.getShell(), 1 << 1);
      messageBox.setMessage(String.format("The car with number %d is selected", car.number()));
      messageBox.open();
    }) // handle chosen car value as field of person
    .dialogTitle("car") // set dialog title
  .build();
Object selection dialog editor

11. Multiple objects selection dialog editor

Create a column using multiObjectsSelectionDialogEditor for objects containing collection fields.

Like other editors, this one supports marking fields as non-editable and customizing the button appearance.

peopleTable
  .column(Person::getCars)
  .name("Cars")
  .format(this::listFormat) // set function which perform list as string
  .<Car>multiObjectsSelectionDialogEditor() // assign list elements type as generic
    .elements(carsObservable) // observable list of all cars available to choose
    .selectedElements(chosenCars) // list of chosen cars
    .columns(dialogTable -> {
      dialogTable.column(Car::number).name("Number");
    }) // create dialog table
    .handler((person, list) -> {
      person.setCars(list);
      MessageBox messageBox = new MessageBox(parent.getShell(), 1 << 1);
      messageBox.setMessage("The cars are selected");
      messageBox.open();
    }) // handle list of chosen cars as corresponding field
    .dialogTitle("available cars") // assign dialog title (optional)
  .build();
Multiple selection editor