Skip to content

Commit 94b4ad0

Browse files
Improved slot element supplier
Instead of using Supplier<SlotElement> for gui and inventory ingredients, a custom SlotElementSupplier type now receives the entire requested list of slots and also returns all slot elements in one go. This also allows for an improved GuiSlotElementSupplier, which now keeps the shape of the Gui and can support holes in the structure.
1 parent dfde742 commit 94b4ad0

File tree

13 files changed

+346
-118
lines changed

13 files changed

+346
-118
lines changed

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,19 @@ public void setSlotElement(char key, Supplier<? extends @Nullable SlotElement> e
833833
}
834834
}
835835

836+
@Override
837+
public void setSlotElement(char key, SlotElementSupplier elementSupplier) {
838+
var matrix = ingredientMatrix;
839+
if (matrix == null)
840+
return;
841+
842+
var slots = matrix.getSlots(key);
843+
var elements = elementSupplier.generateSlotElements(slots);
844+
for (int i = 0; i < slots.size(); i++) {
845+
setSlotElement(slots.get(i), elements.get(i));
846+
}
847+
}
848+
836849
@Override
837850
public void setItem(char key, @Nullable Item item) {
838851
setItem(key, () -> item);
@@ -1099,19 +1112,11 @@ public S applyPreset(IngredientPreset preset) {
10991112
}
11001113

11011114
@Override
1102-
public S addIngredient(char key, SlotElement element) {
1115+
public S addIngredient(char key, SlotElementSupplier elementSupplier) {
11031116
if (structure == null)
11041117
throw new IllegalStateException("Structure is not set");
1105-
structure.addIngredient(key, element);
1106-
return (S) this;
1107-
}
1108-
1109-
@Override
1110-
public S addIngredientElementSupplier(char key, Supplier<? extends SlotElement> elementSupplier) {
1111-
if (structure == null)
1112-
throw new IllegalStateException("Structure is not set");
1113-
structure.addIngredientElementSupplier(key, elementSupplier);
1114-
return (S) this;
1118+
structure.addIngredient(key, elementSupplier);
1119+
return (S) this;
11151120
}
11161121

11171122
@Override

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package xyz.xenondevs.invui.gui;
22

33
import java.util.HashMap;
4-
import java.util.function.Supplier;
54

65
@SuppressWarnings("unchecked")
76
abstract sealed class AbstractIngredientMapper<S extends AbstractIngredientMapper<S>> implements IngredientMapper<S> permits IngredientPreset.Builder, Structure {
@@ -18,14 +17,7 @@ public S applyPreset(IngredientPreset preset) {
1817
}
1918

2019
@Override
21-
public S addIngredient(char key, SlotElement element) {
22-
handleUpdate();
23-
ingredientMap.put(key, new Ingredient(element));
24-
return (S) this;
25-
}
26-
27-
@Override
28-
public S addIngredientElementSupplier(char key, Supplier<? extends SlotElement> elementSupplier) {
20+
public S addIngredient(char key, SlotElementSupplier elementSupplier) {
2921
handleUpdate();
3022
ingredientMap.put(key, new Ingredient(elementSupplier));
3123
return (S) this;

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ static Gui empty(int width, int height) {
5656
*/
5757
static Gui of(Structure structure) {
5858
return new NormalGuiImpl(
59-
structure,
59+
structure,
6060
MutableProperty.of(AbstractGui.DEFAULT_FROZEN),
6161
MutableProperty.of(AbstractGui.DEFAULT_IGNORE_OBSCURED_INVENTORY_SLOTS),
6262
MutableProperty.of(AbstractGui.DEFAULT_BACKGROUND)
@@ -190,6 +190,15 @@ static Gui single(Inventory inventory, int slot, ItemProvider background) {
190190
*/
191191
void setSlotElement(char key, Supplier<? extends @Nullable SlotElement> elementSupplier);
192192

193+
/**
194+
* Sets the {@link SlotElement SlotElements} on all slots associated with the given key through a {@link Structure},
195+
* using the given {@link SlotElementSupplier} to generate them.
196+
*
197+
* @param key The key
198+
* @param elementSupplier The {@link SlotElementSupplier} to generate the {@link SlotElement SlotElements}
199+
*/
200+
void setSlotElement(char key, SlotElementSupplier elementSupplier);
201+
193202
/**
194203
* Sets the {@link SlotElement} on the given {@link Slot}. If you need to set an {@link Item},
195204
* prefer {@link #setItem(int, Item)} instead.
@@ -432,14 +441,25 @@ default void setInventory(char key, Inventory inventory, @Nullable ItemProvider
432441
}
433442

434443
/**
435-
* Fills the slots associated with the given key through a {@link Structure} with the given {@link Gui},
436-
* using the given {@link ItemProvider} as background for empty slots.
444+
* Fills the slots associated with the given key through a {@link Structure} with the given {@link Gui}.
437445
*
438446
* @param key The key
439447
* @param gui The {@link Gui} that should be placed on these slots
440448
*/
441449
default void setGui(char key, Gui gui) {
442-
setSlotElement(key, new GuiSlotElementSupplier(gui));
450+
setGui(key, gui, 0, 0);
451+
}
452+
453+
/**
454+
* Fills the slots associated with the given key through a {@link Structure} with the given {@link Gui}.
455+
*
456+
* @param key The key
457+
* @param gui The {@link Gui} that should be placed on these slots
458+
* @param offsetX The x offset inside the given {@link Gui} to start from
459+
* @param offsetY The y offset inside the given {@link Gui} to start from
460+
*/
461+
default void setGui(char key, Gui gui, int offsetX, int offsetY) {
462+
setSlotElement(key, new GuiSlotElementSupplier(gui, offsetX, offsetY));
443463
}
444464

445465
/**
Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,52 @@
11
package xyz.xenondevs.invui.gui;
22

3-
class GuiSlotElementSupplier implements ResettableSlotElementSupplier<SlotElement.GuiLink> {
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
class GuiSlotElementSupplier implements SlotElementSupplier {
47

58
private final Gui gui;
6-
private int slot;
9+
private final int offsetX;
10+
private final int offsetY;
711

8-
public GuiSlotElementSupplier(Gui gui) {
12+
public GuiSlotElementSupplier(Gui gui, int offsetX, int offsetY) {
913
if (gui.getSize() <= 0)
1014
throw new IllegalArgumentException("Illegal gui size: " + gui.getSize());
1115

1216
this.gui = gui;
17+
this.offsetX = offsetX;
18+
this.offsetY = offsetY;
1319
}
1420

1521
@Override
16-
public SlotElement.GuiLink get() {
17-
if (slot >= gui.getSize())
18-
throw new IllegalStateException("No more slots available");
22+
public List<? extends SlotElement> generateSlotElements(List<? extends Slot> slots) {
23+
if (slots.isEmpty())
24+
return List.of();
1925

20-
return new SlotElement.GuiLink(gui, slot++);
21-
}
22-
23-
@Override
24-
public void reset() {
25-
slot = 0;
26+
int minX = Integer.MAX_VALUE;
27+
int minY = Integer.MAX_VALUE;
28+
for (Slot slot : slots) {
29+
if (slot.x() < minX) minX = slot.x();
30+
if (slot.y() < minY) minY = slot.y();
31+
}
32+
33+
var elements = new ArrayList<SlotElement.GuiLink>();
34+
for (Slot slot : slots) {
35+
var guiX = slot.x() - minX + offsetX;
36+
var guiY = slot.y() - minY + offsetY;
37+
38+
if (guiX < 0 || guiY < 0 || guiX >= gui.getWidth() || guiY >= gui.getHeight())
39+
throw new IndexOutOfBoundsException(
40+
"Structure slot at (" + slot.x() + ", " + slot.y() +
41+
") with offset (" + offsetX + ", " + offsetY +
42+
") is looking for slot (" + guiX + ", " + guiY +
43+
"), which is out of bounds for a gui of size " + gui.getWidth() + "x" + gui.getHeight()
44+
);
45+
46+
elements.add(new SlotElement.GuiLink(gui, guiY * gui.getWidth() + guiX));
47+
}
48+
49+
return elements;
2650
}
2751

2852
}

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

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

33
import org.jspecify.annotations.Nullable;
44

5+
import java.util.List;
56
import java.util.function.Supplier;
67

78
/**
@@ -11,7 +12,7 @@
1112
class Ingredient {
1213

1314
private final @Nullable Marker marker;
14-
private final @Nullable Supplier<? extends SlotElement> elementSupplier;
15+
private final @Nullable SlotElementSupplier elementSupplier;
1516

1617
/**
1718
* Creates a new {@link Ingredient} with the given {@link Marker}.
@@ -28,29 +29,19 @@ public Ingredient(Marker marker) {
2829
*
2930
* @param elementSupplier The {@link Supplier} for the {@link SlotElement}.
3031
*/
31-
public Ingredient(Supplier<? extends SlotElement> elementSupplier) {
32+
public Ingredient(SlotElementSupplier elementSupplier) {
3233
this.elementSupplier = elementSupplier;
3334
this.marker = null;
3435
}
3536

36-
/**
37-
* Creates a new {@link Ingredient} with the given {@link SlotElement}.
38-
*
39-
* @param element The {@link SlotElement} of this {@link Ingredient}.
40-
*/
41-
public Ingredient(SlotElement element) {
42-
this.elementSupplier = () -> element;
43-
this.marker = null;
44-
}
45-
4637
/**
4738
* Gets the {@link SlotElement} of this {@link Ingredient}.
4839
*
4940
* @return The {@link SlotElement} or null if this {@link Ingredient} is a {@link Marker}
5041
*/
5142
@Nullable
52-
SlotElement getSlotElement() {
53-
return elementSupplier != null ? elementSupplier.get() : null;
43+
List<? extends SlotElement> generateSlotElements(List<? extends Slot> slots) {
44+
return elementSupplier != null ? elementSupplier.generateSlotElements(slots) : null;
5445
}
5546

5647
/**
@@ -69,7 +60,7 @@ Marker getMarker() {
6960
*
7061
* @return Whether this {@link Ingredient} is a {@link SlotElement}.
7162
*/
72-
boolean isSlotElement() {
63+
boolean isSlotElementSupplier() {
7364
return elementSupplier != null;
7465
}
7566

@@ -82,14 +73,4 @@ boolean isMarker() {
8273
return marker != null;
8374
}
8475

85-
/**
86-
* Calls {@link ResettableSlotElementSupplier#reset()} if this {@link #isSlotElement()} and
87-
* a {@link ResettableSlotElementSupplier} is used as the supplier.
88-
*/
89-
void reset() {
90-
if (elementSupplier instanceof ResettableSlotElementSupplier<?> e) {
91-
e.reset();
92-
}
93-
}
94-
9576
}

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ default S addIngredient(char key, Inventory inventory, @Nullable ItemProvider ba
124124
* @return This {@link IngredientMapper}
125125
*/
126126
default S addIngredient(char key, Inventory inventory, @Nullable ItemProvider background, int offset) {
127-
return addIngredientElementSupplier(key, new InventorySlotElementSupplier(inventory, background, offset));
127+
return addIngredient(key, new InventorySlotElementSupplier(inventory, background, offset));
128128
}
129129

130130
/**
@@ -135,7 +135,20 @@ default S addIngredient(char key, Inventory inventory, @Nullable ItemProvider ba
135135
* @return This {@link IngredientMapper}
136136
*/
137137
default S addIngredient(char key, Gui gui) {
138-
return addIngredientElementSupplier(key, new GuiSlotElementSupplier(gui));
138+
return addIngredient(key, gui, 0, 0);
139+
}
140+
141+
/**
142+
* Adds a {@link Gui} ingredient under the given key.
143+
*
144+
* @param key The key of the ingredient
145+
* @param gui The {@link Gui} ingredient
146+
* @param offsetX The x offset inside the given {@link Gui} to start from
147+
* @param offsetY The y offset inside the given {@link Gui} to start from
148+
* @return This {@link IngredientMapper}
149+
*/
150+
default S addIngredient(char key, Gui gui, int offsetX, int offsetY) {
151+
return addIngredient(key, new GuiSlotElementSupplier(gui, offsetX, offsetY));
139152
}
140153

141154
/**
@@ -145,7 +158,9 @@ default S addIngredient(char key, Gui gui) {
145158
* @param element The {@link SlotElement} ingredient
146159
* @return This {@link IngredientMapper}
147160
*/
148-
S addIngredient(char key, SlotElement element);
161+
default S addIngredient(char key, SlotElement element) {
162+
return addIngredientElementSupplier(key, () -> element);
163+
}
149164

150165
/**
151166
* Adds a {@link SlotElement} {@link Supplier} ingredient under the given key.
@@ -154,7 +169,18 @@ default S addIngredient(char key, Gui gui) {
154169
* @param elementSupplier The {@link SlotElement} {@link Supplier} ingredient
155170
* @return This {@link IngredientMapper}
156171
*/
157-
S addIngredientElementSupplier(char key, Supplier<? extends SlotElement> elementSupplier);
172+
default S addIngredientElementSupplier(char key, Supplier<? extends SlotElement> elementSupplier) {
173+
return addIngredient(key, SlotElementSupplier.fromSupplier(elementSupplier));
174+
}
175+
176+
/**
177+
* Adds a {@link SlotElementSupplier} ingredient under the given key.
178+
*
179+
* @param key The key of the ingredient
180+
* @param elementSupplier The {@link SlotElementSupplier} ingredient
181+
* @return This {@link IngredientMapper}
182+
*/
183+
S addIngredient(char key, SlotElementSupplier elementSupplier);
158184

159185
/**
160186
* Adds a {@link Marker} ingredient under the given key.

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

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,35 @@ class IngredientMatrix {
4141
this.markers = new Marker[structure.length()];
4242
this.slots = new Char2ObjectOpenHashMap<>();
4343

44-
// for loop order is important to invoke slot element suppliers left-to-right, top-to-bottom
4544
for (int y = 0; y < height; y++) {
4645
for (int x = 0; x < width; x++) {
4746
int i = y * width + x;
48-
4947
char key = structure.charAt(i);
5048

51-
var ingredient = ingredientMap.get(key);
52-
if (ingredient != null) {
53-
slotElements[i] = ingredient.getSlotElement();
54-
markers[i] = ingredient.getMarker();
55-
}
56-
5749
slots.computeIfAbsent(key, ArrayList::new).add(new Slot(x, y));
5850
}
5951
}
52+
53+
for (var entry : slots.char2ObjectEntrySet()) {
54+
char key = entry.getCharKey();
55+
Ingredient ingredient = ingredientMap.get(key);
56+
if (ingredient == null)
57+
continue;
58+
59+
List<Slot> slotsForKey = entry.getValue();
60+
if (ingredient.isSlotElementSupplier()) {
61+
var slotElements = ingredient.generateSlotElements(slotsForKey);
62+
assert slotElements != null;
63+
for (int i = 0; i < slotsForKey.size(); i++) {
64+
var slot = slotsForKey.get(i);
65+
this.slotElements[slot.y() * width + slot.x()] = slotElements.get(i);
66+
}
67+
} else {
68+
for (Slot slot : slotsForKey) {
69+
markers[slot.y() * width + slot.x()] = ingredient.getMarker();
70+
}
71+
}
72+
}
6073
}
6174

6275
/**
@@ -172,7 +185,7 @@ int[] findContentListSlots() {
172185
* @return a collection of slots for the given key
173186
*/
174187
@Unmodifiable
175-
SequencedCollection<Slot> getSlots(char key) {
188+
List<Slot> getSlots(char key) {
176189
return Collections.unmodifiableList(slots.getOrDefault(key, Collections.emptyList()));
177190
}
178191

0 commit comments

Comments
 (0)