aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/gui/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/gui/widgets')
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java82
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java179
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java162
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java157
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java114
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java43
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java73
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java24
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java27
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java174
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java274
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java121
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java80
-rw-r--r--src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java90
14 files changed, 1600 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java b/src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java
new file mode 100644
index 0000000000..883ffb4079
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java
@@ -0,0 +1,82 @@
+package gregtech.api.gui.widgets;
+
+import static gregtech.api.gui.modularui.GT_UITextures.OVERLAY_BUTTON_HOURGLASS;
+import static gregtech.common.covers.CoverInfo.MAX_TICK_RATE_ADDITION;
+
+import java.util.List;
+
+import net.minecraft.util.StatCollector;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.common.covers.CoverInfo;
+
+public class GT_CoverTickRateButton extends ButtonWidget {
+
+ private static final UITexture BACKGROUND = GT_UITextures.BUTTON_COVER_NORMAL.getSubArea(0, 0, 1, 0.5f);
+
+ private final CoverInfo coverInfo;
+ private int clientTickRate;
+ private int tickRateAddition;
+
+ public GT_CoverTickRateButton(@NotNull CoverInfo coverInfo, @NotNull IWidgetBuilder<?> builder) {
+ this.coverInfo = coverInfo;
+ this.clientTickRate = coverInfo.getTickRate();
+ this.tickRateAddition = coverInfo.getTickRateAddition();
+
+ super.setBackground(BACKGROUND, OVERLAY_BUTTON_HOURGLASS);
+ super.setOnClick(this::onClick);
+ super.dynamicTooltip(this::dynamicTooltip);
+ super.attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(this.coverInfo::getTickRate, integer -> clientTickRate = integer),
+ builder,
+ (widget, aInt) -> notifyTooltipChange())
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(
+ this.coverInfo::getTickRateAddition,
+ integer -> tickRateAddition = integer),
+ builder);
+
+ }
+
+ private void onClick(@NotNull ClickData clickData, @NotNull Widget widget) {
+ final int iterations = clickData.ctrl ? 5 : 1;
+ final boolean isDecreasing = clickData.mouseButton == 1;
+
+ // Do five operations at once if Ctrl is held down. Since the actual increase granted by each invocation can be
+ // different on each call, just call the method several times rather than trying to do a bunch of weird math.
+ for (int i = 0; i < iterations; i++) {
+ coverInfo.adjustTickRateMultiplier(isDecreasing);
+ }
+ }
+
+ private List<String> dynamicTooltip() {
+ final String boundsNotification;
+
+ if (tickRateAddition == 0) {
+ boundsNotification = StatCollector.translateToLocal("gt.cover.info.button.bounds_notification.minimum");
+ } else if (tickRateAddition >= MAX_TICK_RATE_ADDITION - 1) {
+ // Clamping can make tickRateAddition approach but never actually equal MAX_ADDITION, so we need this
+ // adjustment.
+ boundsNotification = StatCollector.translateToLocal("gt.cover.info.button.bounds_notification.maximum");
+ } else {
+ boundsNotification = "";
+ }
+
+ return ImmutableList.of(
+ StatCollector.translateToLocalFormatted(
+ "gt.cover.info.button.tick_rate.1",
+ new CoverInfo.ClientTickRateFormatter(clientTickRate),
+ boundsNotification),
+ StatCollector.translateToLocal("gt.cover.info.button.tick_rate.2"),
+ StatCollector.translateToLocal("gt.cover.info.button.tick_rate.3"));
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java
new file mode 100644
index 0000000000..6f4eb0e2c2
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java
@@ -0,0 +1,179 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+import java.util.List;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.lwjgl.opengl.GL11;
+
+import codechicken.nei.api.API;
+import codechicken.nei.api.INEIGuiAdapter;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.gui.GT_GUIContainerMetaTile_Machine;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.net.GT_Packet_GtTileEntityGuiRequest;
+import gregtech.common.GT_Proxy;
+
+/**
+ * Let's you access a GregTech IGregTechTileEntity's covers as tabs on the GUI's sides
+ */
+public class GT_GuiCoverTabLine extends GT_GuiTabLine {
+
+ // Names of the block a cover could be on
+ private static final String[] SIDES = new String[] { "GT5U.interface.coverTabs.down", "GT5U.interface.coverTabs.up",
+ "GT5U.interface.coverTabs.north", "GT5U.interface.coverTabs.south", "GT5U.interface.coverTabs.west",
+ "GT5U.interface.coverTabs.east" };
+
+ // Not sure if there's a point in JIT translation but that's what this is
+ private final String[] translatedSides;
+ private final IGregTechTileEntity tile;
+ private final int colorization;
+
+ /**
+ * Let's you access an IGregTechTileEntity's covers as tabs on the GUI's sides
+ *
+ * @param gui GT_ITabRenderer gui which this tab line attaches to
+ * @param tabLineLeft left position of the tab line in relation to the gui
+ * @param tabLineTop top position of the tab line in relation to the gui
+ * @param tabHeight height of a tab
+ * @param tabWidth width of a tab
+ * @param tabSpacing pixels between each tab
+ * @param xDir whether to extend the line horizontally to the right (NORMAL), the left (INVERSE) or not at
+ * all (NONE)
+ * @param yDir whether to extend the line vertically down (NORMAL), up (INVERSE) or not at all (NONE)
+ * @param displayMode whether to display on the left side of the GT_ITabRenderer (NORMAL), on it's right side
+ * (INVERSE) or not at all (NONE)
+ * @param tabBackground the set of textures used to draw this tab line's tab backgrounds
+ * @param tile The IGregTechTileEntity the covers of which we are accessing
+ * @param colorization The colorization of the GUI we are adding tabs to
+ */
+ public GT_GuiCoverTabLine(GT_GUIContainerMetaTile_Machine gui, int tabLineLeft, int tabLineTop, int tabHeight,
+ int tabWidth, int tabSpacing, DisplayStyle xDir, DisplayStyle yDir, DisplayStyle displayMode,
+ GT_GuiTabIconSet tabBackground, IGregTechTileEntity tile, int colorization) {
+ super(gui, 6, tabLineLeft, tabLineTop, tabHeight, tabWidth, tabSpacing, xDir, yDir, displayMode, tabBackground);
+ this.tile = tile;
+ this.colorization = colorization;
+ this.translatedSides = new String[6];
+ setupTabs();
+ }
+
+ /**
+ * Add a tab for each existing cover on this IGregTechTileEntity at creation time
+ */
+ private void setupTabs() {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final ItemStack cover = tile.getCoverItemAtSide(side);
+ if (cover != null) {
+ addCoverToTabs(side, cover);
+ }
+ }
+ }
+
+ @Override
+ protected void drawBackground(float parTicks, int mouseX, int mouseY) {
+ // Apply this tile's coloration to draw the background
+ GL11.glColor3ub(
+ (byte) ((colorization >> 16) & 0xFF),
+ (byte) ((colorization >> 8) & 0xFF),
+ (byte) (colorization & 0xFF));
+ super.drawBackground(parTicks, mouseX, mouseY);
+ }
+
+ @Override
+ protected void tabClicked(int tabId, int mouseButton) {
+ if (mouseButton == 0 && mTabs[tabId].enabled) {
+ GT_Values.NW.sendToServer(
+ new GT_Packet_GtTileEntityGuiRequest(
+ this.tile.getXCoord(),
+ this.tile.getYCoord(),
+ this.tile.getZCoord(),
+ tabId + GT_Proxy.GUI_ID_COVER_SIDE_BASE,
+ this.tile.getWorld().provider.dimensionId,
+ Minecraft.getMinecraft().thePlayer.getEntityId(),
+ 0));
+ }
+ }
+
+ /**
+ * Add the cover on this side of the IGregTechTileEntity to the tabs
+ *
+ * @param side side to apply the cover to
+ * @param cover cover to add
+ */
+ private void addCoverToTabs(ForgeDirection side, ItemStack cover) {
+ final boolean enabled = this.tile.getCoverBehaviorAtSideNew(side)
+ .hasCoverGUI();
+ final int ordinalSide = side.ordinal();
+ this.setTab(ordinalSide, cover, null, getTooltipForCoverTab(side, cover, enabled));
+ this.setTabEnabled(ordinalSide, enabled);
+ }
+
+ /**
+ * Decorate the cover's tooltips according to the side it's on and on whether the tab is enabled or not
+ *
+ * @param side side
+ * @param cover cover which tooltip to decorate
+ * @param enabled if the tab is enabled
+ * @return This cover tab's tooltip
+ */
+ private String[] getTooltipForCoverTab(ForgeDirection side, ItemStack cover, boolean enabled) {
+ final List<String> tooltip = cover.getTooltip(Minecraft.getMinecraft().thePlayer, true);
+ tooltip.set(
+ 0,
+ (enabled ? EnumChatFormatting.UNDERLINE : EnumChatFormatting.DARK_GRAY) + getSideDescription(side)
+ + (enabled ? EnumChatFormatting.RESET + ": " : ": " + EnumChatFormatting.RESET)
+ + tooltip.get(0));
+ return tooltip.toArray(new String[0]);
+ }
+
+ /**
+ * Get the translated name for a side of the IGregTechTileEntity
+ *
+ * @param side side of the entity
+ * @return translated name for a side of the IGregTechTileEntity
+ */
+ private String getSideDescription(ForgeDirection side) {
+ final int ordinalSide = side.ordinal();
+ if (ordinalSide < SIDES.length) {
+ if (this.translatedSides[ordinalSide] == null) {
+ this.translatedSides[ordinalSide] = StatCollector.translateToLocal(SIDES[ordinalSide]);
+ }
+ return this.translatedSides[ordinalSide];
+ }
+ return null;
+ }
+
+ /**
+ * Hide any NEI slots that would intersect with a cover tab
+ */
+ static class CoverTabLineNEIHandler extends INEIGuiAdapter {
+
+ @Override
+ public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
+ final Rectangle neiSlotArea = new Rectangle(x, y, w, h);
+ if (gui instanceof GT_GUIContainerMetaTile_Machine) {
+ final GT_GuiTabLine tabLine = ((GT_GUIContainerMetaTile_Machine) gui).coverTabs;
+ if (!tabLine.visible) {
+ return false;
+ }
+ for (int i = 0; i < tabLine.mTabs.length; i++) {
+ if (tabLine.mTabs[i] != null && tabLine.mTabs[i].getBounds()
+ .intersects(neiSlotArea)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ static {
+ API.registerNEIGuiHandler(new CoverTabLineNEIHandler());
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java
new file mode 100644
index 0000000000..9f4287a65b
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java
@@ -0,0 +1,162 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+import java.util.List;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.ItemBlock;
+import net.minecraft.item.ItemStack;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+
+import codechicken.lib.gui.GuiDraw;
+import gregtech.api.interfaces.IGuiScreen;
+import gregtech.api.util.GT_UtilityClient;
+
+public class GT_GuiFakeItemButton implements IGuiScreen.IGuiElement {
+
+ private GT_GuiIcon bgIcon;
+ private ItemStack item;
+ private final IGuiScreen gui;
+ private int xPosition, yPosition;
+ private List<String> itemTooltips;
+ private final GT_GuiTooltip tooltip = new GT_GuiTooltip(null) {
+
+ @Override
+ public List<String> getToolTipText() {
+ return itemTooltips;
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public Rectangle getBounds() {
+ return GT_GuiFakeItemButton.this.getBounds();
+ }
+ };
+ private final Rectangle rectangle;
+ private boolean mimicSlot;
+
+ public GT_GuiFakeItemButton(IGuiScreen gui, int x, int y, GT_GuiIcon bgIcon) {
+ this.gui = gui;
+ this.bgIcon = bgIcon;
+ item = null;
+ rectangle = new Rectangle(x, y, 18, 18);
+ gui.addElement(this);
+ }
+
+ public GT_GuiFakeItemButton setItem(ItemStack i) {
+ item = i;
+ if (getMimicSlot()) updateTooltip();
+ return this;
+ }
+
+ private void updateTooltip() {
+ itemTooltips = item == null ? null : GT_UtilityClient.getTooltip(item, true);
+ }
+
+ public ItemStack getItem() {
+ return item;
+ }
+
+ public GT_GuiFakeItemButton setMimicSlot(boolean mimicSlot) {
+ if (mimicSlot != this.mimicSlot) {
+ if (mimicSlot) {
+ updateTooltip();
+ gui.addToolTip(tooltip);
+ } else {
+ gui.removeToolTip(tooltip);
+ }
+ this.mimicSlot = mimicSlot;
+ }
+ return this;
+ }
+
+ public boolean getMimicSlot() {
+ return mimicSlot;
+ }
+
+ public GT_GuiIcon getBgIcon() {
+ return bgIcon;
+ }
+
+ public GT_GuiFakeItemButton setBgIcon(GT_GuiIcon bgIcon) {
+ this.bgIcon = bgIcon;
+ return this;
+ }
+
+ @Override
+ public void onInit() {
+ xPosition = rectangle.x + gui.getGuiLeft();
+ yPosition = rectangle.y + gui.getGuiTop();
+ }
+
+ @Override
+ public void onRemoved() {
+ if (mimicSlot) gui.removeToolTip(tooltip);
+ }
+
+ @Override
+ public void draw(int mouseX, int mouseY, float parTicks) {
+ GL11.glColor4f(1, 1, 1, 1);
+ GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ if (bgIcon != null) {
+ GT_GuiIcon.render(bgIcon, xPosition - 1, yPosition - 1, 18, 18, 0, true);
+ }
+
+ if (item != null) {
+ if (item.getItem() instanceof ItemBlock) {
+ GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
+ GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+ }
+ gui.getItemRenderer()
+ .renderItemAndEffectIntoGUI(
+ gui.getFontRenderer(),
+ Minecraft.getMinecraft()
+ .getTextureManager(),
+ item,
+ xPosition,
+ yPosition);
+
+ if (item.getItem() instanceof ItemBlock) GL11.glPopAttrib();
+ }
+
+ if (getMimicSlot()) if (getBounds().contains(mouseX - gui.getGuiLeft(), mouseY - gui.getGuiTop())) {
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glColorMask(true, true, true, false);
+ GuiDraw.drawGradientRect(xPosition, yPosition, 16, 16, 0x80ffffff, 0x80ffffff);
+ GL11.glColorMask(true, true, true, true);
+ // no glEnable, state will be recovered by glPopAttrib
+ }
+
+ GL11.glPopAttrib();
+ }
+
+ public Rectangle getBounds() {
+ return rectangle;
+ }
+
+ public void setX(int x) {
+ rectangle.x = x;
+ }
+
+ public void setY(int y) {
+ rectangle.y = y;
+ }
+
+ public void setWidth(int width) {
+ rectangle.width = width;
+ }
+
+ public void setHeight(int height) {
+ rectangle.height = height;
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java
new file mode 100644
index 0000000000..66ab27356e
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java
@@ -0,0 +1,157 @@
+package gregtech.api.gui.widgets;
+
+import static gregtech.api.enums.Mods.GregTech;
+
+import java.util.Arrays;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.util.ResourceLocation;
+
+import gregtech.api.interfaces.IGuiIcon;
+
+public enum GT_GuiIcon implements IGuiIcon {
+
+ BUTTON_NORMAL(0, 0, 0),
+ BUTTON_DOWN(0, 32, 0),
+ BUTTON_HIGHLIGHT(0, 32 * 2, 0),
+ BUTTON_HIGHLIGHT_DOWN(0, 32 * 3, 0),
+ BUTTON_DISABLED(0, 32 * 4, 0),
+
+ DISABLE(0, 0, 32),
+ REDSTONE_OFF(0, 32, 32),
+ REDSTONE_ON(0, 32 * 2, 32),
+ CHECKMARK(0, 32 * 3, 32),
+ CROSS(0, 32 * 4, 32),
+ WHITELIST(0, 32 * 5, 32),
+ BLACKLIST(0, 32 * 6, 32),
+ PROGRESS(0, 32 * 7, 32),
+
+ EXPORT(0, 0, 32 * 2),
+ IMPORT(0, 32, 32 * 2),
+ ALLOW_INPUT(0, 32 * 2, 32 * 2),
+ BLOCK_INPUT(0, 32 * 3, 32 * 2),
+ GREEN_ARROW_UP(0, 32 * 4, 32 * 2),
+ GREEN_ARROW_DOWN(0, 32 * 5, 32 * 2),
+ CYCLIC(0, 32 * 6, 32 * 2),
+
+ AND_GATE(0, 0, 32 * 3),
+ NAND_GATE(0, 32, 32 * 3),
+ OR_GATE(0, 32 * 2, 32 * 3),
+ NOR_GATE(0, 32 * 3, 32 * 3),
+ ANALOG_MODE(0, 32 * 4, 32 * 3),
+
+ SLOT_DARKGRAY(1, 176, 0, 18, 18),
+ SLOT_GRAY(1, 176, 18, 18, 18),
+
+ TAB_NORMAL(2, 0, 0, 18, 20),
+ TAB_HIGHLIGHT(2, 18, 0, 18, 20),
+ TAB_DISABLED(2, 18 * 2, 0, 18, 20),
+ TAB_NORMAL_BRONZE(2, 0, 20, 18, 20),
+ TAB_HIGHLIGHT_BRONZE(2, 18, 20, 18, 20),
+ TAB_DISABLED_BRONZE(2, 18 * 2, 20, 18, 20),
+ TAB_NORMAL_STEEL(2, 0, 2 * 20, 18, 20),
+ TAB_HIGHLIGHT_STEEL(2, 18, 2 * 20, 18, 20),
+ TAB_DISABLED_STEEL(2, 18 * 2, 2 * 20, 18, 20),
+ TAB_NORMAL_BRICK(2, 0, 3 * 20, 18, 20),
+ TAB_HIGHLIGHT_BRICK(2, 18, 3 * 20, 18, 20),
+ TAB_DISABLED_BRICK(2, 18 * 2, 3 * 20, 18, 20),
+ TAB_INFO_GRAY(2, 220, 0, 18, 20),
+ TAB_INFO_BLUE(2, 220 + 18, 0, 18, 20),;
+
+ private static final int T_SIZE = 256;
+ private static ResourceLocation[] TEXTURES = { new ResourceLocation(GregTech.ID, "textures/gui/GuiButtons.png"),
+ new ResourceLocation(GregTech.ID, "textures/gui/GuiCover.png"),
+ new ResourceLocation(GregTech.ID, "textures/gui/GuiTabs.png"), };
+
+ public final int x, y, width, height;
+ public final IGuiIcon overlay;
+ private final int texID;
+
+ GT_GuiIcon(int texID, int x, int y, int width, int height, IGuiIcon overlay) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ this.overlay = overlay;
+ this.texID = texID;
+ }
+
+ GT_GuiIcon(int texID, int x, int y) {
+ this(texID, x, y, 32, 32, null);
+ }
+
+ GT_GuiIcon(int texID, int x, int y, int width, int height) {
+ this(texID, x, y, width, height, null);
+ }
+
+ public static void render(IGuiIcon icon, double x, double y, double width, double height, double zLevel,
+ boolean doDraw) {
+ render(icon, x, y, width, height, zLevel, doDraw, false);
+ }
+
+ public static void render(IGuiIcon icon, double x, double y, double width, double height, double zLevel,
+ boolean doDraw, boolean flipHoritontally) {
+ Tessellator tess = Tessellator.instance;
+ if (doDraw) {
+ Minecraft.getMinecraft().renderEngine.bindTexture(TEXTURES[icon.getTexId()]);
+ tess.startDrawingQuads();
+ }
+ double minU = (double) (icon.getX() + (flipHoritontally ? icon.getWidth() : 0)) / T_SIZE;
+ double maxU = (double) (icon.getX() + (flipHoritontally ? 0 : icon.getWidth())) / T_SIZE;
+ double minV = (double) icon.getY() / T_SIZE;
+ double maxV = (double) (icon.getY() + icon.getHeight()) / T_SIZE;
+ tess.addVertexWithUV(x, y + height, zLevel, minU, maxV);
+ tess.addVertexWithUV(x + width, y + height, zLevel, maxU, maxV);
+ tess.addVertexWithUV(x + width, y + 0, zLevel, maxU, minV);
+ tess.addVertexWithUV(x, y + 0, zLevel, minU, minV);
+
+ if (icon.getOverlay() != null) render(icon.getOverlay(), x, y, width, height, zLevel, false);
+
+ if (doDraw) tess.draw();
+ }
+
+ /**
+ * This is intended to enable addon mods to register additional textures. They can then add to this enum using
+ * EnumHelper.addEnum or by creating their enum that implements IGuiIcon (still requires adding a texture here)
+ *
+ * @param location location of the texture to add
+ */
+ public static void addTextures(ResourceLocation... location) {
+ if (location == null || location.length == 0) return;
+
+ int startIndex = TEXTURES.length;
+ TEXTURES = Arrays.copyOf(TEXTURES, location.length);
+ System.arraycopy(location, 0, TEXTURES, startIndex, location.length);
+ }
+
+ @Override
+ public int getX() {
+ return this.x;
+ }
+
+ @Override
+ public int getY() {
+ return this.y;
+ }
+
+ @Override
+ public int getWidth() {
+ return this.width;
+ }
+
+ @Override
+ public int getHeight() {
+ return this.height;
+ }
+
+ @Override
+ public int getTexId() {
+ return this.texID;
+ }
+
+ @Override
+ public IGuiIcon getOverlay() {
+ return this.overlay;
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java
new file mode 100644
index 0000000000..d4bfe31404
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java
@@ -0,0 +1,114 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiButton;
+
+import org.lwjgl.opengl.GL11;
+
+import gregtech.api.interfaces.IGuiScreen;
+
+public class GT_GuiIconButton extends GuiButton implements IGuiScreen.IGuiElement {
+
+ public static final int DEFAULT_WIDTH = 16;
+ public static final int DEFAULT_HEIGHT = 16;
+
+ protected GT_GuiIcon icon;
+ private final int x0;
+ private final int y0;
+ protected final IGuiScreen gui;
+
+ private GT_GuiTooltip tooltip;
+
+ public GT_GuiIconButton(IGuiScreen gui, int id, int x, int y, GT_GuiIcon icon) {
+ super(id, x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, "");
+ this.gui = gui;
+ this.icon = icon;
+ this.x0 = x;
+ this.y0 = y;
+ gui.addElement(this);
+ }
+
+ @Override
+ public void onInit() {
+ if (tooltip != null) gui.addToolTip(tooltip);
+ xPosition = x0 + gui.getGuiLeft();
+ yPosition = y0 + gui.getGuiTop();
+ }
+
+ @Override
+ public void draw(int mouseX, int mouseY, float parTicks) {
+ drawButton(Minecraft.getMinecraft(), mouseX, mouseY);
+ }
+
+ @Override
+ public void drawButton(Minecraft mc, int mouseX, int mouseY) {
+ if (this.tooltip != null) this.tooltip.enabled = true;
+
+ if (this.visible) {
+ // moused over
+ this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition
+ && mouseX < this.xPosition + width
+ && mouseY < this.yPosition + height;
+
+ mouseDragged(mc, mouseX, mouseY);
+
+ GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ int x = xPosition;
+ int y = yPosition;
+ if (!this.field_146123_n) {
+ // GL11.glColor4f(200F/255F, 210F/255F, 1, 1);
+ } else GL11.glColor4f(1, 1, 1, 1);
+
+ GT_GuiIcon.render(getButtonTexture(this.field_146123_n), x, y, width, height, 0, true);
+
+ GL11.glColor4f(1, 1, 1, 1);
+ if (icon != null) {
+ GT_GuiIcon.render(icon, x, y, width, height, 0, true);
+ }
+
+ GL11.glPopAttrib();
+ }
+ }
+
+ @Override
+ public void mouseReleased(int mouseX, int mouseY) {
+ this.gui.clearSelectedButton();
+ if (mousePressed(Minecraft.getMinecraft(), mouseX, mouseY)) this.gui.buttonClicked(this);
+ }
+
+ public GT_GuiIcon getButtonTexture(boolean mouseOver) {
+ if (!enabled) return GT_GuiIcon.BUTTON_DISABLED;
+ if (this.equals(this.gui.getSelectedButton()))
+ return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT_DOWN : GT_GuiIcon.BUTTON_DOWN;
+
+ return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT : GT_GuiIcon.BUTTON_NORMAL;
+ }
+
+ public GT_GuiIcon getIcon() {
+ return icon;
+ }
+
+ public GT_GuiIconButton setIcon(GT_GuiIcon icon) {
+ this.icon = icon;
+ return this;
+ }
+
+ public GT_GuiTooltip getTooltip() {
+ return tooltip;
+ }
+
+ public GT_GuiIconButton setTooltipText(String... text) {
+ if (tooltip == null) tooltip = new GT_GuiTooltip(getBounds(), text);
+ else tooltip.setToolTipText(text);
+ return this;
+ }
+
+ public Rectangle getBounds() {
+ return new Rectangle(x0, y0, width, height);
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java
new file mode 100644
index 0000000000..5b5007fef3
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java
@@ -0,0 +1,43 @@
+package gregtech.api.gui.widgets;
+
+import gregtech.api.interfaces.IGuiScreen;
+
+public class GT_GuiIconCheckButton extends GT_GuiIconButton {
+
+ private final GT_GuiIcon checkedIcon;
+ private final GT_GuiIcon normalIcon;
+ private final String checkedTooltip;
+ private final String normalTooltip;
+ private boolean checked = false;
+
+ public GT_GuiIconCheckButton(IGuiScreen gui, int id, int x, int y, GT_GuiIcon checkedIcon, GT_GuiIcon normalIcon) {
+ this(gui, id, x, y, checkedIcon, normalIcon, null, null);
+ }
+
+ public GT_GuiIconCheckButton(IGuiScreen gui, int id, int x, int y, GT_GuiIcon checkedIcon, GT_GuiIcon normalIcon,
+ String checkedTooltip, String normalTooltip) {
+ super(gui, id, x, y, normalIcon);
+ this.checkedIcon = checkedIcon;
+ this.normalIcon = normalIcon;
+ this.checkedTooltip = checkedTooltip;
+ this.normalTooltip = normalTooltip;
+ }
+
+ @Override
+ public GT_GuiIcon getButtonTexture(boolean mouseOver) {
+ if (!enabled) return GT_GuiIcon.BUTTON_DISABLED;
+ if (this.equals(super.gui.getSelectedButton()))
+ return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT_DOWN : GT_GuiIcon.BUTTON_DOWN;
+ return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT : GT_GuiIcon.BUTTON_NORMAL;
+ }
+
+ public boolean isChecked() {
+ return checked;
+ }
+
+ public void setChecked(boolean checked) {
+ super.setIcon(checked ? checkedIcon : normalIcon);
+ super.setTooltipText(checked ? checkedTooltip : normalTooltip);
+ this.checked = checked;
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java
new file mode 100644
index 0000000000..2d3c7374bd
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java
@@ -0,0 +1,73 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiTextField;
+
+import gregtech.api.interfaces.IGuiScreen;
+
+public class GT_GuiIntegerTextBox extends GuiTextField implements IGuiScreen.IGuiElement {
+
+ private final int x0, y0;
+ private final IGuiScreen gui;
+ public final int id;
+ private boolean enabled;
+
+ public GT_GuiIntegerTextBox(IGuiScreen gui, int id, int x, int y, int width, int height) {
+ super(Minecraft.getMinecraft().fontRenderer, x, y, width, height);
+ super.setText("");
+ this.id = id;
+ x0 = x;
+ y0 = y;
+ this.gui = gui;
+ enabled = true;
+ gui.addElement(this);
+ }
+
+ @Override
+ public void onInit() {
+ xPosition = x0 + gui.getGuiLeft();
+ yPosition = y0 + gui.getGuiTop();
+ }
+
+ @Override
+ public void draw(int mouseX, int mouseY, float parTicks) {
+ super.drawTextBox();
+ }
+
+ public Rectangle getBounds() {
+ return new Rectangle(x0, y0, width, height);
+ }
+
+ public boolean validChar(char c, int key) {
+ return Character.isDigit(c);
+ }
+
+ @Override
+ public boolean textboxKeyTyped(char c, int key) {
+ if (validChar(c, key) || c == 1
+ || c == 3
+ || c == 22
+ || c == 24
+ || key == 14
+ || key == 199
+ || key == 203
+ || key == 205
+ || key == 207
+ || key == 211) {
+ return super.textboxKeyTyped(c, key);
+ }
+ return false;
+ }
+
+ @Override
+ public void setEnabled(boolean p_146184_1_) {
+ super.setEnabled(p_146184_1_);
+ enabled = p_146184_1_;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java
new file mode 100644
index 0000000000..015c8c7697
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java
@@ -0,0 +1,24 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+
+import net.minecraft.inventory.Slot;
+
+import gregtech.api.util.GT_TooltipDataCache.TooltipData;
+
+public class GT_GuiSlotTooltip extends GT_GuiTooltip {
+
+ private final Slot slot;
+
+ public GT_GuiSlotTooltip(Slot slot, TooltipData data) {
+ super(new Rectangle(slot.xDisplayPosition - 1, slot.yDisplayPosition - 1, 18, 18), data);
+ this.slot = slot;
+ }
+
+ @Override
+ protected void onTick() {
+ super.onTick();
+ // If disabled by super, stay disabled.
+ this.enabled = this.enabled && this.slot.getStack() == null;
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java
new file mode 100644
index 0000000000..ffae5c30e6
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java
@@ -0,0 +1,27 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+
+import gregtech.api.util.GT_TooltipDataCache.TooltipData;
+
+public class GT_GuiSmartTooltip extends GT_GuiTooltip {
+
+ public interface TooltipVisibilityProvider {
+
+ boolean shouldShowTooltip();
+ }
+
+ private final TooltipVisibilityProvider visibilityProvider;
+
+ public GT_GuiSmartTooltip(Rectangle bounds, TooltipVisibilityProvider visibilityProvider, TooltipData data) {
+ super(bounds, data);
+ this.visibilityProvider = visibilityProvider;
+ }
+
+ @Override
+ protected void onTick() {
+ super.onTick();
+ // If disabled by super, stay disabled.
+ this.enabled = this.enabled && this.visibilityProvider.shouldShowTooltip();
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java
new file mode 100644
index 0000000000..d06c2bd2eb
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java
@@ -0,0 +1,174 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.ItemBlock;
+import net.minecraft.item.ItemStack;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+
+import gregtech.api.gui.widgets.GT_GuiTabLine.GT_GuiTabIconSet;
+import gregtech.api.gui.widgets.GT_GuiTabLine.GT_ITabRenderer;
+import gregtech.api.interfaces.IGuiIcon;
+
+/**
+ * A tab to be attached to a tab line
+ */
+public class GT_GuiTab {
+
+ private static final int SLOT_SIZE = 18;
+
+ public boolean visible = true, mousedOver, enabled = true;
+
+ private Rectangle bounds;
+ private final GT_GuiTabIconSet tabBackground;
+ private final ItemStack item;
+ private final GT_ITabRenderer gui;
+ private GT_GuiTooltip tooltip;
+ private final IGuiIcon overlay;
+ private final boolean flipHorizontally;
+
+ /**
+ * A tab to be attached to a tab line
+ *
+ * @param gui IGregTechTileEntity the tab line this tab belongs to is attached to
+ * @param id both the ID and position in the tab line of this tab. Not used, kept for compatibility.
+ * @param bounds bounds of this tab
+ * @param tabBackground set of background textures
+ * @param item item to draw atop the background texture, not colored
+ * @param overlay texture to draw atop the background texture, not colored
+ * @param tooltipText tooltip of this tab
+ * @param flipHorizontally whether to draw this tab on the right side of the IGregTechTileEntity
+ */
+ public GT_GuiTab(GT_ITabRenderer gui, int id, Rectangle bounds, GT_GuiTabIconSet tabBackground, ItemStack item,
+ IGuiIcon overlay, String[] tooltipText, boolean flipHorizontally) {
+ this.gui = gui;
+ this.bounds = bounds;
+ this.item = item;
+ this.tabBackground = tabBackground;
+ this.overlay = overlay;
+ if (tooltipText != null) {
+ setTooltipText(tooltipText);
+ }
+ this.flipHorizontally = flipHorizontally;
+ }
+
+ public GT_GuiTab(GT_ITabRenderer gui, int id, Rectangle bounds, GT_GuiTabIconSet tabBackground) {
+ this(gui, id, bounds, tabBackground, null, null, null, false);
+ }
+
+ /**
+ * Set this tab's tooltip text
+ *
+ * @param text text to set
+ * @return This tab for chaining
+ */
+ public GT_GuiTab setTooltipText(String... text) {
+ if (tooltip == null) {
+ tooltip = new GT_GuiTooltip(bounds, text);
+ gui.addToolTip(tooltip);
+ } else {
+ tooltip.setToolTipText(text);
+ }
+ return this;
+ }
+
+ /**
+ * @return This tab's tooltip object
+ */
+ public GT_GuiTooltip getTooltip() {
+ return tooltip;
+ }
+
+ /**
+ * Draw the background texture for this tab
+ *
+ * @param mouseX not used, likely kept for backward compatibility
+ * @param mouseY not used, likely kept for backward compatibility
+ * @param parTicks not used, likely kept for backward compatibility
+ */
+ public void drawBackground(int mouseX, int mouseY, float parTicks) {
+ if (this.visible) {
+ GT_GuiIcon.render(
+ getBackgroundTexture(),
+ bounds.x,
+ bounds.y,
+ bounds.width,
+ bounds.height,
+ 1,
+ true,
+ this.flipHorizontally);
+ }
+ }
+
+ /**
+ * Draw overlay textures and items atop the background texture
+ *
+ * @param mouseX X mouse coordinate
+ * @param mouseY Y mouse coordinate
+ * @param parTicks not used, likely kept for backward compatibility
+ */
+ public void drawOverlays(int mouseX, int mouseY, float parTicks) {
+ this.mousedOver = bounds.contains(mouseX, mouseY);
+
+ if (this.tooltip != null) {
+ this.tooltip.enabled = this.visible;
+ }
+
+ if (this.visible) {
+ if (overlay != null) {
+ GL11.glColor4f(1, 1, 1, 1);
+ GT_GuiIcon.render(overlay, bounds.x, bounds.y, bounds.width, bounds.height, 1, true);
+ }
+ if (item != null) {
+ GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
+
+ if (item.getItem() instanceof ItemBlock) {
+ GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
+ GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+ }
+ int margin = (bounds.height - SLOT_SIZE);
+ gui.getItemRenderer()
+ .renderItemAndEffectIntoGUI(
+ gui.getFontRenderer(),
+ Minecraft.getMinecraft()
+ .getTextureManager(),
+ item,
+ bounds.x + (this.flipHorizontally ? 0 : margin),
+ bounds.y + margin);
+
+ if (item.getItem() instanceof ItemBlock) GL11.glPopAttrib();
+
+ GL11.glPopAttrib();
+ }
+ }
+ }
+
+ /**
+ * @return the texture this tab should currently use as it's background
+ */
+ protected IGuiIcon getBackgroundTexture() {
+ if (!enabled) return tabBackground.disabled;
+
+ return mousedOver ? tabBackground.highlight : tabBackground.normal;
+ }
+
+ /**
+ * @return the screen space occupied by this tab
+ */
+ public Rectangle getBounds() {
+ return this.bounds;
+ }
+
+ /**
+ * Reposition this tab on the screen
+ *
+ * @param xPos X tab coordinate
+ * @param yPos Y tab coordinate
+ */
+ public void setPosition(int xPos, int yPos) {
+ this.bounds = new Rectangle(xPos, yPos, bounds.width, bounds.height);
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java
new file mode 100644
index 0000000000..950478cdfa
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java
@@ -0,0 +1,274 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.entity.RenderItem;
+import net.minecraft.item.ItemStack;
+
+import org.lwjgl.opengl.GL11;
+
+import gregtech.api.interfaces.IGuiIcon;
+
+/**
+ * Draws clickable and configurable tabs on the left or right side of another GUI
+ */
+public class GT_GuiTabLine {
+
+ /**
+ * Defines a set of textures a tab line can use to render it's tab backgrounds
+ */
+ public static class GT_GuiTabIconSet {
+
+ public IGuiIcon disabled;
+ public IGuiIcon normal;
+ public IGuiIcon highlight;
+
+ public GT_GuiTabIconSet(IGuiIcon normalIcon, IGuiIcon highlightIcon, IGuiIcon disabledIcon) {
+ this.normal = normalIcon;
+ this.highlight = highlightIcon;
+ this.disabled = disabledIcon;
+ }
+ }
+
+ /**
+ * Controls the rendering style of the tab line
+ */
+ public enum DisplayStyle {
+
+ NONE((byte) 0),
+ NORMAL((byte) 1),
+ INVERSE((byte) -1);
+
+ private final byte value;
+
+ DisplayStyle(byte value) {
+ this.value = value;
+ }
+
+ public byte getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * A GUI should implement these methods as well as call the tab line's onMouseClicked, onInit and drawTabs for the
+ * tab line to attach to it properly.
+ */
+ public interface GT_ITabRenderer {
+
+ int getGuiLeft();
+
+ int getGuiTop();
+
+ int getXSize();
+
+ RenderItem getItemRenderer();
+
+ FontRenderer getFontRenderer();
+
+ void addToolTip(GT_GuiTooltip tooltip);
+
+ boolean removeToolTip(GT_GuiTooltip tooltip);
+ }
+
+ // The tabs are arranged according to their index in this array
+ protected final GT_GuiTab[] mTabs;
+
+ private final int tabLineLeft;
+ private final int tabLineTop;
+ private final int tabHeight;
+ private final int tabWidth;
+ private final int tabSpacing;
+
+ // In which direction to draw the tab line
+ private final DisplayStyle xDir;
+ private final DisplayStyle yDir;
+
+ // Whether to display on the right side of the GT_ITabRenderer instead of left
+ protected final boolean flipHorizontally;
+ protected final boolean visible;
+
+ private final GT_GuiTabIconSet tabBackground;
+ private final GT_ITabRenderer gui;
+
+ /**
+ * Draws clickable and configurable tabs on the left or right side of a GT_ITabRenderer
+ *
+ * @param gui GT_ITabRenderer gui which this tab line attaches to
+ * @param numTabs number of tab positions in this tab line
+ * @param tabLineLeft left position of the tab line in relation to the gui
+ * @param tabLineTop top position of the tab line in relation to the gui
+ * @param tabHeight height of a tab
+ * @param tabWidth width of a tab
+ * @param tabSpacing pixels between each tab
+ * @param xDir whether to extend the line horizontally to the right (NORMAL), the left (INVERSE) or not at
+ * all (NONE)
+ * @param yDir whether to extend the line vertically down (NORMAL), up (INVERSE) or not at all (NONE)
+ * @param displayMode whether to display on the left side of the GT_ITabRenderer (NORMAL), on it's right side
+ * (INVERSE) or not at all (NONE)
+ * @param tabBackground the set of textures used to draw this tab line's tab backgrounds
+ */
+ public GT_GuiTabLine(GT_ITabRenderer gui, int numTabs, int tabLineLeft, int tabLineTop, int tabHeight, int tabWidth,
+ int tabSpacing, DisplayStyle xDir, DisplayStyle yDir, DisplayStyle displayMode,
+ GT_GuiTabIconSet tabBackground) {
+ this.gui = gui;
+ this.mTabs = new GT_GuiTab[numTabs];
+ this.tabLineLeft = tabLineLeft;
+ this.tabLineTop = tabLineTop;
+ this.tabHeight = tabHeight;
+ this.tabWidth = tabWidth;
+ this.tabSpacing = tabSpacing;
+ this.xDir = xDir;
+ this.yDir = yDir;
+ this.tabBackground = tabBackground;
+ this.flipHorizontally = displayMode == DisplayStyle.INVERSE;
+ this.visible = !(displayMode == DisplayStyle.NONE);
+ }
+
+ /**
+ * Creates a new tab at the specified position with the given parameters. This class handles the positioning.
+ *
+ * @param tabId tab ID
+ * @param item item to draw atop the background texture, not colored
+ * @param overlay texture to draw atop the background texture, not colored
+ * @param text tooltip of this tab
+ */
+ public void setTab(int tabId, ItemStack item, IGuiIcon overlay, String[] text) {
+ mTabs[tabId] = new GT_GuiTab(
+ this.gui,
+ tabId,
+ getBoundsForTab(tabId),
+ this.tabBackground,
+ item,
+ overlay,
+ text,
+ this.flipHorizontally);
+ }
+
+ /**
+ * Get the bounds a given tab should occupy
+ *
+ * @param tabId tab ID
+ * @return tab bounds
+ */
+ protected Rectangle getBoundsForTab(int tabId) {
+ return new Rectangle(getTabX(tabId), getTabY(tabId), this.tabWidth, this.tabHeight);
+ }
+
+ /**
+ * Enable or disable a tab. Disabled tabs have a dark background.
+ *
+ * @param tabId tab ID
+ * @param enable true to enable, false to disable
+ */
+ public void setTabEnabled(int tabId, boolean enable) {
+ if (mTabs[tabId] != null) {
+ mTabs[tabId].enabled = enable;
+ }
+ }
+
+ /**
+ * Draw the tabs for this tab bar GT_ITabRenderer must call this method on drawGuiContainerBackgroundLayer or on
+ * drawScreen.
+ *
+ * @param parTicks
+ */
+ public void drawTabs(float parTicks, int mouseX, int mouseY) {
+ if (this.visible) {
+ GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ drawBackground(parTicks, mouseX, mouseY);
+ drawOverlays(parTicks, mouseX, mouseY);
+ GL11.glPopAttrib();
+ }
+ }
+
+ /**
+ * Draw the tab's backgrounds first
+ *
+ * @param parTicks not used, likely kept for compatibility
+ * @param mouseX mouse X position
+ * @param mouseY mouse Y position
+ */
+ protected void drawOverlays(float parTicks, int mouseX, int mouseY) {
+ for (GT_GuiTab mTab : mTabs) {
+ if (mTab != null) {
+ mTab.drawOverlays(mouseX, mouseY, parTicks);
+ }
+ }
+ }
+
+ /**
+ * Draw anything that overlays the tab's background texture
+ *
+ * @param parTicks not used, likely kept for compatibility
+ * @param mouseX mouse X position
+ * @param mouseY mouse Y position
+ */
+ protected void drawBackground(float parTicks, int mouseX, int mouseY) {
+ for (GT_GuiTab mTab : mTabs) {
+ if (mTab != null) {
+ mTab.drawBackground(mouseX, mouseY, parTicks);
+ }
+ }
+ }
+
+ /**
+ * Call tabClick for every tab that was clicked. GT_ITabRenderer must call this method on mouseClicked.
+ *
+ * @param mouseX mouse X position
+ * @param mouseY mouse Y position
+ * @param mouseButton which mouse button was used to click
+ */
+ public void onMouseClicked(int mouseX, int mouseY, int mouseButton) {
+ for (int tabId = 0; tabId < mTabs.length; tabId++) {
+ if (mTabs[tabId] != null && mTabs[tabId].getBounds()
+ .contains(mouseX, mouseY)) {
+ tabClicked(tabId, mouseButton);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Act on a tab being clicked.
+ *
+ * @param tabId tab ID
+ * @param mouseButton which mouse button was used to click
+ */
+ protected void tabClicked(int tabId, int mouseButton) {}
+
+ /**
+ * Reposition ourselves whenever the GT_ITabRenderer does so. GT_ITabRenderer must call this method on Init.
+ */
+ public void onInit() {
+ for (int i = 0; i < mTabs.length; i++) {
+ if (mTabs[i] != null) {
+ mTabs[i].setPosition(getTabX(i), getTabY(i));
+ }
+ }
+ }
+
+ /**
+ * Get the proper X position for a given tab
+ *
+ * @param tabId tab ID
+ * @return X position of the tab
+ */
+ private int getTabX(int tabId) {
+ return this.gui.getGuiLeft() + (flipHorizontally ? (gui.getXSize() - tabLineLeft - tabWidth) : tabLineLeft)
+ + (tabId * (tabWidth + tabSpacing) * xDir.getValue());
+ }
+
+ /**
+ * Get the proper Y position for a given tab
+ *
+ * @param tabId tab ID
+ * @return Y position of the tab
+ */
+ private int getTabY(int tabId) {
+ return this.gui.getGuiTop() + tabLineTop + (tabId * (tabHeight + tabSpacing) * yDir.getValue());
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java
new file mode 100644
index 0000000000..fe20b2b57a
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java
@@ -0,0 +1,121 @@
+package gregtech.api.gui.widgets;
+
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.lwjgl.input.Keyboard;
+
+import gregtech.api.util.GT_TooltipDataCache.TooltipData;
+
+public class GT_GuiTooltip {
+
+ protected final Rectangle bounds;
+ protected TooltipData data;
+ private List<String> displayedText;
+ public boolean enabled = true;
+
+ /**
+ * Used to create a tooltip that will appear over the specified bounds. This will initially be a "static" tooltip
+ * that doesn't respect verbosity levels or respond to the shift key.
+ *
+ * @param bounds tooltip bounds
+ * @param text tooltip text
+ */
+ public GT_GuiTooltip(Rectangle bounds, String... text) {
+ this.bounds = bounds;
+ setToolTipText(text);
+ }
+
+ /**
+ * Used to create a tooltip that will appear over the specified bounds. This will initially be a "dynamic" tooltip
+ * that respects verbosity levels and responds to the shift key.
+ *
+ * @param bounds tooltip bounds
+ * @param data tooltip data
+ */
+ public GT_GuiTooltip(Rectangle bounds, TooltipData data) {
+ this.bounds = bounds;
+ // Trust that the tooltips have already been formatted and colored, just make sure it has no nulls
+ this.data = sanitizeTooltipData(data);
+ }
+
+ private TooltipData sanitizeTooltipData(TooltipData data) {
+ if (data.text == null) {
+ data.text = Collections.emptyList();
+ }
+ if (data.shiftText == null) {
+ data.shiftText = Collections.emptyList();
+ }
+ return data;
+ }
+
+ /**
+ * Called before the tooltip manager checks whether this tooltip is enabled
+ */
+ protected void onTick() {
+ // Switch which of our 2 stored texts we're displaying now.
+ this.displayedText = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? this.data.shiftText : this.data.text;
+ // If this text is empty, let's not display a tooltip at all.
+ this.enabled = this.displayedText.size() != 0;
+ }
+
+ /**
+ * Called once this tooltip has been determined to be enabled
+ */
+ protected void updateText() {}
+
+ /**
+ * Used to set a "static" tooltip that doesn't respect verbosity levels or respond to the shift key
+ *
+ * @param text tooltip text
+ */
+ public void setToolTipText(String... text) {
+ this.data = formatTooltip(text);
+ this.displayedText = data.text;
+ }
+
+ /**
+ * Used to set a "dynamic" tooltip that respects verbosity levels and responds to the shift key
+ *
+ * @param data tooltip data
+ */
+ public void setToolTipText(TooltipData data) {
+ // Trust that the tooltips have already been formatted and colored, just make sure it has no nulls
+ this.data = sanitizeTooltipData(data);
+ }
+
+ /**
+ * Apply tooltip colors in case the text doesn't contain them and return as tooltip data
+ *
+ * @param text text to apply the colors to
+ * @return colored tooltip lines as list
+ */
+ protected TooltipData formatTooltip(String[] text) {
+ List<String> list;
+ if (text != null) {
+ list = new ArrayList<>(text.length);
+ for (String s : text) {
+ if (s == null) continue;
+ if (list.isEmpty()) list.add("\u00a7f" + s);
+ else list.add("\u00a77" + s);
+ }
+ } else {
+ list = Collections.emptyList();
+ }
+ return new TooltipData(list, list);
+ }
+
+ public List<String> getToolTipText() {
+ return this.displayedText;
+ }
+
+ public Rectangle getBounds() {
+ return bounds;
+ }
+
+ public boolean isDelayed() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java
new file mode 100644
index 0000000000..4304ca14fc
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java
@@ -0,0 +1,80 @@
+package gregtech.api.gui.widgets;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.client.gui.FontRenderer;
+
+public class GT_GuiTooltipManager {
+
+ public interface GT_IToolTipRenderer {
+
+ int getGuiLeft();
+
+ int getGuiTop();
+
+ int getXSize();
+
+ FontRenderer getFontRenderer();
+
+ void drawHoveringText(List<String> text, int mouseX, int mouseY, FontRenderer font);
+ }
+
+ private static final long DELAY = 5;
+ private int mouseStopped;
+ private int lastMouseX = -1;
+ private int lastMouseY = -1;
+ private final List<GT_GuiTooltip> tips = new ArrayList<>();
+
+ public void addToolTip(GT_GuiTooltip tip) {
+ if (tip != null && !tips.contains(tip)) tips.add(tip);
+ }
+
+ public boolean removeToolTip(GT_GuiTooltip tip) {
+ return tips.remove(tip);
+ }
+
+ public final void onTick(GT_IToolTipRenderer render, int mouseX, int mouseY) {
+ if ((Math.abs(mouseX - lastMouseX) < 2) && (Math.abs(mouseY - lastMouseY) < 2)) {
+ mouseStopped = Math.min(mouseStopped + 1, 50);
+ } else {
+ mouseStopped = 0;
+ }
+
+ lastMouseX = mouseX;
+ lastMouseY = mouseY;
+
+ mouseX -= render.getGuiLeft();
+ mouseY -= render.getGuiTop();
+ for (GT_GuiTooltip tip : tips) {
+ // Give the tooltip the opportunity to decide whether they should be enabled
+ tip.onTick();
+ if (tip.enabled && (!tip.isDelayed() || mouseStopped > DELAY)
+ && tip.getBounds()
+ .contains(mouseX, mouseY)) {
+ tip.updateText();
+ drawTooltip(tip, mouseX, mouseY, render);
+ break;
+ }
+ }
+ }
+
+ private void drawTooltip(GT_GuiTooltip tip, int mouseX, int mouseY, GT_IToolTipRenderer render) {
+ List<String> text = tip.getToolTipText();
+ if (text == null) return;
+
+ if (mouseX > render.getGuiLeft() + render.getXSize() / 2) {
+ int maxWidth = 0;
+ for (String s : text) {
+ int w = render.getFontRenderer()
+ .getStringWidth(s);
+ if (w > maxWidth) {
+ maxWidth = w;
+ }
+ }
+ mouseX -= (maxWidth + 18);
+ }
+
+ render.drawHoveringText(text, mouseX, mouseY, render.getFontRenderer());
+ }
+}
diff --git a/src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java b/src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java
new file mode 100644
index 0000000000..9a93a8fadf
--- /dev/null
+++ b/src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java
@@ -0,0 +1,90 @@
+package gregtech.api.gui.widgets;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+import net.minecraft.util.StatCollector;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.tileentity.IMachineProgress;
+
+public class GT_LockedWhileActiveButton extends ButtonWidget {
+
+ @NotNull
+ private final IMachineProgress machine;
+
+ public GT_LockedWhileActiveButton(@NotNull IMachineProgress machine, @NotNull ModularWindow.Builder builder) {
+ super();
+ this.machine = machine;
+
+ super.attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(machine::isActive, a -> {}),
+ builder,
+ (widget, aBoolean) -> widget.notifyTooltipChange());
+
+ super.dynamicTooltip(this::generateTooltip);
+ }
+
+ @NotNull
+ @Override
+ public ButtonWidget setOnClick(@NotNull BiConsumer<ClickData, Widget> clickAction) {
+ return super.setOnClick((clickData, widget) -> {
+ if (!machine.isActive()) {
+ clickAction.accept(clickData, widget);
+ }
+ });
+ }
+
+ @NotNull
+ @Override
+ public Widget setBackground(@NotNull IDrawable... drawables) {
+ return super.setBackground(() -> appendLockedOverlay(drawables));
+ }
+
+ @NotNull
+ @Override
+ public Widget setBackground(@NotNull Supplier<IDrawable[]> drawablesSupplier) {
+ return super.setBackground(() -> appendLockedOverlay(drawablesSupplier.get()));
+ }
+
+ @NotNull
+ @Override
+ public Widget dynamicTooltip(@NotNull Supplier<List<String>> dynamicTooltip) {
+ return super.dynamicTooltip(() -> {
+ ImmutableList.Builder<String> tooltips = ImmutableList.<String>builder()
+ .addAll(dynamicTooltip.get());
+ tooltips.addAll(generateTooltip());
+
+ return tooltips.build();
+ });
+ }
+
+ @NotNull
+ private IDrawable[] appendLockedOverlay(@NotNull IDrawable[] drawables) {
+ if (machine.isActive()) {
+ final IDrawable[] copy = Arrays.copyOf(drawables, drawables.length + 1);
+ copy[drawables.length] = GT_UITextures.OVERLAY_BUTTON_LOCKED;
+ return copy;
+ }
+ return drawables;
+ }
+
+ @NotNull
+ private List<String> generateTooltip() {
+ if (machine.isActive()) {
+ return ImmutableList.of(StatCollector.translateToLocal("GT5U.gui.button.forbidden_while_running"));
+ }
+ return ImmutableList.of();
+ }
+}