EffectiveMenu

abstract class EffectiveMenu

Base class for a chest-style GUI menu laid out with a character pattern.

A subclass supplies a title, a row-based getPattern and a getSymbolsToItems mapping each pattern character to an item plus its click handlers. Optionally, getFreeSlotSymbol marks slots the player may place/take items in — changes are reported through onSlotChanged. Like the other bases, the menu registers itself on construction; open it with getMenu.

Per-viewer layout

getPattern, getSymbolsToItems and getFreeSlots all receive whoOpen — the player the menu is being built/handled for (nullable: null means the default, no-viewer layout) — so the layout can differ per viewer (e.g. slots unlocked by rank/purchase). A single menu object serves every player; there is no shared mutable "current player" field, so this is race-free even when several players open it at once. Ignore the parameter if your menu is static.

object ExampleMenu : EffectiveMenu() {
override fun getMenuTitle() = "Example"
override fun getPattern(whoOpen: Player?) = listOf(
" ",
" x ",
" ",
)
override fun getSymbolsToItems(whoOpen: Player?) = mapOf(
'x' to SlotData(ItemStack(Material.DIAMOND), listOf(
ClickData(ClickType.LEFT) { player -> player.sendMessage("hi") }
))
)
override fun getNamespacedData() = ExamplePlugin.instance to "example"
override fun getFreeSlotSymbol() = null
override fun getSlotsCount() = 27
override fun onSlotChanged(player: Player, slot: Int, item: ItemStack, wasPlaced: Boolean) = SlotChangeResult.ALLOW
}
// player.openInventory(ExampleMenu.getMenu(player))

A built-in /emenu <menu> command opens any registered menu in-game.

Inheritors

Constructors

Link copied to clipboard
constructor()

Types

Link copied to clipboard
data class ClickData(val clicks: Set<ClickType>, val callback: (Player) -> Unit)

A click handler for a slot: which clicks (Bukkit ClickTypes, e.g. LEFT, RIGHT, MIDDLE, SHIFT_LEFT) trigger it — matched exactly, any one of the set — and the action to run for the clicking player. Register one type via ClickData(ClickType.LEFT) { … }, or several via ClickData(ClickType.LEFT, ClickType.SHIFT_LEFT) { … }.

Link copied to clipboard

What the framework does with the free-slot contents after onClose runs.

Link copied to clipboard
object Companion
Link copied to clipboard

Whether a pending free-slot change is allowed. Returned from onSlotChanged before the change is applied: ALLOW lets it happen, CANCEL vetoes it (the underlying Bukkit event is cancelled, so the item stays where it was).

Link copied to clipboard
data class SlotData(val item: ItemStack, val clickHandlers: List<EffectiveMenu.ClickData>)

The item shown in a slot together with its click handlers.

Properties

Link copied to clipboard
val inventoryHolder: InventoryHolder

The stable InventoryHolder shared by every inventory this menu opens. Read-only: it can't be replaced, but it is exposed so external code can identify "is this open/closed inventory mine?" via identity — inventory.holder === someMenu.inventoryHolder (every getMenu call builds a fresh Inventory but reuses this same holder).

Functions

Link copied to clipboard
fun getFreeSlots(whoOpen: Player? = null): List<Int>?

Slot indices marked editable by getFreeSlotSymbol in whoOpen's layout, or null if none.

Link copied to clipboard
abstract fun getFreeSlotSymbol(): Char?

Pattern character marking player-editable slots, or null if the menu is read-only.

Link copied to clipboard
fun getItemsWithPattern(whoOpen: Player? = null): Map<Int, EffectiveMenu.SlotData>

Resolves whoOpen's pattern (or the default when null) into a slot-index → SlotData map.

Link copied to clipboard
fun getMenu(whoOpen: Player? = null): Inventory

Builds a fresh inventory instance of this menu laid out for whoOpen; pass to player.openInventory(...). whoOpen may be null to build the default (no-viewer) layout — pass the real player for per-viewer menus.

Link copied to clipboard
abstract fun getMenuTitle(): String

Inventory title shown at the top.

Link copied to clipboard
abstract fun getNamespacedData(): Pair<JavaPlugin, String>

Owning plugin and a plugin-unique id; together they form the getNamespacedName.

Link copied to clipboard

Unique identity as "<plugin-name>:<id>", lowercased.

Link copied to clipboard
abstract fun getPattern(whoOpen: Player? = null): List<String>?

Row strings (9 chars each) mapping characters to items via getSymbolsToItems; null for empty.

Link copied to clipboard
abstract fun getSlotsCount(): Int?

Explicit inventory size, or null to size automatically from the pattern.

Link copied to clipboard
abstract fun getSymbolsToItems(whoOpen: Player? = null): Map<Char, EffectiveMenu.SlotData>

Maps each pattern character to its SlotData (item + click handlers).

Link copied to clipboard
fun getViewers(): List<Player>

Players who currently have this menu open.

Link copied to clipboard
open fun onClose(player: Player, inventory: Inventory): EffectiveMenu.CloseAction

Called when player closes this menu, before the framework decides what to do with the free-slot items. Read the final inventory state here if you need it (e.g. persist the contents), then return a CloseAction telling the framework how to dispose of the free slots.

Link copied to clipboard
abstract fun onSlotChanged(player: Player, slot: Int, item: ItemStack, wasPlaced: Boolean): EffectiveMenu.SlotChangeResult

Called before a free-slot change is applied. React to it and/or veto it: return SlotChangeResult.CANCEL to block the change (the Bukkit interaction is cancelled, so the item stays put), or SlotChangeResult.ALLOW to let it through. Use it as a slot filter (reject certain items), a read-only guard, or just to persist/update on change.

Link copied to clipboard
fun openWithFreeSlots(whoOpen: Player, contents: List<ItemStack?>)

Opens the menu for whoOpen with contents pre-loaded into the free slots (in getFreeSlots order): the i-th non-null entry is placed into the i-th free slot; extras beyond the free-slot count are ignored.