Skip to content

Commit e4bbf55

Browse files
committed
implemented click handler logic
1 parent 43dfade commit e4bbf55

File tree

3 files changed

+476
-11
lines changed

3 files changed

+476
-11
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package me.touchie771.minecraftGUI.api;
2+
3+
import org.bukkit.event.inventory.ClickType;
4+
import org.bukkit.event.inventory.InventoryClickEvent;
5+
import org.jetbrains.annotations.Contract;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
import java.util.Arrays;
9+
import java.util.EnumSet;
10+
import java.util.function.Consumer;
11+
12+
/**
13+
* Handles click events for menu slots with filtering and cancellation options.
14+
*
15+
* <p>This class provides a way to register callbacks for inventory clicks with
16+
* fine-grained control over which click types are handled and whether the event
17+
* should be automatically cancelled.</p>
18+
*
19+
* <p>Example usage:
20+
* <pre>{@code
21+
* ClickHandler handler = ClickHandler.newBuilder()
22+
* .callback(event -> {
23+
* Player player = (Player) event.getWhoClicked();
24+
* player.sendMessage("You clicked the item!");
25+
* })
26+
* .filter(ClickType.LEFT, ClickType.RIGHT)
27+
* .autoCancel(true)
28+
* .build();
29+
* }</pre></p>
30+
*/
31+
public class ClickHandler {
32+
33+
private final Consumer<InventoryClickEvent> callback;
34+
private final EnumSet<ClickType> allowedClickTypes;
35+
private final boolean autoCancel;
36+
37+
/**
38+
* Creates a new ClickHandler with the specified configuration.
39+
* This constructor is package-private and should only be called by ClickHandlerBuilder.
40+
*
41+
* @param builder the configured builder containing handler properties
42+
* @throws IllegalArgumentException if builder or callback is null
43+
*/
44+
ClickHandler(@NotNull ClickHandlerBuilder builder) {
45+
if (builder.callback == null) {
46+
throw new IllegalArgumentException("Callback cannot be null");
47+
}
48+
this.callback = builder.callback;
49+
this.allowedClickTypes = builder.allowedClickTypes != null ?
50+
EnumSet.copyOf(builder.allowedClickTypes) : EnumSet.allOf(ClickType.class);
51+
this.autoCancel = builder.autoCancel;
52+
}
53+
54+
/**
55+
* Handles the given inventory click event if it matches the configured filters.
56+
*
57+
* @param event the inventory click event to handle
58+
*/
59+
public void handleClick(@NotNull InventoryClickEvent event) {
60+
61+
// Check if the click type is allowed
62+
if (!allowedClickTypes.contains(event.getClick())) {
63+
return;
64+
}
65+
66+
// Auto-cancel if configured
67+
if (autoCancel) {
68+
event.setCancelled(true);
69+
}
70+
71+
// Execute the callback
72+
try {
73+
callback.accept(event);
74+
} catch (Exception e) {
75+
// Log the exception but don't rethrow to avoid breaking other handlers
76+
System.err.println("Error in ClickHandler callback: " + e.getMessage());
77+
}
78+
79+
}
80+
81+
/**
82+
* Creates a new ClickHandlerBuilder instance for constructing click handlers.
83+
* This is the recommended way to create new ClickHandler instances.
84+
*
85+
* @return a new ClickHandlerBuilder ready for configuration
86+
*/
87+
@Contract(" -> new")
88+
public static @NotNull ClickHandlerBuilder newBuilder() {
89+
return new ClickHandlerBuilder();
90+
}
91+
92+
/**
93+
* Builder class for creating ClickHandler instances with a fluent API.
94+
*
95+
* <p>This builder allows you to configure all aspects of a click handler before creation:
96+
* the callback function, allowed click types, and auto-cancellation behavior.</p>
97+
*/
98+
public static class ClickHandlerBuilder {
99+
100+
private Consumer<InventoryClickEvent> callback;
101+
private EnumSet<ClickType> allowedClickTypes;
102+
private boolean autoCancel = true;
103+
104+
/**
105+
* Sets the callback function to execute when a click event is handled.
106+
*
107+
* @param callback the consumer that will receive the click event
108+
* @return this builder instance for method chaining
109+
* @throws IllegalArgumentException if callback is null
110+
*/
111+
public ClickHandlerBuilder callback(@NotNull Consumer<InventoryClickEvent> callback) {
112+
this.callback = callback;
113+
return this;
114+
}
115+
116+
/**
117+
* Sets which click types should be handled.
118+
* If not specified, all click types will be handled.
119+
*
120+
* @param clickTypes the click types to handle
121+
* @return this builder instance for method chaining
122+
*/
123+
public ClickHandlerBuilder filter(@NotNull ClickType... clickTypes) {
124+
if (clickTypes == null || clickTypes.length == 0) {
125+
this.allowedClickTypes = EnumSet.allOf(ClickType.class);
126+
} else {
127+
this.allowedClickTypes = EnumSet.copyOf(Arrays.asList(clickTypes));
128+
}
129+
return this;
130+
}
131+
132+
/**
133+
* Sets whether the event should be automatically cancelled before executing the callback.
134+
* Default is true.
135+
*
136+
* @param autoCancel true to auto-cancel events, false otherwise
137+
* @return this builder instance for method chaining
138+
*/
139+
public ClickHandlerBuilder autoCancel(boolean autoCancel) {
140+
this.autoCancel = autoCancel;
141+
return this;
142+
}
143+
144+
/**
145+
* Builds and returns a new ClickHandler instance with the configured properties.
146+
*
147+
* @return a new ClickHandler instance
148+
* @throws IllegalArgumentException if callback is not set
149+
*/
150+
public ClickHandler build() {
151+
return new ClickHandler(this);
152+
}
153+
}
154+
}

