-
Notifications
You must be signed in to change notification settings - Fork 0
v5 Menus
Available in spigot-utils and its inheritors (spigot-jar).
In v3 this was the "GUI system" in com.kamikazejam.kamicommon.gui. It moved to
com.kamikazejam.kamicommon.menu in v4.
A menu belongs to one player, is built through a builder, and is opened with open().
| type | for |
|---|---|
SimpleMenu |
a fixed set of icons in a fixed size |
PaginatedMenu |
a list of icons spread over pages, with next/previous controls |
OneClickMenu |
allows exactly one click. Close it yourself from the handler if you want that |
All three are final. Build them, do not extend them.
SimpleMenu menu = SimpleMenu.Builder.create(3) // 3 rows
.titleFromMiniMessage("<gold>Shop")
.modifyIcons(icons -> {
icons.setMenuIcon("buy", new ItemBuilder(XMaterial.EMERALD), 11);
icons.setMenuClick("buy", data -> data.getPlayer().sendMessage("bought"));
})
.build(player);
menu.open();Builder.create(...) takes a MenuSize, an int row count, or an InventoryType. The constructors
take the same, plus (MenuSize, MenuEvents<M>, MenuOptions<M>) if you want to supply your own. That
three-argument form exists only in the MenuSize flavour.
PaginatedMenu menu = new PaginatedMenu.Builder(new SimplePaginationLayout(), 6)
.titleFromMiniMessage("<gold>Backpack")
.modifyPageIcons(pages -> {
for (ItemStack item : contents) {
pages.addPagedIcon(new ItemBuilder(item));
}
})
.build(player);
menu.open(); // page 0
menu.open(2); // straight to page 3
menu.getCurrentPage();
⚠️ PaginatedMenu.Builderhas no staticcreate(...)factories and the layout is mandatory. This is the first thing that breaks a copy-pastedSimpleMenuexample.
The PaginationLayout decides which slots hold paged icons and where the next and previous controls go.
SimplePaginationLayout is the default; implement the interface for anything else. Titles come from
AbstractPaginatedMenuTitle, with DefaultPaginatedMenuTitle supplied.
open(int) throws IllegalStateException if the layout yields no usable slots for the menu size.
OneClickMenu menu = OneClickMenu.Builder.create(1)
.titleFromMiniMessage("<red>Confirm?")
.build(player, data -> {
// the single click
});
menu.open();build takes either a MenuClick<OneClickMenu> or a MenuClickTransform<OneClickMenu>. The no-arg
open() resets the used-click flag, so the menu is reusable; open(boolean) and reopenMenu(...) do
not. oneClickOptions(...) controls whether clicking the filler counts.
Every builder has these, and each returns the builder:
size(MenuSize)
titleFromMiniMessage(String) titleFromLegacySection(String)
titleFromComponent(VersionedComponent)
title(ComponentMenuTitleProvider) // per-player titles
titleReplacement(CharSequence, CharSequence)
options(Consumer<MenuOptions<M>>)
events(Consumer<MenuEvents<M>>)
fillerIcon(@Nullable MenuIcon<M>)
modifyIcons(Consumer<IMenuIconsAccess<M>>)titleReplacement is how you get the player's name into a title that came from config:
.titleFromMiniMessage(config.getString("title")) // "<gold>{player}'s bag"
.titleReplacement("{player}", player.getName())The plain
title(String)andtitle(MenuTitleProvider)overloads are deprecated. A bare lambda is ambiguous betweentitle(MenuTitleProvider)andtitle(ComponentMenuTitleProvider), whose functional interfaces differ only in return type. Cast to(ComponentMenuTitleProvider)if you hit that.
menu.open(); // returns @Nullable InventoryView, null if the player is not valid
menu.open(true); // reset the tick counter, as if freshly opened
menu.close();
menu.reopenMenu(); menu.reopenMenu(boolean reset);
menu.setSize(MenuSize); menu.resizeMenu(MenuSize); // both work while the menu is open
menu.modifyIcons(...); menu.placeIcons(predicate); // null predicate refreshes everything
menu.getPlayer(); menu.getEvents(); menu.getOptions(); menu.getMenuSize();Call placeIcons(null) after changing icons from inside a click handler, to make the change visible
immediately.
A MenuIcon<M> wraps one or more ItemBuilders plus a click handler.
MenuIcon<SimpleMenu> icon = new MenuIcon<>(new ItemBuilder(XMaterial.DIAMOND));
icon.setMenuClick(data -> data.getPlayer().sendMessage("clicked"));
icon.setClickSound(XSound.ITEM_GOAT_HORN_SOUND_0);An icon does not know its own slot. The menu assigns it, either through config or through
IMenuIconsAccess.
Give an icon several builders and it cycles between them every builderRotateTicks ticks (default 20).
A modifier rebuilds the icon's item on a schedule, which is how you get live data into a menu:
icon.setAutoUpdate(builder -> builder.displayName(
serializer.fromMiniMessage("<gray>Online: " + Bukkit.getOnlinePlayers().size())), 20);
⚠️ Modifiers must return theItemBuilder. In v4 they returnedvoidand mutated in place. A modifier that mutates and returns nothing no longer has any effect. Returning a value lets you swap the prototype.
StaticIconModifier receives the builder; StatefulIconModifier also receives the previous ItemStack,
the player and the tick.
One interface, MenuClick<M>, receives a MenuClickData<M> carrying the player, click type, raw event,
slot, the menu itself, and the page for paginated menus.
icons.setMenuClick("buy", data -> {
Player p = data.getPlayer();
if (data.getClickType().isRightClick()) { /* ... */ }
});PlayerSlotClick<M> with PlayerClickData<M> handles clicks in the player's own inventory.
Migrating from v4:
MenuClickEventandMenuClickPageare gone, replaced byMenuClick<M>, and the v4 transform classes were consolidated intoMenuClickTransform<M>andPlayerSlotClickTransform. Everything is generic on the menu type now, soMenuIconbecomesMenuIcon<SimpleMenu>and so on.
.events(events -> {
events.addOpenCallback((player, view) -> { });
events.addCloseCallback((player, event) -> { });
events.addClickPredicate(event -> true); // false blocks the click
})
.options(options -> {
options.getExcludedFillSlots().add(4);
})Every add* has an overload taking a String id so you can remove it again later.
MenuOptions also carries the drag toggles, and both default to true, so a player cannot drag
items anywhere while the menu is open until one of them is turned off:
-
cancelDragEventcancels drags that touch at least one menu slot -
cancelPlayerDragEventcancels drags confined to the player's own inventory
A drag touching any menu slot is judged by cancelDragEvent alone; cancelPlayerDragEvent never
applies to it.
Migrating from v4: the
MenuEventsModificationandMenuOptionsModificationinterfaces were removed in favour of plainConsumers, so a v4 lambda still works but the type name is gone.
Empty slots are filled automatically. To change or disable:
.fillerIcon(null) // no filler
.fillerIcon(new MenuIcon<>(new ItemBuilder(XMaterial.BLACK_STAINED_GLASS_PANE)))MenuIcon.Config sets the global default filler item.
- Menu Configuration, for loading a menu from YAML
- ItemBuilder
Setup
Spigot
Text
Data
Migration
Other versions