diff options
Diffstat (limited to 'src/main/java/gregtech/common/gui/modularui')
15 files changed, 1422 insertions, 0 deletions
diff --git a/src/main/java/gregtech/common/gui/modularui/UIHelper.java b/src/main/java/gregtech/common/gui/modularui/UIHelper.java new file mode 100644 index 0000000000..64d686d350 --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/UIHelper.java @@ -0,0 +1,200 @@ +package gregtech.common.gui.modularui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.math.Pos2d; + +import gregtech.api.enums.SteamVariant; +import gregtech.api.gui.modularui.SteamTexture; +import gregtech.api.recipe.BasicUIProperties; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class UIHelper { + + /** + * Iterates over candidates for slot placement. + */ + @SuppressWarnings("SimplifyStreamApiCallChains") + public static void forEachSlots(ForEachSlot forEachItemInputSlot, ForEachSlot forEachItemOutputSlot, + ForEachSlot forEachSpecialSlot, ForEachSlot forEachFluidInputSlot, ForEachSlot forEachFluidOutputSlot, + IDrawable itemSlotBackground, IDrawable fluidSlotBackground, BasicUIProperties uiProperties, int itemInputCount, + int itemOutputCount, int fluidInputCount, int fluidOutputCount, SteamVariant steamVariant, Pos2d offset) { + List<Pos2d> itemInputPositions = uiProperties.itemInputPositionsGetter.apply(itemInputCount) + .stream() + .map(p -> p.add(offset)) + .collect(Collectors.toList()); + for (int i = 0; i < itemInputPositions.size(); i++) { + forEachItemInputSlot.accept( + i, + getBackgroundsForSlot(itemSlotBackground, uiProperties, false, false, i, false, steamVariant), + itemInputPositions.get(i)); + } + + List<Pos2d> itemOutputPositions = uiProperties.itemOutputPositionsGetter.apply(itemOutputCount) + .stream() + .map(p -> p.add(offset)) + .collect(Collectors.toList()); + for (int i = 0; i < itemOutputPositions.size(); i++) { + forEachItemOutputSlot.accept( + i, + getBackgroundsForSlot(itemSlotBackground, uiProperties, false, true, i, false, steamVariant), + itemOutputPositions.get(i)); + } + + forEachSpecialSlot.accept( + 0, + getBackgroundsForSlot(itemSlotBackground, uiProperties, false, false, 0, true, steamVariant), + uiProperties.specialItemPositionGetter.get() + .add(offset)); + + List<Pos2d> fluidInputPositions = uiProperties.fluidInputPositionsGetter.apply(fluidInputCount) + .stream() + .map(p -> p.add(offset)) + .collect(Collectors.toList()); + for (int i = 0; i < fluidInputPositions.size(); i++) { + forEachFluidInputSlot.accept( + i, + getBackgroundsForSlot(fluidSlotBackground, uiProperties, true, false, i, false, steamVariant), + fluidInputPositions.get(i)); + } + + List<Pos2d> fluidOutputPositions = uiProperties.fluidOutputPositionsGetter.apply(fluidOutputCount) + .stream() + .map(p -> p.add(offset)) + .collect(Collectors.toList()); + for (int i = 0; i < fluidOutputPositions.size(); i++) { + forEachFluidOutputSlot.accept( + i, + getBackgroundsForSlot(fluidSlotBackground, uiProperties, true, true, i, false, steamVariant), + fluidOutputPositions.get(i)); + } + } + + /** + * @return Display positions for GUI, including border (18x18 size) + */ + public static List<Pos2d> getItemInputPositions(int itemInputCount) { + return switch (itemInputCount) { + case 0 -> Collections.emptyList(); + case 1 -> getGridPositions(itemInputCount, 52, 24, 1, 1); + case 2 -> getGridPositions(itemInputCount, 34, 24, 2, 1); + case 3 -> getGridPositions(itemInputCount, 16, 24, 3, 1); + case 4 -> getGridPositions(itemInputCount, 34, 15, 2, 2); + case 5, 6 -> getGridPositions(itemInputCount, 16, 15, 3, 2); + default -> getGridPositions(itemInputCount, 16, 6, 3); + }; + } + + /** + * @return Display positions for GUI, including border (18x18 size) + */ + public static List<Pos2d> getItemOutputPositions(int itemOutputCount) { + return switch (itemOutputCount) { + case 0 -> Collections.emptyList(); + case 1 -> getGridPositions(itemOutputCount, 106, 24, 1, 1); + case 2 -> getGridPositions(itemOutputCount, 106, 24, 2, 1); + case 3 -> getGridPositions(itemOutputCount, 106, 24, 3, 1); + case 4 -> getGridPositions(itemOutputCount, 106, 15, 2, 2); + case 5, 6 -> getGridPositions(itemOutputCount, 106, 15, 3, 2); + default -> getGridPositions(itemOutputCount, 106, 6, 3); + }; + } + + /** + * @return Display position for GUI, including border (18x18 size) + */ + public static Pos2d getSpecialItemPosition() { + return new Pos2d(124, 62); + } + + /** + * @return Display positions for GUI, including border (18x18 size) + */ + public static List<Pos2d> getFluidInputPositions(int fluidInputCount) { + List<Pos2d> results = new ArrayList<>(); + int x = 52; + for (int i = 0; i < fluidInputCount; i++) { + results.add(new Pos2d(x, 62)); + x -= 18; + } + return results; + } + + /** + * @return Display positions for GUI, including border (18x18 size) + */ + public static List<Pos2d> getFluidOutputPositions(int fluidOutputCount) { + List<Pos2d> results = new ArrayList<>(); + int x = 106; + for (int i = 0; i < fluidOutputCount; i++) { + results.add(new Pos2d(x, 62)); + x += 18; + } + return results; + } + + public static List<Pos2d> getGridPositions(int totalCount, int xOrigin, int yOrigin, int xDirMaxCount, + int yDirMaxCount) { + // 18 pixels to get to a new grid for placing an item tile since they are 16x16 and have 1 pixel buffers + // around them. + int distance = 18; + + List<Pos2d> results = new ArrayList<>(); + int count = 0; + loop: for (int j = 0; j < yDirMaxCount; j++) { + for (int i = 0; i < xDirMaxCount; i++) { + if (count >= totalCount) break loop; + results.add(new Pos2d(xOrigin + i * distance, yOrigin + j * distance)); + count++; + } + } + return results; + } + + public static List<Pos2d> getGridPositions(int totalCount, int xOrigin, int yOrigin, int xDirMaxCount) { + return getGridPositions(totalCount, xOrigin, yOrigin, xDirMaxCount, 100); + } + + private static IDrawable[] getBackgroundsForSlot(IDrawable base, BasicUIProperties uiProperties, boolean isFluid, + boolean isOutput, int index, boolean isSpecial, SteamVariant steamVariant) { + IDrawable overlay = getOverlay(uiProperties, isFluid, isOutput, index, isSpecial, steamVariant); + if (overlay != null) { + return new IDrawable[] { base, overlay }; + } else { + return new IDrawable[] { base }; + } + } + + @Nullable + private static IDrawable getOverlay(BasicUIProperties uiProperties, boolean isFluid, boolean isOutput, int index, + boolean isSpecial, SteamVariant steamVariant) { + if (isSpecial && !uiProperties.useSpecialSlot) { + return null; + } + if (steamVariant != SteamVariant.NONE) { + SteamTexture steamTexture = uiProperties.getOverlayForSlotSteam(index, isFluid, isOutput, isSpecial); + if (steamTexture != null) { + return steamTexture.get(steamVariant); + } else { + return null; + } + } else { + return uiProperties.getOverlayForSlot(index, isFluid, isOutput, isSpecial); + } + } + + @FunctionalInterface + public interface ForEachSlot { + + void accept(int index, IDrawable[] backgrounds, Pos2d pos); + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/uifactory/SelectItemUIFactory.java b/src/main/java/gregtech/common/gui/modularui/uifactory/SelectItemUIFactory.java new file mode 100644 index 0000000000..452ca36c0c --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/uifactory/SelectItemUIFactory.java @@ -0,0 +1,229 @@ +package gregtech.common.gui.modularui.uifactory; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.StatCollector; + +import com.gtnewhorizons.modularui.api.GlStateManager; +import com.gtnewhorizons.modularui.api.ModularUITextures; +import com.gtnewhorizons.modularui.api.drawable.GuiHelper; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.ItemDrawable; +import com.gtnewhorizons.modularui.api.drawable.Text; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import gregtech.api.enums.Dyes; +import gregtech.api.gui.GT_GUIColorOverride; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; + +/** + * Creates UI for selecting item from given list. This is client-only UI to allow using client-preferred settings. + */ +public class SelectItemUIFactory { + + private final String header; + private final ItemStack headerItem; + public static final int UNSELECTED = -1; + private static final int cols = 9; + private final Consumer<ItemStack> selectedCallback; + // passed in stack + private final List<ItemStack> stacks; + private final boolean noDeselect; + private int selected; + private boolean anotherWindow = false; + private AtomicBoolean dialogOpened; + private int guiTint = GT_Util.getRGBInt(Dyes.MACHINE_METAL.getRGBA()); + private Supplier<ItemStack> currentGetter; + + private final GT_GUIColorOverride colorOverride = GT_GUIColorOverride.get("SelectItemUIFactory"); + + private int getTextColorOrDefault(String textType, int defaultColor) { + return colorOverride.getTextColorOrDefault(textType, defaultColor); + } + + private final Supplier<Integer> COLOR_TITLE = () -> getTextColorOrDefault("title", 0x222222); + private final Supplier<Integer> COLOR_TEXT_GRAY = () -> getTextColorOrDefault("text_gray", 0x555555); + + public SelectItemUIFactory(String header, ItemStack headerItem, Consumer<ItemStack> selectedCallback, + List<ItemStack> stacks) { + this(header, headerItem, selectedCallback, stacks, UNSELECTED); + } + + public SelectItemUIFactory(String header, ItemStack headerItem, Consumer<ItemStack> selectedCallback, + List<ItemStack> stacks, int selected) { + this(header, headerItem, selectedCallback, stacks, selected, false); + } + + /** + * Constructor for a dialog to select an item from given list. Given callback may be called zero or more times + * depending on user action. + * + * @param header Header text + * @param headerItem ItemStack to use as Dialog icon + * @param selectedCallback callback upon selected + * @param stacks list to choose from + * @param selected preselected item. Use {@link #UNSELECTED} for unselected. Invalid selected will be + * clamped to 0 or highest index + * @param noDeselect true if player cannot deselect, false otherwise. If this is set to true, selectedCallback + * is guaranteed to be called with a nonnull stack + */ + public SelectItemUIFactory(String header, ItemStack headerItem, Consumer<ItemStack> selectedCallback, + List<ItemStack> stacks, int selected, boolean noDeselect) { + this.header = header; + this.headerItem = headerItem; + this.selectedCallback = selectedCallback; + this.stacks = stacks; + this.noDeselect = noDeselect; + this.selected = noDeselect ? Math.max(0, selected) : selected; + } + + /** + * @param anotherWindow If UI is shown on top of another window + * @param dialogOpened Flag to store whether this UI is opened and hence it should block duplicated creation of + * this UI + */ + public SelectItemUIFactory setAnotherWindow(boolean anotherWindow, AtomicBoolean dialogOpened) { + this.anotherWindow = anotherWindow; + this.dialogOpened = dialogOpened; + return this; + } + + public SelectItemUIFactory setGuiTint(int guiTint) { + this.guiTint = guiTint; + return this; + } + + /** + * @param currentGetter Getter for "current" item displayed that may change from external reasons + */ + public SelectItemUIFactory setCurrentGetter(Supplier<ItemStack> currentGetter) { + this.currentGetter = currentGetter; + return this; + } + + public ModularWindow createWindow(UIBuildContext buildContext) { + ModularWindow.Builder builder = ModularWindow + .builder(getGUIWidth(), 53 + 18 * ((stacks.size() - 1) / cols + 1)); + builder.setBackground(ModularUITextures.VANILLA_BACKGROUND); + builder.setGuiTint(guiTint); + + if (headerItem != null) { + builder.widget( + new ItemDrawable(headerItem).asWidget() + .setPos(5, 5) + .setSize(16, 16)); + } + builder.widget( + new TextWidget(header).setDefaultColor(COLOR_TITLE.get()) + .setPos(25, 9)); + + int currentSlotX = 9 + + getFontRenderer().getStringWidth(StatCollector.translateToLocal("GT5U.gui.select.current")); + int currentSlotY = 24; + builder.widget(new DrawableWidget() { + + @Override + public void onScreenUpdate() { + super.onScreenUpdate(); + if (currentGetter != null) { + ItemStack current = currentGetter.get(); + selected = GT_Utility.findMatchingStackInList(stacks, current); + } + } + }.setDrawable(GT_UITextures.SLOT_DARK_GRAY) + .setPos(currentSlotX, currentSlotY) + .setSize(18, 18)) + .widget( + new ItemDrawable(() -> getCandidate(getSelected())).asWidgetWithTooltip() + .setPos(currentSlotX + 1, currentSlotY + 1)); + builder.widget( + new TextWidget(StatCollector.translateToLocal("GT5U.gui.select.current")) + .setDefaultColor(COLOR_TEXT_GRAY.get()) + .setPos(8, 25 + (18 - getFontRenderer().FONT_HEIGHT) / 2)); + + for (int i = 0; i < stacks.size(); i++) { + final int index = i; + builder.widget(new ButtonWidget() { + + @Override + public void draw(float partialTicks) { + GlStateManager.pushMatrix(); + // so that item z levels are properly ordered + GlStateManager.translate(0, 0, 150 * getWindowLayer()); + new ItemDrawable(stacks.get(index)).draw(1, 1, 16, 16, partialTicks); + GlStateManager.popMatrix(); + } + }.setOnClick((clickData, widget) -> { + if (clickData.mouseButton == 0) { + setSelected(index); + } else { + setSelected(UNSELECTED); + } + selectedCallback.accept(getCandidate(getSelected())); + }) + .setSynced(false, false) + .dynamicTooltip(() -> GuiHelper.getItemTooltip(stacks.get(index))) + .setUpdateTooltipEveryTick(true) + .setBackground( + () -> new IDrawable[] { + index == selected ? GT_UITextures.SLOT_DARK_GRAY : ModularUITextures.ITEM_SLOT, }) + .setPos(7 + 18 * (index % cols), 43 + 18 * (index / cols)) + .setSize(18, 18)); + } + + if (anotherWindow) { + dialogOpened.set(true); + builder.widget(new ButtonWidget() { + + @Override + public void onDestroy() { + dialogOpened.set(false); + } + }.setOnClick( + (clickData, widget) -> widget.getWindow() + .tryClose()) + .setSynced(false, false) + .setBackground(ModularUITextures.VANILLA_BACKGROUND, new Text("x")) + .setPos(getGUIWidth() - 15, 3) + .setSize(12, 12)); + } + + return builder.build(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + if (selected == this.selected) return; + int newSelected = GT_Utility.clamp(selected, UNSELECTED, stacks.size() - 1); + if (noDeselect && newSelected == UNSELECTED) return; + + this.selected = newSelected; + } + + private ItemStack getCandidate(int listIndex) { + return listIndex < 0 || listIndex >= stacks.size() ? null : stacks.get(listIndex); + } + + private FontRenderer getFontRenderer() { + return Minecraft.getMinecraft().fontRenderer; + } + + private int getGUIWidth() { + return 176; + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java b/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java new file mode 100644 index 0000000000..368f219ef4 --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/widget/AESlotWidget.java @@ -0,0 +1,64 @@ +package gregtech.common.gui.modularui.widget; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; +import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; + +import appeng.api.storage.IItemDisplayRegistry.ItemRenderHook; +import appeng.client.render.AppEngRenderItem; +import appeng.core.AELog; +import appeng.util.Platform; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class AESlotWidget extends SlotWidget { + + private static class HookHolder { + + static ItemRenderHook SKIP_ITEM_STACK_SIZE_HOOK = new ItemRenderHook() { + + @Override + public boolean renderOverlay(FontRenderer fr, TextureManager tm, ItemStack is, int x, int y) { + return true; + } + + @Override + public boolean showStackSize(ItemStack is) { + return false; + } + }; + } + + public AESlotWidget(BaseSlot slot) { + super(slot); + } + + @Override + @SideOnly(Side.CLIENT) + protected void drawSlot(Slot slotIn) { + final AppEngRenderItem aeRenderItem = new AppEngRenderItem(); + AppEngRenderItem.POST_HOOKS.add(HookHolder.SKIP_ITEM_STACK_SIZE_HOOK); + final RenderItem pIR = this.setItemRender(aeRenderItem); + try { + aeRenderItem.setAeStack(Platform.getAEStackInSlot(slotIn)); + super.drawSlot(slotIn, true); + } catch (final Exception err) { + AELog.warn("[AppEng] AE prevented crash while drawing slot: " + err); + } + AppEngRenderItem.POST_HOOKS.remove(HookHolder.SKIP_ITEM_STACK_SIZE_HOOK); + this.setItemRender(pIR); + } + + @SideOnly(Side.CLIENT) + private RenderItem setItemRender(final RenderItem item) { + final RenderItem ri = ModularGui.getItemRenderer(); + ModularGui.setItemRenderer(item); + return ri; + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/CheckRecipeResultSyncer.java b/src/main/java/gregtech/common/gui/modularui/widget/CheckRecipeResultSyncer.java new file mode 100644 index 0000000000..9028c66be8 --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/widget/CheckRecipeResultSyncer.java @@ -0,0 +1,26 @@ +package gregtech.common.gui.modularui.widget; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; + +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; + +public class CheckRecipeResultSyncer extends FakeSyncWidget<CheckRecipeResult> { + + public CheckRecipeResultSyncer(Supplier<CheckRecipeResult> getter, Consumer<CheckRecipeResult> setter) { + super(getter, setter, (buffer, result) -> { + NetworkUtils.writeStringSafe(buffer, result.getID()); + result.encode(buffer); + }, buffer -> { + String id = NetworkUtils.readStringSafe(buffer); + CheckRecipeResult result = CheckRecipeResultRegistry.getSampleFromRegistry(id) + .newInstance(); + result.decode(buffer); + return result; + }); + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/CoverCycleButtonWidget.java b/src/main/java/gregtech/common/gui/modularui/widget/CoverCycleButtonWidget.java new file mode 100644 index 0000000000..4d1c6649ae --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/widget/CoverCycleButtonWidget.java @@ -0,0 +1,91 @@ +package gregtech.common.gui.modularui.widget; + +import org.lwjgl.opengl.GL11; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.gui.modularui.GT_UITextures; + +/** + * Fires click action on mouse release, not on press. Draws different backgrounds depending on whether the mouse is + * being pressed or the widget is hovered. + */ +public class CoverCycleButtonWidget extends CycleButtonWidget { + + private static final UITexture BUTTON_NORMAL_NOT_PRESSED = GT_UITextures.BUTTON_COVER_NORMAL + .getSubArea(0, 0, 1, 0.5f); + private static final UITexture BUTTON_NORMAL_PRESSED = GT_UITextures.BUTTON_COVER_NORMAL.getSubArea(0, 0.5f, 1, 1); + private static final UITexture BUTTON_HOVERED_NOT_PRESSED = GT_UITextures.BUTTON_COVER_NORMAL_HOVERED + .getSubArea(0, 0, 1, 0.5f); + private static final UITexture BUTTON_HOVERED_PRESSED = GT_UITextures.BUTTON_COVER_NORMAL_HOVERED + .getSubArea(0, 0.5f, 1, 1); + + private boolean clickPressed; + + private static final int TOOLTIP_DELAY = 5; + + public CoverCycleButtonWidget() { + setSize(16, 16); + setTooltipShowUpDelay(TOOLTIP_DELAY); + } + + @Override + public ClickResult onClick(int buttonId, boolean doubleClick) { + updateState(); + if (!canClick()) return ClickResult.REJECT; + clickPressed = true; + return ClickResult.SUCCESS; + } + + @Override + public boolean onClickReleased(int buttonId) { + clickPressed = false; + updateState(); + if (!isHovering() || !canClick()) return false; + return onClickImpl(buttonId); + } + + protected boolean onClickImpl(int buttonId) { + super.onClick(buttonId, false); + return true; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + protected boolean canClick() { + return true; + } + + @SideOnly(Side.CLIENT) + protected void updateState() {} + + public boolean isClickPressed() { + return clickPressed; + } + + @Override + public void drawBackground(float partialTicks) { + GL11.glColor4f(1, 1, 1, 1); + super.drawBackground(partialTicks); + } + + @Override + public IDrawable[] getBackground() { + if (isHovering()) { + if (clickPressed) { + return new IDrawable[] { BUTTON_HOVERED_PRESSED }; + } else { + return new IDrawable[] { BUTTON_HOVERED_NOT_PRESSED }; + } + } else { + if (clickPressed) { + return new IDrawable[] { BUTTON_NORMAL_PRESSED }; + } else { + return new IDrawable[] { BUTTON_NORMAL_NOT_PRESSED }; + } + } + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/CoverDataControllerWidget.java b/src/main/java/gregtech/common/gui/modularui/widget/CoverDataControllerWidget.java new file mode 100644 index 0000000000..4543980ce1 --- /dev/null +++ b/src/main/java/gregtech/common/gui/modularui/widget/CoverDataControllerWidget.java @@ -0,0 +1,133 @@ +package gregtech.common.gui.modularui.widget; + +import java.io.IOException; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import net.minecraft.network.PacketBuffer; + +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; + +import gregtech.api.gui.modularui.IDataFollowerWidget; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.ISerializableObject; + +public class CoverDataControllerWidget<T extends ISerializableObject> extends DataControllerWidget<T> { + + protected final GT_CoverBehaviorBase<T> coverBehavior; + + /** + * @param dataGetter () -> cover data this widget handles + * @param dataSetter data to set -> if setting cover data is successful + * @param coverBehavior cover this widget handles data update + */ + public CoverDataControllerWidget(Supplier<T> dataGetter, Function<T, Boolean> dataSetter, + GT_CoverBehaviorBase<T> coverBehavior) { + super(dataGetter, dataSetter); + this.coverBehavior = coverBehavior; + } + + @Override + public <U, W extends Widget & IDataFollowerWidget<T, U>> CoverDataControllerWidget<T> addFollower(W widget, + Function<T, U> dataToStateGetter, BiFunction<T, U, T> dataUpdater, Consumer<W> applyForWidget) { + super.addFollower(widget, dataToStateGetter, dataUpdater, applyForWidget); + return this; + } + + @Override + protected void writeToPacket(PacketBuffer buffer, T data) { + try { + NetworkUtils.writeNBTBase(buffer, data.saveDataToNBT()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + protected T readFromPacket(PacketBuffer buffer) throws IOException { + return coverBehavior.createDataObject(NetworkUtils.readNBTBase(buffer)); + } + + /** + * Uses int index to determine toggle button behaviors. + */ + public static class CoverDataIndexedControllerWidget_ToggleButtons<T extends ISerializableObject> + extends CoverDataControllerWidget<T> { + + private final BiFunction<Integer, T, Boolean> dataToStateGetter; + private final BiFunction<Integer, T, T> dataUpdater; + + /** + * @param coverDataGetter () -> cover data this widget handles + * @param coverDataSetter data to set -> if setting cover data is successful + * @param coverBehavior cover this widget handles data update + * @param dataToStateGetter (index of button, given cover data) -> button state + * @param dataUpdater (index of button, current cover data) -> new cover data + */ + public CoverDataIndexedControllerWidget_ToggleButtons(Supplier<T> coverDataGetter, + Function<T, Boolean> coverDataSetter, GT_CoverBehaviorBase<T> coverBehavior, + BiFunction<Integer, T, Boolean> dataToStateGetter, BiFunction<Integer, T, T> dataUpdater) { + super(coverDataGetter, coverDataSetter, coverBehavior); + this.dataToStateGetter = dataToStateGetter; + this.dataUpdater = dataUpdater; + } + + /** + * @param index index of widget to add + * @param widget widget to add + * @param applyForWidget methods to call for widget to add + */ + public <W extends CoverDataFollower_ToggleButtonWidget<T>> CoverDataIndexedControllerWidget_ToggleButtons<T> addToggleButton( + int index, W widget, Consumer<CoverDataFollower_ToggleButtonWidget<T>> applyForWidget) { + addFollower( + widget, + data -> dataToStateGetter.apply(index, data), + (data, state) -> dataUpdater.apply(index, data), + applyForWidget); + return this; + } + } + + /** + * Uses int index to determine cycle button behaviors. + */ + public static class CoverDataIndexedControllerWidget_CycleButtons<T extends ISerializableObject> + extends CoverDataControllerWidget<T> { + + private final BiFunction<Integer, T, Integer> dataToStateGetter; + private final BiFunction<Integer, T, T> dataUpdater; + + /** + * @param coverDataGetter () -> cover data this widget handles + * @param coverDataSetter data to set -> if setting cover data is successful + * @param coverBehavior cover this widget handles data update + * @param dataToStateGetter (index of button, given cover data) -> button state + * @param dataUpdater (index of button, current cover data) -> new cover data + */ + public CoverDataIndexedControllerWidget_CycleButtons(Supplier<T> coverDataGetter, + Function<T, Boolean> coverDataSetter, GT_CoverBehaviorBase<T> coverBehavior, + BiFunction<Integer, T, Integer> dataToStateGetter, BiFunction<Integer, T, T> dataUpdater) { + super(coverDataGetter, coverDataSetter, coverBehavior); + this.dataToStateGetter = dataToStateGetter; + this.dataUpdater = dataUpdater; + } + + /** + * @param index index of widget to add + * @param widget widget to add + * @param applyForWidget methods to call for the widget to add + */ + public <W extends CoverDataFollower_CycleButtonWidget<T>> CoverDataIndexedControllerWidget_CycleButtons<T> addCycleButton( + int index, W widget, Consumer<CoverDataFollower_CycleButtonWidget<T>> applyForWidget) { + addFollower( + widget, + data -> dataToStateGetter.apply(index, data), + (data, state) -> dataUpdater.apply(index, data), + applyForWidget); + return this; + } + } +} diff --git a/src/main/java/gregtech/common/gui/modularui/widget/CoverDataFollower_CycleButtonWidget.java b/src/main/java/gregtech/common/gui/modularui/widget/CoverDataFollower_CycleButtonWidget.java new file mode 100644 index 0000000000..ec7d31ca7e --- /dev/null ++ |
