package uk.co.wehavecookies56.kk.client.gui; import java.lang.reflect.Field; import java.util.ArrayList; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; public abstract class GuiTooltip extends GuiScreen { /** * Show a white "?" in the top right part of any button with a tooltip * assigned to it */ public static boolean ShowTooltipButtonEffect = true; /** * Show an aqua "?" in the top right part of any button with a tooltip * assigned to it when mouseovered */ public static boolean ShowTooltipButtonMouseoverEffect = true; /** Putting this string into a tooltip will cause a line break */ public String tooltipNewlineDelimeter = "_p"; /** The amount of time in milliseconds until a tooltip is rendered */ public long tooltipDelay = 900; /** * The maximum width in pixels a tooltip can occupy before word wrapping * occurs */ public int tooltipMaxWidth = 150; protected int tooltipXOffset = 0; protected int tooltipYOffset = 10; private final static int LINE_HEIGHT = 11; private long mouseoverTime = 0; private long prevSystemTime = -1; @Override public void drawScreen (int mouseX, int mouseY, float f) { super.drawScreen(mouseX, mouseY, f); DrawTooltipScreen(mouseX, mouseY); } /** * This method must be overriden. Gets a tooltip String for a specific * button. Recommended to use a switch/case statement for buttonId for easy * implementation. * * @param buttonId * The ID of the button this tooltip corresponds to * @return The tooltip string for the specified buttonId. null if no tooltip * exists for this button. */ protected abstract String GetButtonTooltip (int buttonId); /** * Renders any special effects applied to tooltip buttons, and renders any * tooltips for GuiButtons that are being mouseovered. * * @param mouseX * @param mouseY */ protected void DrawTooltipScreen (int mouseX, int mouseY) { if (ShowTooltipButtonEffect) RenderTooltipButtonEffect(); int mousedOverButtonId = -1; // find out which button is being mouseovered for (int i = 0; i < buttonList.size(); i++) { GuiButton button = buttonList.get(i); if (IsButtonMouseovered(mouseX, mouseY, button)) { mousedOverButtonId = button.id; if (ShowTooltipButtonMouseoverEffect && GetButtonTooltip(mousedOverButtonId) != null) RenderTooltipButtonMouseoverEffect(button); break; } } // calculate how long this button has been mouseovered for if (mousedOverButtonId > -1) { long systemTime = System.currentTimeMillis(); if (prevSystemTime > 0) mouseoverTime += systemTime - prevSystemTime; prevSystemTime = systemTime; } else mouseoverTime = 0; // render the button's tooltip if (mouseoverTime > tooltipDelay) { String tooltip = GetButtonTooltip(mousedOverButtonId); if (tooltip != null) RenderTooltip(mouseX, mouseY, tooltip); } } /** * Determines if a GuiButton is being mouseovered. * * @param mouseX * @param mouseY * @param button * @return true if this button is mouseovered */ protected boolean IsButtonMouseovered (int mouseX, int mouseY, GuiButton button) { if (mouseX >= button.xPosition && mouseX <= button.xPosition + button.getButtonWidth() && mouseY >= button.yPosition) { // for some god-forsaken reason they made GuiButton.getButtonWidth() // public but not height, // so use reflection to grab it int buttonHeight = GetFieldByReflection(GuiButton.class, button, "height", "field_146121_g"); if (mouseY <= button.yPosition + buttonHeight) return true; } return false; } /** * Render anything special onto all buttons that have tooltips assigned to * them. */ protected void RenderTooltipButtonEffect () { for (int i = 0; i < buttonList.size(); i++) { GuiButton button = buttonList.get(i); if (GetButtonTooltip(button.id) != null) { boolean flag = mc.fontRendererObj.getUnicodeFlag(); mc.fontRendererObj.setUnicodeFlag(true); mc.fontRendererObj.drawString("?", button.xPosition + button.getButtonWidth() - 5, button.yPosition, 0x99FFFFFF); mc.fontRendererObj.setUnicodeFlag(flag); } } } /** * Render anything special onto buttons that have tooltips assigned to them * when they are mousevered. * * @param button */ protected void RenderTooltipButtonMouseoverEffect (GuiButton button) { boolean flag = mc.fontRendererObj.getUnicodeFlag(); mc.fontRendererObj.setUnicodeFlag(true); mc.fontRendererObj.drawString(FontCodes.AQUA + "?", button.xPosition + button.getButtonWidth() - 5, button.yPosition, 0xFFFFFF); mc.fontRendererObj.setUnicodeFlag(flag); } /** * Renders a tooltip at (x,y). * * @param x * @param y * @param tooltip */ protected void RenderTooltip (int x, int y, String tooltip) { String[] tooltipArray = ParseTooltipArrayFromString(tooltip); int tooltipWidth = GetTooltipWidth(tooltipArray); int tooltipHeight = GetTooltipHeight(tooltipArray); int tooltipX = x + tooltipXOffset; int tooltipY = y + tooltipYOffset; if (tooltipX > width - tooltipWidth - 7) tooltipX = width - tooltipWidth - 7; if (tooltipY > height - tooltipHeight - 8) tooltipY = height - tooltipHeight - 8; // render the background inside box int innerAlpha = -0xFEFFFF0; // very very dark purple drawGradientRect(tooltipX, tooltipY - 1, tooltipX + tooltipWidth + 6, tooltipY, innerAlpha, innerAlpha); drawGradientRect(tooltipX, tooltipY + tooltipHeight + 6, tooltipX + tooltipWidth + 6, tooltipY + tooltipHeight + 7, innerAlpha, innerAlpha); drawGradientRect(tooltipX, tooltipY, tooltipX + tooltipWidth + 6, tooltipY + tooltipHeight + 6, innerAlpha, innerAlpha); drawGradientRect(tooltipX - 1, tooltipY, tooltipX, tooltipY + tooltipHeight + 6, innerAlpha, innerAlpha); drawGradientRect(tooltipX + tooltipWidth + 6, tooltipY, tooltipX + tooltipWidth + 7, tooltipY + tooltipHeight + 6, innerAlpha, innerAlpha); // render the background outside box int outerAlpha1 = 0x505000FF; int outerAlpha2 = (outerAlpha1 & 0xFEFEFE) >> 1 | outerAlpha1 & -0x1000000; drawGradientRect(tooltipX, tooltipY + 1, tooltipX + 1, tooltipY + tooltipHeight + 6 - 1, outerAlpha1, outerAlpha2); drawGradientRect(tooltipX + tooltipWidth + 5, tooltipY + 1, tooltipX + tooltipWidth + 7, tooltipY + tooltipHeight + 6 - 1, outerAlpha1, outerAlpha2); drawGradientRect(tooltipX, tooltipY, tooltipX + tooltipWidth + 3, tooltipY + 1, outerAlpha1, outerAlpha1); drawGradientRect(tooltipX, tooltipY + tooltipHeight + 5, tooltipX + tooltipWidth + 7, tooltipY + tooltipHeight + 6, outerAlpha2, outerAlpha2); // render the foreground text int lineCount = 0; for (String s : tooltipArray) { mc.fontRendererObj.drawString(s, tooltipX + 2, tooltipY + 2 + lineCount * LINE_HEIGHT, 0xFFFFFF); lineCount++; } } /** * Converts a String representation of a tooltip into a String[], and also * decodes any font codes used. * * @param s * Ex: "Hello,_nI am your _ltooltip_r and you love me." * @return An array of Strings such that each String width does not exceed * tooltipMaxWidth */ protected String[] ParseTooltipArrayFromString (String s) { s = DecodeStringCodes(s); String[] tooltipSections = s.split(tooltipNewlineDelimeter); ArrayList<String> tooltipArrayList = new ArrayList<String>(); for (String section : tooltipSections) { String tooltip = ""; String[] tooltipWords = section.split(" "); for (String tooltipWord : tooltipWords) { int lineWidthWithNextWord = mc.fontRendererObj.getStringWidth(tooltip + tooltipWord); if (lineWidthWithNextWord > tooltipMaxWidth) { tooltipArrayList.add(tooltip.trim()); tooltip = tooltipWord + " "; } else tooltip += tooltipWord + " "; } tooltipArrayList.add(tooltip.trim()); } String[] tooltipArray = new String[tooltipArrayList.size()]; tooltipArrayList.toArray(tooltipArray); return tooltipArray; } /** * Decodes any font codes into something useable by the FontRenderer. * * @param s * E.x.: "Hello,_nI am your _ltooltip_r and you love me." * @return E.x. output (html not included): <br> * "Hello,<br>I am your <b>tooltip</b> and you love me." */ private String DecodeStringCodes (String s) { return s.replace("_0", FontCodes.BLACK).replace("_1", FontCodes.DARK_BLUE).replace("_2", FontCodes.DARK_GREEN).replace("_3", FontCodes.DARK_AQUA).replace("_4", FontCodes.DARK_RED).replace("_5", FontCodes.DARK_PURPLE).replace("_6", FontCodes.GOLD).replace("_7", FontCodes.GRAY).replace("_8", FontCodes.DARK_GREY).replace("_9", FontCodes.BLUE).replace("_a", FontCodes.GREEN).replace("_b", FontCodes.AQUA).replace("_c", FontCodes.RED).replace("_d", FontCodes.LIGHT_PURPLE).replace("_e", FontCodes.YELLOW).replace("_f", FontCodes.WHITE).replace("_k", FontCodes.OBFUSCATED).replace("_l", FontCodes.BOLD).replace("_m", FontCodes.STRIKETHROUGH).replace("_n", FontCodes.UNDERLINE).replace("_o", FontCodes.ITALICS).replace("_r", FontCodes.RESET); } /*** * Gets the width of the tooltip in pixels. * * @param tooltipArray * @return */ private int GetTooltipWidth (String[] tooltipArray) { int longestWidth = 0; for (String s : tooltipArray) { int width = mc.fontRendererObj.getStringWidth(s); if (width > longestWidth) longestWidth = width; } return longestWidth; } /** * Gets the height of the tooltip in pixels. * * @param tooltipArray * @return */ private int GetTooltipHeight (String[] tooltipArray) { int tooltipHeight = mc.fontRendererObj.FONT_HEIGHT - 2; if (tooltipArray.length > 1) tooltipHeight += (tooltipArray.length - 1) * LINE_HEIGHT; return tooltipHeight; } /** * Gets a protected/private field from a class using reflection. * * @param <T> * The return type of the field you are getting * @param <E> * The class the field is in * @param classToAccess * The ".class" of the class the field is in * @param instance * The instance of the class * @param fieldNames * comma seperated names the field may have (i.e. obfuscated, non * obfuscated). Obfustated field names can be found in * fml/conf/fields.csv * @return */ public static <T, E> T GetFieldByReflection (Class<? super E> classToAccess, E instance, String... fieldNames) { Field field = null; for (String fieldName : fieldNames) { try { field = classToAccess.getDeclaredField(fieldName); } catch (NoSuchFieldException e) {} if (field != null) break; } if (field != null) { field.setAccessible(true); T fieldT = null; try { fieldT = (T) field.get(instance); } catch (IllegalArgumentException e) {} catch (IllegalAccessException e) {} return fieldT; } return null; } public class FontCodes { // color codes for rendered strings public static final String BLACK = "\2470"; public static final String DARK_BLUE = "\2471"; public static final String DARK_GREEN = "\2472"; public static final String DARK_AQUA = "\2473"; public static final String DARK_RED = "\2474"; public static final String DARK_PURPLE = "\2475"; public static final String GOLD = "\2476"; public static final String GRAY = "\2477"; public static final String DARK_GREY = "\2478"; public static final String BLUE = "\2479"; public static final String GREEN = "\247a"; public static final String AQUA = "\247b"; public static final String RED = "\247c"; public static final String LIGHT_PURPLE = "\247d"; public static final String YELLOW = "\247e"; public static final String WHITE = "\247f"; // font styles public static final String OBFUSCATED = "\247k"; public static final String BOLD = "\247l"; public static final String STRIKETHROUGH = "\247m"; public static final String UNDERLINE = "\247n"; public static final String ITALICS = "\247o"; public static final String RESET = "\247r"; } }