aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java
diff options
context:
space:
mode:
authormiozune <miozune@gmail.com>2022-11-26 01:45:28 +0900
committerGitHub <noreply@github.com>2022-11-25 17:45:28 +0100
commit9a2741128a78bb52eba50a631126e090a5a2abd8 (patch)
treea90f47aa94951acb4050e45dc3ed60698e79cf32 /src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java
parent51537482fefc4f9c6d3fbd93d119c333a63dcd7b (diff)
downloadGT5-Unofficial-9a2741128a78bb52eba50a631126e090a5a2abd8.tar.gz
GT5-Unofficial-9a2741128a78bb52eba50a631126e090a5a2abd8.tar.bz2
GT5-Unofficial-9a2741128a78bb52eba50a631126e090a5a2abd8.zip
Rewrite GUIs with ModularUI (#1381)
* Base work for ModularUI compat * Remove useless interface * Add almost all the widgets * Invert method * Refactor NEI stack placement positions * NEI handlers on ModularUI * Add some more docs * AdvDebugStructureWriter * Fix NEI progressbar not working * PrimitiveBlastFurnace * clean * derp * clean * spotlessApply * Boilers * Buffers * clean * N by N slots containers * Fix boilers not having bucket interaction Put opening UI to individual MetaTEs * Maintenance Hatch * clean * spotlessApply * Add dependency * IndustrialApiary * Adapt to ModularUI change * Base work for covers & fix crash with MP * Fix crash with server * Rewrite base work for covers * Send initial cover data on cover GUI open so that the time of showing incorrect data will be eliminated * Covers part 1 * Rename package: ModularUI -> modularui * Rename class: GT_UIInfo -> GT_UIInfos * Fix build * Covers part2 * Fix missing client check with tile UI & fix title overlap * CoverTabLine * Move cover window creators to inner class * Fix crash with null base TE * Close GUI when tile is broken * Color cover window with tile colorization * Change signature of addUIWidgets * FluidFilter cover, FluidDisplaySlotWidget, BasicTank, BasicGenerator, Output Hatch, MicrowaveEnergyTransmitter, Teleporter, DigitalChest, DigitalTank * Add title tab * Move package: modularui -> modularui/widget * Programmed circuit + IConfigurationCircuitSupport * clean * VolumetricFlask * Remove integrated circuit overlay from recipe input slots * Input Hatch & Quadruple Input Hatch * Multiblock * Deprecate old cover GUI * BasicMachines * Finish BasicMachine & NEI * Expand DTPF NEI to 9 slots * Fix ME input bus on MP * Move AESlotWidget to public class * Move GT_Recipe_Map constructors with mNEIUnificateOutput to setter method * Move SteamTexture.Variant to outer enum * Switch to remote repository * oops * Update MUI * Update MUI * Minor refactor for change amount buttons * Display items and fluids that exceed usual count * blah * use +=, why didn't I do this * Update MUI * Move ModularUI to Base (#1510) * Move ModularUI to Base * Move most of the ModularUI functionality to `BaseTileEntity` (and `CoverableTileEntity`) * `CommonMetaTileEntity` delegates ato the MetaTileEntity * Added several interfaces (with defaults) to indicate if a tile/metatile override/implement certain behaviors. * Moved `IConfigurationCircuitSupport` interface such that it will work with BaseTileEntity or a MetaTileEntity * Address reviews Co-authored-by: miozune <miozune@gmail.com> * Update MUI * Minor changes to NEI * Return :facepalm: * IGetTabIconSet override * Some more changes to NEI * Merge texture getter interfaces to new class GUITextureSet * Remove BBF structure picture as it's auto-buildable now * Make unified title tab style of texture angular * Expose some boiler texture getters for addon * Fix crash with cover GUI on pipe * Lower the number of recipe per page for DTPF & update MUI * Update MUI * Fix crash with middle-clicking slot on circuit selection GUI * Fix circuit selection window not syncing item from base machine * Merge GT_NEI_AssLineHandler into GT_NEI_DefaultHandler * Update MUI * Add in TecTech multi message * Allow changing the way of binding player inventory * Update MUI * Update MUI * Update MUI * Update MUI * Update MUI * Make MUI non-transitive to allow addons to use their own version * Force enable mixin * Format fluid amount tooltip * Add GUITextureSet.STEAM * Add guard against null ModularWindow creation * Add constructors for Muffler Hatch with inventory * Fix output slot on digital chest and tank allowing insertion * Don't log null ModularWindow * Add default implementation for IHasWorldObjectAndCoords#openGUI * Make openGTTileEntityUI accept MultiTE & cleanup Co-authored-by: Jason Mitchell <mitchej@gmail.com>
Diffstat (limited to 'src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java')
-rw-r--r--src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java474
1 files changed, 474 insertions, 0 deletions
diff --git a/src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java b/src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java
new file mode 100644
index 0000000000..99200e5e8d
--- /dev/null
+++ b/src/main/java/gregtech/common/gui/modularui/widget/FluidDisplaySlotWidget.java
@@ -0,0 +1,474 @@
+package gregtech.common.gui.modularui.widget;
+
+import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import gregtech.GT_Mod;
+import gregtech.api.interfaces.IFluidAccess;
+import gregtech.api.interfaces.IHasFluidDisplayItem;
+import gregtech.api.interfaces.metatileentity.IFluidLockable;
+import gregtech.api.util.GT_Utility;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Supplier;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidContainerItem;
+
+public class FluidDisplaySlotWidget extends SlotWidget {
+
+ private IHasFluidDisplayItem iHasFluidDisplay;
+ private Supplier<IFluidAccess> fluidAccessConstructor;
+ private Supplier<Boolean> canDrainGetter;
+ private Supplier<Boolean> canFillGetter;
+ private Action actionRealClick = Action.NONE;
+ private Action actionDragAndDrop = Action.NONE;
+ private BiFunction<ClickData, FluidDisplaySlotWidget, Boolean> beforeRealClick;
+ private BiFunction<ClickData, FluidDisplaySlotWidget, Boolean> beforeDragAndDrop;
+ private Runnable updateFluidDisplayItem = () -> {
+ if (iHasFluidDisplay != null) {
+ iHasFluidDisplay.updateFluidDisplayItem();
+ }
+ };
+
+ public FluidDisplaySlotWidget(BaseSlot slot) {
+ super(slot);
+ setAccess(false, false);
+ disableShiftInsert();
+ }
+
+ public FluidDisplaySlotWidget(IItemHandlerModifiable handler, int index) {
+ this(new BaseSlot(handler, index, true));
+ }
+
+ // === client actions ===
+
+ @Override
+ public ClickResult onClick(int buttonId, boolean doubleClick) {
+ if (actionRealClick == Action.NONE) return ClickResult.REJECT;
+ if (interactionDisabled) return ClickResult.REJECT;
+
+ /*
+ * While a logical client don't really need to process fluid cells upon click (it could have just wait
+ * for server side to send the result), doing so would result in every fluid interaction having a
+ * noticeable delay between clicking and changes happening even on single player.
+ * I'd imagine this lag to become only more severe when playing MP over ethernet, which would have much more latency
+ * than a memory connection
+ */
+ ClickData clickData = ClickData.create(buttonId, doubleClick);
+ ItemStack verifyToken = executeRealClick(clickData);
+ syncToServer(2, buffer -> {
+ clickData.writeToPacket(buffer);
+ try {
+ buffer.writeItemStackToBuffer(verifyToken);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ return ClickResult.ACCEPT;
+ }
+
+ @Override
+ public boolean handleDragAndDrop(ItemStack draggedStack, int button) {
+ if (actionDragAndDrop == Action.NONE || actionDragAndDrop == Action.TRANSFER) return false;
+ if (interactionDisabled) return false;
+
+ ClickData clickData = ClickData.create(button, false);
+ executeDragAndDrop(clickData, draggedStack);
+ syncToServer(5, buffer -> {
+ try {
+ clickData.writeToPacket(buffer);
+ buffer.writeItemStackToBuffer(draggedStack);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ draggedStack.stackSize = 0;
+ return true;
+ }
+
+ @Override
+ public List<String> getExtraTooltip() {
+ return Collections.emptyList();
+ }
+
+ // === server actions ===
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) throws IOException {
+ if (id == 1) {
+ getMcSlot().xDisplayPosition = buf.readVarIntFromBuffer();
+ getMcSlot().yDisplayPosition = buf.readVarIntFromBuffer();
+ } else if (id == 2) {
+ onClickServer(ClickData.readPacket(buf), buf.readItemStackFromBuffer());
+ } else if (id == 3) {
+ phantomScroll(buf.readVarIntFromBuffer());
+ } else if (id == 4) {
+ setEnabled(buf.readBoolean());
+ } else if (id == 5) {
+ executeDragAndDrop(ClickData.readPacket(buf), buf.readItemStackFromBuffer());
+ if (onDragAndDropComplete != null) {
+ onDragAndDropComplete.accept(this);
+ }
+ }
+ markForUpdate();
+ }
+
+ private void onClickServer(ClickData clickData, ItemStack clientVerifyToken) {
+ ItemStack serverVerifyToken = executeRealClick(clickData);
+ // similar to what NetHandlerPlayServer#processClickWindow does
+ if (!ItemStack.areItemStacksEqual(clientVerifyToken, serverVerifyToken)) {
+ ((EntityPlayerMP) getContext().getPlayer())
+ .sendContainerToPlayer(getContext().getContainer());
+ }
+ }
+
+ // === client/server actions ===
+
+ private ItemStack executeRealClick(ClickData clickData) {
+ if (actionRealClick == Action.NONE) return null;
+ if (beforeRealClick != null && !beforeRealClick.apply(clickData, this)) return null;
+
+ ItemStack ret = null;
+ if (actionRealClick == Action.TRANSFER) {
+ if (fluidAccessConstructor == null) {
+ GT_Mod.GT_FML_LOGGER.warn(
+ "FluidDisplaySlotWidget is asked to transfer fluid, but fluidAccessConstructor is null!");
+ return null;
+ }
+ ret = transferFluid(
+ fluidAccessConstructor.get(),
+ getContext().getPlayer(),
+ clickData.mouseButton == 0,
+ canDrainGetter != null ? canDrainGetter.get() : true,
+ canFillGetter != null ? canFillGetter.get() : true);
+ } else if (actionRealClick == Action.LOCK) {
+ lockFluid(getContext().getPlayer().inventory.getItemStack());
+ }
+
+ updateFluidDisplayItem.run();
+ return ret;
+ }
+
+ protected ItemStack transferFluid(
+ IFluidAccess aFluidAccess,
+ EntityPlayer aPlayer,
+ boolean aProcessFullStack,
+ boolean aCanDrain,
+ boolean aCanFill) {
+ ItemStack tStackHeld = aPlayer.inventory.getItemStack();
+ ItemStack tStackSizedOne = GT_Utility.copyAmount(1, tStackHeld);
+ if (tStackSizedOne == null || tStackHeld.stackSize == 0) return null;
+ FluidStack tInputFluid = aFluidAccess.get();
+ FluidStack tFluidHeld = GT_Utility.getFluidForFilledItem(tStackSizedOne, true);
+ if (tFluidHeld != null && tFluidHeld.amount <= 0) tFluidHeld = null;
+ if (tInputFluid == null) {
+ // tank empty, consider fill only from now on
+ if (!aCanFill)
+ // cannot fill and nothing to take, bail out
+ return null;
+ if (tFluidHeld == null)
+ // no fluid to fill
+ return null;
+ return fillFluid(aFluidAccess, aPlayer, tFluidHeld, aProcessFullStack);
+ }
+ // tank not empty, both action possible
+ if (tFluidHeld != null && tInputFluid.amount < aFluidAccess.getCapacity()) {
+ // both nonnull and have space left for filling.
+ if (aCanFill)
+ // actually both pickup and fill is reasonable, but I'll go with fill here
+ return fillFluid(aFluidAccess, aPlayer, tFluidHeld, aProcessFullStack);
+ if (!aCanDrain)
+ // cannot take AND cannot fill, why make this call then?
+ return null;
+ // the slot does not allow filling, so try take some
+ return drainFluid(aFluidAccess, aPlayer, aProcessFullStack);
+ } else {
+ // cannot fill and there is something to take
+ if (!aCanDrain)
+ // but the slot does not allow taking, so bail out
+ return null;
+ return drainFluid(aFluidAccess, aPlayer, aProcessFullStack);
+ }
+ }
+
+ protected static ItemStack drainFluid(IFluidAccess aFluidAccess, EntityPlayer aPlayer, boolean aProcessFullStack) {
+ FluidStack tTankStack = aFluidAccess.get();
+ if (tTankStack == null) return null;
+ ItemStack tStackHeld = aPlayer.inventory.getItemStack();
+ ItemStack tStackSizedOne = GT_Utility.copyAmount(1, tStackHeld);
+ if (tStackSizedOne == null || tStackHeld.stackSize == 0) return null;
+ int tOriginalFluidAmount = tTankStack.amount;
+ ItemStack tFilledContainer = GT_Utility.fillFluidContainer(tTankStack, tStackSizedOne, true, false);
+ if (tFilledContainer == null && tStackSizedOne.getItem() instanceof IFluidContainerItem) {
+ IFluidContainerItem tContainerItem = (IFluidContainerItem) tStackSizedOne.getItem();
+ int tFilledAmount = tContainerItem.fill(tStackSizedOne, tTankStack, true);
+ if (tFilledAmount > 0) {
+ tFilledContainer = tStackSizedOne;
+ tTankStack.amount -= tFilledAmount;
+ }
+ }
+ if (tFilledContainer != null) {
+ if (aProcessFullStack) {
+ int tFilledAmount = tOriginalFluidAmount - tTankStack.amount;
+ /*
+ work out how many more items we can fill
+ one cell is already used, so account for that
+ the round down behavior will left over a fraction of a cell worth of fluid
+ the user then get to decide what to do with it
+ it will not be too fancy if it spills out partially filled cells
+ */
+ int tAdditionalParallel = Math.min(tStackHeld.stackSize - 1, tTankStack.amount / tFilledAmount);
+ tTankStack.amount -= tFilledAmount * tAdditionalParallel;
+ tFilledContainer.stackSize += tAdditionalParallel;
+ }
+ replaceCursorItemStack(aPlayer, tFilledContainer);
+ }
+ aFluidAccess.verifyFluidStack();
+ return tFilledContainer;
+ }
+
+ protected static ItemStack fillFluid(
+ IFluidAccess aFluidAccess, EntityPlayer aPlayer, FluidStack aFluidHeld, boolean aProcessFullStack) {
+ // we are not using aMachine.fill() here any more, so we need to check for fluid type here ourselves
+ if (aFluidAccess.get() != null && !aFluidAccess.get().isFluidEqual(aFluidHeld)) return null;
+ ItemStack tStackHeld = aPlayer.inventory.getItemStack();
+ ItemStack tStackSizedOne = GT_Utility.copyAmount(1, tStackHeld);
+ if (tStackSizedOne == null) return null;
+
+ int tFreeSpace = aFluidAccess.getCapacity() - (aFluidAccess.get() != null ? aFluidAccess.get().amount : 0);
+ if (tFreeSpace <= 0)
+ // no space left
+ return null;
+
+ // find out how much fluid can be taken
+ // some cells cannot be partially filled
+ ItemStack tStackEmptied = null;
+ int tAmountTaken = 0;
+ if (tFreeSpace >= aFluidHeld.amount) {
+ // fully accepted - try take it from item now
+ // IFluidContainerItem is intentionally not checked here. it will be checked later
+ tStackEmptied = GT_Utility.getContainerForFilledItem(tStackSizedOne, false);
+ tAmountTaken = aFluidHeld.amount;
+ }
+ if (tStackEmptied == null && tStackSizedOne.getItem() instanceof IFluidContainerItem) {
+ // either partially accepted, or is IFluidContainerItem
+ IFluidContainerItem container = (IFluidContainerItem) tStackSizedOne.getItem();
+ FluidStack tDrained = container.drain(tStackSizedOne, tFreeSpace, true);
+ if (tDrained != null && tDrained.amount > 0) {
+ // something is actually drained - change the cell and drop it to player
+ tStackEmptied = tStackSizedOne;
+ tAmountTaken = tDrained.amount;
+ }
+ }
+ if (tStackEmptied == null)
+ // somehow the cell refuse to give out that amount of fluid, no op then
+ return null;
+
+ // find out how many fill can we do
+ // same round down behavior as above
+ // however here the fluid stack is not changed at all, so the exact code will slightly differ
+ int tParallel = aProcessFullStack ? Math.min(tFreeSpace / tAmountTaken, tStackHeld.stackSize) : 1;
+ if (aFluidAccess.get() == null) {
+ FluidStack tNewFillableStack = aFluidHeld.copy();
+ tNewFillableStack.amount = tAmountTaken * tParallel;
+ aFluidAccess.set(tNewFillableStack);
+ } else {
+ aFluidAccess.addAmount(tAmountTaken * tParallel);
+ }
+ tStackEmptied.stackSize = tParallel;
+ replaceCursorItemStack(aPlayer, tStackEmptied);
+ return tStackEmptied;
+ }
+
+ protected static void replaceCursorItemStack(EntityPlayer aPlayer, ItemStack tStackResult) {
+ int tStackResultMaxStackSize = tStackResult.getMaxStackSize();
+ while (tStackResult.stackSize > tStackResultMaxStackSize) {
+ aPlayer.inventory.getItemStack().stackSize -= tStackResultMaxStackSize;
+ GT_Utility.addItemToPlayerInventory(aPlayer, tStackResult.splitStack(tStackResultMaxStackSize));
+ }
+ if (aPlayer.inventory.getItemStack().stackSize == tStackResult.stackSize) {
+ // every cell is mutated. it could just stay on the cursor.
+ aPlayer.inventory.setItemStack(tStackResult);
+ } else {
+ // some cells not mutated. The mutated cells must go into the inventory
+ // or drop into the world if there isn't enough space.
+ ItemStack tStackHeld = aPlayer.inventory.getItemStack();
+ tStackHeld.stackSize -= tStackResult.stackSize;
+ GT_Utility.addItemToPlayerInventory(aPlayer, tStackResult);
+ }
+ }
+
+ protected void executeDragAndDrop(ClickData clickData, ItemStack draggedStack) {
+ if (actionDragAndDrop == Action.NONE || actionDragAndDrop == Action.TRANSFER) return;
+ if (beforeDragAndDrop != null && !beforeDragAndDrop.apply(clickData, this)) return;
+
+ if (actionDragAndDrop == Action.LOCK) {
+ lockFluid(draggedStack);
+ }
+ updateFluidDisplayItem.run();
+ }
+
+ protected void lockFluid(ItemStack cursorStack) {
+ if (!(iHasFluidDisplay instanceof IFluidLockable)) return;
+ IFluidLockable mteToLock = (IFluidLockable) iHasFluidDisplay;
+
+ if (cursorStack == null) {
+ if (!mteToLock.allowChangingLockedFluid(null)) return;
+
+ mteToLock.lockFluid(false);
+ mteToLock.setLockedFluidName(null);
+ GT_Utility.sendChatToPlayer(getContext().getPlayer(), GT_Utility.trans("300.1", "Fluid Lock Cleared."));
+
+ if (!isClient()) {
+ mteToLock.onFluidLockPacketReceived(null);
+ }
+ } else {
+ FluidStack fluidStack = GT_Utility.getFluidFromContainerOrFluidDisplay(cursorStack);
+ if (fluidStack == null) return;
+ Fluid tFluid = fluidStack.getFluid();
+ if (tFluid == null) return;
+
+ if (!mteToLock.allowChangingLockedFluid(tFluid.getName())) return;
+
+ mteToLock.lockFluid(true);
+ mteToLock.setLockedFluidName(tFluid.getName());
+ GT_Utility.sendChatToPlayer(
+ getContext().getPlayer(),
+ String.format(
+ GT_Utility.trans("151.4", "Successfully locked Fluid to %s"),
+ new FluidStack(tFluid, 1).getLocalizedName()));
+
+ if (!isClient()) {
+ mteToLock.onFluidLockPacketReceived(tFluid.getName());
+ }
+ }
+ }
+
+ protected void updateFluidDisplayItem() {
+ if (iHasFluidDisplay != null) {
+ iHasFluidDisplay.updateFluidDisplayItem();
+ }
+ }
+
+ // === setters ===
+
+ public FluidDisplaySlotWidget setFluidAccessConstructor(Supplier<IFluidAccess> fluidAccessConstructor) {
+ this.fluidAccessConstructor = fluidAccessConstructor;
+ return this;
+ }
+
+ public FluidDisplaySlotWidget setIHasFluidDisplay(IHasFluidDisplayItem iHasFluidDisplay) {
+ this.iHasFluidDisplay = iHasFluidDisplay;
+ return this;
+ }
+
+ public FluidDisplaySlotWidget setCanDrainGetter(Supplier<Boolean> canDrainGetter) {
+ this.canDrainGetter = canDrainGetter;
+ return this;
+ }
+
+ public FluidDisplaySlotWidget setCanDrain(boolean canDrain) {
+ return setCanDrainGetter(() -> canDrain);
+ }
+
+ public FluidDisplaySlotWidget setCanFillGetter(Supplier<Boolean> canFillGetter) {
+ this.canFillGetter = canFillGetter;
+ return this;
+ }
+
+ public FluidDisplaySlotWidget setCanFill(boolean canFill) {
+ return setCanFillGetter(() -> canFill);
+ }
+
+ /**
+ * Sets action called on click while holding the real item.
+ */
+ public FluidDisplaySlotWidget setActionRealClick(Action actionRealClick) {
+ this.actionRealClick = actionRealClick;
+ return this;
+ }
+
+ /**
+ * Sets action called on drag-and-drop from NEI.
+ * You can't use {@link Action#TRANSFER} here.
+ */
+ public FluidDisplaySlotWidget setActionDragAndDrop(Action actionDragAndDrop) {
+ this.actionDragAndDrop = actionDragAndDrop;
+ return this;
+ }
+
+ /**
+ * Sets function called before {@link #executeRealClick}.
+ * @param beforeRealClick (click data, this widget) -> if allow click
+ */
+ public FluidDisplaySlotWidget setBeforeRealClick(
+ BiFunction<ClickData, FluidDisplaySlotWidget, Boolean> beforeRealClick) {
+ this.beforeRealClick = beforeRealClick;
+ return this;
+ }
+
+ /**
+ * Sets function called before {@link #executeDragAndDrop}.
+ * @param beforeDragAndDrop (click data, this widget) -> if allow click
+ */
+ public FluidDisplaySlotWidget setBeforeDragAndDrop(
+ BiFunction<ClickData, FluidDisplaySlotWidget, Boolean> beforeDragAndDrop) {
+ this.beforeDragAndDrop = beforeDragAndDrop;
+ return this;
+ }
+
+ /**
+ * Sets function called before both of {@link #executeRealClick} and {@link #executeDragAndDrop}.
+ * @param beforeClick (click data, this widget) -> if allow click
+ */
+ public FluidDisplaySlotWidget setBeforeClick(BiFunction<ClickData, FluidDisplaySlotWidget, Boolean> beforeClick) {
+ setBeforeRealClick(beforeClick);
+ setBeforeDragAndDrop(beforeClick);
+ return this;
+ }
+
+ /**
+ * By default, this widget runs {@link IHasFluidDisplayItem#updateFluidDisplayItem} after click.
+ * You can specify custom update action with this method.
+ */
+ public FluidDisplaySlotWidget setUpdateFluidDisplayItem(Runnable updateFluidDisplayItem) {
+ this.updateFluidDisplayItem = updateFluidDisplayItem;
+ return this;
+ }
+
+ /**
+ * Action triggered on mouse click or NEI drag-and-drop.
+ */
+ public enum Action {
+
+ /**
+ * Fill/drain fluid into/from the tank.
+ * Uses fluid amount, so drag-and-drop cannot use this mode.
+ */
+ TRANSFER,
+
+ /**
+ * Lock fluid for {@link IFluidLockable}.
+ * Does not use fluid amount.
+ */
+ LOCK,
+
+ /**
+ * Set filter for the tank. (not implemented yet)
+ * Does not use fluid amount.
+ */
+ FILTER,
+
+ /**
+ * Does nothing.
+ */
+ NONE
+ }
+}