src/main/java/me/touchie771/minecraftGUI/api/Menu.java

Lines changed: 123 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22

33
import net.kyori.adventure.text.Component;
44
import org.bukkit.Bukkit;
5+
import org.bukkit.plugin.Plugin;
6+
import org.bukkit.event.inventory.InventoryCloseEvent;
57
import org.bukkit.inventory.Inventory;
68
import org.bukkit.inventory.ItemStack;
79
import org.jetbrains.annotations.Contract;
810
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
912

1013
import java.util.Collections;
14+
import java.util.HashMap;
1115
import java.util.HashSet;
1216
import java.util.List;
17+
import java.util.Map;
18+
import java.util.function.Consumer;
1319

1420
/**
1521
* Represents a Minecraft GUI menu that can be displayed to players.
@@ -32,6 +38,7 @@ public class Menu {
3238

3339
private final Inventory menu;
3440
private final HashSet<SlotItem> items;
41+
private final MenuClickHandler clickHandler;
3542

3643
/**
3744
* Creates a new menu from the specified builder.
@@ -40,15 +47,29 @@ public class Menu {
4047
* @param builder the configured builder containing menu properties
4148
* @throws IllegalArgumentException if builder is null
4249
*/
43-
public Menu(@NotNull MenuBuilder builder) {
50+
public Menu(@NotNull MenuBuilder builder, @NotNull Plugin plugin) {
4451
this.menu = Bukkit.createInventory(null, builder.inventorySize, builder.inventoryTitle);
4552
this.items = new HashSet<>(builder.items);
53+
this.clickHandler = new MenuClickHandler(this.menu, plugin.getLogger());
54+
55+
// Copy click handlers from builder
56+
for (Map.Entry<Integer, ClickHandler> entry : builder.clickHandlers.entrySet()) {
57+
this.clickHandler.setClickHandler(entry.getKey(), entry.getValue());
58+
}
59+
60+
// Set close handler from builder
61+
if (builder.closeHandler != null) {
62+
this.clickHandler.setCloseHandler(builder.closeHandler);
63+
}
4664

4765
for (SlotItem item : items) {
4866
ItemStack itemStack = new ItemStack(item.material(), item.quantity());
4967
itemStack.editMeta(meta -> meta.displayName(item.itemName()));
5068
menu.setItem(item.itemSlot(), itemStack);
5169
}
70+
71+
// Register event handlers
72+
registerEvents(plugin);
5273
}
5374

5475
/**
@@ -126,17 +147,69 @@ public HashSet<SlotItem> getItems() {
126147
}
127148

128149
/**
129-
* Returns the SlotItem at the specified slot position.
130-
* This is useful for handling inventory click events.
150+
* Registers a click handler for the specified slot.
131151
*
132-
* @param slot the slot position to check (0-53 for chest inventories)
133-
* @return the SlotItem at the specified slot, or null if the slot is empty
152+
* @param slot the slot position (0-53 for chest inventories)
153+
* @param handler the click handler to register
154+
* @throws IllegalArgumentException if slot is invalid or handler is null
155+
*/
156+
public void onClick(int slot, @NotNull ClickHandler handler) {
157+
if (slot < 0 || slot > 53) {
158+
throw new IllegalArgumentException("Slot must be between 0 and 53");
159+
}
160+
clickHandler.setClickHandler(slot, handler);
161+
}
162+
163+
/**
164+
* Removes the click handler for the specified slot.
165+
*
166+
* @param slot the slot position to remove the handler from
167+
*/
168+
public void removeClickHandler(int slot) {
169+
clickHandler.removeClickHandler(slot);
170+
}
171+
172+
/**
173+
* Gets the click handler for the specified slot.
174+
*
175+
* @param slot the slot position
176+
* @return the ClickHandler for the slot, or null if none is registered
177+
*/
178+
public @Nullable ClickHandler getClickHandler(int slot) {
179+
return clickHandler.getClickHandler(slot);
180+
}
181+
182+
/**
183+
* Sets a handler that will be called when the inventory is closed.
184+
*
185+
* @param closeHandler the consumer that will receive the close event
186+
*/
187+
public void onClose(@NotNull Consumer<InventoryCloseEvent> closeHandler) {
188+
clickHandler.setCloseHandler(closeHandler);
189+
}
190+
191+
/**
192+
* Removes the close handler.
193+
*/
194+
public void removeCloseHandler() {
195+
clickHandler.removeCloseHandler();
196+
}
197+
198+
/**
199+
* Registers this menu's event handlers with Bukkit.
200+
* This method is called automatically in the constructor.
201+
*/
202+
private void registerEvents(Plugin plugin) {
203+
Bukkit.getPluginManager().registerEvents(clickHandler, plugin);
204+
}
205+
206+
/**
207+
* Unregisters this menu's event handlers.
208+
* This method is automatically called when the inventory is closed to prevent memory leaks.
209+
* You only need to call this manually if you want to unregister events before closing the menu.
134210
*/
135-
public SlotItem getItemAt(int slot) {
136-
return this.items.stream()
137-
.filter(item -> item.itemSlot() == slot)
138-
.findFirst()
139-
.orElse(null);
211+
public void unregisterEvents() {
212+
clickHandler.unregister();
140213
}
141214

142215
/**
@@ -171,6 +244,14 @@ public static class MenuBuilder {
171244
private int inventorySize;
172245
private Component inventoryTitle;
173246
private final HashSet<SlotItem> items = new HashSet<>();
247+
private final Map<Integer, ClickHandler> clickHandlers = new HashMap<>();
248+
private Consumer<InventoryCloseEvent> closeHandler;
249+
private Plugin plugin;
250+
251+
public MenuBuilder plugin(@NotNull Plugin plugin) {
252+
this.plugin = plugin;
253+
return this;
254+
}
174255

175256
/**
176257
* Sets the size of the inventory.
@@ -244,7 +325,38 @@ public Menu build() {
244325
if (inventoryTitle == null) {
245326
throw new IllegalArgumentException("Inventory title cannot be null");
246327
}
247-
return new Menu(this);
328+
if (plugin == null) {
329+
throw new IllegalArgumentException("Plugin cannot be null");
330+
}
331+
return new Menu(this, plugin);
332+
}
333+
334+
/**
335+
* Registers a click handler for the specified slot.
336+
*
337+
* @param slot the slot position (0-53 for chest inventories)
338+
* @param handler the click handler to register
339+
* @return this builder instance for method chaining
340+
* @throws IllegalArgumentException if slot is invalid or handler is null
341+
*/
342+
public MenuBuilder onClick(int slot, @NotNull ClickHandler handler) {
343+
if (slot < 0 || slot > 53) {
344+
throw new IllegalArgumentException("Slot must be between 0 and 53");
345+
}
346+
clickHandlers.put(slot, handler);
347+
return this;
348+
}
349+
350+
/**
351+
* Sets a handler that will be called when the inventory is closed.
352+
*
353+
* @param closeHandler the consumer that will receive the close event
354+
* @return this builder instance for method chaining
355+
* @throws IllegalArgumentException if closeHandler is null
356+
*/
357+
public MenuBuilder onClose(@NotNull Consumer<InventoryCloseEvent> closeHandler) {
358+
this.closeHandler = closeHandler;
359+
return this;
248360
}
249361
}
250362
}

0 commit comments

Comments
 (0)