VirtualInventory ItemDrag

This commit is contained in:
NichtStudioCode 2021-02-12 01:44:46 +01:00
parent 8d3f4e3690
commit 9ec262a122
10 changed files with 250 additions and 131 deletions

@ -203,6 +203,17 @@ public interface GUI extends GUIParent {
*/ */
void handleItemShift(InventoryClickEvent event); void handleItemShift(InventoryClickEvent event);
/**
* A method called when an {@link ItemStack} has been dragged over the {@link GUI}.
*
* @param player The player that is responsible for this action
* @param slot The slot index
* @param oldStack The {@link ItemStack} that was previously on that slot
* @param newStack The new {@link ItemStack} that would be there if the action isn't cancelled
* @return If the action has been cancelled
*/
boolean handleItemDrag(Player player, int slot, ItemStack oldStack, ItemStack newStack);
/** /**
* Adds a {@link GUIParent} to the set of {@link GUIParent}s. * Adds a {@link GUIParent} to the set of {@link GUIParent}s.
* *

@ -14,6 +14,7 @@ import de.studiocode.invui.virtualinventory.VirtualInventory;
import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent; import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
import de.studiocode.invui.window.WindowManager; import de.studiocode.invui.window.WindowManager;
import de.studiocode.invui.window.impl.merged.MergedWindow;
import de.studiocode.invui.window.impl.merged.combined.CombinedWindow; import de.studiocode.invui.window.impl.merged.combined.CombinedWindow;
import de.studiocode.invui.window.impl.merged.split.SplitWindow; import de.studiocode.invui.window.impl.merged.split.SplitWindow;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -108,22 +109,30 @@ abstract class IndexedGUI implements GUI {
cancelled = virtualInventory.setToMaxAmount(player, index); cancelled = virtualInventory.setToMaxAmount(player, index);
break; break;
case SWAP_WITH_CURSOR:
cancelled = virtualInventory.setItemStack(player, index, event.getCursor());
break;
case MOVE_TO_OTHER_INVENTORY: case MOVE_TO_OTHER_INVENTORY:
event.setCancelled(true); event.setCancelled(true);
Window window = WindowManager.getInstance().findOpenWindow(player).orElse(null); Window window = WindowManager.getInstance().findOpenWindow(player).orElse(null);
if (window instanceof CombinedWindow) break; // can't move if there is no other gui if (window instanceof CombinedWindow) break; // can't move if there is no other gui
ItemStack invStack = virtualInventory.getItemStack(index); ItemStack invStack = virtualInventory.getItemStack(index);
ItemUpdateEvent updateEvent = new ItemUpdateEvent(virtualInventory, player, invStack, ItemUpdateEvent updateEvent = new ItemUpdateEvent(virtualInventory, index, player, invStack, null);
index, invStack.getAmount(), -1);
Bukkit.getPluginManager().callEvent(updateEvent); Bukkit.getPluginManager().callEvent(updateEvent);
if (!updateEvent.isCancelled()) { if (!updateEvent.isCancelled()) {
int leftOverAmount; int leftOverAmount;
if (window instanceof MergedWindow) {
GUI otherGui;
if (window instanceof SplitWindow) { if (window instanceof SplitWindow) {
SplitWindow splitWindow = (SplitWindow) window; SplitWindow splitWindow = (SplitWindow) window;
GUI[] guis = splitWindow.getGuis(); GUI[] guis = splitWindow.getGuis();
GUI otherGui = guis[0] == this ? guis[1] : guis[0]; otherGui = guis[0] == this ? guis[1] : guis[0];
} else {
otherGui = this;
}
leftOverAmount = ((IndexedGUI) otherGui).putIntoVirtualInventories(player, invStack, virtualInventory); leftOverAmount = ((IndexedGUI) otherGui).putIntoVirtualInventories(player, invStack, virtualInventory);
} else { } else {
@ -146,6 +155,22 @@ abstract class IndexedGUI implements GUI {
} else event.setCancelled(true); } else event.setCancelled(true);
} }
@Override
public boolean handleItemDrag(Player player, int slot, ItemStack oldStack, ItemStack newStack) {
SlotElement element = getSlotElement(slot);
if (element != null) element = element.getHoldingElement();
if (element instanceof VISlotElement) {
VISlotElement viSlotElement = ((VISlotElement) element);
VirtualInventory virtualInventory = viSlotElement.getVirtualInventory();
int viIndex = viSlotElement.getIndex();
if (virtualInventory.isSynced(viIndex, oldStack)) {
return virtualInventory.setItemStack(player, viIndex, newStack);
}
}
return true;
}
@Override @Override
public void handleItemShift(InventoryClickEvent event) { public void handleItemShift(InventoryClickEvent event) {
event.setCancelled(true); event.setCancelled(true);

@ -101,12 +101,23 @@ public class VirtualInventory implements ConfigurationSerializable {
/** /**
* Sets an {@link ItemStack} on a specific slot. * Sets an {@link ItemStack} on a specific slot.
* *
* @param player The player that did this or <code>null</code> if it wasn't a player.
* @param index The slot index * @param index The slot index
* @param itemStack The {@link ItemStack} that should be put on that slot * @param itemStack The {@link ItemStack} that should be put on that slot
*
* @return If the action has been cancelled
*/ */
public void setItem(int index, ItemStack itemStack) { public boolean setItemStack(Player player, int index, ItemStack itemStack) {
items[index] = itemStack.clone(); ItemStack newStack = itemStack.clone();
ItemUpdateEvent event = createAndCallEvent(index, player, items[index], newStack);
if (!event.isCancelled()) {
items[index] = newStack;
notifyWindows(); notifyWindows();
return false;
}
return true;
} }
/** /**
@ -125,7 +136,7 @@ public class VirtualInventory implements ConfigurationSerializable {
* @param index The slot index * @param index The slot index
* @return If there is an {@link ItemStack} on that slot * @return If there is an {@link ItemStack} on that slot
*/ */
public boolean hasItem(int index) { public boolean hasItemStack(int index) {
return items[index] != null; return items[index] != null;
} }
@ -139,20 +150,21 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean place(Player player, int index, ItemStack itemStack) { public boolean place(Player player, int index, ItemStack itemStack) {
ItemStack there = items[index]; ItemStack currentStack = items[index];
int currentAmount = there == null ? 0 : there.getAmount();
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index, currentAmount, ItemStack newStack;
currentAmount + itemStack.getAmount()); if (currentStack == null) {
newStack = itemStack.clone();
if (!event.isCancelled()) {
if (there == null) {
setItem(index, itemStack);
} else { } else {
there.setAmount(currentAmount + itemStack.getAmount()); newStack = currentStack.clone();
notifyWindows(); newStack.setAmount(newStack.getAmount() + itemStack.getAmount());
} }
ItemUpdateEvent event = createAndCallEvent(index, player, currentStack, newStack);
if (!event.isCancelled()) {
items[index] = newStack;
notifyWindows();
return false; return false;
} }
@ -169,22 +181,22 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean placeOne(Player player, int index, ItemStack itemStack) { public boolean placeOne(Player player, int index, ItemStack itemStack) {
ItemStack there = items[index]; ItemStack currentStack = items[index];
int currentAmount = there == null ? 0 : there.getAmount();
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index, ItemStack newStack;
currentAmount, currentAmount + 1); if (currentStack == null) {
newStack = itemStack.clone();
if (!event.isCancelled()) { newStack.setAmount(1);
if (there == null) {
ItemStack single = itemStack.clone();
single.setAmount(1);
setItem(index, single);
} else { } else {
there.setAmount(currentAmount + 1); newStack = currentStack.clone();
notifyWindows(); newStack.setAmount(newStack.getAmount() + 1);
} }
ItemUpdateEvent event = createAndCallEvent(index, player, currentStack, newStack);
if (!event.isCancelled()) {
items[index] = newStack;
notifyWindows();
return false; return false;
} }
@ -198,10 +210,10 @@ public class VirtualInventory implements ConfigurationSerializable {
* @param amount The new amount * @param amount The new amount
*/ */
public void setAmountSilently(int index, int amount) { public void setAmountSilently(int index, int amount) {
ItemStack itemStack = items[index]; ItemStack currentStack = items[index];
if (itemStack != null) { if (currentStack != null) {
if (amount == 0) items[index] = null; if (amount == 0) items[index] = null;
else itemStack.setAmount(amount); else currentStack.setAmount(amount);
notifyWindows(); notifyWindows();
} }
} }
@ -215,21 +227,22 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean setToMaxAmount(Player player, int index) { public boolean setToMaxAmount(Player player, int index) {
ItemStack itemStack = items[index]; ItemStack currentStack = items[index];
if (itemStack != null) { if (currentStack != null) {
int currentAmount = itemStack.getAmount(); ItemStack newStack = currentStack.clone();
int newAmount = itemStack.getMaxStackSize(); newStack.setAmount(newStack.getMaxStackSize());
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index,
currentAmount, newAmount);
ItemUpdateEvent event = createAndCallEvent(index, player, currentStack, newStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
itemStack.setAmount(newAmount); items[index] = newStack;
notifyWindows(); notifyWindows();
} else return true;
}
return false; return false;
} }
}
return true;
}
/** /**
* Removes an {@link ItemStack} on a specific slot from * Removes an {@link ItemStack} on a specific slot from
@ -240,20 +253,20 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean removeItem(Player player, int index) { public boolean removeItem(Player player, int index) {
ItemStack itemStack = items[index]; ItemStack currentStack = items[index];
if (itemStack != null) { if (currentStack != null) {
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index,
itemStack.getAmount(), 0);
ItemUpdateEvent event = createAndCallEvent(index, player, currentStack, null);
if (!event.isCancelled()) { if (!event.isCancelled()) {
items[index] = null; items[index] = null;
notifyWindows(); notifyWindows();
} else return true;
}
return false; return false;
} }
}
return true;
}
/** /**
* Removes one from an {@link ItemStack} on a specific slot. * Removes one from an {@link ItemStack} on a specific slot.
@ -263,23 +276,26 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean removeOne(Player player, int index) { public boolean removeOne(Player player, int index) {
ItemStack itemStack = items[index]; ItemStack currentStack = items[index];
if (itemStack != null) { if (currentStack != null) {
int currentAmount = itemStack.getAmount(); int newAmount = currentStack.getAmount() - 1;
int newAmount = currentAmount - 1;
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index, currentAmount, newAmount); if (newAmount > 0) {
ItemStack newStack = currentStack.clone();
newStack.setAmount(newAmount);
ItemUpdateEvent event = createAndCallEvent(index, player, currentStack, newStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
if (newAmount > 0) itemStack.setAmount(newAmount); items[index] = newStack;
else items[index] = null;
notifyWindows(); notifyWindows();
} else return true;
}
return false; return false;
} }
} else return removeItem(player, index);
}
return true;
}
/** /**
* Removes half of the {@link ItemStack} on a specific slot. * Removes half of the {@link ItemStack} on a specific slot.
@ -289,23 +305,28 @@ public class VirtualInventory implements ConfigurationSerializable {
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean removeHalf(Player player, int index) { public boolean removeHalf(Player player, int index) {
ItemStack itemStack = items[index]; ItemStack currentStack = items[index];
if (itemStack != null) { if (currentStack != null) {
int currentAmount = itemStack.getAmount(); int newAmount = currentStack.getAmount() / 2;
int newAmount = itemStack.getAmount() / 2;
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index, currentAmount, newAmount); if (newAmount > 0) {
ItemStack newStack = currentStack.clone();
newStack.setAmount(newAmount);
ItemUpdateEvent event = createAndCallEvent(index, player, currentStack, newStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
if (newAmount > 0) itemStack.setAmount(newAmount); items[index] = newStack;
else items[index] = null;
notifyWindows(); notifyWindows();
} else return true;
}
return false; return false;
} }
} else return removeItem(player, index);
}
return true;
}
/** /**
* Adds an {@link ItemStack} to the {@link VirtualInventory}. * Adds an {@link ItemStack} to the {@link VirtualInventory}.
* *
@ -359,10 +380,13 @@ public class VirtualInventory implements ConfigurationSerializable {
int currentAmount = itemStack.getAmount(); int currentAmount = itemStack.getAmount();
int newAmount = currentAmount + maxAddable; int newAmount = currentAmount + maxAddable;
ItemUpdateEvent event = createAndCallEvent(player, itemStack, index, currentAmount, newAmount); ItemStack newStack = itemStack.clone();
newStack.setAmount(newAmount);
ItemUpdateEvent event = createAndCallEvent(index, player, itemStack, newStack);
if (!event.isCancelled()) { if (!event.isCancelled()) {
itemStack.setAmount(itemStack.getAmount() + maxAddable); items[index] = newStack;
notifyWindows();
return amount - maxAddable; return amount - maxAddable;
} else return amount; } else return amount;
} }
@ -392,8 +416,8 @@ public class VirtualInventory implements ConfigurationSerializable {
windows.forEach(window -> window.handleVirtualInventoryUpdate(this))); windows.forEach(window -> window.handleVirtualInventoryUpdate(this)));
} }
private ItemUpdateEvent createAndCallEvent(Player player, ItemStack itemStack, int index, int previousAmount, int newAmount) { private ItemUpdateEvent createAndCallEvent(int index, Player player, ItemStack previousItemStack, ItemStack newItemStack) {
ItemUpdateEvent event = new ItemUpdateEvent(this, player, itemStack, index, previousAmount, newAmount); ItemUpdateEvent event = new ItemUpdateEvent(this, index, player, previousItemStack, newItemStack);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
return event; return event;

@ -17,11 +17,10 @@ public class ItemUpdateEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private final VirtualInventory virtualInventory; private final VirtualInventory virtualInventory;
private final ItemStack itemStack; private final ItemStack previousItemStack;
private final ItemStack newItemStack;
private final Player player; private final Player player;
private final int slot; private final int slot;
private final int previousAmount;
private final int newAmount;
private boolean cancelled; private boolean cancelled;
@ -31,20 +30,17 @@ public class ItemUpdateEvent extends Event implements Cancellable {
* @param virtualInventory The {@link VirtualInventory} where this action takes place. * @param virtualInventory The {@link VirtualInventory} where this action takes place.
* @param player The {@link Player} who changed the {@link ItemStack} or <code>null</code> * @param player The {@link Player} who changed the {@link ItemStack} or <code>null</code>
* if it wasn't a {@link Player} * if it wasn't a {@link Player}
* @param itemStack The {@link ItemStack} that is affected
* @param slot The slot that is affected * @param slot The slot that is affected
* @param previousAmount The previous amount of the {@link ItemStack} * @param previousItemStack The {@link ItemStack} that was there previously
* @param newAmount The amount that the {@link ItemStack} will have if the event is not being * @param newItemStack The {@link ItemStack} that will be there if the event isn't cancelled
* cancelled or -1 if not known
*/ */
public ItemUpdateEvent(@NotNull VirtualInventory virtualInventory, @Nullable Player player, @NotNull ItemStack itemStack, int slot, int previousAmount, public ItemUpdateEvent(@NotNull VirtualInventory virtualInventory, int slot, @Nullable Player player,
int newAmount) { @Nullable ItemStack previousItemStack, @Nullable ItemStack newItemStack) {
this.virtualInventory = virtualInventory; this.virtualInventory = virtualInventory;
this.player = player;
this.itemStack = itemStack;
this.slot = slot; this.slot = slot;
this.previousAmount = previousAmount; this.player = player;
this.newAmount = newAmount; this.previousItemStack = previousItemStack;
this.newItemStack = newItemStack;
} }
/** /**
@ -76,12 +72,21 @@ public class ItemUpdateEvent extends Event implements Cancellable {
} }
/** /**
* Gets the {@link ItemStack} involved in this action. * Gets the {@link ItemStack} that was there previously.
* *
* @return The {@link ItemStack} * @return The {@link ItemStack}
*/ */
public ItemStack getItemStack() { public ItemStack getPreviousItemStack() {
return itemStack; return previousItemStack;
}
/**
* The new {@link ItemStack} that will be there if the event isn't cancelled.
*
* @return The new {@link ItemStack}
*/
public ItemStack getNewItemStack() {
return newItemStack;
} }
/** /**
@ -93,25 +98,6 @@ public class ItemUpdateEvent extends Event implements Cancellable {
return slot; return slot;
} }
/**
* Gets the previous amount of the {@link ItemStack}
*
* @return The previous amount
*/
public int getPreviousAmount() {
return previousAmount;
}
/**
* Gets the new amount of the {@link ItemStack} if the event
* isn't being cancelled or -1 if not unknown.
*
* @return The new amount
*/
public int getNewAmount() {
return newAmount;
}
@Override @Override
public boolean isCancelled() { public boolean isCancelled() {
return cancelled; return cancelled;

@ -11,6 +11,7 @@ import de.studiocode.invui.window.impl.single.SimpleWindow;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -42,7 +43,7 @@ public interface Window extends GUIParent {
GUI[] getGuis(); GUI[] getGuis();
/** /**
* A method called by the {@link WindowManager} to notify the Window * A method called by the {@link WindowManager} to notify the {@link Window}
* that one of its {@link Item}s has been clicked. * that one of its {@link Item}s has been clicked.
* *
* @param event The {@link InventoryClickEvent} associated with this action. * @param event The {@link InventoryClickEvent} associated with this action.
@ -50,7 +51,15 @@ public interface Window extends GUIParent {
void handleClick(InventoryClickEvent event); void handleClick(InventoryClickEvent event);
/** /**
* A method called by the {@link WindowManager} to notify the Window * A method called by the {@link WindowManager} to notify the {@link Window}
* that {@link ItemStack}s have been dragged inside it.
*
* @param event The {@link InventoryDragEvent} associated with this action.
*/
void handleDrag(InventoryDragEvent event);
/**
* A method called by the {@link WindowManager} to notify the {@link Window}
* that {@link ItemStack}s have been shift-clicked from the lower * that {@link ItemStack}s have been shift-clicked from the lower
* {@link Inventory} to this {@link Window} * {@link Inventory} to this {@link Window}
* *
@ -59,7 +68,7 @@ public interface Window extends GUIParent {
void handleItemShift(InventoryClickEvent event); void handleItemShift(InventoryClickEvent event);
/** /**
* A method called by the {@link WindowManager} to notify the Window * A method called by the {@link WindowManager} to notify the {@link Window}
* that its underlying {@link Inventory} is being opened. * that its underlying {@link Inventory} is being opened.
* *
* @param event The {@link InventoryOpenEvent} associated with this action. * @param event The {@link InventoryOpenEvent} associated with this action.
@ -67,7 +76,7 @@ public interface Window extends GUIParent {
void handleOpen(InventoryOpenEvent event); void handleOpen(InventoryOpenEvent event);
/** /**
* A method called by the {@link WindowManager} to notify the Window * A method called by the {@link WindowManager} to notify the {@link Window}
* that its underlying {@link Inventory} is being closed. * that its underlying {@link Inventory} is being closed.
* *
* @param player The {@link Player} who closed this inventory. * @param player The {@link Player} who closed this inventory.
@ -75,7 +84,7 @@ public interface Window extends GUIParent {
void handleClose(Player player); void handleClose(Player player);
/** /**
* A method called by the {@link WindowManager} to notify the Window * A method called by the {@link WindowManager} to notify the {@link Window}
* that it's viewer has died. * that it's viewer has died.
* *
* @param event The {@link PlayerDeathEvent} associated with this action. * @param event The {@link PlayerDeathEvent} associated with this action.
@ -83,7 +92,7 @@ public interface Window extends GUIParent {
void handleViewerDeath(PlayerDeathEvent event); void handleViewerDeath(PlayerDeathEvent event);
/** /**
* A method called by the {@link Item} itself to notify the Window * A method called by the {@link Item} itself to notify the {@link Window}
* that its {@link ItemBuilder} has been updated and the {@link ItemStack} * that its {@link ItemBuilder} has been updated and the {@link ItemStack}
* in the {@link Inventory} should be replaced. * in the {@link Inventory} should be replaced.
* *
@ -93,7 +102,7 @@ public interface Window extends GUIParent {
/** /**
* A method called by the {@link VirtualInventory} to notify the * A method called by the {@link VirtualInventory} to notify the
* Window that one if it's contents has been updated and the {@link ItemStack}'s * {@link Window} that one if it's contents has been updated and the {@link ItemStack}'s
* displayed in the {@link Inventory} should be replaced. * displayed in the {@link Inventory} should be replaced.
* *
* @param virtualInventory The {@link VirtualInventory} * @param virtualInventory The {@link VirtualInventory}
@ -155,9 +164,9 @@ public interface Window extends GUIParent {
Player getCurrentViewer(); Player getCurrentViewer();
/** /**
* Gets the viewer's UUID or null if there is no viewer. * Gets the viewer's {@link UUID}
* *
* @return The viewer's UUID or null if there is now viewer. * @return The viewer's {@link UUID}
*/ */
UUID getViewerUUID(); UUID getViewerUUID();

@ -124,8 +124,7 @@ public class WindowManager implements Listener {
@EventHandler @EventHandler
public void handleInventoryDrag(InventoryDragEvent event) { public void handleInventoryDrag(InventoryDragEvent event) {
// currently, dragging items is not supported findWindow(event.getInventory()).ifPresent(window -> window.handleDrag(event));
findWindow(event.getInventory()).ifPresent(w -> event.setCancelled(true));
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)

@ -2,17 +2,21 @@ package de.studiocode.invui.window.impl;
import de.studiocode.inventoryaccess.api.version.InventoryAccess; import de.studiocode.inventoryaccess.api.version.InventoryAccess;
import de.studiocode.invui.InvUI; import de.studiocode.invui.InvUI;
import de.studiocode.invui.gui.GUI;
import de.studiocode.invui.gui.SlotElement; import de.studiocode.invui.gui.SlotElement;
import de.studiocode.invui.gui.SlotElement.ItemSlotElement; import de.studiocode.invui.gui.SlotElement.ItemSlotElement;
import de.studiocode.invui.gui.SlotElement.VISlotElement; import de.studiocode.invui.gui.SlotElement.VISlotElement;
import de.studiocode.invui.item.Item; import de.studiocode.invui.item.Item;
import de.studiocode.invui.util.ArrayUtils; import de.studiocode.invui.util.ArrayUtils;
import de.studiocode.invui.util.Pair;
import de.studiocode.invui.virtualinventory.VirtualInventory; import de.studiocode.invui.virtualinventory.VirtualInventory;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
import de.studiocode.invui.window.WindowManager; import de.studiocode.invui.window.WindowManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -36,6 +40,10 @@ public abstract class BaseWindow implements Window {
WindowManager.getInstance().addWindow(this); WindowManager.getInstance().addWindow(this);
} }
protected void redrawItem(int index) {
redrawItem(index, getSlotElement(index), false);
}
protected void redrawItem(int index, SlotElement element, boolean setItem) { protected void redrawItem(int index, SlotElement element, boolean setItem) {
// put ItemStack in inventory // put ItemStack in inventory
ItemStack itemStack = element == null ? null : element.getItemStack(viewerUUID); ItemStack itemStack = element == null ? null : element.getItemStack(viewerUUID);
@ -73,6 +81,36 @@ public abstract class BaseWindow implements Window {
} }
} }
@Override
public void handleDrag(InventoryDragEvent event) {
Player player = ((Player) event.getWhoClicked()).getPlayer();
Map<Integer, ItemStack> newItems = event.getNewItems();
int itemsLeft = event.getCursor() == null ? 0 : event.getCursor().getAmount();
for (int rawSlot : event.getRawSlots()) { // loop over all affected slots
ItemStack currentStack = event.getView().getItem(rawSlot);
if (currentStack != null && currentStack.getType() == Material.AIR) currentStack = null;
// get the GUI at that index and ask for permission to drag an Item there
Pair<GUI, Integer> pair = getGuiAt(rawSlot);
if (pair != null && pair.getFirst().handleItemDrag(player, pair.getSecond(), currentStack, newItems.get(rawSlot))) {
// the drag was cancelled
int currentAmount = currentStack == null ? 0 : currentStack.getAmount();
int newAmount = newItems.get(rawSlot).getAmount();
itemsLeft += newAmount - currentAmount;
// Redraw cancelled items after the event so there won't be any Items that aren't actually there
Bukkit.getScheduler().runTask(InvUI.getInstance().getPlugin(), () -> redrawItem(rawSlot));
}
}
// update the amount on the cursor
ItemStack cursorStack = event.getOldCursor();
cursorStack.setAmount(itemsLeft);
event.setCursor(cursorStack);
}
@Override @Override
public void handleOpen(InventoryOpenEvent event) { public void handleOpen(InventoryOpenEvent event) {
if (!event.getPlayer().equals(getViewer())) if (!event.getPlayer().equals(getViewer()))
@ -183,6 +221,10 @@ public abstract class BaseWindow implements Window {
protected abstract void setInvItem(int slot, ItemStack itemStack); protected abstract void setInvItem(int slot, ItemStack itemStack);
protected abstract SlotElement getSlotElement(int index);
protected abstract Pair<GUI, Integer> getGuiAt(int index);
protected abstract void handleOpened(); protected abstract void handleOpened();
protected abstract void handleClosed(); protected abstract void handleClosed();

@ -44,6 +44,11 @@ public abstract class CombinedWindow extends MergedWindow {
return new Pair<>(gui, clickedIndex); return new Pair<>(gui, clickedIndex);
} }
@Override
protected Pair<GUI, Integer> getGuiAt(int index) {
return index < gui.getSize() ? new Pair<>(gui, index) : null;
}
@Override @Override
public GUI[] getGuis() { public GUI[] getGuis() {
return new GUI[] {gui}; return new GUI[] {gui};

@ -51,6 +51,13 @@ public abstract class SplitWindow extends MergedWindow {
} }
} }
@Override
protected Pair<GUI, Integer> getGuiAt(int index) {
if (index < upperGui.getSize()) return new Pair<>(upperGui, index);
else if (index < (upperGui.getSize() + lowerGui.getSize())) return new Pair<>(lowerGui, index - upperGui.getSize());
else return null;
}
@Override @Override
public GUI[] getGuis() { public GUI[] getGuis() {
return new GUI[] {upperGui, lowerGui}; return new GUI[] {upperGui, lowerGui};

@ -2,6 +2,7 @@ package de.studiocode.invui.window.impl.single;
import de.studiocode.invui.gui.GUI; import de.studiocode.invui.gui.GUI;
import de.studiocode.invui.gui.SlotElement; import de.studiocode.invui.gui.SlotElement;
import de.studiocode.invui.util.Pair;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
import de.studiocode.invui.window.impl.BaseWindow; import de.studiocode.invui.window.impl.BaseWindow;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -63,6 +64,16 @@ public abstract class SingleWindow extends BaseWindow {
gui.handleClick(event.getSlot(), (Player) event.getWhoClicked(), event.getClick(), event); gui.handleClick(event.getSlot(), (Player) event.getWhoClicked(), event.getClick(), event);
} }
@Override
protected Pair<GUI, Integer> getGuiAt(int index) {
return index < gui.getSize() ? new Pair<>(gui, index) : null;
}
@Override
protected SlotElement getSlotElement(int index) {
return gui.getSlotElement(index);
}
@Override @Override
public void handleItemShift(InventoryClickEvent event) { public void handleItemShift(InventoryClickEvent event) {
gui.handleItemShift(event); gui.handleItemShift(event);