diff --git a/src/main/java/me/touchie771/minecraftGUI/api/Menu.java b/src/main/java/me/touchie771/minecraftGUI/api/Menu.java index b685b36..f71a985 100644 --- a/src/main/java/me/touchie771/minecraftGUI/api/Menu.java +++ b/src/main/java/me/touchie771/minecraftGUI/api/Menu.java @@ -2,6 +2,7 @@ import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.plugin.Plugin; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.Inventory; @@ -352,6 +353,98 @@ public MenuBuilder items(SlotItem @NotNull ... items) { return this; } + /** + * Fills all empty slots in the inventory with the specified material. + * + * @param material the material to fill with + * @return this builder instance + * @throws IllegalStateException if inventory size is not set + */ + public MenuBuilder fillEmptyWith(@NotNull Material material) { + return fillEmptyWith(material, null); + } + + /** + * Fills all empty slots in the inventory with the specified material and display name. + * + * @param material the material to fill with + * @param name the display name for the filler items, or null to keep default name + * @return this builder instance + * @throws IllegalStateException if inventory size is not set + */ + public MenuBuilder fillEmptyWith(@NotNull Material material, @Nullable Component name) { + if (inventorySize <= 0) { + throw new IllegalStateException("Inventory size must be set before filling empty slots"); + } + + for (int i = 0; i < inventorySize; i++) { + if (isSlotFree(i)) { + items.add(SlotItem.builder(i) + .material(material) + .itemName(name) + .build()); + } + } + return this; + } + + /** + * Fills all empty slots except the specified ones with the given material. + * + * @param material the material to fill with + * @param excludedSlots the slots to exclude from filling + * @return this builder instance + * @throws IllegalStateException if inventory size is not set + */ + public MenuBuilder fillExcept(@NotNull Material material, int... excludedSlots) { + if (inventorySize <= 0) { + throw new IllegalStateException("Inventory size must be set before filling empty slots"); + } + + Set excluded = new HashSet<>(); + for (int slot : excludedSlots) { + excluded.add(slot); + } + + for (int i = 0; i < inventorySize; i++) { + if (!excluded.contains(i) && isSlotFree(i)) { + items.add(SlotItem.builder(i) + .material(material) + .build()); + } + } + return this; + } + + /** + * Fills a range of slots with the specified material. + * The range is from start (inclusive) to end (exclusive). + * + * @param start the starting slot (inclusive) + * @param end the ending slot (exclusive) + * @param material the material to fill with + * @return this builder instance + * @throws IllegalArgumentException if start or end are invalid + */ + public MenuBuilder fillRange(int start, int end, @NotNull Material material) { + if (start < 0 || end > 54 || start >= end) { + throw new IllegalArgumentException("Invalid range: " + start + " to " + end); + } + + for (int i = start; i < end; i++) { + if (isSlotFree(i)) { + items.add(SlotItem.builder(i) + .material(material) + .build()); + } + } + return this; + } + + private boolean isSlotFree(int slot) { + return items.stream().noneMatch(item -> item.itemSlot() == slot); + } + /** * Builds and returns a new Menu instance with the configured properties. * This method validates all parameters before creating the menu. diff --git a/src/test/java/me/touchie771/minecraftGUI/api/MenuTest.java b/src/test/java/me/touchie771/minecraftGUI/api/MenuTest.java index 6cefded..b8d9b7c 100644 --- a/src/test/java/me/touchie771/minecraftGUI/api/MenuTest.java +++ b/src/test/java/me/touchie771/minecraftGUI/api/MenuTest.java @@ -1,7 +1,6 @@ package me.touchie771.minecraftGUI.api; import be.seeseemelk.mockbukkit.MockBukkit; -import be.seeseemelk.mockbukkit.ServerMock; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.plugin.Plugin; @@ -9,16 +8,17 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Objects; + import static org.junit.jupiter.api.Assertions.*; class MenuTest { - private ServerMock server; private Plugin plugin; @BeforeEach void setUp() { - server = MockBukkit.mock(); + MockBukkit.mock(); plugin = MockBukkit.createMockPlugin(); } @@ -60,11 +60,75 @@ void testAddItem() { .plugin(plugin) .build(); - SlotItem item = new SlotItem(Component.text("Diamond"), (short) 0, Material.DIAMOND, 1); + SlotItem item = SlotItem.builder(0) + .itemName(Component.text("Diamond")) + .material(Material.DIAMOND) + .build(); menu.addItems(item); assertTrue(menu.getItems().contains(item)); assertNotNull(menu.getInventory().getItem(0)); - assertEquals(Material.DIAMOND, menu.getInventory().getItem(0).getType()); + assertEquals(Material.DIAMOND, Objects.requireNonNull(menu.getInventory().getItem(0)).getType()); + } + + @Test + void testFillEmptyWith() { + Menu menu = Menu.newBuilder() + .size(9) + .title(Component.text("Filler Test")) + .plugin(plugin) + .items(SlotItem.builder(4).material(Material.DIAMOND).build()) + .fillEmptyWith(Material.GRAY_STAINED_GLASS_PANE) + .build(); + + // Slot 4 should be Diamond + assertEquals(Material.DIAMOND, Objects.requireNonNull(menu.getInventory().getItem(4)).getType()); + + // Other slots should be filler + assertEquals(Material.GRAY_STAINED_GLASS_PANE, Objects.requireNonNull(menu.getInventory().getItem(0)).getType()); + assertEquals(Material.GRAY_STAINED_GLASS_PANE, Objects.requireNonNull(menu.getInventory().getItem(8)).getType()); + + // Total items should be 9 + assertEquals(9, menu.getItems().size()); + } + + @Test + void testFillExcept() { + Menu menu = Menu.newBuilder() + .size(9) + .title(Component.text("Fill Except Test")) + .plugin(plugin) + .fillExcept(Material.STONE, 0, 8) + .build(); + + // Slots 0 and 8 should be empty (null) + assertNull(menu.getInventory().getItem(0)); + assertNull(menu.getInventory().getItem(8)); + + // Middle slot should be filled + assertEquals(Material.STONE, Objects.requireNonNull(menu.getInventory().getItem(4)).getType()); + + // Total filled items should be 7 + assertEquals(7, menu.getItems().size()); + } + + @Test + void testFillRange() { + Menu menu = Menu.newBuilder() + .size(9) + .title(Component.text("Fill Range Test")) + .plugin(plugin) + .items(SlotItem.builder(4).material(Material.DIAMOND).build()) + .fillRange(3, 6, Material.DIRT) + .build(); + + // Range 3-6 (3, 4, 5). 4 is occupied by Diamond. + // So 3 and 5 should be DIRT. 4 should be DIAMOND. + + assertNull(menu.getInventory().getItem(2)); // Outside range + assertEquals(Material.DIRT, Objects.requireNonNull(menu.getInventory().getItem(3)).getType()); + assertEquals(Material.DIAMOND, Objects.requireNonNull(menu.getInventory().getItem(4)).getType()); + assertEquals(Material.DIRT, Objects.requireNonNull(menu.getInventory().getItem(5)).getType()); + assertNull(menu.getInventory().getItem(6)); // Outside range (end exclusive) } } \ No newline at end of file