Skip to content

Commit f49d3f2

Browse files
authored
Add click-and-drag support for HEI BookmarkItems (#33)
Phantom slot widgets now support BookmarkItem objects, unwrapping them into ItemStack/FluidStack as appropriate. The display size of ingredients are transferred as well, clamping the stack between 1 and 2^31-1. FluidStack also clamps this way, but uses the amount of the bookmarked FluidStack if the display size is exactly zero (which it usually is, and 1B is a more useful default amount than 1mB - or at least it will be, eventually).
1 parent 139c12b commit f49d3f2

4 files changed

Lines changed: 245 additions & 31 deletions

File tree

src/main/java/gregtech/api/gui/igredient/IGhostIngredientTarget.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,13 @@ public interface IGhostIngredientTarget {
88

99
List<Target<?>> getPhantomTargets(Object ingredient);
1010

11+
/** Clamps and casts {@code in} to an {@code int} valued between {@code 1} and {@code Integer.MAX_VALUE} */
12+
default int clampLong(long in) {
13+
if(in <= 1L)
14+
return 1;
15+
if(in > Integer.MAX_VALUE)
16+
return Integer.MAX_VALUE;
17+
return (int) in;
18+
}
19+
1120
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package gregtech.api.gui.widgets;
2+
3+
import net.minecraft.item.ItemStack;
4+
import net.minecraftforge.fluids.FluidStack;
5+
import org.jetbrains.annotations.Contract;
6+
import org.jetbrains.annotations.NotNull;
7+
import org.jetbrains.annotations.Nullable;
8+
9+
import java.lang.reflect.Field;
10+
import java.lang.reflect.Method;
11+
import java.util.function.Consumer;
12+
13+
/**
14+
* Loads HEI's {@code BookmarkItem} class via reflection and encapsulates interactions with it to maintain
15+
* backwards-compatibility with JEI while supporting click-and-drag from the HEI bookmarks panel.
16+
*/
17+
public class Bookmark {
18+
private static final Class<?> bookmarkItemClass;
19+
private static final Field bookmarkIngredientField;
20+
private static final Method bookmarkDisplayAmount;
21+
22+
static {
23+
Class<?> bmTemp;
24+
Field ingTmp;
25+
Method daTmp;
26+
27+
try {
28+
bmTemp = Class.forName("mezz.jei.bookmarks.BookmarkItem", false, Bookmark.class.getClassLoader());
29+
} catch(ClassNotFoundException e) {
30+
bmTemp = null;
31+
}
32+
bookmarkItemClass = bmTemp;
33+
if(bookmarkItemClass != null) {
34+
try {
35+
ingTmp = bookmarkItemClass.getDeclaredField("ingredient");
36+
} catch(NoSuchFieldException e) {
37+
ingTmp = null;
38+
}
39+
try {
40+
daTmp = bookmarkItemClass.getDeclaredMethod("getDisplayAmount");
41+
} catch(NoSuchMethodException e) {
42+
daTmp = null;
43+
}
44+
} else {
45+
ingTmp = null;
46+
daTmp = null;
47+
}
48+
bookmarkIngredientField = ingTmp;
49+
bookmarkDisplayAmount = daTmp;
50+
51+
}
52+
53+
private final Object bookmark;
54+
private Bookmark(@NotNull Object in) {
55+
this.bookmark = in;
56+
}
57+
58+
/** @throws RuntimeException if {@code in} is not a BookmarkItem */
59+
@NotNull
60+
public static Bookmark wrap(@NotNull Object in) {
61+
if(isBookmark(in))
62+
return new Bookmark(in);
63+
throw new RuntimeException("Skill Issue");
64+
}
65+
66+
public static void ifIsItemBookmark(Object in, Consumer<Bookmark> handler) {
67+
if(isItemBookmark(in))
68+
handler.accept(wrap(in));
69+
}
70+
71+
/** @return {@code true} if {@code in} is a bookmark wrapping a FluidStack */
72+
@Contract(pure = true)
73+
public static boolean isFluidBookmark(Object in) {
74+
return (isBookmark(in) && getIngredient(in) instanceof FluidStack);
75+
}
76+
77+
public void ifHasFluid(Consumer<FluidStack> fs) {
78+
if(hasFluid())
79+
fs.accept(getFluid());
80+
}
81+
82+
public boolean hasFluid() {
83+
return getIngredient() instanceof FluidStack;
84+
}
85+
86+
@Nullable
87+
public FluidStack getFluid() {
88+
return getIngredient() instanceof FluidStack stack ? stack : null;
89+
}
90+
91+
@Nullable
92+
public static FluidStack getFluid(Object in) {
93+
return getIngredient(in) instanceof FluidStack stack ? stack : null;
94+
}
95+
96+
/** @return {@code true} if {@code in} is a bookmark wrapping an ItemStack */
97+
@Contract(pure = true)
98+
public static boolean isItemBookmark(Object in) {
99+
return (isBookmark(in) && getIngredient(in) instanceof ItemStack);
100+
}
101+
102+
public void ifHasItem(Consumer<ItemStack> is) {
103+
if(hasItem())
104+
is.accept(getItem());
105+
}
106+
107+
public boolean hasItem() {
108+
return getIngredient() instanceof ItemStack;
109+
}
110+
111+
@Nullable
112+
public ItemStack getItem() {
113+
return getIngredient() instanceof ItemStack stack ? stack : null;
114+
}
115+
116+
@Nullable
117+
public static ItemStack getItem(Object in) {
118+
return getIngredient(in) instanceof ItemStack stack ? stack : null;
119+
}
120+
121+
@Contract(pure = true)
122+
public static boolean isBookmark(Object in) {
123+
if(bookmarkItemClass != null)
124+
return bookmarkItemClass.isAssignableFrom(in.getClass());
125+
return false;
126+
}
127+
128+
@Contract(pure = true)
129+
public static @Nullable Object getIngredient(Object o) {
130+
if(bookmarkIngredientField != null)
131+
try {
132+
return bookmarkIngredientField.get(o);
133+
} catch (Exception ignored) {}
134+
return null;
135+
}
136+
137+
@Contract(pure = true)
138+
public static @Nullable Long getDisplayAmount(Object o) {
139+
if(bookmarkDisplayAmount != null)
140+
try {
141+
return (long) bookmarkDisplayAmount.invoke(o);
142+
} catch(Exception ignored) {}
143+
return null;
144+
}
145+
146+
public Object getIngredient() {
147+
return getIngredient(bookmark);
148+
}
149+
150+
public long getDisplayAmount() {
151+
Long amount = getDisplayAmount(bookmark);
152+
return amount != null ? amount : 0L;
153+
}
154+
155+
}

src/main/java/gregtech/api/gui/widgets/PhantomFluidWidget.java

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import net.minecraftforge.fluids.FluidStack;
1919
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
2020
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
21+
import org.jetbrains.annotations.NotNull;
2122

2223
import java.awt.*;
2324
import java.io.IOException;
@@ -26,12 +27,14 @@
2627
import java.util.function.Consumer;
2728
import java.util.function.Supplier;
2829

30+
import static gregtech.api.gui.widgets.Bookmark.*;
31+
2932
public class PhantomFluidWidget extends Widget implements IIngredientSlot, IGhostIngredientTarget {
3033

3134
protected TextureArea backgroundTexture = GuiTextures.FLUID_SLOT;
3235

33-
private Supplier<FluidStack> fluidStackSupplier;
34-
private Consumer<FluidStack> fluidStackUpdater;
36+
private final Supplier<FluidStack> fluidStackSupplier;
37+
private final Consumer<FluidStack> fluidStackUpdater;
3538
protected FluidStack lastFluidStack;
3639

3740
public PhantomFluidWidget(int xPosition, int yPosition, int width, int height, Supplier<FluidStack> fluidStackSupplier, Consumer<FluidStack> fluidStackUpdater) {
@@ -40,42 +43,71 @@ public PhantomFluidWidget(int xPosition, int yPosition, int width, int height, S
4043
this.fluidStackUpdater = fluidStackUpdater;
4144
}
4245

43-
private FluidStack drainFrom(Object ingredient) {
44-
if (ingredient instanceof ItemStack) {
45-
ItemStack itemStack = (ItemStack) ingredient;
46+
private static FluidStack drainFrom(Object ingredient) {
47+
if (ingredient instanceof ItemStack itemStack) {
4648
IFluidHandlerItem fluidHandler = itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
4749
if (fluidHandler != null)
4850
return fluidHandler.drain(Integer.MAX_VALUE, false);
4951
}
5052
return null;
5153
}
5254

55+
// Usable if the ingredient is a fluid, a drainable item, or a bookmark containing either
56+
protected boolean isUsable(Object ingredient) {
57+
if(ingredient instanceof FluidStack)
58+
return true;
59+
if(ingredient instanceof ItemStack stack && drainFrom(stack) != null)
60+
return true;
61+
if(isBookmark(ingredient)) {
62+
Bookmark bookmark = wrap(ingredient);
63+
return bookmark.hasFluid() || (bookmark.hasItem() && drainFrom(bookmark.getItem()) != null);
64+
}
65+
return false;
66+
}
67+
5368
@Override
5469
public List<Target<?>> getPhantomTargets(Object ingredient) {
55-
if (!(ingredient instanceof FluidStack) && drainFrom(ingredient) == null) {
70+
if (!isUsable(ingredient))
5671
return Collections.emptyList();
57-
}
5872

5973
Rectangle rectangle = toRectangleBox();
60-
return Lists.newArrayList(new Target<Object>() {
74+
return Lists.newArrayList(new Target<>() {
6175
@Override
76+
@NotNull
6277
public Rectangle getArea() {
6378
return rectangle;
6479
}
6580

6681
@Override
67-
public void accept(Object ingredient) {
68-
FluidStack ingredientStack;
69-
if (ingredient instanceof FluidStack)
70-
ingredientStack = (FluidStack) ingredient;
71-
else
72-
ingredientStack = drainFrom(ingredient);
73-
74-
if (ingredientStack != null) {
75-
NBTTagCompound tagCompound = ingredientStack.writeToNBT(new NBTTagCompound());
76-
writeClientAction(2, buffer -> buffer.writeCompoundTag(tagCompound));
82+
public void accept(@NotNull Object ingredient) {
83+
if(ingredient instanceof FluidStack stack)
84+
finish(stack);
85+
else if (ingredient instanceof ItemStack) {
86+
FluidStack drained;
87+
if((drained = drainFrom(ingredient)) != null)
88+
finish(drained);
89+
} else if (isBookmark(ingredient)) {
90+
Bookmark bookmark = wrap(ingredient);
91+
bookmark.ifHasFluid(fluid -> {
92+
FluidStack copy = fluid.copy();
93+
if (bookmark.getDisplayAmount() != 0)
94+
copy.amount = clampLong(bookmark.getDisplayAmount());
95+
finish(copy);
96+
});
97+
bookmark.ifHasItem(item -> {
98+
ItemStack temp = item.copy();
99+
temp.setCount(clampLong(bookmark.getDisplayAmount()));
100+
FluidStack drained = drainFrom(temp);
101+
if (drained != null)
102+
finish(drained);
103+
});
77104
}
78105
}
106+
107+
private void finish(FluidStack stack) {
108+
NBTTagCompound tagCompound = stack.writeToNBT(new NBTTagCompound());
109+
writeClientAction(2, buffer -> buffer.writeCompoundTag(tagCompound));
110+
}
79111
});
80112
}
81113

src/main/java/gregtech/api/gui/widgets/PhantomSlotWidget.java

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.item.ItemStack;
1010
import net.minecraft.network.PacketBuffer;
1111
import net.minecraftforge.items.IItemHandlerModifiable;
12+
import org.jetbrains.annotations.NotNull;
1213
import org.lwjgl.input.Keyboard;
1314
import org.lwjgl.input.Mouse;
1415

@@ -17,6 +18,8 @@
1718
import java.util.Collections;
1819
import java.util.List;
1920

21+
import static gregtech.api.gui.widgets.Bookmark.*;
22+
2023
public class PhantomSlotWidget extends SlotWidget implements IGhostIngredientTarget {
2124

2225
public PhantomSlotWidget(IItemHandlerModifiable itemHandler, int slotIndex, int xPosition, int yPosition) {
@@ -34,29 +37,44 @@ public boolean canMergeSlot(ItemStack stack) {
3437
return false;
3538
}
3639

40+
// Usable if target is an item or item bookmark
41+
protected boolean isUsable(Object ingredient) {
42+
return ingredient instanceof ItemStack || isItemBookmark(ingredient);
43+
}
44+
3745
@Override
3846
public List<Target<?>> getPhantomTargets(Object ingredient) {
39-
if (!(ingredient instanceof ItemStack)) {
47+
if (!isUsable(ingredient))
4048
return Collections.emptyList();
41-
}
49+
4250
Rectangle rectangle = toRectangleBox();
43-
return Lists.newArrayList(new Target<Object>() {
51+
return Lists.newArrayList(new Target<>() {
4452
@Override
53+
@NotNull
4554
public Rectangle getArea() {
4655
return rectangle;
4756
}
4857

4958
@Override
50-
public void accept(Object ingredient) {
51-
if (ingredient instanceof ItemStack) {
52-
int mouseButton = Mouse.getEventButton();
53-
boolean shiftDown = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
54-
writeClientAction(1, buffer -> {
55-
buffer.writeItemStack((ItemStack) ingredient);
56-
buffer.writeVarInt(mouseButton);
57-
buffer.writeBoolean(shiftDown);
58-
});
59-
}
59+
public void accept(@NotNull Object ingredient) {
60+
if (ingredient instanceof ItemStack stack)
61+
finish(stack);
62+
else
63+
ifIsItemBookmark(ingredient, bookmark -> bookmark.ifHasItem(stack -> {
64+
ItemStack copy = stack.copy();
65+
copy.setCount(clampLong(bookmark.getDisplayAmount()));
66+
finish(copy);
67+
}));
68+
}
69+
70+
private void finish(ItemStack stack) {
71+
int mouseButton = Mouse.getEventButton();
72+
boolean shiftDown = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
73+
writeClientAction(1, buffer -> {
74+
buffer.writeItemStack(stack);
75+
buffer.writeVarInt(mouseButton);
76+
buffer.writeBoolean(shiftDown);
77+
});
6078
}
6179
});
6280
}

0 commit comments

Comments
 (0)