Slot menus
A menu with real item slots, like a chest. GaldurMenu places the slots and handles Shift-click on both sides; GaldurContainerScreen draws the themed screen.
How it fits together
A slot menu has two halves, as in vanilla Minecraft:
| Class | Side | Role |
|---|---|---|
GaldurMenu | client and server | The logic: which slots exist, where, and how Shift-click moves items. |
GaldurContainerScreen | client | The screen: theme panel, slot frames, titles, opening animation. |
api.menu is the only Galdur UI package that loads on a dedicated server.
Write the menu
Extend GaldurMenu, add the container slots, then the player inventory:
public class CrateMenu extends GaldurMenu { private final Container crate; public CrateMenu(int id, Inventory player) { // client side this(id, player, new SimpleContainer(27)); } public CrateMenu(int id, Inventory player, Container crate) { // server side super(MY_MENUS.CRATE.get(), id); this.crate = crate; addSlotGrid(crate, 0, 9, 3, 8, 18); // container slots first addPlayerInventory(player, 8, 84); // player inventory last } @Override public boolean stillValid(Player player) { return crate.stillValid(player); }}Add the container slots first (addSlotGrid or addSlot), then the player inventory (addPlayerInventory), once. Every slot added before the player inventory counts as a container slot for Shift-click. Adding a grid after the inventory throws an IllegalStateException.
Coordinates
Coordinates are Minecraft's: GUI pixels from the top-left corner of the menu panel, pointing at the top-left corner of the 16 × 16 item. The slot frame extends one pixel around it.
| Constant | Value | Meaning |
|---|---|---|
SLOT_SIZE | 18 | Distance between two neighbouring slots: 16 for the item, 2 for the frames. |
HOTBAR_GAP | 4 | Vertical gap between the three inventory rows and the hotbar. |
addSlotGrid(container, firstIndex, columns, rows, x, y) fills row by row, left to right. addPlayerInventory(inventory, x, y) adds three rows of nine, then the hotbar HOTBAR_GAP pixels lower.
Register the menu type
Register the menu type as usual, with a DeferredRegister on the mod bus:
private static final DeferredRegister<MenuType<?>> MENUS = DeferredRegister.create(Registries.MENU, MyMod.MODID);public static final DeferredHolder<MenuType<?>, MenuType<CrateMenu>> CRATE = MENUS.register("crate", () -> new MenuType<>(CrateMenu::new, FeatureFlags.DEFAULT_FLAGS));The client constructor CrateMenu(int, Inventory) is the one MenuType calls on the client. On the server, open the menu with your container, for instance player.openMenu(new SimpleMenuProvider((id, inv, p) -> new CrateMenu(id, inv, crate), title)).
Register the screen
Client side, on the mod bus. factory() gives a screen constructor without writing a subclass:
modEventBus.addListener(RegisterMenuScreensEvent.class, event -> event.register(MY_MENUS.CRATE.get(), GaldurContainerScreen.factory()));Shift-click, drag and drop, number keys, item tooltips, the theme panel and the slot frames work without more code. factory() works for any menu, vanilla ones included.
What the screen does for you
- Panel size is derived from the slots: the margin left of the first slot is repeated on the right and at the bottom. A menu without slots gets the size of a vanilla chest (176 × 166).
- "Inventory" label sits right above the player inventory when the menu has one, and is hidden otherwise.
- Hover: the hovered slot gets a translucent accent veil and an
accent_hoveroutline, as does every slot targeted by a drag-spread. - Title in the brand font (Title font).
- Opening: the panel rises slightly when the screen opens.
- Narration: the hovered slot's item is read ("Iron Ingot, 32"), which Minecraft does not do.
Customise the screen
Subclass GaldurContainerScreen to force a panel size, or to add content:
public class CrateScreen extends GaldurContainerScreen<CrateMenu> { public CrateScreen(CrateMenu menu, Inventory inventory, Component title) { super(menu, inventory, title, 176, 190); // forced panel size } @Override protected void init() { super.init(); // add widgets here, relative to leftPos / topPos }}Override renderBg or renderLabels to draw more. If a button or a side tab sticks out of the panel, return its area from getExclusionZones() so JEI and EMI keep it clear.
Next: show thousands of items without real slots with Item grids & terminals.