From da2964fbd38a13884fe4f4bf26dbfef6456932e0 Mon Sep 17 00:00:00 2001 From: mezz Date: Thu, 24 Mar 2016 20:39:08 -0700 Subject: [PATCH] Make tooltips layout in the right direction, wrap if there is no room Same as #2649, but for Minecraft 1.9 --- .../minecraft/client/gui/GuiScreen.java.patch | 37 ++-- .../fml/client/config/GuiConfig.java | 9 +- .../fml/client/config/GuiConfigEntries.java | 16 +- .../fml/client/config/GuiEditArray.java | 12 +- .../fml/client/config/GuiSelectString.java | 12 +- .../fml/client/config/GuiUtils.java | 184 ++++++++++++++++++ 6 files changed, 217 insertions(+), 53 deletions(-) diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreen.java.patch b/patches/minecraft/net/minecraft/client/gui/GuiScreen.java.patch index 8f6fd8340d..3110c8976f 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiScreen.java.patch +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreen.java.patch @@ -10,37 +10,22 @@ } protected void func_146279_a(String p_146279_1_, int p_146279_2_, int p_146279_3_) -@@ -151,6 +152,11 @@ +@@ -151,7 +152,13 @@ protected void func_146283_a(List p_146283_1_, int p_146283_2_, int p_146283_3_) { +- if (!p_146283_1_.isEmpty()) + drawHoveringText(p_146283_1_, p_146283_2_, p_146283_3_, field_146289_q); + } + + protected void drawHoveringText(List p_146283_1_, int p_146283_2_, int p_146283_3_, FontRenderer font) + { - if (!p_146283_1_.isEmpty()) ++ net.minecraftforge.fml.client.config.GuiUtils.drawHoveringText(p_146283_1_, p_146283_2_, p_146283_3_, field_146294_l, field_146295_m, -1, font); ++ if (false && !p_146283_1_.isEmpty()) { GlStateManager.func_179101_C(); -@@ -161,7 +167,7 @@ - - for (String s : p_146283_1_) - { -- int j = this.field_146289_q.func_78256_a(s); -+ int j = font.func_78256_a(s); - - if (j > i) - { -@@ -206,7 +212,7 @@ - for (int k1 = 0; k1 < p_146283_1_.size(); ++k1) - { - String s1 = (String)p_146283_1_.get(k1); -- this.field_146289_q.func_175063_a(s1, (float)l1, (float)i2, -1); -+ font.func_175063_a(s1, (float)l1, (float)i2, -1); - - if (k1 == 0) - { -@@ -421,6 +427,7 @@ + RenderHelper.func_74518_a(); +@@ -421,6 +428,7 @@ { this.field_146297_k.field_71456_v.func_146158_b().func_146239_a(p_175281_1_); } @@ -48,7 +33,7 @@ this.field_146297_k.field_71439_g.func_71165_d(p_175281_1_); } -@@ -435,9 +442,15 @@ +@@ -435,9 +443,15 @@ if (guibutton.func_146116_c(this.field_146297_k, p_73864_1_, p_73864_2_)) { @@ -64,7 +49,7 @@ } } } -@@ -467,8 +480,12 @@ +@@ -467,8 +481,12 @@ this.field_146289_q = p_146280_1_.field_71466_p; this.field_146294_l = p_146280_2_; this.field_146295_m = p_146280_3_; @@ -77,7 +62,7 @@ } public void func_183500_a(int p_183500_1_, int p_183500_2_) -@@ -487,7 +504,9 @@ +@@ -487,7 +505,9 @@ { while (Mouse.next()) { @@ -87,7 +72,7 @@ } } -@@ -495,7 +514,9 @@ +@@ -495,7 +515,9 @@ { while (Keyboard.next()) { @@ -97,7 +82,7 @@ } } } -@@ -557,6 +578,7 @@ +@@ -557,6 +579,7 @@ public void func_146276_q_() { this.func_146270_b(0); diff --git a/src/main/java/net/minecraftforge/fml/client/config/GuiConfig.java b/src/main/java/net/minecraftforge/fml/client/config/GuiConfig.java index 40585a3470..068a84ddf3 100644 --- a/src/main/java/net/minecraftforge/fml/client/config/GuiConfig.java +++ b/src/main/java/net/minecraftforge/fml/client/config/GuiConfig.java @@ -17,6 +17,7 @@ import static net.minecraftforge.fml.client.config.GuiUtils.UNDO_CHAR; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import net.minecraft.client.Minecraft; @@ -336,15 +337,15 @@ public class GuiConfig extends GuiScreen super.drawScreen(mouseX, mouseY, partialTicks); this.entryList.drawScreenPost(mouseX, mouseY, partialTicks); if (this.undoHoverChecker.checkHover(mouseX, mouseY)) - this.drawToolTip(this.mc.fontRendererObj.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.undoAll"), 300), mouseX, mouseY); + this.drawToolTip(Arrays.asList(I18n.format("fml.configgui.tooltip.undoAll").split("\n")), mouseX, mouseY); if (this.resetHoverChecker.checkHover(mouseX, mouseY)) - this.drawToolTip(this.mc.fontRendererObj.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.resetAll"), 300), mouseX, mouseY); + this.drawToolTip(Arrays.asList(I18n.format("fml.configgui.tooltip.resetAll").split("\n")), mouseX, mouseY); if (this.checkBoxHoverChecker.checkHover(mouseX, mouseY)) - this.drawToolTip(this.mc.fontRendererObj.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.applyGlobally"), 300), mouseX, mouseY); + this.drawToolTip(Arrays.asList(I18n.format("fml.configgui.tooltip.applyGlobally").split("\n")), mouseX, mouseY); } public void drawToolTip(List stringList, int x, int y) { - this.drawHoveringText(stringList, x, y); + GuiUtils.drawHoveringText(stringList, x, y, width, height, 300, fontRendererObj); } } diff --git a/src/main/java/net/minecraftforge/fml/client/config/GuiConfigEntries.java b/src/main/java/net/minecraftforge/fml/client/config/GuiConfigEntries.java index 53ede6229b..e4780b3e60 100644 --- a/src/main/java/net/minecraftforge/fml/client/config/GuiConfigEntries.java +++ b/src/main/java/net/minecraftforge/fml/client/config/GuiConfigEntries.java @@ -16,6 +16,7 @@ import static net.minecraftforge.fml.client.config.GuiUtils.UNDO_CHAR; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -1490,6 +1491,7 @@ public class GuiConfigEntries extends GuiListExtended this.defaultHoverChecker = new HoverChecker(this.btnDefault, 800); this.undoToolTip = Arrays.asList(new String[] { I18n.format("fml.configgui.tooltip.undoChanges") }); this.defaultToolTip = Arrays.asList(new String[] { I18n.format("fml.configgui.tooltip.resetToDefault") }); + this.toolTip = new ArrayList(); this.drawLabel = true; @@ -1498,23 +1500,19 @@ public class GuiConfigEntries extends GuiListExtended comment = I18n.format(configElement.getLanguageKey() + ".tooltip").replace("\\n", "\n"); if (!comment.equals(configElement.getLanguageKey() + ".tooltip")) - toolTip = new ArrayList(this.mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + name + "\n" + TextFormatting.YELLOW + comment, 300)); + Collections.addAll(toolTip, (TextFormatting.GREEN + name + "\n" + TextFormatting.YELLOW + comment).split("\n")); else if (configElement.getComment() != null && !configElement.getComment().trim().isEmpty()) - toolTip = new ArrayList(this.mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + name + "\n" + TextFormatting.YELLOW + configElement.getComment(), 300)); + Collections.addAll(toolTip, (TextFormatting.GREEN + name + "\n" + TextFormatting.YELLOW + configElement.getComment()).split("\n")); else - toolTip = new ArrayList(this.mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + name + "\n" + TextFormatting.RED + "No tooltip defined.", 300)); + Collections.addAll(toolTip, (TextFormatting.GREEN + name + "\n" + TextFormatting.RED + "No tooltip defined.").split("\n")); if ((configElement.getType() == ConfigGuiType.INTEGER && (Integer.valueOf(configElement.getMinValue().toString()) != Integer.MIN_VALUE || Integer.valueOf(configElement.getMaxValue().toString()) != Integer.MAX_VALUE)) || (configElement.getType() == ConfigGuiType.DOUBLE && (Double.valueOf(configElement.getMinValue().toString()) != -Double.MAX_VALUE || Double.valueOf(configElement.getMaxValue().toString()) != Double.MAX_VALUE))) - toolTip.addAll(this.mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.AQUA + I18n.format("fml.configgui.tooltip.defaultNumeric", configElement.getMinValue(), configElement.getMaxValue(), configElement.getDefault()), 300)); + Collections.addAll(toolTip, (TextFormatting.AQUA + I18n.format("fml.configgui.tooltip.defaultNumeric", configElement.getMinValue(), configElement.getMaxValue(), configElement.getDefault())).split("\n")); else if (configElement.getType() != ConfigGuiType.CONFIG_CATEGORY) - toolTip.addAll(this.mc.fontRendererObj.listFormattedStringToWidth(TextFormatting.AQUA + I18n.format("fml.configgui.tooltip.default", configElement.getDefault()),300)); + Collections.addAll(toolTip, (TextFormatting.AQUA + I18n.format("fml.configgui.tooltip.default", configElement.getDefault())).split("\n")); if (configElement.requiresMcRestart() || owningScreen.allRequireMcRestart) toolTip.add(TextFormatting.RED + "[" + I18n.format("fml.configgui.gameRestartTitle") + "]"); diff --git a/src/main/java/net/minecraftforge/fml/client/config/GuiEditArray.java b/src/main/java/net/minecraftforge/fml/client/config/GuiEditArray.java index 13e4cc95b9..403e07d26b 100644 --- a/src/main/java/net/minecraftforge/fml/client/config/GuiEditArray.java +++ b/src/main/java/net/minecraftforge/fml/client/config/GuiEditArray.java @@ -15,6 +15,7 @@ package net.minecraftforge.fml.client.config; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import net.minecraft.client.Minecraft; @@ -66,14 +67,11 @@ public class GuiEditArray extends GuiScreen "\n" + TextFormatting.AQUA, configElement.getDefault(), configElement.getMinValue(), configElement.getMaxValue()); if (!comment.equals(configElement.getLanguageKey() + ".tooltip")) - toolTip = mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + comment, 300); + Collections.addAll(toolTip, (TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + comment).split("\n")); else if (configElement.getComment() != null && !configElement.getComment().trim().isEmpty()) - toolTip = mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + configElement.getComment(), 300); + Collections.addAll(toolTip, (TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + configElement.getComment()).split("\n")); else - toolTip = mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + propName + "\n" + TextFormatting.RED + "No tooltip defined.", 300); + Collections.addAll(toolTip, (TextFormatting.GREEN + propName + "\n" + TextFormatting.RED + "No tooltip defined.").split("\n")); if (parentScreen instanceof GuiConfig) { @@ -205,6 +203,6 @@ public class GuiEditArray extends GuiScreen public void drawToolTip(List stringList, int x, int y) { - this.drawHoveringText(stringList, x, y); + GuiUtils.drawHoveringText(stringList, x, y, width, height, 300, fontRendererObj); } } diff --git a/src/main/java/net/minecraftforge/fml/client/config/GuiSelectString.java b/src/main/java/net/minecraftforge/fml/client/config/GuiSelectString.java index df245bdbae..afcc062363 100644 --- a/src/main/java/net/minecraftforge/fml/client/config/GuiSelectString.java +++ b/src/main/java/net/minecraftforge/fml/client/config/GuiSelectString.java @@ -14,6 +14,7 @@ package net.minecraftforge.fml.client.config; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -66,14 +67,11 @@ public class GuiSelectString extends GuiScreen "\n" + TextFormatting.AQUA, configElement.getDefault(), configElement.getMinValue(), configElement.getMaxValue()); if (!comment.equals(configElement.getLanguageKey() + ".tooltip")) - toolTip = mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + comment, 300); + Collections.addAll(toolTip, (TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + comment).split("\n")); else if (configElement.getComment() != null && !configElement.getComment().trim().isEmpty()) - toolTip = mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + configElement.getComment(), 300); + Collections.addAll(toolTip, (TextFormatting.GREEN + propName + "\n" + TextFormatting.YELLOW + configElement.getComment()).split("\n")); else - toolTip = mc.fontRendererObj.listFormattedStringToWidth( - TextFormatting.GREEN + propName + "\n" + TextFormatting.RED + "No tooltip defined.", 300); + Collections.addAll(toolTip, (TextFormatting.GREEN + propName + "\n" + TextFormatting.RED + "No tooltip defined.").split("\n")); if (parentScreen instanceof GuiConfig) { @@ -178,6 +176,6 @@ public class GuiSelectString extends GuiScreen public void drawToolTip(List stringList, int x, int y) { - this.drawHoveringText(stringList, x, y); + GuiUtils.drawHoveringText(stringList, x, y, width, height, 300, fontRendererObj); } } diff --git a/src/main/java/net/minecraftforge/fml/client/config/GuiUtils.java b/src/main/java/net/minecraftforge/fml/client/config/GuiUtils.java index b76587a143..3d3ae26c99 100644 --- a/src/main/java/net/minecraftforge/fml/client/config/GuiUtils.java +++ b/src/main/java/net/minecraftforge/fml/client/config/GuiUtils.java @@ -13,7 +13,9 @@ package net.minecraftforge.fml.client.config; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; @@ -21,6 +23,9 @@ import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL11; +import java.util.ArrayList; +import java.util.List; + /** * This class provides several methods and constants used by the Config GUI classes. * @@ -193,4 +198,183 @@ public class GuiUtils tessellator.draw(); } + /** + * Draws a tooltip box on the screen with text in it. + * Automatically positions the box relative to the mouse to match Mojang's implementation. + * Automatically wraps text when there is not enough space on the screen to display the text without wrapping. + * Can have a maximum width set to avoid creating very wide tooltips. + * + * @param textLines the lines of text to be drawn in a hovering tooltip box. + * @param mouseX the mouse X position + * @param mouseY the mouse Y position + * @param screenWidth the available screen width for the tooltip to drawn in + * @param screenHeight the available screen height for the tooltip to drawn in + * @param maxTextWidth the maximum width of the text in the tooltip box. + * Set to a negative number to have no max width. + * @param font the font for drawing the text in the tooltip box + */ + public static void drawHoveringText(List textLines, final int mouseX, final int mouseY, final int screenWidth, final int screenHeight, final int maxTextWidth, FontRenderer font) + { + if (!textLines.isEmpty()) + { + GlStateManager.disableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + int tooltipTextWidth = 0; + + for (String textLine : textLines) + { + int textLineWidth = font.getStringWidth(textLine); + + if (textLineWidth > tooltipTextWidth) + { + tooltipTextWidth = textLineWidth; + } + } + + boolean needsWrap = false; + + int titleLinesCount = 1; + int tooltipX = mouseX + 12; + if (tooltipX + tooltipTextWidth + 4 > screenWidth) + { + tooltipX = mouseX - 16 - tooltipTextWidth; + if (tooltipX < 4) // if the tooltip doesn't fit on the screen + { + if (mouseX > screenWidth / 2) + { + tooltipTextWidth = mouseX - 12 - 8; + } + else + { + tooltipTextWidth = screenWidth - 16 - mouseX; + } + needsWrap = true; + } + } + + if (maxTextWidth > 0 && tooltipTextWidth > maxTextWidth) + { + tooltipTextWidth = maxTextWidth; + needsWrap = true; + } + + if (needsWrap) + { + int wrappedTooltipWidth = 0; + List wrappedTextLines = new ArrayList(); + for (int i = 0; i < textLines.size(); i++) + { + String textLine = textLines.get(i); + List wrappedLine = font.listFormattedStringToWidth(textLine, tooltipTextWidth); + if (i == 0) + { + titleLinesCount = wrappedLine.size(); + } + + for (String line : wrappedLine) + { + int lineWidth = font.getStringWidth(line); + if (lineWidth > wrappedTooltipWidth) + { + wrappedTooltipWidth = lineWidth; + } + wrappedTextLines.add(line); + } + } + tooltipTextWidth = wrappedTooltipWidth; + textLines = wrappedTextLines; + + if (mouseX > screenWidth / 2) + { + tooltipX = mouseX - 16 - tooltipTextWidth; + } + else + { + tooltipX = mouseX + 12; + } + } + + int tooltipY = mouseY - 12; + int tooltipHeight = 8; + + if (textLines.size() > 1) + { + tooltipHeight += (textLines.size() - 1) * 10; + if (textLines.size() > titleLinesCount) { + tooltipHeight += 2; // gap between title lines and next lines + } + } + + if (tooltipY + tooltipHeight + 6 > screenHeight) + { + tooltipY = screenHeight - tooltipHeight - 6; + } + + final int zLevel = 300; + final int backgroundColor = 0xF0100010; + drawGradientRect(zLevel, tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3, tooltipY - 3, backgroundColor, backgroundColor); + drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, backgroundColor, backgroundColor); + drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + drawGradientRect(zLevel, tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 3, tooltipY - 3, tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + final int borderColorStart = 0x505000FF; + final int borderColorEnd = (borderColorStart & 0xFEFEFE) >> 1 | borderColorStart & 0xFF000000; + drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd); + drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd); + drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColorStart, borderColorStart); + drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 2, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, borderColorEnd, borderColorEnd); + + for (int lineNumber = 0; lineNumber < textLines.size(); ++lineNumber) + { + String line = textLines.get(lineNumber); + font.drawStringWithShadow(line, (float)tooltipX, (float)tooltipY, -1); + + if (lineNumber + 1 == titleLinesCount) + { + tooltipY += 2; + } + + tooltipY += 10; + } + + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableRescaleNormal(); + } + } + + public static void drawGradientRect(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) + { + float startAlpha = (float)(startColor >> 24 & 255) / 255.0F; + float startRed = (float)(startColor >> 16 & 255) / 255.0F; + float startGreen = (float)(startColor >> 8 & 255) / 255.0F; + float startBlue = (float)(startColor & 255) / 255.0F; + float endAlpha = (float)(endColor >> 24 & 255) / 255.0F; + float endRed = (float)(endColor >> 16 & 255) / 255.0F; + float endGreen = (float)(endColor >> 8 & 255) / 255.0F; + float endBlue = (float)(endColor & 255) / 255.0F; + + GlStateManager.disableTexture2D(); + GlStateManager.enableBlend(); + GlStateManager.disableAlpha(); + GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + GlStateManager.shadeModel(7425); + + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer vertexbuffer = tessellator.getBuffer(); + vertexbuffer.begin(7, DefaultVertexFormats.POSITION_COLOR); + vertexbuffer.pos(right, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex(); + vertexbuffer.pos(left, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex(); + vertexbuffer.pos(left, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex(); + vertexbuffer.pos(right, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex(); + tessellator.draw(); + + GlStateManager.shadeModel(7424); + GlStateManager.disableBlend(); + GlStateManager.enableAlpha(); + GlStateManager.enableTexture2D(); + } }