> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.refineryteam.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# GUI Framework

> Building inventory menus with RefineryGUI, annotations, layouts, and pagination.

`xyz.refineryteam.refinerycore.api.gui` provides an inventory GUI framework built around `InventoryHolder`, so you never write raw `InventoryClickEvent` boilerplate per-menu.

## How click routing works

RefineryCore registers a single shared listener, `GUIHandlerListener`, once (in `RefineryCorePlugin#onEnable`). For every inventory click/drag/close event, it checks whether the inventory's holder is a `RefineryGUI` — if so, it delegates to that GUI instance. This means **you never register your own inventory listener** — just extend `RefineryGUI` and it's handled automatically.

## Basic usage

```java ExampleGUI.java icon="java" theme={"system"}
public class ExampleGUI extends RefineryGUI {

    private final Player target;

    public ExampleGUI(Player target) {
        super(27, "<bold>Example</bold>"); // 27 slots (3 rows), MiniMessage title
        this.target = target;
    }

    @Override
    public GUILayout[] layouts() {
        return new GUILayout[]{ GUILayout.border(3, new ItemStack(Material.BLACK_STAINED_GLASS_PANE)) };
    }

    @SlotItem(slots = { 13 })
    public ItemStack profileItem() {
        return ItemBuilder.of(Material.PLAYER_HEAD)
            .name("<yellow>" + target.getName())
            .build();
    }

    @SlotAction(slots = { 13 })
    public void onProfileClick(@NonNull Player clicker) {
        clicker.sendMessage(EasyMiniMessage.format("<green>Clicked profile!"));
    }

    @Override
    public void onInitialize(Player player) {}
}
```

Open it with:

```java ExampleClass.java icon="java" theme={"system"}
RefineryGUI.open(player, new ExampleGUI(player));
```

## Annotations

### `@SlotItem`

Applied to a no-argument method returning an `ItemStack`. The method is invoked during GUI initialization, and its result is placed at every slot listed in `slots`.

### `@SlotAction`

Applied to a method handling clicks on the given `slots`. The method can take any of these parameter shapes:

* No parameters
* `Player`
* `InventoryClickEvent`
* `Player, InventoryClickEvent`

```java ExampleGUI.java icon="java" theme={"system"}
@SlotAction(slots = { 22 })
public void onCloseClick(Player player, InventoryClickEvent event) {
    player.closeInventory();
}
```

## Lifecycle methods

Override these on your `RefineryGUI` subclass as needed:

| Method                        | Called when                                                                                                         |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `onInitialize(Player player)` | **Required.** Called before the inventory is shown — set static items/layouts here                                  |
| `onOpen(Player player)`       | After initialization completes and the inventory has been populated                                                 |
| `onClose(Player player)`      | When the player closes the inventory                                                                                |
| `onDestroy()`                 | When `destroy()` is called explicitly                                                                               |
| `cancelClicks()`              | Return `false` to allow normal inventory interaction (e.g. item pickup); defaults to `true`, cancelling every click |
| `layouts()`                   | Return an array of `GUILayout`s applied automatically after `onInitialize`                                          |

## Manually setting items

Beyond `@SlotItem`/`@SlotAction`, you can set items imperatively:

```java ExampleGUI.java icon="java" theme={"system"}
setItem(11, someItem, (player, event) -> {
    player.sendMessage("Clicked!");
});

setItem(15, anotherItem); // no click handler
onSlot(20, (player, event) -> { /* just a handler, item set elsewhere */ });
```

## `GUILayout`

A reusable, composable way to describe static filler/border patterns without repeating `setItem` calls across every GUI:

```java ExampleGUI.java icon="java" theme={"system"}
GUILayout layout = GUILayout.border(3, fillerItem)   // border of a 3-row inventory
    .merge(GUILayout.fill(9, otherFiller))            // combine with another layout
    .slot(4, someSpecificItem);                       // and a one-off slot
```

