feat(api): add ItemClickHandler<T> and ItemRenderer<T> role interfaces#22
Merged
Conversation
Introduce two functional role interfaces in the api/ package: - ItemRenderer<T> — T → ItemTemplate - ItemClickHandler<T> — (ClickContext, T) → void These mark user-defined classes whose sole job is to render or act on a typed item. They are designed to be implemented by classes (not just used as lambdas), so the contract is documented at the type level instead of buried inside a menu subclass. Pairs naturally with ListMenu<T>: the render() and onClick() hooks delegate to these interfaces when the menu is composed instead of subclassed. Also serves as the foundation for decorators (cooldown, permission, confirmation) that wrap a click handler without altering the handler itself. Purely additive — no existing signatures change.
|
There was a problem hiding this comment.
Pull request overview
This PR adds two small, typed “role interfaces” to the public API—one for rendering a domain object into an ItemTemplate, and one for handling clicks on a typed item—along with a small test suite and a changelog entry documenting the new API surface.
Changes:
- Added
ItemRenderer<T>(T + humanIndex -> ItemTemplate) as a@FunctionalInterface. - Added
ItemClickHandler<T>((ClickContext, T) -> void) as a@FunctionalInterface. - Added
RoleInterfacesTestand updatedCHANGELOG.mdto cover/document the new role interfaces.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/main/java/com/github/hanielcota/menuframework/api/ItemRenderer.java | Introduces a functional role interface for rendering typed items into ItemTemplate. |
| src/main/java/com/github/hanielcota/menuframework/api/ItemClickHandler.java | Introduces a functional role interface for typed click handling, distinct from per-slot ClickHandler. |
| src/test/java/com/github/hanielcota/menuframework/RoleInterfacesTest.java | Adds tests for lambda/class usability of the new interfaces. |
| CHANGELOG.md | Documents the newly added API interfaces under “Unreleased”. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+9
to
+11
| * builder DSL. {@code ItemClickHandler<T>} is what {@link ListMenu#onClick(ClickContext, Object)} | ||
| * delegates to when the menu is composed instead of subclassed, and what user-defined click | ||
| * handler classes should implement so the contract is documented at the type level. |
Comment on lines
+38
to
+43
| @Test | ||
| @DisplayName("ItemClickHandler is a functional interface") | ||
| void itemClickHandlerIsFunctional() { | ||
| ItemClickHandler<String> handler = (click, item) -> {}; | ||
| assertNotNull(handler); | ||
| } |
| */ | ||
| @FunctionalInterface | ||
| public interface ItemRenderer<T> { | ||
|
|
4 tasks
HanielCota
added a commit
that referenced
this pull request
May 21, 2026
…rs (#24) Introduce two opt-in decorators that wrap any ItemClickHandler<T>: - WithCooldown<T> — throttles per-player clicks to one per Duration window - WithPermission<T> — gates clicks behind a Bukkit permission node Both compose with the role interface added in #22, so users can stack them: new WithCooldown<>( new WithPermission<>(new HomeTeleportAction(homes), "myplugin.home"), Duration.ofSeconds(3)); Both intentionally drop blocked clicks silently. Adding feedback (cooldown message, no-permission message) is a UX concern with too many shapes to ship a single default for; users wrap further or implement a custom handler. Tests use Mockito to drive ClickContext + Player. Purely additive.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
com.github.hanielcota.menuframework.api.ItemRenderer<T>—T → ItemTemplate.com.github.hanielcota.menuframework.api.ItemClickHandler<T>—(ClickContext, T) → void.@FunctionalInterfaceso they double as lambda targets, but are designed to be implemented by classes whose sole job is one role.Motivation
Two patterns repeat across plugins built on top of
Menu/ListMenu<T>:ItemTemplate(icon + name + lore + glow).Today these are implemented as plain classes (e.g.,
BackEntryRenderer,BackClickHandler) with no framework-level contract. Implementing role interfaces gives:ItemClickHandler<Home>".WithCooldown/WithPermissionwrappers that compose around anyItemClickHandler<T>.ComposedListMenu<T>can take these interfaces in its constructor instead of forcing subclass overrides.Naming
ItemClickHandler(notClickHandler<T>) avoids collision with the existingcom.github.hanielcota.menuframework.api.ClickHandler— which is the per-slot lambda type used inside the builder DSL.Example
```java
public final class HomeEntryRenderer implements ItemRenderer {
@OverRide
public ItemTemplate render(Home home, int humanIndex) {
return ItemTemplate.builder(Material.WHITE_BED)
.name("Home #" + humanIndex + ": " + home.name())
.glow(home.favorite())
.build();
}
}
public final class HomeTeleportAction implements ItemClickHandler {
private final HomeService homes;
public HomeTeleportAction(HomeService homes) { this.homes = homes; }
@OverRide
public void handle(ClickContext click, Home home) {
click.close();
homes.teleport(click.player(), home);
}
}
```
Test plan
Follow-ups (separate PRs)