Foundation’s Menu class provides a powerful system for creating interactive chest GUIs with automatic button registration, pagination, and animations.
Extend Menu and override getItemAt() to define your menu items:
public class ShopMenu extends Menu {
public ShopMenu () {
setTitle ( "&6&lShop" );
setSize ( 9 * 3 );
}
@ Override
public ItemStack getItemAt ( int slot ) {
if (slot == 11 )
return ItemCreator . of ( CompMaterial . DIAMOND_SWORD )
. name ( "&bDiamond Sword" )
. lore ( "&7Price: &e100 coins" )
. make ();
if (slot == 15 )
return ItemCreator . of ( CompMaterial . GOLDEN_APPLE )
. name ( "&eGolden Apple" )
. lore ( "&7Price: &e50 coins" )
. make ();
return NO_ITEM;
}
@ Override
protected void onMenuClick ( Player player , int slot , ItemStack clicked ) {
if (slot == 11 ) {
player . getInventory (). addItem ( new ItemStack ( Material . DIAMOND_SWORD ));
tellSuccess ( "Purchased Diamond Sword!" );
}
}
}
new ShopMenu (). displayTo (player);
Create interactive buttons as fields that are automatically registered:
public class SettingsMenu extends Menu {
private final Button soundButton ;
private final Button particlesButton ;
public SettingsMenu () {
setTitle ( "Settings" );
setSize ( 9 * 3 );
soundButton = new Button () {
@ Override
public void onClickedInMenu ( Player player , Menu menu , ClickType click ) {
PlayerCache cache = PlayerCache . from (player);
cache . toggleSounds ();
restartMenu ();
}
@ Override
public ItemStack getItem () {
PlayerCache cache = PlayerCache . from ( getViewer ());
return ItemCreator . of ( CompMaterial . NOTE_BLOCK )
. name ( cache . hasSoundsEnabled () ? "&aEnable" : "&cDisable" )
. make ();
}
};
particlesButton = Button . makeSimple (
ItemCreator . of ( CompMaterial . BLAZE_POWDER ). name ( "&dParticles" ),
"&7Toggle particle effects" ,
player -> {
PlayerCache . from (player). toggleParticles ();
restartMenu ();
});
}
@ Override
public ItemStack getItemAt ( int slot ) {
if (slot == 11 )
return soundButton . getItem ();
if (slot == 15 )
return particlesButton . getItem ();
return NO_ITEM;
}
}
Use the @Position annotation for automatic button placement:
public class MyMenu extends Menu {
@ Position ( 11 )
private final Button item1 ;
@ Position ( value = 0 , start = StartPosition . BOTTOM_CENTER )
private final Button closeButton ;
public MyMenu () {
setSize ( 9 * 5 );
item1 = Button . makeSimple (
ItemCreator . of ( CompMaterial . DIAMOND ). name ( "Item 1" ),
player -> tell ( "Clicked item 1!" )
);
closeButton = Button . makeSimple (
ItemCreator . of ( CompMaterial . BARRIER ). name ( "&cClose" ),
Player :: closeInventory
);
}
}
Position modes
Absolute slot
Relative to center
Bottom center
Bottom left
@ Position ( 13 ) // Center of 3-row menu
Create hierarchical menus with automatic return buttons:
public class CategoryMenu extends Menu {
private final Menu parent ;
public CategoryMenu ( Menu parent ) {
super (parent);
this . parent = parent;
setTitle ( "Category" );
setSize ( 9 * 3 );
}
}
// Open child menu
new CategoryMenu ( this ). displayTo (player);
The return button appears automatically in the bottom-right corner.
Before display
Override onPreDisplay() to modify the inventory before opening: @ Override
protected void onPreDisplay ( InventoryDrawer drawer) {
// Last-minute modifications
drawer . setItem ( 0 , customItem);
}
On display
Override onDisplay() to customize how the menu opens: @ Override
protected void onDisplay ( InventoryDrawer drawer, Player player) {
drawer . display (player);
tellSuccess ( "Menu opened!" );
}
After display
Run code after the menu is shown: @ Override
protected void onPostDisplay ( Player viewer) {
CompSound . BLOCK_CHEST_OPEN . play (viewer);
}
On close
Handle menu closing: @ Override
protected void onMenuClose ( Player player, Inventory inventory) {
// Save data or clean up
}
Update menu contents without closing:
// Simple refresh
restartMenu ();
// With title animation
restartMenu ( "&aUpdated!" );
// Redraw only buttons
redrawButtons ();
Animations
Title animations
animateTitle ( "&a&lItem Purchased!" );
Repeating tasks
@ Override
protected void onPostDisplay ( Player viewer) {
// Update every second (20 ticks)
animate ( 20 , () -> {
// Update logic
redrawButtons ();
});
}
Async animations
animateAsync ( 20 , () -> {
// Heavy computation
Common . runLater (() -> redrawButtons ());
});
Add an info button with menu description:
@ Override
protected String [] getInfo () {
return new String [] {
"Welcome to the shop!" ,
"Click items to purchase." ,
"&eYour balance: " + getBalance ()
};
}
Action control
Control what players can do in menus:
@ Override
protected boolean isActionAllowed ( MenuClickLocation location,
int slot,
ItemStack clicked,
ItemStack cursor,
InventoryAction action) {
// Allow taking items from bottom (player inventory)
return location == MenuClickLocation . PLAYER_INVENTORY ;
}
Allow shift-click
@ Override
public boolean isAllowShift ( int slot) {
return slot >= 27 ; // Allow in bottom inventory only
}
Extend MenuPagged for automatic pagination:
public class WarpMenu extends MenuPagged < Warp > {
public WarpMenu () {
super ( null , Warp . getWarps ());
setTitle ( "Warps" );
}
@ Override
protected ItemStack convertToItemStack ( Warp warp ) {
return ItemCreator . of ( CompMaterial . ENDER_PEARL )
. name ( "&b" + warp . getName ())
. lore (
"&7Location: " + warp . getLocation (),
"" ,
"&eClick to teleport!"
)
. make ();
}
@ Override
protected void onPageClick ( Player player , Warp warp , ClickType click ) {
player . teleport ( warp . getLocation ());
player . closeInventory ();
}
}
Extend MenuTools for a tool selection menu:
public class ToolsMenu extends MenuTools {
@ Override
protected Tool [] getTools () {
return new Tool [] {
new RegionTool (),
new InspectorTool ()
};
}
}
Advanced features
Menu menu = Menu . getMenu (player);
Menu previous = Menu . getPreviousMenu (player);
Menu lastClosed = Menu . getLastClosedMenu (player);
Custom sounds
Menu . setSound ( new SimpleSound ( CompSound . BLOCK_NOTE_BLOCK_PLING , 1F , 1.5F ));
Menu . setSound ( null ); // Disable
Title animation duration
Menu . setTitleAnimationDurationTicks ( 40 ); // 2 seconds
Menu . setTitleAnimationEnabled ( false );
Slot numbers debug
public MyMenu () {
setSlotNumbersVisible (); // Shows slot numbers in empty spaces
}
Messaging
tell ( "Message to viewer" );
tellInfo ( "Info message" );
tellSuccess ( "Success!" );
tellWarn ( "Warning!" );
tellError ( "Error!" );
tellQuestion ( "Are you sure?" );
tellAnnounce ( "Announcement!" );
Buttons are automatically registered when they’re declared as fields in your menu class.
Don’t call getViewer() in your constructor - it’s null until the menu is displayed!