Skip to content

Commit 1e66b33

Browse files
StonecutterWindow
1 parent 40c6986 commit 1e66b33

19 files changed

+1180
-140
lines changed

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/window/ReactiveWindows.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,23 @@ fun <S : Window.Builder<*, *>, A, B, C, D, E, F, G, H, I, J> S.setTitle(
123123
@ExperimentalReactiveApi
124124
fun <S : AnvilWindow.Builder<*>> S.addRenameHandler(handler: MutableProvider<String>): S {
125125
return addRenameHandler(handler::set) as S
126+
}
127+
128+
@ExperimentalReactiveApi
129+
fun <S : StonecutterWindow.Builder<*>> S.setSelectedSlot(provider: MutableProvider<Int>): S {
130+
addModifier { window ->
131+
window.selectedSlot = provider.get()
132+
window.addSelectedSlotChangeHandler { _, to -> provider.set(to) }
133+
provider.subscribeWeak(window) { weakWindow, slot -> weakWindow.selectedSlot = slot }
134+
}
135+
return this
136+
}
137+
138+
@ExperimentalReactiveApi
139+
fun <S : StonecutterWindow.Builder<*>> S.setSelectedSlot(provider: Provider<Int>): S {
140+
addModifier { window ->
141+
window.selectedSlot = provider.get()
142+
provider.subscribeWeak(window) { weakWindow, slot -> weakWindow.selectedSlot = slot }
143+
}
144+
return this
126145
}

invui/src/main/java/xyz/xenondevs/invui/gui/AbstractGui.java

Lines changed: 87 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import org.bukkit.GameMode;
44
import org.bukkit.entity.Player;
5-
import org.bukkit.event.inventory.ClickType;
65
import org.bukkit.event.inventory.InventoryAction;
76
import org.bukkit.event.inventory.InventoryClickEvent;
87
import org.bukkit.inventory.ItemStack;
@@ -23,7 +22,9 @@
2322
import xyz.xenondevs.invui.inventory.event.UpdateReason;
2423
import xyz.xenondevs.invui.item.*;
2524
import xyz.xenondevs.invui.util.ItemUtils;
26-
import xyz.xenondevs.invui.window.*;
25+
import xyz.xenondevs.invui.window.AbstractWindow;
26+
import xyz.xenondevs.invui.window.Window;
27+
import xyz.xenondevs.invui.window.WindowManager;
2728

2829
import java.util.*;
2930
import java.util.function.Consumer;
@@ -61,7 +62,7 @@ public sealed abstract class AbstractGui
6162
viewers = new Set[size];
6263
}
6364

