Which widget to use
Every widget of xis-mvui at a glance - what the player sees, when to pick it, the options all widgets share and which may go in the footer.
On this page
A screen is a MenuScreen: a title, a scrolling list of entries (one widget per row) and a footer row of buttons. The widget set is fixed; you compose screens from these.
Pick a widget#
| You need… | Use | The player sees |
|---|---|---|
| an action | Button | a vanilla button |
| read-only text | Label | text on its own row; long text wraps |
| the result of an action | StatusLine | a colored message line, empty until shown |
| an on/off setting | Toggle | a button "label: ON" / "label: OFF" |
| several independent yes/no choices | Checkbox | a check box with its label beside it |
| one of a few values (an enum) | Cycle | a button "label: value" that steps on each click |
| one of many values | Dropdown | like a cycle; a click opens a picker screen |
| a list of items, one selected | SelectList | a scrolling list like the world list |
| a number in a range, dragged | Slider | a vanilla slider "label: value" |
| an exact number, typed | NumberField | a text field with range errors below it |
| one line of text | TextInput | a text field with a label above and an error line below |
| several lines of text | MultiLineInput | a text box of 2 – 12 lines with word wrap |
| a list of strings to edit | StringListEditor | a list, an input field, Add and Remove |
| a key or mouse button | Keybind | a button "label: key" like the Controls screen |
| a color | ColorPicker | a button "label: #RRGGBB" with a swatch; opens an editor |
| a group of entries | Section | a titled group, collapsible ("▶ title" / "▼ title") |
The other screen type is ConfirmDialog, a yes/no question on its own screen (Several screens and dialogs).
How every widget is built#
Each widget has a builder that starts with an id and ends with build():
Toggle sounds = Toggle.builder("sounds") // id: unique on the screen
.label(Text.translatable("mymod.sounds", "Sounds"))
.value(() -> config.sounds)
.onChange(v -> config.sounds = v)
.build(); // throws if a required field is missing
A missing required field makes build() throw IllegalStateException naming the widget and the field. Setters
of optional fields reject null at once.
Options every widget has#
| Builder method | Default | Effect |
|---|---|---|
enabled(BooleanSupplier) or enabled(boolean) |
enabled | while false the widget is greyed out and ignores input; it still shows its label, value and tooltip |
tooltip(Supplier<Text>) or tooltip(Text) |
none | text shown while the mouse is over the widget; the supplier may return null for none. Disabled widgets show it too, so it's a good place to say why. |
visible(BooleanSupplier) |
visible | while false the entry has no row at all, and the rows below move up |
Button.builder("export")
.label(Text.translatable("mymod.export", "Export"))
.onPress(this::export)
.enabled(() -> !profiles.isEmpty())
.tooltip(() -> profiles.isEmpty() ? Text.translatable("mymod.export.none", "Nothing to export") : null)
.visible(() -> config.advanced)
.build();
Every setter that takes a supplier also has a constant overload; the supplier form is re-read on every refresh (State and refresh).
The footer#
MenuScreen.Builder.footer(entry) puts an entry in the row at the bottom, which stays put while the list
scrolls. Use it for the screen's main actions: Done, Save, Cancel.
| May go in the footer | May not |
|---|---|
| Button, Label, StatusLine, Toggle, Cycle, Dropdown, SelectList, TextInput, NumberField | Checkbox, Slider, MultiLineInput, StringListEditor, Keybind, ColorPicker, Section |
A footer entry also must have no visible(…) and must not be in a section. Anything else makes footer(…) throw.
In practice, keep the footer to a few buttons.
Building a screen#
MenuScreen.builder("main")
.title(Text.translatable("mymod.title", "My Mod")) // required
.add(entry) // a row; a Section brings its children along
.add(stringListEditor) // its four entries
.footer(button) // the bottom row, left to right
.build(); // throws on a missing title or a duplicate id