-
Notifications
You must be signed in to change notification settings - Fork 0
07 5 inventory click handling
KLibrary replaces manual raw InventoryClickEvent handling with an object-oriented Button architecture. The system automatically secures inventories, prevents unintended item theft, and maps interactions strictly to the displayed Button elements.
When a player interacts with an open KLibrary GUI, the following deterministic event sequence occurs:
- Player Click: The raw Bukkit event is fired.
-
Event Interception: The
InventoryManagerintercepts the event and universally cancels it (event.setCancelled(true)), preventing the physical movement of items. -
Target Resolution: The manager evaluates the exact clicked slot and searches the prioritized layers for an associated
Button. -
Action Dispatch: If a button exists and is currently visible,
button.onClick(event, context)is invoked. -
State Modification (Optional): The button's click logic may choose to alter the contextual state and call
manager.update(...). - Rerender: The provider redraws the items corresponding to the new state.
KLibrary implements layered priority rendering. This protects critical UI controls (like Back or Next buttons) from being accidentally hijacked by dynamically loaded data grids.
The click resolution hierarchy is:
-
View-Level Buttons: The manager immediately checks if the active
InventoryViewhas a button registered at the clicked slot (viaview.getButton(slot)). If found, the event is consumed here. -
Provider-Level Handling: If no view-level button exists, the click falls back to the
InventoryProvider#onClickmethod, which typically maps to the generic content items built during the provider'supdate()loop.
To build custom interactivity without boilerplate, use the BaseButton builder. By passing logic to anyClick, leftClick, or rightClick, you bind discrete actions that have full access to the current InventoryContext.
import io.github.kaivian.klibrary.inventory.api.InventoryContext;
import io.github.kaivian.klibrary.inventory.button.BaseButton;
import io.github.kaivian.klibrary.inventory.button.Button;
import io.github.kaivian.klibrary.action.api.ActionResult;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public Button createInteractiveButton() {
return BaseButton.builder()
.template(new ItemStack(Material.GOLD_BLOCK))
.anyClick(List.of(actionContext -> {
// 1. Read current state
int goldCount = (int) actionContext.getMetadata().getOrDefault("gold_collected", 0);
// 2. Build modified context with updated state
InventoryContext newContext = InventoryContext.from(actionContext)
.metadata("gold_collected", goldCount + 1)
.build();
// 3. Dispatch the state update to trigger a re-render
manager.update(actionContext.getPlayer().get(), newContext);
// 4. Return success action result
return ActionResult.success();
}))
.build();
}For exceptionally complex interactions that require direct access to the raw Bukkit click event properties (such as Shift-Clicks or checking the exact cursor item), you can implement the Button interface directly instead of using BaseButton.
public class AdvancedTradeButton implements Button {
@Override
public @NotNull ItemStack getItem(@NotNull InventoryContext context) {
return new ItemStack(Material.EMERALD);
}
@Override
public void onClick(@NotNull InventoryClickEvent event, @NotNull InventoryContext context) {
if (event.isShiftClick()) {
// Bulk trade logic
} else if (event.isRightClick()) {
// Half trade logic
}
}
@Override
public boolean isVisible(@NotNull InventoryContext context) {
return true;
}
}-
Do not cancel events manually: The
InventoryManagerhandles baseline cancellation universally. You only need to focus on your business logic. -
Never mutate the GUI directly in the click handler: e.g., avoid
event.getClickedInventory().setItem(...). Always modify theInventoryContextstate and letupdate()recalculate the visual output.