22
33import net .kyori .adventure .text .Component ;
44import org .bukkit .Bukkit ;
5+ import org .bukkit .plugin .Plugin ;
6+ import org .bukkit .event .inventory .InventoryCloseEvent ;
57import org .bukkit .inventory .Inventory ;
68import org .bukkit .inventory .ItemStack ;
79import org .jetbrains .annotations .Contract ;
810import org .jetbrains .annotations .NotNull ;
11+ import org .jetbrains .annotations .Nullable ;
912
1013import java .util .Collections ;
14+ import java .util .HashMap ;
1115import java .util .HashSet ;
1216import 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