Skip to content

feat(api): add ItemClickHandler<T> and ItemRenderer<T> role interfaces#22

Merged
HanielCota merged 1 commit into
masterfrom
feat/role-interfaces
May 21, 2026
Merged

feat(api): add ItemClickHandler<T> and ItemRenderer<T> role interfaces#22
HanielCota merged 1 commit into
masterfrom
feat/role-interfaces

Conversation

@HanielCota
Copy link
Copy Markdown
Owner

Summary

  • Adds com.github.hanielcota.menuframework.api.ItemRenderer<T>T → ItemTemplate.
  • Adds com.github.hanielcota.menuframework.api.ItemClickHandler<T>(ClickContext, T) → void.
  • Both are @FunctionalInterface so 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>:

  1. A class whose only job is to convert a domain object into an ItemTemplate (icon + name + lore + glow).
  2. A class whose only job is to react to a click on a typed item (close, perform action, reply).

Today these are implemented as plain classes (e.g., BackEntryRenderer, BackClickHandler) with no framework-level contract. Implementing role interfaces gives:

  • Explicit contract at the type level — IDE shows "this class is an ItemClickHandler<Home>".
  • Substitutability — different click handlers (with cooldown / permission / confirmation) become interchangeable.
  • Foundation for decorators — the next planned PR adds WithCooldown/WithPermission wrappers that compose around any ItemClickHandler<T>.
  • Composition path — a future ComposedListMenu<T> can take these interfaces in its constructor instead of forcing subclass overrides.

Naming

ItemClickHandler (not ClickHandler<T>) avoids collision with the existing com.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

  • New `RoleInterfacesTest` covering: functional-interface usability as lambda, parameter routing, class-based implementation
  • `./gradlew build` passes locally (compile + spotless + spotbugs + pmd + tests)
  • No changes to existing public signatures — purely additive

Follow-ups (separate PRs)

  • PR4 — `WithCooldown` / `WithPermission` decorators built on `ItemClickHandler`
  • PR5 — `ConfirmDialog` opinionated base for yes/no dialogs (independent of this PR)

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.
Copilot AI review requested due to automatic review settings May 21, 2026 18:55
@github-actions github-actions Bot added the area: api Public API changes label May 21, 2026
@sonarqubecloud
Copy link
Copy Markdown

@HanielCota HanielCota merged commit f695d7e into master May 21, 2026
11 checks passed
@HanielCota HanielCota deleted the feat/role-interfaces branch May 21, 2026 18:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 RoleInterfacesTest and updated CHANGELOG.md to 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> {

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: api Public API changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants