DUXPLIMA Documentation

Layout and animation

Fillers, mask patterns, adaptive slot layouts, and moving-highlight slot animations.

The filler

menu.filler() returns a fluent GuiFiller over the menu. Every method returns the filler, so calls chain.

menu.filler()
        .fillBorder(GuiItem.display(pane))
        .fillRow(3, GuiItem.display(divider))
        .fillEmpty(GuiItem.display(background));
MethodFills
fill(GuiItem)Every slot
fill(List<GuiItem>)Every slot, cycling the list
fillEmpty(GuiItem)Only the slots still free
fillBorder(GuiItem)The outer ring
fillBorder(int offset, GuiItem)A ring inset by offset rings
fillRow(int row, GuiItem)One 1-indexed row
fillColumn(int col, GuiItem)One 1-indexed column
fillRect(r1, c1, r2, c2, GuiItem)A rectangle between two corners
pattern(List<String>, Map<Character, GuiItem>)A character mask

Order matters. fillEmpty after your buttons is background; fill after your buttons erases them.

Mask patterns

The mask is the readable way to lay a menu out, and it is the same shape a config-defined menu uses.

menu.filler().pattern(
        List.of(
                "XXXXXXXXX",
                "X  A B  X",
                "XXXXXXXXX"),
        Map.of(
                'X', GuiItem.display(pane),
                'A', GuiItem.button(acceptIcon, e -> accept()),
                'B', GuiItem.button(cancelIcon, e -> cancel())));

A space, or any character with no legend entry, leaves the slot alone.

GuiFiller.orderedBorderSlots(width, height, offset) is the static behind fillBorder, exposed so you can compute a ring yourself.

Layout arithmetic

GuiLayout is pure, side-effect-free slot maths, kept static so it is testable without an inventory.

MethodReturns
contentRows(rows)Rows left for content, reserving the bottom row for navigation once the menu has one to spare
adaptivePageSize(rows)A sensible page size for a chest of that height, never below one
clampPageSize(count)A caller-supplied page size clamped to at least one
fillCapacity(GuiType)The true slot count of a non-chest menu, which is not rows * 9
adaptiveSlots(count, layouts)The nicest fixed layout for that many items
singleRowLayouts()A ready-made count-to-slots map for small pages
List<Integer> slots = GuiLayout.adaptiveSlots(rewards.size(), GuiLayout.singleRowLayouts());

One item centres, two sit either side of centre, three make a tidy row, five fill it. This is the difference between a reward menu that looks designed and one that looks left-aligned.

Slot animation

A moving highlight over the menu: a pattern of lit slots plus the icon shown in them, advanced on the menu's tick clock.

SlotPattern pattern = SlotPattern.clockwiseBorder(9, 3, 4, 0);
menu.addAnimation(SlotAnimation.of(pattern, ItemBuilder.of(Material.LIME_STAINED_GLASS_PANE).build()));
FactoryPattern
SlotPattern.clockwiseBorder(width, height, litCount, offset)A run of lit slots chasing round the border
SlotPattern.sweep(width, height)A column sweeping left to right
SlotPattern.of(List<List<Integer>>)Your own frames, as slot lists

Each advance diffs the new frame against the last and writes only the slots that changed, so a border animation is a handful of item writes per tick rather than a full re-render.

An animation never paints over a button

A slot is lit only when it is free or still shows this overlay's own highlight, and cleared only when it still shows that highlight. Place a button onto a lit slot afterwards and the overlay loses ownership of it: it will not re-paint over the button, and it will not later clear it.

Animation needs the Scheduler overload of Guis.install. Without it, addAnimation is accepted and inert.

Reading a pattern directly

SlotPattern is a value you can inspect and test.

pattern.frameCount();
pattern.frame(2);
pattern.frameIndexAt(ticks);
pattern.slotsAt(ticks);
pattern.diff(fromFrame, toFrame);   // toClear and toLight

Auto refresh

Guis.gui().rows(3).autoRefresh(Duration.ofSeconds(1)).build();

Re-resolves every item on a timer while the menu is open. This is how a dynamic icon showing a live balance or a countdown stays current without you scheduling anything. It also needs the Scheduler overload.