64-
public void handleClick(int slotNumber, Player player, ClickType clickType, InventoryClickEvent event) {
65+
public void handleClick(int slotNumber, Player player, InventoryClickEvent event) {
6566
// cancel all clicks if the gui is frozen or an animation is running
6667
if (frozen || animation != null) {
6768
event.setCancelled(true);
@@ -72,12 +73,12 @@ public void handleClick(int slotNumber, Player player, ClickType clickType, Inve
7273
switch (slotElement) {
7374
case SlotElement.GuiLink linkedElement -> {
7475
AbstractGui gui = (AbstractGui) linkedElement.gui();
75-
gui.handleClick(linkedElement.slot(), player, clickType, event);
76+
gui.handleClick(linkedElement.slot(), player, event);
7677
}
7778

7879
case SlotElement.Item itemElement -> {
7980
event.setCancelled(true); // if it is an Item, don't let the player move it
80-
itemElement.item().handleClick(clickType, player, new Click(event));
81+
itemElement.item().handleClick(event.getClick(), player, new Click(event));
8182
}
8283

8384
case SlotElement.InventoryLink inventorySlotElement ->
@@ -88,7 +89,7 @@ public void handleClick(int slotNumber, Player player, ClickType clickType, Inve
8889
}
8990

9091
//<editor-fold desc="inventories">
91-
protected void handleInvSlotElementClick(SlotElement.InventoryLink element, InventoryClickEvent event) {
92+
private void handleInvSlotElementClick(SlotElement.InventoryLink element, InventoryClickEvent event) {
9293
Inventory inventory = element.inventory();
9394
int slot = element.slot();
9495

@@ -158,7 +159,7 @@ private boolean isBuilderSimilar(@Nullable ItemProvider builder, Locale lang, @N
158159
}
159160

160161
@SuppressWarnings("deprecation")
161-
protected void handleInvLeftClick(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked, @Nullable ItemStack cursor) {
162+
private void handleInvLeftClick(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked, @Nullable ItemStack cursor) {
162163
// nothing happens if both cursor and clicked stack are empty
163164
if (clicked == null && cursor == null)
164165
return;
@@ -186,7 +187,7 @@ protected void handleInvLeftClick(InventoryClickEvent event, Inventory inventory
186187
}
187188

188189
@SuppressWarnings("deprecation")
189-
protected void handleInvRightClick(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked, @Nullable ItemStack cursor) {
190+
private void handleInvRightClick(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked, @Nullable ItemStack cursor) {
190191
// nothing happens if both cursor and clicked stack are empty
191192
if (clicked == null && cursor == null)
192193
return;
@@ -219,47 +220,46 @@ protected void handleInvRightClick(InventoryClickEvent event, Inventory inventor
219220
}
220221
}
221222

222-
protected void handleInvItemShift(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
223+
private void handleInvItemShift(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
223224
if (clicked == null)
224225
return;
225226

226-
ItemStack previousStack = clicked.clone();
227-
228227
UpdateReason updateReason = new PlayerUpdateReason(player, event);
229-
Window window = WindowManager.getInstance().getOpenWindow(player);
230-
ItemPreUpdateEvent updateEvent = inventory.callPreUpdateEvent(updateReason, slot, previousStack, null);
231-
232-
if (!updateEvent.isCancelled()) {
233-
int leftOverAmount;
234-
if (window instanceof AbstractDoubleWindow) {
235-
Gui otherGui;
236-
if (window instanceof AbstractSplitWindow splitWindow) {
237-
Gui[] guis = splitWindow.getGuis();
238-
otherGui = guis[0] == this ? guis[1] : guis[0];
239-
} else {
240-
otherGui = this;
241-
}
242-
243-
leftOverAmount = ((AbstractGui) otherGui).putIntoFirstInventory(updateReason, clicked, inventory);
244-
} else {
245-
Inventory playerInventory = ReferencingInventory.fromReversedPlayerStorageContents(player.getInventory());
246-
leftOverAmount = playerInventory.addItem(null, inventory.getItem(slot));
247-
}
248-
249-
clicked.setAmount(leftOverAmount);
250-
if (ItemUtils.isEmpty(clicked))
251-
clicked = null;
252-
253-
inventory.setItemSilently(slot, clicked);
228+
ItemPreUpdateEvent updateEvent = inventory.callPreUpdateEvent(updateReason, slot, clicked, null);
229+
if (updateEvent.isCancelled())
230+
return;
231+
232+
var window = WindowManager.getInstance().getOpenWindow(player);
233+
assert window != null;
234+
235+
int leftOverAmount;
236+
if (window.isDouble()) {
237+
// for double windows, move into the first inventory that accepts the item, sorted by priority
238+
var inventories = window.getGuis().stream()
239+
.flatMap(gui -> gui.getInventories(inventory).stream())
240+
.sorted(Comparator.comparingInt(Inventory::getGuiPriority).reversed())
241+
.toList();
254242

255-
inventory.callPostUpdateEvent(updateReason, slot, previousStack, clicked);
243+
leftOverAmount = putIntoFirstInventory(updateReason, clicked, inventories);
244+
} else {
245+
// for single windows, the player inventory takes priority
246+
Inventory playerInventory = ReferencingInventory.fromReversedPlayerStorageContents(player.getInventory());
247+
leftOverAmount = playerInventory.addItem(null, clicked);
256248
}
249+
250+
ItemStack newStack = clicked.clone();
251+
newStack.setAmount(leftOverAmount);
252+
253+
inventory.setItemSilently(slot, newStack);
254+
inventory.callPostUpdateEvent(updateReason, slot, clicked, newStack);
257255
}
258256

259257
// TODO: add support for merged windows
260-
protected void handleInvNumberKey(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
258+
private void handleInvNumberKey(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
261259
Window window = WindowManager.getInstance().getOpenWindow(player);
262-
if (window instanceof AbstractSingleWindow) {
260+
assert window != null;
261+
262+
if (!window.isDouble()) {
263263
org.bukkit.inventory.Inventory playerInventory = player.getInventory();
264264
int hotbarButton = event.getHotbarButton();
265265
ItemStack hotbarItem = ItemUtils.takeUnlessEmpty(playerInventory.getItem(hotbarButton));
@@ -272,9 +272,11 @@ protected void handleInvNumberKey(InventoryClickEvent event, Inventory inventory
272272
}
273273

274274
// TODO: add support for merged windows
275-
protected void handleInvOffHandKey(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
275+
private void handleInvOffHandKey(InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
276276
Window window = WindowManager.getInstance().getOpenWindow(player);
277-
if (window instanceof AbstractSingleWindow) {
277+
assert window != null;
278+
279+
if (!window.isDouble()) {
278280
PlayerInventory playerInventory = player.getInventory();
279281
ItemStack offhandItem = ItemUtils.takeUnlessEmpty(playerInventory.getItemInOffHand());
280282

@@ -285,7 +287,7 @@ protected void handleInvOffHandKey(InventoryClickEvent event, Inventory inventor
285287
}
286288
}
287289

288-
protected void handleInvDrop(boolean ctrl, InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
290+
private void handleInvDrop(boolean ctrl, InventoryClickEvent event, Inventory inventory, int slot, Player player, @Nullable ItemStack clicked) {
289291
if (clicked == null)
290292
return;
291293

@@ -302,7 +304,7 @@ protected void handleInvDrop(boolean ctrl, InventoryClickEvent event, Inventory
302304

303305
}
304306

305-
protected void handleInvDoubleClick(InventoryClickEvent event, Player player, @Nullable ItemStack cursor) {
307+
private void handleInvDoubleClick(InventoryClickEvent event, Player player, @Nullable ItemStack cursor) {
306308
if (cursor == null)
307309
return;
308310

@@ -312,7 +314,7 @@ protected void handleInvDoubleClick(InventoryClickEvent event, Player player, @N
312314
}
313315

314316
@SuppressWarnings("deprecation")
315-
protected void handleInvMiddleClick(InventoryClickEvent event, Inventory inventory, int slot, Player player) {
317+
private void handleInvMiddleClick(InventoryClickEvent event, Inventory inventory, int slot, Player player) {
316318
if (player.getGameMode() != GameMode.CREATIVE)
317319
return;
318320

@@ -361,22 +363,50 @@ public void handleItemShift(InventoryClickEvent event) {
361363
}
362364
}
363365

366+
/**
367+
* Puts the given {@link ItemStack} into the first inventory that accepts it, starting with the
368+
* {@link Inventory#getGuiPriority() highest priority} inventory. If one inventory accepts any amount
369+
* of items, further inventories will not be queried, meaning that an item stack will not be split
370+
* across multiple inventories.
371+
*
372+
* @param updateReason the update reason to use
373+
* @param itemStack the item stack to put
374+
* @param ignored the inventories to ignore
375+
* @return the amount of items that are left over
376+
*/
364377
protected int putIntoFirstInventory(UpdateReason updateReason, ItemStack itemStack, Inventory... ignored) {
365-
Collection<Inventory> inventories = getAllInventories(ignored);
378+
return putIntoFirstInventory(updateReason, itemStack, getInventories(ignored));
379+
}
380+
381+
/**
382+
* Puts the given {@link ItemStack} into the first inventory that accepts it of the given collection of inventories.
383+
* If one inventory accepts any amount of items, further inventories will not be queried, meaning that an item stack
384+
* will not be split across multiple inventories.
385+
*
386+
* @param updateReason the update reason to use
387+
* @param itemStack the item stack to put
388+
* @param inventories the inventories to put the item stack into
389+
* @return the amount of items that are left over
390+
*/
391+
protected int putIntoFirstInventory(UpdateReason updateReason, ItemStack itemStack, SequencedCollection<? extends Inventory> inventories) {
366392
int originalAmount = itemStack.getAmount();
367-
368-
if (!inventories.isEmpty()) {
369-
for (Inventory inventory : inventories) {
370-
int amountLeft = inventory.addItem(updateReason, itemStack);
371-
if (originalAmount != amountLeft)
372-
return amountLeft;
373-
}
393+
for (Inventory inventory : inventories) {
394+
int amountLeft = inventory.addItem(updateReason, itemStack);
395+
if (originalAmount != amountLeft)
396+
return amountLeft;
374397
}
375398

376399
return originalAmount;
377400
}
378401

379-
public Map<Inventory, Set<Integer>> getAllInventorySlots(Inventory... ignored) {
402+
/**
403+
* Gets a map of all inventories and their visible slots in this gui, ignoring the specified inventories,
404+
* sorted by their {@link Inventory#getGuiPriority()}, with the highest priorities coming first.
405+
*
406+
* @param ignored the inventories to ignore
407+
* @return a map of all inventories and their visible slots
408+
*/
409+
private SequencedMap<Inventory, Set<Integer>> getAllInventorySlots(Inventory... ignored) {
380410
HashMap<Inventory, Set<Integer>> slots = new HashMap<>();
381411
Set<Inventory> ignoredSet = Arrays.stream(ignored).collect(Collectors.toSet());
382412

@@ -399,9 +429,10 @@ public Map<Inventory, Set<Integer>> getAllInventorySlots(Inventory... ignored) {
399429
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));
400430
}
401431

402-
public Collection<Inventory> getAllInventories(Inventory... ignored) {
432+
@Override
433+
public SequencedCollection<? extends Inventory> getInventories(Inventory... ignored) {
403434
if (!ignoreObscuredInventorySlots)
404-
return getAllInventorySlots(ignored).keySet();
435+
return Collections.unmodifiableSequencedCollection(getAllInventorySlots(ignored).sequencedKeySet());
405436

406437
ArrayList<Inventory> inventories = new ArrayList<>();
407438
for (Map.Entry<Inventory, Set<Integer>> entry : getAllInventorySlots(ignored).entrySet()) {
@@ -410,7 +441,7 @@ public Collection<Inventory> getAllInventories(Inventory... ignored) {
410441
inventories.add(new ObscuredInventory(inventory, slot -> !slots.contains(slot)));
411442
}
412443

413-
return inventories;
444+
return Collections.unmodifiableList(inventories);
414445
}
415446
//</editor-fold>
416447

invui/src/main/java/xyz/xenondevs/invui/gui/Gui.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,18 @@ static Gui of(Structure structure) {
576576
*/
577577
boolean isIgnoreObscuredInventorySlots();
578578

579+
/**
580+
* Gets an unmodifiable sequenced collection of all inventories visible in this gui,
581+
* including those that are part of nested guis, ignoring the specified inventories,
582+
* sorted by their {@link Inventory#getGuiPriority()}, with the highest priorities coming first.
583+
* If {@link #isIgnoreObscuredInventorySlots()} is true, the obscured slots will not be visible
584+
* in the returned inventories.
585+
*
586+
* @param ignored the inventories to ignore
587+
* @return a sequenced collection of all inventories visible in this gui
588+
*/
589+
SequencedCollection<? extends Inventory> getInventories(Inventory... ignored);
590+
579591
//<editor-fold desc="fill methods">
580592

581593
/**

0 commit comments

Comments
 (0)