Menus
Packages: dev.anchorlight.stonelib.menu, dev.anchorlight.stonelib
Chest-style menus are inventories the player must not take items out of. The common shortcut, matching the inventory by its title, breaks as soon as two menus share a title or a title is translated, and gives you nowhere to keep the menu's context. MenuHolder identifies a menu by its holder object instead, carries whatever the menu is about, and holds the click handler. MenuListener then makes every such menu read-only in one place.
Setup
Register the listener once, in onEnable:
getServer().getPluginManager().registerEvents(new MenuListener(), this);
It covers every menu the plugin opens through MenuHolder, so no menu needs its own listener, and no menu can be the one that forgot to cancel a click.
Opening a menu
MenuHolder<Arena> holder = MenuHolder.open(
this, // plugin
player, // who sees it
arena, // context
27, // size
MiniMessages.parse("<dark_gray>Arena shop"), // title
(viewer, slot, event) -> buy(viewer, arena, slot), // click handler
inventory -> { // populator
inventory.setItem(11, ItemBuilder.of(Material.IRON_SWORD)
.name(MiniMessages.parse("<white>Sword <gray>(50)"))
.build());
inventory.setItem(15, ItemBuilder.of(Material.BOW)
.name(MiniMessages.parse("<white>Bow <gray>(80)"))
.lore(MiniMessages.parse("<gray>Comes with 16 arrows"))
.build());
});
MenuHolder.open(plugin, player, context, size, title, clickHandler, populator) creates the inventory, attaches it to a new holder, runs the populator, and opens it. Returns the holder.
| Parameter | Meaning |
|---|---|
context | Anything the menu is about: an arena, a shop category, a page number. May be null. |
size | A multiple of 9, from 9 to 54. Bukkit throws otherwise. |
title | An Adventure Component. |
clickHandler | Called for clicks in the menu. May be null for a display-only menu. |
populator | Fills the inventory before it opens. May be null. |
MenuHolder
| Method | Meaning |
|---|---|
new MenuHolder<>(T context, ClickHandler) | For creating the inventory yourself. Prefer open, which also sets the holder's inventory. |
context() | The context the menu was opened with. |
clickHandler() | The handler. |
getInventory() | The inventory. null if you built the holder yourself without open. |
@FunctionalInterface
interface ClickHandler { void onClick(Player player, int slot, InventoryClickEvent event); }
@FunctionalInterface
interface Populator { void populate(Inventory inventory); }
What MenuListener does
| Event | Handling |
|---|---|
Any click while a MenuHolder inventory is open | Cancelled first, always, wherever the click landed. Shift-clicks and hotbar swaps from the player's own inventory could otherwise push items into the menu. |
| A click inside the menu itself, by a player | Passed to the click handler with the clicked slot. |
| A click in the player's own inventory while the menu is open | Cancelled, and not passed to the handler. |
Any drag involving a MenuHolder inventory | Cancelled. |
The click is already cancelled when your handler runs, so there is no need to cancel it again.
Paging and refreshing
To change a menu in place, edit holder.getInventory() directly, or event.getInventory() from inside a click handler. To move to another page, open a new menu with the new context. The old one closes as the new one opens.
void openShop(Player player, ShopPage page) {
MenuHolder.open(this, player, page, 27, page.title(),
(viewer, slot, event) -> {
if (slot == 26) {
openShop(viewer, page.next());
}
},
page::fill);
}
The click handler cannot refer to the holder it is being passed to, since that holder does not exist yet. Capture the context variable instead, as page is here.
ItemBuilder
Class: dev.anchorlight.stonelib.ItemBuilder
A small fluent helper for menu icons.
| Method | Meaning |
|---|---|
ItemBuilder.of(Material) | Starts an item stack of one. |
name(Component) | Sets the display name. |
lore(Component...) | Sets the lore lines, replacing any existing lore. |
build() | Applies the changes and returns the ItemStack. |
For amounts, enchantments, or other metadata, edit the stack that build() returns.
:::tip Italics in names and lore
Minecraft renders custom item names and lore in italics unless told otherwise. Start the MiniMessage template with <!italic> to turn that off.
:::