package gminers.glasspane.component; import gminers.glasspane.HorzAlignment; import gminers.glasspane.VertAlignment; import gminers.kitchensink.Rendering; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.FieldDefaults; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import org.lwjgl.opengl.GL11; /** * Implements a basic container that can have a border (with text!) displayed around it's contents. * * @author Aesen Vismea * */ @FieldDefaults(level = AccessLevel.PROTECTED) @ToString @Getter @Setter public class PanePanel extends PaneContainer { /** * The X alignment of the text in the border. */ HorzAlignment borderAlignmentX = HorzAlignment.LEFT; /** * The Y alignment of the text in the border. Does not allow MIDDLE. */ VertAlignment borderAlignmentY = VertAlignment.TOP; /** * The font renderer to use for the text in the border. * * @see Minecraft#fontRendererObj * @see Minecraft#standardGalacticFontRenderer */ FontRenderer renderer = Minecraft.getMinecraft().fontRendererObj; /** * The text to render in the border. <code>null</code> for a solid border with no text. */ String borderText = null; /** * The thickness of the border, in 'big' pixels. */ int borderThickness = 1; /** * The color to use for the border and the border text. */ int borderColor = 0xFFFFFF; /** * Whether or not to render a shadow for the border. */ boolean borderShadow = true; /** * Whether or not to render a shadow for the border text. */ boolean borderTextShadow = true; /** * Whether or not to actually render a border - setting this to false makes this a dumb container that just renders it's children. */ boolean showBorder = true; @Override protected void doRender(final int mouseX, final int mouseY, final float partialTicks) { // if we're drawing a border, do a bunch of super complicated awesome if (showBorder) { // precalc the border offsets final int borderOffsetY = Math.max(borderThickness + 1, (borderText == null ? 0 : renderer.FONT_HEIGHT)); final int borderOffsetX = borderThickness + 1; // make new vars for width and height to use since we're clipped to our size int percievedWidth = width; if (borderShadow) { percievedWidth--; } int percievedHeight = height; if (borderShadow) { percievedHeight--; } // precalc borderOffsetY's halved value final int borderOffsetYH = borderOffsetY / 2; if (borderText != null) { // if we have text, we'll render a different border // first, precalc the text's width final int textWidth = renderer.getStringWidth(borderText); // calculate the X offset of the text based off the alignment int textX = 8; if (borderAlignmentX == HorzAlignment.MIDDLE) { textX = (width / 2) - (textWidth / 2); } else if (borderAlignmentX == HorzAlignment.RIGHT) { textX = (width - 8) - textWidth; } if (borderAlignmentY == VertAlignment.TOP) { // if it's the top, draw a border like so // top & text drawRect(0, borderOffsetYH, textX - 2, (borderOffsetYH) + borderThickness, borderColor | 0xFF000000); drawRect((textX + textWidth) + 2, borderOffsetYH, percievedWidth, (borderOffsetYH) + borderThickness, borderColor | 0xFF000000); // draw the other 3 sides drawDefaultBorder(percievedWidth, percievedHeight, borderOffsetYH, false, true, true, true); // draw the text renderer.drawString(borderText, textX, 0, borderColor, borderTextShadow); } else if (borderAlignmentY == VertAlignment.BOTTOM) { // if it's the bottom, draw a border like so // draw the other 3 sides drawDefaultBorder(percievedWidth, percievedHeight - borderOffsetYH, 0, true, true, true, false); // bottom & text drawRect(0, percievedHeight - (borderOffsetYH), textX - 2, (percievedHeight - (borderOffsetYH)) + borderThickness, borderColor | 0xFF000000); drawRect((textX + textWidth) + 2, percievedHeight - (borderOffsetYH), percievedWidth, (percievedHeight - (borderOffsetYH)) + borderThickness, borderColor | 0xFF000000); // draw the text renderer.drawString(borderText, textX, percievedHeight - renderer.FONT_HEIGHT, borderColor, borderTextShadow); } else { // we can't draw a middle-aligned Y, so just draw the default as a fallback // TODO - draw 90-degree rotated text on X align LEFT or RIGHT? drawDefaultBorder(percievedWidth, percievedHeight, borderOffsetYH, true, true, true, true); } } else { // no text, a solid border will do drawDefaultBorder(percievedWidth, percievedHeight, borderOffsetYH, true, true, true, true); } // perform the translation GL11.glTranslatef(borderOffsetX, borderOffsetY, 0); // render the components final int pX = getPX(); final int pY = getPY(); for (final PaneComponent pc : components) { pc.render(mouseX - pX, mouseY - pY, partialTicks); } } else { // otherwise, just call super super.doRender(mouseX, mouseY, partialTicks); } } private void drawDefaultBorder(final int percievedWidth, final int percievedHeight, final int borderOffsetYH, final boolean top, final boolean right, final boolean left, final boolean bottom) { if (top) { drawRect(0, 0, percievedWidth, borderThickness, borderColor | 0xFF000000); } if (right) { drawRect(percievedWidth - borderThickness, borderOffsetYH, percievedWidth, percievedHeight, borderColor | 0xFF000000); } if (left) { drawRect(0, borderOffsetYH, borderThickness, percievedHeight, borderColor | 0xFF000000); } if (bottom) { drawRect(0, percievedHeight - borderThickness, percievedWidth, percievedHeight, borderColor | 0xFF000000); } } private void drawRect(final int x1, final int y1, final int x2, final int y2, final int color) { if (borderShadow) { final int shadowColor = (color & 16579836) >> 2 | color & -16777216; Rendering.drawRect(x1 + 1, y1 + 1, x2 + 1, y2 + 1, shadowColor); } Rendering.drawRect(x1, y1, x2, y2, color); } @Override protected int getPX() { final int borderOffsetX = borderThickness + 1; return super.getPX() + borderOffsetX; } @Override protected int getPY() { final int borderOffsetY = Math.max(borderThickness + 1, (borderText == null ? 0 : renderer.FONT_HEIGHT)); final int translateY = borderAlignmentY == VertAlignment.TOP ? borderOffsetY : borderThickness + 1; return super.getPY() + translateY; } }