package mhfc.net.client.util.gui; import static org.lwjgl.opengl.GL11.glColor4f; import java.util.List; import java.util.Objects; import com.google.common.base.Preconditions; import mhfc.net.MHFCMain; import mhfc.net.common.util.stringview.Viewable; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.Tessellator; import net.minecraft.util.StatCollector; public class MHFCGuiUtil { public static final int COLOUR_FOREGROUND = 0xd8953c; public static final int COLOUR_TEXT = 0x404040; public static final int COLOUR_TITLE = 0x000000; private static final String WIDTH_WARNING = "The width of the draw was smaller than the first character. This creates a stack overflow.\n Please don't do this, track it down. Stacktrace:"; public static float zLevel; private static ScaledResolution s; public static int realScreenWidth(Minecraft mc) { if (mc == null) { throw new IllegalArgumentException("Gui utils may only be accessed with valid minecraft"); } return mc.displayWidth; } public static int realScreenHeight(Minecraft mc) { if (mc == null) { throw new IllegalArgumentException("Gui utils may only be accessed with valid minecraft"); } return mc.displayHeight; } public static int minecraftWidth(Minecraft mc) { refreshScaled(mc); return s.getScaledWidth(); } public static int minecraftHeight(Minecraft mc) { refreshScaled(mc); return s.getScaledHeight(); } public static int guiScaleFactor(Minecraft mc) { refreshScaled(mc); return s.getScaleFactor(); } private static void refreshScaled(Minecraft mc) { if (mc == null) { throw new IllegalArgumentException("Gui utils may only be accessed with valid minecraft"); } s = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); } /** * Draws the content of the view according to the following rules:<br> * A '\n' character represents a line break. A '\f' represents a page break.<br> * page decides which page to render. When page refers to an illegal page, nothing is rendered.<br> * scrolledHeight refers to the portion of text that is skipped by rendering. Every line break will add one * lineheight of height to the text. Every line that is not fully in view will not be rendered.<br> * width refers to the maximal width of the rendered text. A linebreak is inserted before a word if the line would * be longer than width if it were rendered.<br> * height sets the height of text that is being render after scrolledHeight has been skipped.<br> * color is the color of the text to render with. * * @param view * the view to render * @param buffer * the buffer to use to store strings (to keep memory profile small) * @param pageNbr * the page to render, see 'page break' * @param scrolledHeight * the skipped height, that is not rendered * @param width * the width to render the page with * @param height * the height to render * @param lineHeight * the height of one line * @param posX * the pos to render at * @param posY * the pos to render at * @param color * the color to render the text with * @param fRend * the font renderer to render with */ public static void drawViewable( Viewable view, StringBuilder buffer, int pageNbr, int scrolledHeight, int width, int height, int posX, int posY, int lineHeight, int color, FontRenderer fRend) { Preconditions.checkArgument(width >= 0, "width must be positive"); Preconditions.checkArgument(height >= 0, "height must be positive"); if (pageNbr < 0) { return; } buffer.setLength(0); view.appendTo(buffer); String rawContent = buffer.toString(); String[] pages = rawContent.split("\f"); if (pageNbr >= pages.length) { return; } String pageContent = pages[pageNbr]; String[] paragraphs = pageContent.split("\n"); int relPosY = -scrolledHeight; for (String pgrh : paragraphs) { relPosY = renderParagraph(pgrh, width, height, posX, posY, relPosY, lineHeight, color, fRend); relPosY += lineHeight; // Line break after paragraph if (relPosY + lineHeight > height) { // Shortcurcuit, we are beyond the rendered area return; } } } /** * Renders a paragraph * * @param paragraph * the content of the paragraph * @param width * @param height * @param posX * @param posY * @param relPosY * @param lineHeight * @param color * @param fRend * @return the new relPosY */ private static int renderParagraph( String paragraph, int width, int height, int posX, int posY, int relPosY, int lineHeight, int color, FontRenderer fRend) { List<String> lines = fRend.listFormattedStringToWidth(paragraph, width); int lineSize = lines.size(); for (int i = 0; i < lineSize; i++, relPosY += lineHeight) { String line = lines.get(i); if (relPosY + lineHeight > height) { break; } if (relPosY >= 0) { fRend.drawString(line, posX, posY + relPosY, color); } } relPosY -= lineHeight; return relPosY; } public static int drawTextLocalizedAndReturnHeight( FontRenderer fRend, String string, int posX, int posY, int width, int colour) { String localized = StatCollector.translateToLocal(string); return drawTextAndReturnHeight(fRend, localized, posX, posY, width, colour); } /** * Draws a string onto the screen at the desired position. If width is > 0, then the draw split string method is * used instead. The amount if vertical space occupied by the draw is calculated and returned. If one attempts to * draw a null String or with a null renderer, a warning is printed (including a stack trace) and 0 is returned. * * @return The drawn height of the string. Always line height for valid parameters and width==0 */ public static int drawTextAndReturnHeight( FontRenderer fRend, String string, int posX, int posY, int width, int colour) { Objects.requireNonNull(fRend); Objects.requireNonNull(string); int lines = 1; if (width <= 0) { fRend.drawString(string, posX, posY, colour); } else if (isDrawWidthTooSmall(fRend, width, string)) { MHFCMain.logger().info(WIDTH_WARNING); Thread.dumpStack(); lines = 0; } else { lines = fRend.listFormattedStringToWidth(string, width).size(); fRend.drawSplitString(string, posX, posY, width, colour); } return lines * fRend.FONT_HEIGHT; } /** * Considers if the draw width would cause Minecraft to crash using the given string. This can happen when the first * character can't even fit into the width. * * @return If the font renderer will crash the game with this string and this width */ public static boolean isDrawWidthTooSmall(FontRenderer fRend, int width, String string) { return !string.isEmpty() && width < fRend.getStringWidth(string.substring(0, 1)); } public static void drawTexturedRectangle( double xMin, double yMin, double width, double height, float u, float v, float uWidth, float vHeight) { Tessellator t = Tessellator.instance; t.startDrawingQuads(); t.addVertexWithUV(xMin, yMin, zLevel, u, v); t.addVertexWithUV(xMin, yMin + height, zLevel, u, v + vHeight); t.addVertexWithUV(xMin + width, yMin + height, zLevel, u + uWidth, v + vHeight); t.addVertexWithUV(xMin + width, yMin, zLevel, u + uWidth, v); t.draw(); } public static void drawTexturedBoxFromBorder(float x, float y, float width, float height) { drawTexturedBoxFromBorder(x, y, zLevel, width, height); } public static void drawTexturedBoxFromBorder(float x, float y, float zLevel, float width, float height) { drawTexturedBoxFromBorder(x, y, zLevel, width, height, Math.min(Math.min(15, width / 2), height / 2)); } public static void drawTexturedBoxFromBorder( float x, float y, float zLevel, float width, float height, float borderSize) { drawTexturedBoxFromBorder(x, y, zLevel, width, height, borderSize, borderSize / 256f); } public static void drawTexturedBoxFromBorder( float x, float y, float zLevel, float width, float height, float borderSize, float borderUV) { drawTexturedBoxFromBorder(x, y, zLevel, width, height, borderSize, borderUV, 1, 1); } public static void drawTexturedBoxFromBorder( float x, float y, float zLevel, float width, float height, float borderSize, float borderUV, float maxU, float maxV) { drawTexturedBoxFromBorder(x, y, zLevel, width, height, borderSize, borderUV, borderUV, maxU, maxV); } public static void drawTexturedBoxFromBorder( float x, float y, float zLevel, float width, float height, float borderSize, float borderU, float borderV, float maxU, float maxV) { Tessellator tess = Tessellator.instance; tess.startDrawingQuads(); tess.addVertexWithUV(x, y, zLevel, 0, 0); tess.addVertexWithUV(x, y + borderSize, zLevel, 0, borderV); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, borderU, borderV); tess.addVertexWithUV(x + borderSize, y, zLevel, borderU, 0); tess.draw(); tess.startDrawingQuads(); tess.addTranslation(width - borderSize, 0, 0); tess.addVertexWithUV(x, y, zLevel, maxU - borderU, 0); tess.addVertexWithUV(x, y + borderSize, zLevel, maxU - borderU, borderV); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, maxU, borderV); tess.addVertexWithUV(x + borderSize, y, zLevel, maxU, 0); tess.draw(); tess.startDrawingQuads(); tess.addTranslation(0, height - borderSize, 0); tess.addVertexWithUV(x, y, zLevel, maxU - borderU, maxV - borderV); tess.addVertexWithUV(x, y + borderSize, zLevel, maxU - borderU, maxV); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, maxU, maxV); tess.addVertexWithUV(x + borderSize, y, zLevel, maxU, maxV - borderV); tess.draw(); tess.startDrawingQuads(); tess.addTranslation(-width + borderSize, 0, 0); tess.addVertexWithUV(x, y, zLevel, 0, maxV - borderV); tess.addVertexWithUV(x, y + borderSize, zLevel, 0, maxV); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, borderU, maxV); tess.addVertexWithUV(x + borderSize, y, zLevel, borderU, maxV - borderV); tess.draw(); tess.addTranslation(0, -height + borderSize, 0); tess.startDrawingQuads(); tess.addVertexWithUV(x, y + borderSize, zLevel, 0, borderV); tess.addVertexWithUV(x, y + height - borderSize, zLevel, 0, maxV - borderV); tess.addVertexWithUV(x + borderSize, y + height - borderSize, zLevel, borderU, maxV - borderV); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, borderU, borderV); tess.draw(); tess.startDrawingQuads(); tess.addVertexWithUV(x + width - borderSize, y + borderSize, zLevel, maxU - borderU, borderV); tess.addVertexWithUV(x + width - borderSize, y + height - borderSize, zLevel, maxU - borderU, maxV - borderV); tess.addVertexWithUV(x + width, y + height - borderSize, zLevel, maxU, maxV - borderV); tess.addVertexWithUV(x + width, y + borderSize, zLevel, maxU, borderV); tess.draw(); tess.startDrawingQuads(); tess.addVertexWithUV(x + borderSize, y, zLevel, borderU, 0); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, borderU, borderV); tess.addVertexWithUV(x + width - borderSize, y + borderSize, zLevel, maxU - borderU, borderV); tess.addVertexWithUV(x + width - borderSize, y, zLevel, maxU - borderU, 0); tess.draw(); tess.startDrawingQuads(); tess.addVertexWithUV(x + borderSize, y + height - borderSize, zLevel, borderU, maxV - borderV); tess.addVertexWithUV(x + borderSize, y + height, zLevel, borderU, maxV); tess.addVertexWithUV(x + width - borderSize, y + height, zLevel, maxU - borderU, maxV); tess.addVertexWithUV(x + width - borderSize, y + height - borderSize, zLevel, maxU - borderU, maxV - borderV); tess.draw(); tess.startDrawingQuads(); tess.addVertexWithUV(x + borderSize, y + borderSize, zLevel, borderU, borderV); tess.addVertexWithUV(x + borderSize, y + height - borderSize, zLevel, borderU, maxV - borderV); tess.addVertexWithUV(x + width - borderSize, y + height - borderSize, zLevel, maxU - borderU, maxV - borderV); tess.addVertexWithUV(x + width - borderSize, y + borderSize, zLevel, maxU - borderU, borderV); tess.draw(); } public static void setColor(int colorRGB) { setColor(colorRGB, 1.0f); } public static void setColor(int colorRGB, float alpha) { float r = ((colorRGB >> 16) & 0xFF) / 255.f, g = ((colorRGB >> 8) & 0xFF) / 255.f, b = (colorRGB & 0xFF) / 255.f; glColor4f(r, g, b, alpha); } }