55import org .bukkit .entity .Player ;
66import org .bukkit .inventory .ItemStack ;
77import org .bukkit .inventory .PlayerInventory ;
8- import org .jetbrains .annotations .ApiStatus .Internal ;
98import org .jetbrains .annotations .Unmodifiable ;
109import org .jspecify .annotations .Nullable ;
1110import xyz .xenondevs .invui .Click ;
12- import xyz .xenondevs .invui .internal . ViewerAtSlot ;
11+ import xyz .xenondevs .invui .Observer ;
1312import xyz .xenondevs .invui .internal .util .*;
13+ import xyz .xenondevs .invui .inventory .CompositeInventory ;
1414import xyz .xenondevs .invui .inventory .Inventory ;
1515import xyz .xenondevs .invui .inventory .ObscuredInventory ;
1616import xyz .xenondevs .invui .inventory .OperationCategory ;
2222import xyz .xenondevs .invui .item .ItemProvider ;
2323import xyz .xenondevs .invui .state .MutableProperty ;
2424import xyz .xenondevs .invui .util .ItemUtils ;
25- import xyz .xenondevs .invui .window . AbstractWindow ;
25+ import xyz .xenondevs .invui .util . ObserverAtSlot ;
2626import xyz .xenondevs .invui .window .Window ;
2727import xyz .xenondevs .invui .window .WindowManager ;
2828
3131import java .util .function .Supplier ;
3232import java .util .stream .Collectors ;
3333
34- /**
35- * @hidden
36- */
37- @ Internal
38- public sealed abstract class AbstractGui
39- implements Gui
40- permits NormalGuiImpl , AbstractPagedGui , AbstractScrollGui , TabGuiImpl
41- {
34+ non-sealed abstract class AbstractGui implements Gui {
4235
4336 public static final boolean DEFAULT_FROZEN = false ;
4437 public static final boolean DEFAULT_IGNORE_OBSCURED_INVENTORY_SLOTS = true ;
@@ -48,7 +41,7 @@ public sealed abstract class AbstractGui
4841 private final int height ;
4942 private final int size ;
5043 private final @ Nullable SlotElement [] slotElements ;
51- private final @ Nullable Set <ViewerAtSlot >[] viewers ;
44+ private final @ Nullable Set <ObserverAtSlot >[] observers ;
5245
5346 private final MutableProperty <Boolean > frozen ;
5447 private final MutableProperty <Boolean > ignoreObscuredInventorySlots ;
@@ -84,33 +77,35 @@ public sealed abstract class AbstractGui
8477 this .background = background ;
8578
8679 slotElements = new SlotElement [size ];
87- viewers = new Set [size ];
80+ observers = new Set [size ];
8881
8982 this .background .observeWeak (this , AbstractGui ::notifyWindowsOnBackgroundSlots );
9083 }
9184
85+ @ Override
9286 public void handleClick (int slot , Click click ) {
9387 // ignore all clicks if the gui is frozen
9488 if (isFrozen ())
9589 return ;
9690
9791 SlotElement slotElement = slotElements [slot ];
9892 switch (slotElement ) {
99- case SlotElement .GuiLink le -> (( AbstractGui ) le .gui () ).handleClick (le .slot (), click );
93+ case SlotElement .GuiLink le -> le .gui ().handleClick (le .slot (), click );
10094 case SlotElement .Item ie -> ie .item ().handleClick (click .clickType (), click .player (), click );
10195 case SlotElement .InventoryLink ie -> handleInvSlotElementClick (ie , click );
10296 case null -> {}
10397 }
10498 }
10599
106- public void handleBundleSelect (Player player , int slot , int bundleSlot ) {
100+ @ Override
101+ public void handleBundleSelect (int slot , Player player , int bundleSlot ) {
107102 // ignore all clicks if the gui is frozen
108103 if (isFrozen ())
109104 return ;
110105
111106 SlotElement slotElement = slotElements [slot ];
112107 switch (slotElement ) {
113- case SlotElement .GuiLink le -> (( AbstractGui ) le .gui ()) .handleBundleSelect (player , le .slot (), bundleSlot );
108+ case SlotElement .GuiLink le -> le .gui ().handleBundleSelect (le .slot (), player , bundleSlot );
114109 case SlotElement .Item ie -> ie .item ().handleBundleSelect (player , bundleSlot );
115110 case SlotElement .InventoryLink ie -> handleInvBundleSelect (player , ie .inventory (), ie .slot (), bundleSlot );
116111 case null -> {}
@@ -284,18 +279,14 @@ private void handleInvNumberKey(Click click, Inventory inventory, int slot) {
284279 Player player = click .player ();
285280 ItemStack clicked = inventory .getItem (slot );
286281
287- AbstractWindow <?> window = ( AbstractWindow <?>) WindowManager .getInstance ().getOpenWindow (player );
282+ Window window = WindowManager .getInstance ().getOpenWindow (player );
288283 assert window != null ;
289284
290285 SlotElement .GuiLink link = window .getGuiAtHotbar (click .hotbarButton ());
291286 if (link == null )
292287 return ;
293- SlotElement hotbarElement = link .gui ().getSlotElement (link .slot ());
294- if (hotbarElement == null )
295- return ;
296- hotbarElement = hotbarElement .getHoldingElement ();
297288
298- if (hotbarElement instanceof SlotElement .InventoryLink (var otherInventory , var otherSlot , var unused )) {
289+ if (link . getHoldingElement () instanceof SlotElement .InventoryLink (var otherInventory , var otherSlot , var unused )) {
299290 if (inventory == otherInventory && slot == otherSlot )
300291 return ;
301292
@@ -351,10 +342,27 @@ private void handleInvDoubleClick(Click click) {
351342 if (ItemUtils .isEmpty (player .getItemOnCursor ()))
352343 return ;
353344
354- // windows handle cursor collect because it is a cross-inventory / cross-gui operation
345+ // requires window as collect to cursor is a cross-gui operation
355346 Window window = WindowManager .getInstance ().getOpenWindow (player );
356347 assert window != null ;
357- ((AbstractWindow <?>) window ).handleCursorCollect (click );
348+
349+ // the template item stack that is used to collect similar items
350+ ItemStack template = player .getItemOnCursor ();
351+
352+ // create a composite inventory consisting of all the gui's inventories and the player's inventory
353+ List <? extends Inventory > inventories = window .getGuis ().stream ()
354+ .flatMap (g -> g .getInventories ().stream ())
355+ .sorted (Comparator .<Inventory >comparingInt (inv -> inv .getGuiPriority (OperationCategory .COLLECT )).reversed ())
356+ .toList ();
357+ Inventory inventory = new CompositeInventory (inventories );
358+
359+ // collect items from inventories until the cursor is full
360+ UpdateReason updateReason = new PlayerUpdateReason .Click (player , click );
361+ int amount = inventory .collectSimilar (updateReason , template );
362+
363+ // put collected items on cursor
364+ template .setAmount (amount );
365+ player .setItemOnCursor (template );
358366 }
359367
360368 private void handleInvMiddleClick (Click click , Inventory inventory , int slot ) {
@@ -476,10 +484,10 @@ private Map<? extends Inventory, Set<Integer>> getAllActiveInventorySlots(Invent
476484
477485 @ Override
478486 public void notifyWindows () {
479- synchronized (viewers ) {
480- for (var viewerSet : viewers ) {
481- if (viewerSet != null ) {
482- for (var viewerAtSlot : viewerSet ) {
487+ synchronized (observers ) {
488+ for (var observerSet : observers ) {
489+ if (observerSet != null ) {
490+ for (var viewerAtSlot : observerSet ) {
483491 viewerAtSlot .notifyUpdate ();
484492 }
485493 }
@@ -493,65 +501,69 @@ public void notifyWindows(int index) {
493501 if (element == null )
494502 return ;
495503
496- synchronized (viewers ) {
497- var viewerSet = viewers [index ];
498- if (viewerSet == null )
504+ synchronized (observers ) {
505+ var observerSet = observers [index ];
506+ if (observerSet == null )
499507 return ;
500508
501- for (var viewerAtSlot : viewerSet ) {
509+ for (var viewerAtSlot : observerSet ) {
502510 viewerAtSlot .notifyUpdate ();
503511 }
504512 }
505513 }
506514
507515 private void notifyWindowsOnBackgroundSlots () {
508- synchronized (viewers ) {
516+ synchronized (observers ) {
509517 for (int i = 0 ; i < getSize (); i ++) {
510518 if (slotElements [i ] != null )
511519 continue ;
512520
513- var viewerSet = viewers [i ];
514- if (viewerSet == null )
521+ var observerSet = observers [i ];
522+ if (observerSet == null )
515523 continue ;
516524
517- for (var viewerAtSlot : viewerSet ) {
525+ for (var viewerAtSlot : observerSet ) {
518526 viewerAtSlot .notifyUpdate ();
519527 }
520528 }
521529 }
522530 }
523531
524- public void addViewer (AbstractWindow <?> who , int what , int how ) {
525- synchronized (viewers ) {
526- var viewerSet = this .viewers [what ];
527- if (viewerSet == null ) {
528- viewerSet = new HashSet <>();
529- this .viewers [what ] = viewerSet ;
532+ @ Override
533+ public void addObserver (Observer who , int what , int how ) {
534+ synchronized (observers ) {
535+ var observerSet = this .observers [what ];
536+ if (observerSet == null ) {
537+ observerSet = new HashSet <>();
538+ this .observers [what ] = observerSet ;
530539 }
531- viewerSet .add (new ViewerAtSlot (who , how ));
540+ observerSet .add (new ObserverAtSlot (who , how ));
532541 }
533542 }
534543
535- public void removeViewer (AbstractWindow <?> who , int what , int how ) {
536- synchronized (viewers ) {
537- var viewerSet = this .viewers [what ];
538- if (viewerSet != null ) {
539- viewerSet .remove (new ViewerAtSlot (who , how ));
540- if (viewerSet .isEmpty ())
541- this .viewers [what ] = null ;
544+ @ Override
545+ public void removeObserver (Observer who , int what , int how ) {
546+ synchronized (observers ) {
547+ var observerSet = this .observers [what ];
548+ if (observerSet != null ) {
549+ observerSet .remove (new ObserverAtSlot (who , how ));
550+ if (observerSet .isEmpty ())
551+ this .observers [what ] = null ;
542552 }
543553 }
544554 }
545555
546556 @ Override
547557 public @ Unmodifiable Collection <Window > getWindows () {
548- synchronized (viewers ) {
558+ synchronized (observers ) {
549559 var windows = new HashSet <Window >();
550- for (var viewerSet : viewers ) {
551- if (viewerSet != null ) {
552- for (var viewerAtSlot : viewerSet ) {
553- windows .add (viewerAtSlot .window ());
554- }
560+ for (var observerSet : observers ) {
561+ if (observerSet == null )
562+ continue ;
563+ for (var viewerAtSlot : observerSet ) {
564+ if (!(viewerAtSlot .observer () instanceof Window w ))
565+ continue ;
566+ windows .add (w );
555567 }
556568 }
557569
@@ -670,7 +682,7 @@ public void setSlotElement(int index, @Nullable SlotElement slotElement) {
670682 }
671683
672684 // notify parents that a slot element has been changed
673- var viewers = this .viewers [index ];
685+ var viewers = this .observers [index ];
674686 if (viewers != null ) {
675687 for (var viewer : viewers ) {
676688 viewer .notifyUpdate ();
@@ -731,10 +743,9 @@ public void addItems(Item... items) {
731743
732744 if (slotElement instanceof SlotElement .Item ) {
733745 return ((SlotElement .Item ) slotElement ).item ();
734- } else if (slotElement instanceof SlotElement .GuiLink ) {
735- SlotElement holdingElement = slotElement .getHoldingElement ();
736- if (holdingElement instanceof SlotElement .Item )
737- return ((SlotElement .Item ) holdingElement ).item ();
746+ } else if (slotElement instanceof SlotElement .GuiLink l ) {
747+ if (l .getHoldingElement () instanceof SlotElement .Item (Item item ))
748+ return item ;
738749 }
739750
740751 return null ;
0 commit comments