Skip to content

Advanced Entry Types

Endor H edited this page Dec 11, 2022 · 5 revisions

Besides Basic Entry Types, Simple Config provide a few useful composite entry types you can build on top.

Additionally, you may be interested in how you could define your own Custom Entry Types.

Lists

Lists are the most simple composite type. In Simple Config, list entries can contain any other entry type, including other lists.

  • list(entry) creates a list of the type passed as entry, and defaults to an empty list
  • list(entry, defaultValue) likewise, but with an explicit default value

Players will be able to easily insert/delete/drag list elements around.

For example, to create a list of colors, defaulting to [Color.RED, Color.GREEN, Color.BLUE] you could use list(color(Color.GRAY), asList(Color.RED, Color.GREEN, Color.BLUE)). New elements added to the list will default to Color.GRAY, as specified for the inner entry.

Color List Entry

As another example, to create a list of lists of sliders between 0 and 1, you could use list(list(number(0F).range(0, 10).slider(), asList(0F, 1F)), asList(asList(0F, 0.1F), asList(0.2F, 0.3F))). In this case, there are multiple default values. The outer is the default value for the entry itself, the second is the default value for new lists added to the entry, and the first is the default value for sliders added to inner lists.

List of Lists


There are also some legacy type-specific list types, but you should always prefer general lists
  • stringList(defaultValue)
  • byteList(defaultValue)
  • shortList(defaultValue)
  • intList(defaultValue)
  • longList(defaultValue)
  • floatList(defaultValue)
  • doubleList(defaultValue)

These type-specific list types won't let you modify the internal entries to your liking, as you could with general lists, for instance, to limit string width as in list(string("").maxLength(20), asList("a", "b", "c")), so you're encouraged to always use general lists.

Sets

Set entries are identical to list entries, just using sets. This means the order is not necessarily preserved, and entries cannot be duplicated.

  • set(entry) creates a set of the type passed as entry, and defaults to an empty set
  • set(entry, defaultValue) likewise, but with an explicit default value

Unlike with lists, there are no legacy type-specific set entries, for the better.

Maps

Map entries depend on two types of entries, key entries and value entries. They represent a map from the key entries' type to the value entries' one.

  • map(keyEntry, valueEntry) creates a map from the type of keyEntry to valueEntry, and defaults to an empty map
  • map(keyEntry, valueEntry, defaultValue) likewise, but with an explicit default value
  • map(entry) defaults to string("") keys, and an empty default value
  • map(entry, defaultValue) defaults to string("") keys, with an explicit default value

Map from strings to strings

Maps have an additional restriction on key entry types, they must implement AtomicEntryBuilder. This only excludes other collection types, that is, you cannot use lists/sets/maps/pair lists as keys in map entries, since there'd be no easy way to display this in a graphical interface. This limitation doesn't apply to value entry types, for example, you can create nested maps.

Map from strings to maps from strings to integers

By default, maps do not necessarily preserve their order, but you may request that they do by using the linked method on them, so they use LinkedHashMaps instead and serialize to YAML sorted maps.

Pair Lists

Pair lists are exactly like maps, but allowing duplicate keys, and preserving their order. They have as type List<Pair<K, V>>.

  • pairList(keyEntry, valueEntry) creates a pair list with types of keyEntry and valueEntry, and defaults to an empty list
  • pairList(keyEntry, valueEntry, defaultValue) likewise, but with an explicit default value

As in maps, key entry types must implement AtomicEntryBuilder, that is, they can't be collection entries.

Instruction list

Pairs & Triples

Another form of composite type are pair and triple entries:

  • pair(left, right) creates a pair entry with the types of left and right, and defaults to a pair of the default values of these entries
  • pair(left, right, defaultValue) likewise, but with an explicit default value
  • triple(left, middle, right) creates a triple entry with the types of left, middle and right, and defaults to a triple of their defaults
  • triple(left, middle, right, defaultValue) likewise, but with an explicit default value

As with maps, all inner entry types must implement AtomicEntryBuilder, so pairs or triples cannot contain collection types.

The Pair and Triple classes used are from the org.apache.commons.lang3.tuple package, although the Kotlin API does some effort to translate them to Kotlin types under the hood, when used from Kotlin.

Triple and pair entries

Bean Entries (Java only)

If you're using Kotlin, see data class entries

Bean entries let you create your own compound groups of entries. This is specially powerful if you use them inside collection entries, such as lists/maps of beans. To define a bean entry, use the bean factory method:

  • bean(defaultValue) creates a bean entry

However, you'll now need to use the add methods of the returned builder to map bean properties to entries. This behaves just as if you were defining a config group.

Bean entry

Data Class Entries (Kotlin only)

If you're using Java, see bean entries

Data class entries let you create your own compound groups of entries. This is specially powerful if you use them inside collection entries, such as lists/maps of data class entries. To define a data class entry, use the data factory method:

  • data(defaultValue, binding) create a data class entry with the given default value and applying the binding lambda to configure the entries

Inside the binding lambda, you'll need to call the bind method (until Kotlin supports multiple context receivers), inside which you can bind property references from your data class to entry builders. For example, you could have

data class Data(val name: String, val age: Int)
val data = data(Data("Steve", 20)) { bind {
    ::name caption string()
    ::age by number()
}}

This is safer than bean entries, as you cannot misspell or bind the wrong entry type to data class properties.

Captioned Entries

It is possible to attach a caption entry to all collection (list/set/map/pair list) and bean/data class entries, by using the caption factory method, or the caption method of bean/data class entry builders.

Caption entries are displayed in the title of the host entry, to make use of the physical space left. As such, they need to implement the AtomicEntryBuilder interface.

In collection entries, attaching a caption to the entry will produce an entry of type Pair<CaptionType, EntryType> where CaptionType is the type of the entry added as caption, and EntryType is the type of the original entry.

List of lists of colors with color captions

In the Java API, it's possible to define separate backing fields for the caption and the collection entry, by using the captionField/collectionField methods of the entry builder. With the Kotlin API, you can simply define a baked property for them instead.

For bean/data class entries, the caption entry is just one more of the subentries, and it'll be bound with the property you provide to the caption method.

This is only intended for entries for which there's a natural meaning for the caption entry, such as a master volume slider as the caption of a map of volume sliders, or the name/ID of a bean entry. Do not abuse it in situations where players will have doubts of what the caption does.