* `GUILayout.border(rows, filler)` — fills the outer ring of a `rows`-row inventory
* `GUILayout.fill(size, filler)` — fills every slot from `0` to `size - 1`
* `.slot(int, ItemStack)` — sets a specific static item
* `.slot(int, Consumer<RefineryGUI>)` — runs custom logic against the GUI for a specific slot
* `.merge(GUILayout)` — combines two layouts (later layout's entries win on conflict)

Return one or more layouts from `layouts()` and they're applied automatically after `onInitialize`.

## Pagination — `PaginatedGUI<T>`

For menus displaying a list of items across multiple pages (players, kits, warps, etc.), extend `PaginatedGUI<T>` instead of `RefineryGUI` directly:

```java KitsGUI.java icon="java" theme={"system"}
public class KitsGUI extends PaginatedGUI<Kit> {

    public KitsGUI(List<Kit> kits) {
        super(54, "<bold>Kits</bold>", kits, 45); // 45 content slots per page
    }

    @Override
    protected int[] contentSlots() {
        return IntStream.range(0, 45).toArray();
    }

    @Override
    protected ItemStack renderItem(Player player, Kit kit, int index) {
        return ItemBuilder.of(kit.icon()).name(kit.displayName()).build();
    }

    @Override
    protected void onItemClick(Player player, Kit kit, int index, InventoryClickEvent event) {
        kit.giveTo(player);
    }

    @Override
    protected ItemStack previousPageItem() {
        return ItemBuilder.of(Material.ARROW).name("<gray>Previous Page").build();
    }

    @Override
    protected ItemStack nextPageItem() {
        return ItemBuilder.of(Material.ARROW).name("<gray>Next Page").build();
    }
}
```

Required abstract methods:

| Method                                    | Purpose                                                                                                                       |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `contentSlots()`                          | The slots available for paginated content, in display order                                                                   |
| `renderItem(player, item, index)`         | The `ItemStack` to display for a given item                                                                                   |
| `onItemClick(player, item, index, event)` | Click handler for a content item                                                                                              |
| `previousPageItem()` / `nextPageItem()`   | Navigation button items (return `null` to hide when unavailable — handled automatically based on `hasPrevious()`/`hasNext()`) |

Optional overrides:

| Method                              | Default                                                                |
| ----------------------------------- | ---------------------------------------------------------------------- |
| `onPageInitialize(player, context)` | No-op — set static items alongside the paginated content               |
| `emptySlotItem(slot)`               | `null` — item shown in unused content slots on a partially-filled page |
| `previousSlot()` / `nextSlot()`     | Bottom-left / bottom-right slot of the inventory                       |

Navigation, page-index tracking, and re-rendering on page change are handled internally via `PageContext<T>`. Call `updateItems(List<T>)` to refresh the underlying list (e.g. after a kit is added) without recreating the GUI.

## Confirmation dialogs — `ConfirmationGUI`

A ready-made yes/no screen so you don't rebuild a confirm/cancel inventory for every destructive action:

```java BanCommand.java icon="java" theme={"system"}
ConfirmationGUI.builder()
    .title("<red>Confirm ban")
    .description(ItemBuilder.of(Material.PLAYER_HEAD)
        .name("<red>Ban " + target.getName() + "?")
        .lore("<gray>This action cannot be undone.")
        .build())
    .onConfirm(player -> banPlayer(target))
    .onCancel(player -> player.sendMessage("Cancelled."))
    .open(player);
```

Builder options:

| Method                                             | Description                                                                                                     |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `title(String)`                                    | MiniMessage title, default `<gray>Confirm?`                                                                     |
| `description(ItemStack)`                           | Optional item shown between the confirm/cancel buttons                                                          |
| `confirmItem(ItemStack)` / `cancelItem(ItemStack)` | Override the default green/red glass pane buttons                                                               |
| `onConfirm(handler)` / `onCancel(handler)`         | Callbacks — closing the inventory without choosing also triggers `onCancel`                                     |
| `closeOnChoice(boolean)`                           | Default `true`. Set `false` if your handler opens another GUI immediately and you want to avoid a flicker-close |

See also [Interaction Prompts](/refinerycore/interaction) for chat- and anvil-based text input, which pairs well with confirmation flows.
