diff --git a/invui/src/main/java/xyz/xenondevs/invui/window/AbstractWindow.java b/invui/src/main/java/xyz/xenondevs/invui/window/AbstractWindow.java index 7bcc80e..b4b582a 100644 --- a/invui/src/main/java/xyz/xenondevs/invui/window/AbstractWindow.java +++ b/invui/src/main/java/xyz/xenondevs/invui/window/AbstractWindow.java @@ -11,6 +11,7 @@ import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -45,6 +46,7 @@ public abstract class AbstractWindow implements Window, GuiParent { private final SlotElement[] elementsDisplayed; private List openHandlers; private List closeHandlers; + private List> outsideClickHandlers; private ComponentWrapper title; private boolean closeable; private boolean currentlyOpen; @@ -133,7 +135,7 @@ public abstract class AbstractWindow implements Window, GuiParent { } } - public void handleDrag(InventoryDragEvent event) { + public void handleDragEvent(InventoryDragEvent event) { Player player = ((Player) event.getWhoClicked()).getPlayer(); UpdateReason updateReason = new PlayerUpdateReason(player, event); Map newItems = event.getNewItems(); @@ -197,6 +199,32 @@ public abstract class AbstractWindow implements Window, GuiParent { } } + public void handleClickEvent(InventoryClickEvent event) { + if (Arrays.asList(getInventories()).contains(event.getClickedInventory())) { + // The inventory that was clicked is part of the open window + handleClick(event); + } else if (event.getSlotType() == InventoryType.SlotType.OUTSIDE) { + // The player clicked outside the inventory + if (outsideClickHandlers != null) { + for (var handler : outsideClickHandlers) { + handler.accept(event); + } + } + } else { + switch (event.getAction()) { + // The inventory that was clicked is not part of the open window, so it is the player inventory + case MOVE_TO_OTHER_INVENTORY: + handleItemShift(event); + break; + + // items have been collected by clicking a slot in the player inv + case COLLECT_TO_CURSOR: + handleCursorCollect(event); + break; + } + } + } + public void handleItemProviderUpdate(Item item) { getItemSlotElements(item).forEach((index, slotElement) -> redrawItem(index, slotElement, false)); @@ -324,6 +352,25 @@ public abstract class AbstractWindow implements Window, GuiParent { closeHandlers.remove(closeHandler); } + @Override + public void setOutsideClickHandlers(@NotNull List<@NotNull Consumer<@NotNull InventoryClickEvent>> outsideClickHandlers) { + this.outsideClickHandlers = outsideClickHandlers; + } + + @Override + public void addOutsideClickHandler(@NotNull Consumer<@NotNull InventoryClickEvent> outsideClickHandlers) { + if (this.outsideClickHandlers == null) + this.outsideClickHandlers = new ArrayList<>(); + + this.outsideClickHandlers.add(outsideClickHandlers); + } + + @Override + public void removeOutsideClickHandler(@NotNull Consumer<@NotNull InventoryClickEvent> outsideClickHandlers) { + if (this.outsideClickHandlers != null) + this.outsideClickHandlers.remove(outsideClickHandlers); + } + @Override public @Nullable Player getCurrentViewer() { List viewers = getInventories()[0].getViewers(); @@ -380,11 +427,11 @@ public abstract class AbstractWindow implements Window, GuiParent { protected abstract void handleClosed(); - public abstract void handleClick(InventoryClickEvent event); + protected abstract void handleClick(InventoryClickEvent event); - public abstract void handleItemShift(InventoryClickEvent event); + protected abstract void handleItemShift(InventoryClickEvent event); - public abstract void handleCursorCollect(InventoryClickEvent event); + protected abstract void handleCursorCollect(InventoryClickEvent event); public abstract void handleViewerDeath(PlayerDeathEvent event); @@ -396,6 +443,7 @@ public abstract class AbstractWindow implements Window, GuiParent { protected boolean closeable = true; protected List openHandlers; protected List closeHandlers; + protected List> outsideClickHandlers; protected List> modifiers; @Override @@ -458,6 +506,21 @@ public abstract class AbstractWindow implements Window, GuiParent { return (S) this; } + @Override + public @NotNull S setOutsideClickHandlers(@NotNull List<@NotNull Consumer<@NotNull InventoryClickEvent>> outsideClickHandlers) { + this.outsideClickHandlers = outsideClickHandlers; + return (S) this; + } + + @Override + public @NotNull S addOutsideClickHandler(@NotNull Consumer<@NotNull InventoryClickEvent> outsideClickHandler) { + if (outsideClickHandlers == null) + outsideClickHandlers = new ArrayList<>(); + + outsideClickHandlers.add(outsideClickHandler); + return (S) this; + } + @Override public @NotNull S setModifiers(List> modifiers) { this.modifiers = modifiers; @@ -477,6 +540,9 @@ public abstract class AbstractWindow implements Window, GuiParent { if (closeHandlers != null) window.setCloseHandlers(closeHandlers); + if (outsideClickHandlers != null) + window.setOutsideClickHandlers(outsideClickHandlers); + if (modifiers != null) modifiers.forEach(modifier -> modifier.accept(window)); } diff --git a/invui/src/main/java/xyz/xenondevs/invui/window/CartographySingleWindowImpl.java b/invui/src/main/java/xyz/xenondevs/invui/window/CartographySingleWindowImpl.java index 1bd6995..bcf2e64 100644 --- a/invui/src/main/java/xyz/xenondevs/invui/window/CartographySingleWindowImpl.java +++ b/invui/src/main/java/xyz/xenondevs/invui/window/CartographySingleWindowImpl.java @@ -2,7 +2,6 @@ package xyz.xenondevs.invui.window; import org.bukkit.Material; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.MapMeta; import org.jetbrains.annotations.NotNull; @@ -13,6 +12,7 @@ import xyz.xenondevs.inventoryaccess.component.ComponentWrapper; import xyz.xenondevs.inventoryaccess.map.MapIcon; import xyz.xenondevs.inventoryaccess.map.MapPatch; import xyz.xenondevs.invui.gui.AbstractGui; +import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.util.MathUtils; import java.util.List; @@ -28,7 +28,7 @@ final class CartographySingleWindowImpl extends AbstractSingleWindow implements @NotNull AbstractGui gui, boolean closeable ) { - super(player.getUniqueId(), title, gui, null, closeable); + super(player.getUniqueId(), title, createWrappingGui(gui), null, closeable); if (gui.getWidth() != 2 || gui.getHeight() != 1) throw new IllegalArgumentException("Gui has to be 2x1"); cartographyInventory = InventoryAccess.createCartographyInventory(player, title.localized(player)); @@ -37,6 +37,15 @@ final class CartographySingleWindowImpl extends AbstractSingleWindow implements resetMap(); } + private static AbstractGui createWrappingGui(Gui upperGui) { + if (upperGui.getWidth() != 2 || upperGui.getHeight() != 1) + throw new IllegalArgumentException("Gui has to be 2x1"); + + Gui wrapperGui = Gui.empty(3, 1); + wrapperGui.fillRectangle(1, 0, upperGui, true); + return (AbstractGui) wrapperGui; + } + @Override public void updateMap(@Nullable MapPatch patch, @Nullable List icons) { InventoryAccess.getPlayerUtils().sendMapUpdate(getViewer(), mapId, (byte) 0, false, patch, icons); @@ -53,20 +62,6 @@ final class CartographySingleWindowImpl extends AbstractSingleWindow implements cartographyInventory.setItem(0, map); } - @Override - protected void setInvItem(int slot, ItemStack itemStack) { - cartographyInventory.setItem(slot + 1, itemStack); - } - - @Override - public void handleClick(InventoryClickEvent event) { - if (event.getSlot() != 0) { - getGui().handleClick(event.getSlot() - 1, (Player) event.getWhoClicked(), event.getClick(), event); - } else { - event.setCancelled(true); - } - } - @Override protected void openInventory(@NotNull Player viewer) { cartographyInventory.open(); diff --git a/invui/src/main/java/xyz/xenondevs/invui/window/Window.java b/invui/src/main/java/xyz/xenondevs/invui/window/Window.java index 7f66414..1ddbf6f 100644 --- a/invui/src/main/java/xyz/xenondevs/invui/window/Window.java +++ b/invui/src/main/java/xyz/xenondevs/invui/window/Window.java @@ -2,6 +2,7 @@ package xyz.xenondevs.invui.window; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -196,6 +197,26 @@ public interface Window { */ void removeCloseHandler(@NotNull Runnable closeHandler); + /** + * Replaces the currently registered outside click handlers with the given list. + * @param outsideClickHandlers The new outside click handlers + */ + void setOutsideClickHandlers(@NotNull List<@NotNull Consumer<@NotNull InventoryClickEvent>> outsideClickHandlers); + + /** + * Adds an outside click handler that will be called when a player clicks outside the inventory. + * + * @param outsideClickHandlers The outside click handler to add + */ + void addOutsideClickHandler(@NotNull Consumer<@NotNull InventoryClickEvent> outsideClickHandlers); + + /** + * Removes an outside click handler that has been added previously. + * + * @param outsideClickHandlers The outside click handler to remove + */ + void removeOutsideClickHandler(@NotNull Consumer<@NotNull InventoryClickEvent> outsideClickHandlers); + /** * A {@link Window} builder. * @@ -285,6 +306,24 @@ public interface Window { @Contract("_ -> this") @NotNull S addCloseHandler(Runnable closeHandler); + /** + * Sets the outside click handlers of the {@link Window}. + * + * @param outsideClickHandlers The outside click handlers of the {@link Window} + * @return This {@link Builder Window Builder} + */ + @Contract("_ -> this") + @NotNull S setOutsideClickHandlers(@NotNull List<@NotNull Consumer<@NotNull InventoryClickEvent>> outsideClickHandlers); + + /** + * Adds an outside click handler to the {@link Window}. + * + * @param outsideClickHandler The outside click handler to add + * @return This {@link Builder Window Builder} + */ + @Contract("_ -> this") + @NotNull S addOutsideClickHandler(@NotNull Consumer<@NotNull InventoryClickEvent> outsideClickHandler); + /** * Sets the modifiers of the {@link Window}. * diff --git a/invui/src/main/java/xyz/xenondevs/invui/window/WindowManager.java b/invui/src/main/java/xyz/xenondevs/invui/window/WindowManager.java index 1639083..c4d11bc 100644 --- a/invui/src/main/java/xyz/xenondevs/invui/window/WindowManager.java +++ b/invui/src/main/java/xyz/xenondevs/invui/window/WindowManager.java @@ -105,28 +105,9 @@ public class WindowManager implements Listener { @EventHandler private void handleInventoryClick(InventoryClickEvent event) { - Player player = (Player) event.getWhoClicked(); - AbstractWindow window = (AbstractWindow) getOpenWindow(player); - + AbstractWindow window = (AbstractWindow) getOpenWindow((Player) event.getWhoClicked()); if (window != null) { - Inventory clicked = event.getClickedInventory(); - - if (Arrays.asList(window.getInventories()).contains(clicked)) { - // The inventory that was clicked is part of the open window - window.handleClick(event); - } else { - // The inventory that was clicked is not part of the open window, so it is the player inventory - switch (event.getAction()) { - case MOVE_TO_OTHER_INVENTORY: - window.handleItemShift(event); - break; - - // items have been collected by clicking a slot in the player inv - case COLLECT_TO_CURSOR: - window.handleCursorCollect(event); - break; - } - } + window.handleClickEvent(event); } } @@ -134,17 +115,17 @@ public class WindowManager implements Listener { private void handleInventoryDrag(InventoryDragEvent event) { AbstractWindow window = (AbstractWindow) getOpenWindow((Player) event.getWhoClicked()); if (window != null) { - window.handleDrag(event); + window.handleDragEvent(event); } } @EventHandler(priority = EventPriority.HIGHEST) private void handleInventoryClose(InventoryCloseEvent event) { Player player = (Player) event.getPlayer(); - AbstractWindow window = (AbstractWindow) getWindow(event.getInventory()); - if (window != null) + if (window != null) { window.handleCloseEvent(player); + } openWindows.remove(player); }