.dev

Modules2 min read

Server UI

Declarative screens rendered in Teho's own design system.

ui:1Tier A · Decorate

Good for

  • Shops and kit pickers that work with a controller
  • Settings menus that are not chest GUIs
  • Live widgets on the HUD

Server UI lets you describe a screen as a tree of nodes, and Teho draws it in its own design system. Shops, kit pickers, party menus and settings look native, work with a controller automatically, and cannot be dressed up to look like Teho's own screens, because servers never send HTML, JavaScript or free-form drawing.

Open a screen

Java
Screen shop = Screen.modal("shop", "Skyline Shop")
        .width(560)
        .add(
                Node.tabs("tabs",
                        Node.tab("blocks", "Blocks",
                                Node.grid("blockGrid", 3,
                                        Node.item("wool", "white_wool"),
                                        Node.item("planks", "oak_planks"),
                                        Node.item("glass", "glass")),
                                Node.button("buyBlocks", "Buy 16 for 4 iron").style(Node.Style.PRIMARY)),
                        Node.tab("tools", "Tools",
                                Node.row("pick",
                                        Node.item("pickIcon", "iron_pickaxe"),
                                        Node.text("pickText", "Iron pickaxe"),
                                        Node.button("buyPick", "Buy").icon("iron_ingot")))),
                Node.divider("rule"),
                Node.progress("budget", "Iron", 0.4))
        .onInteract((player, interaction) -> {
            if (interaction.clicked("buyBlocks")) {
                buyBlocks(player);
            }
        })
        .build();

teho.ui().open(shop);

Surfaces

FactoryWhat it is
Screen.modal(id, title)A centred dialog that takes input focus.
Screen.panel(id, title)A side panel that leaves the game visible.
Screen.hud(id)A HUD widget with no input focus, such as a live scoreboard card.
Builder methodNotes
add(Node…)Top-level nodes. A screen holds at most 400 nodes.
width(int)Width in pixels, up to 1,200.
closeable(boolean)Whether the player can close it themselves.
anchor(Anchor)CENTER, LEFT, RIGHT, TOP or BOTTOM.
onInteract(UiHandler)Called for every interaction on this screen.

Nodes

FactoryWhat it draws
stack(id, children…), row(id, children…), grid(id, columns, children…)Vertical, horizontal and grid layout.
list(id, children…)A scrolling list.
tabs(id, tab…), tab(id, label, children…)Tabs.
text(id, text)Text. Use style(TITLE) or style(CAPTION) for hierarchy.
image(id, Asset, width, height)A PNG asset.
item(id, itemId)An item icon.
head(id, playerUuid, size)A player head.
button(id, label)A button.
toggle(id, label, on)A switch.
slider(id, label, min, max, step, value)A slider.
input(id, label, placeholder, maxLength)A text field.
select(id, label, options, selected)A dropdown.
progress(id, label, fraction)A progress bar.
countdown(id, label, endsAtEpochMs)A live countdown the client ticks.
divider(id)A rule.

Every node takes style(Style) (DEFAULT, PRIMARY, DANGER, SUBTLE, TITLE, CAPTION), color(argb), disabled(boolean), tooltip(String), icon(itemId) and size(width, height).

Interactions

Each interaction has the screen id, node id, an action and a value.

ActionSent whenValue
CLICKA button is pressed.Empty
CHANGEA toggle, slider, select or input changes.The new value
SUBMITAn input is submitted.The text
CLOSEDThe player closed the screen.Empty
Java
.onInteract((player, interaction) -> {
    switch (interaction.node()) {
        case "music":
            setMusic(player, interaction.booleanValue());
            break;
        case "volume":
            setVolume(player, interaction.intValue());
            break;
        case "nickname":
            if (interaction.action() == UiInteraction.Action.SUBMIT) {
                rename(player, interaction.value());
            }
            break;
    }
})

You can also listen globally with TehoUiEvent on Bukkit or TehoListener.onUi.

Update a live screen

Java
teho.ui().patch("shop", Node.progress("budget", "Iron", 0.7));
teho.ui().remove("shop", "rule");
teho.ui().retitle("shop", "Skyline Shop · Sale");
teho.ui().close("shop");

patch replaces nodes with the same ids and leaves the rest alone, so live values such as balances update without redrawing the screen. openScreens() lists what this player has open.