Community listing page, reviews may not be monitored by the author.
Community voices
Reviews
No reviews yet. Be the first to review this project!
Get it on
Available Platforms
About
Project Details
For authors
Embed Badge
If you're the author of this project, you can embed a live badge anywhere that supports HTML or Markdown. It updates automatically whenever ratings change.
Use HTML for any page that supports it, or Markdown for README files and Markdown-based descriptions.
Identifiers
Platform IDs
Resources
External Links
About
Description
Gui Helper
A Fabric 1.20.6 framework for building Minecraft GUIs from a single Gui object that holds every object and its data.
You describe a screen once, register it under an Identifier, and the server opens it by that id. Values come back the same way.
Quick start
// client — build and register
Gui shop = GuiRegistry.register(GuiHelper.id("shop"), new Gui()
.setTitle(Text.literal("Shop"))
.setBackground(new Texture(GuiHelper.id("textures/gui/shop.png"), 0, 0, 176, 166, 256, 256))
.setDarkBackground(true));
shop.addObject(new TextObject("price", new TextWithDetail(Text.literal("0"), ColorHelper.WHITE, 0.5f),
8, 20, 60, 10, Align.CENTER));
// server — open it
GuiNetwork.open(player, GuiHelper.id("shop"), new GuiData().set("price", 2000));
Every object takes a String id first. That id is the key for lookup, for server-sent data, and for values sent back — so it must be unique within a Gui (addObject throws otherwise).
Objects
All extend GuiObject. Coordinates are relative to the GUI's top-left corner.
| Object | Purpose | Value type |
|---|---|---|
TextObject |
Text, with Align / VerticalAlign inside a box |
— |
TextureObject |
A texture | — |
ItemRenderObject |
An ItemStack with count, optional vanilla item tooltip |
— |
ModelItemRenderObject |
An item's 3D model, spinning, with a fixed tilt | — |
EntityGuiObject |
A live entity, scaled to fit, optionally cursor-tracking | — |
ButtonObject |
Clickable, Runnable onPress |
— |
ToggleObject |
On/off, two textures | boolean |
RadioButtonObject |
One group, exclusive options | String |
StepperObject |
Arrows and ticks to pick a step | int |
DropdownObject |
Vector-drawn expanding list, wheel or draggable indicator, optional search | String |
TextFieldObject |
Text input | String |
IntegerFieldObject / DecimalFieldObject |
Validated numeric input, optional setRange(min, max) |
String (getIntValue() / getNumber()) |
PercentFieldObject |
Decimal 0–100, drawn as 65 % |
String (getNumber()) |
TextAreaObject |
Multi-line text input | String |
BoxObject |
Container, clips and scrolls its children | — |
ScrollBarObject |
Bar owned by a BoxObject |
— |
SlotGridObject |
Client proxy placing a server slot grid inside a BoxObject |
— |
Shared by every object
object.setLabel(new TextWithDetail(Text.literal("Name"), 0.5f)); // drawn above, left-aligned
object.setTooltip(Text.literal("Line one"), Text.literal("Line two"));
object.setShowWhen(toggle::getValue); // re-evaluated every frame
object.setDisabledWhen(() -> !toggle.getValue()); // 50% opacity, input blocked
setShowWhen / setDisabledWhen return GuiObject, so call them last in a chain.
Dropdown search
dropdown.setSearchable(true).setSearchPlaceholder(Text.literal("Search..."));
A search row filters options by label (case-insensitive contains); Enter picks the first match. Cleared on close.
Reading values
gui.getObject("sound", ToggleObject.class).getValue();
gui.collectValues(); // GuiData of every stateful object, boxes included
getObject recurses into boxes.
State
Values live on the object, not the widget, which decides when they reset:
| Event | Result |
|---|---|
| Window resize | Preserved — widgets rebuild and re-read the object |
| Reopening the GUI | Reset to defaults |
A Gui is a singleton once registered, so resetState() runs from the GuiRenderer constructor — one per screen instance, not per init(). Set your own baseline with setDefaultValue(...).
Data flow
GuiData is a String -> String map with typed accessors, used in both directions.
// server -> client, on open
GuiNetwork.open(player, id, new GuiData().set("price", 2000).set("sound", true));
// client -> server, on demand
ClientPlayNetworking.send(new GuiValuesPayload(id, gui.collectValues()));
An object picks up the entry matching its own id via applyData, and writes it back via collectData. ItemRenderObject and ModelItemRenderObject also read <id>.count.
Defaults are applied first, then server data — so an id absent from the payload leaves the default alone.
Receiving values
GuiValuesRegistry.register(GuiHelper.id("shop"), (player, values) -> {
// runs on the server thread
});
The receiver rejects a payload unless the player has that GUI open (GuiSessions) and a listener is registered. The wire format is capped at 64 entries, 64-char keys, 256-char values.
GuiData is untrusted. The server cannot validate ids or types — Gui is client-only — so re-check everything against server state.
Handler GUIs (inventories)
Slots are the sync protocol: index, order and count must match on both sides. So the layout is common (src/main) and the look is client (src/client), keyed by the same Identifier.
// common — onInitialize, both sides
GuiHandlerRegistry.register(GuiHelper.id("forge"), new GuiHandler()
.setTitle(Text.literal("Forge"))
.addSlotGrid("input", 0, 8, 18, 3, 3) // idPrefix, startIndex, x, y, columns, rows
.addSlot(new SlotObject("output", 9, 120, 35))
.setPlayerInventory(8, 84));
// client — a Gui under the same id
GuiRegistry.register(GuiHelper.id("forge"), buildForgeGui());
// server
GuiNetwork.openHandler(player, GuiHelper.id("forge"), data, inventory);
getInventorySize() is derived from the highest slot index, so size is never declared twice. To decorate slots, read their positions back rather than repeating them:
for (SlotObject slot : GuiHandlerRegistry.get(id).getSlots()) {
gui.addObject(new TextureObject(slot.getId(), Textures.TEXTURE_SLOT,
slot.getObjectStartX() - 1, slot.getObjectStartY() - 1));
}
Slots inside a box
// common: the grid's coordinates only define its shape
new GuiHandler().addSlotGrid("storage", 9, 0, 0, 3, 6);
// client: id = the grid prefix
box.addObject(new SlotGridObject("storage", GuiHelper.id("forge"), 0, 0));
The server keeps the slots and the sync; the client places them. Scrolling is smooth — partly visible slots are clipped and only their visible part takes hover and clicks (HandledScreenMixin + an access widener making Slot.x/y mutable). box.setScrollSnap(18) scrolls row by row instead.
One ExtendedScreenHandlerType<CustomScreenHandler, GuiOpenData> carries the id and data in the vanilla open packet, so handlers can be registered at any time.
Textures
new Texture(id, drawStartX, drawStartY, drawSizeX, drawSizeY, textureSizeX, textureSizeY)
drawStart + drawSize select a region of a sheet; textureSize is the file's dimensions. The region stretches to whatever size the object declares.
Nine-slice keeps corners crisp at any size — it works on sub-regions, unlike vanilla's GUI sprite atlas:
new Texture(...).setNineSlice(3); // all sides
new Texture(...).setNineSlice(6, 3, 6, 3); // left, top, right, bottom
Scaling
gui.setSizeMultiplicator(2) renders everything twice as big. Mouse coordinates are divided to match, and slot hit-testing follows. Tooltips stay at normal size at the real cursor.
Overlays
A DropdownObject's open list floats above everything and takes input first — clicks, wheel and ESC — so a widget underneath can't steal a click or show its tooltip. Implement OverlayRenderer for anything else that needs to escape draw order and clipping; BoxWidget forwards all five members with its scroll offset, so overlays work inside boxes.
Debug
GuiHelper.DEBUG follows FabricLoader.isDevelopmentEnvironment(), so these exist under runClient and not in a built jar:
| Command | Opens |
|---|---|
/testGui [id] |
a registered Gui as a plain screen (default debug) |
/testHandler [id] |
a registered GuiHandler (default debug_handler, tab-completes) |
Known limits
- A registered
Guiis a shared instance — one screen at a time on the client, which is fine, but it is not re-entrant. SlotObject.backgroundSpritegoes throughGuiSlot, so the identifier must be a sprite stitched into the block atlas, not a loosetextures/gui/*.png.- Shift-click can insert into slots scrolled out of a box;
setDisabledWhenon aSlotGridObjectonly dims it — lock slots server-side. SlotGridObjectonly works in a handler GUI, and only claims slots named<id>_<digits>.- The dropdown search text uses the first labelled option's style and needs rows at least 9px tall.
GuiObjectstill carriestexture/text/onPressfields that most subclasses leave null.
Screenshots
Gallery
Versions
Files
Relations
Project Relations
More like this
Similar Mods
Suggestions use data such as tags, dependencies, dependents, descriptions, titles, and more to rank how much they overlap with this mod.
On ModDex
Community snapshot
By the numbers
Statistics
Get it on
Available Platforms
On ModDex
Community snapshot
By the numbers
Statistics
Resources