Skip to content

UI Builders

The builder classes are used together to manipulate UI and handle user interactions. See Markup for .ui file syntax.

Builds commands that manipulate UI elements using CSS-like selectors.

#ElementId - by ID
#Parent #Child - nested (space-separated)
#List[0] - indexed element
#List[0] #Name - child of indexed element
#Element.Property - property access
// load a .ui file as root
cmd.append("Pages/MyPage.ui");
// load into a container
cmd.append("#Container", "Pages/ListItem.ui");
// inline markup
cmd.appendInline("#Container", "Label { Text: Hello; }");
// insert before a sibling
cmd.insertBefore("#Sibling", "Pages/Item.ui");
// insert inline before a sibling
cmd.insertBeforeInline("#Sibling", "Label { Text: New; }");
// text
cmd.set("#Label.Text", "Hello World");
// numbers
cmd.set("#Slider.Value", 50);
// booleans
cmd.set("#Button.Disabled", true);
cmd.set("#Panel.Visible", false);
// null
cmd.setNull("#Optional.Value");
// arrays/lists
cmd.set("#Items", new String[]{"a", "b", "c"});
cmd.set("#Items", List.of("a", "b", "c"));

For complex types, use setObject():

// styling
cmd.setObject("#Element.Style", new PatchStyle(...));
cmd.setObject("#Element.Anchor", new Anchor(...));
// value references
cmd.set("#Element.Style", Value.ref("Pages/Styles.ui", "ButtonStyle"));
// clear children of a container
cmd.clear("#Container");
// remove an element entirely
cmd.remove("#Element");

Binds UI events to server handlers.

TypeTrigger
ActivatingClick/activate (buttons, list items)
RightClickingRight-click
DoubleClickingDouble-click
MouseEnteredCursor enters element
MouseExitedCursor leaves element
ValueChangedInput value changed
ElementReorderedElement reordered in list
ValidatingValidation event
DismissingPage dismiss attempt
FocusGainedElement gained focus
FocusLostElement lost focus
KeyDownKey pressed while focused
MouseButtonReleasedMouse button released (useful for sliders)
SlotClickingInventory slot clicked
SlotDoubleClickingInventory slot double-clicked
SlotMouseEnteredCursor entered slot
SlotMouseExitedCursor left slot
DragCancelledDrag operation cancelled
DroppedDrag-drop completed
SlotMouseDragCompletedSlot drag completed
SlotMouseDragExitedCursor left slot during drag
SlotClickReleaseWhileDraggingClick released on slot while dragging
SlotClickPressWhileDraggingClick pressed on slot while dragging
SelectedTabChangedTab selection changed
events.addEventBinding(
CustomUIEventBindingType.Activating,
"#Button",
EventData.of("Action", "submit")
);

Static data - value is set at binding time:

// "ItemId" will always be "sword_01" when this button is clicked
events.addEventBinding(
CustomUIEventBindingType.Activating,
"#BuyButton",
EventData.of("ItemId", "sword_01")
);

Dynamic data - value is read from UI at event time (use @ prefix):

// reads current value of #SearchInput when event fires
events.addEventBinding(
CustomUIEventBindingType.ValueChanged,
"#SearchInput",
EventData.of("@Query", "#SearchInput.Value")
);
events.addEventBinding(
CustomUIEventBindingType.Activating,
"#SaveButton",
new EventData()
.append("Action", "save") // static
.append("@Name", "#NameInput.Value") // dynamic
.append("@Count", "#CountSlider.Value") // dynamic
);

By default, events lock the interface until the server responds (prevents spam clicks).

// locks interface (default)
events.addEventBinding(type, selector, data, true);
// does NOT lock - good for sliders, search inputs
events.addEventBinding(type, selector, data, false);

Use locksInterface: false for:

  • Search inputs (instant feedback)
  • Sliders (smooth updates)
  • Any high-frequency interaction
// just notify server, no data needed
events.addEventBinding(CustomUIEventBindingType.Activating, "#CloseBtn");

@Override
public void build(Ref<EntityStore> ref, UICommandBuilder cmd,
UIEventBuilder events, Store<EntityStore> store) {
cmd.append("Pages/ItemBrowser.ui");
// search input - dynamic value, no lock for instant feedback
events.addEventBinding(
CustomUIEventBindingType.ValueChanged,
"#SearchInput",
EventData.of("@Query", "#SearchInput.Value"),
false
);
// render list items
for (int i = 0; i < items.size(); i++) {
Item item = items.get(i);
cmd.append("#ItemList", "Pages/ItemRow.ui");
cmd.set("#ItemList[" + i + "] #Name.Text", item.getName());
cmd.set("#ItemList[" + i + "] #Icon.Texture", item.getIcon());
// click handler with static item ID
events.addEventBinding(
CustomUIEventBindingType.Activating,
"#ItemList[" + i + "]",
EventData.of("ItemId", item.getId())
);
}
}
@Override
public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store,
EventData data) {
if (data.query != null) {
// search query changed
this.filterItems(data.query);
this.rebuild();
} else if (data.itemId != null) {
// item selected
this.selectItem(data.itemId);
}
}