diff --git a/src/main/java/mekanism/client/gui/element/window/GuiConfirmationDialog.java b/src/main/java/mekanism/client/gui/element/window/GuiConfirmationDialog.java index 384f94f1c19..42a15858a35 100644 --- a/src/main/java/mekanism/client/gui/element/window/GuiConfirmationDialog.java +++ b/src/main/java/mekanism/client/gui/element/window/GuiConfirmationDialog.java @@ -13,9 +13,9 @@ public class GuiConfirmationDialog extends GuiWindow { private final WrappedTextRenderer wrappedTextRenderer; - private GuiConfirmationDialog(IGuiWrapper gui, int x, int y, int width, int height, Component title, Runnable onConfirm, DialogType type) { + private GuiConfirmationDialog(IGuiWrapper gui, int x, int y, int width, int height, ReplaceableWrappedTextRenderer renderer, Runnable onConfirm, DialogType type) { super(gui, x, y, width, height, WindowType.CONFIRMATION); - this.wrappedTextRenderer = new WrappedTextRenderer(this, title); + this.wrappedTextRenderer = renderer.replaceFont(this); active = true; addChild(new TranslationButton(gui, relativeX + width / 2 - 51, relativeY + height - 24, 50, 18, MekanismLang.BUTTON_CANCEL, this::close)); @@ -27,8 +27,9 @@ private GuiConfirmationDialog(IGuiWrapper gui, int x, int y, int width, int heig public static void show(IGuiWrapper gui, Component title, Runnable onConfirm, DialogType type) { int width = 140; - int height = 33 + WrappedTextRenderer.calculateHeightRequired(gui.font(), title, width, width - 10); - gui.addWindow(new GuiConfirmationDialog(gui, (gui.getXSize() - width) / 2, (gui.getYSize() - height) / 2, width, height, title, onConfirm, type)); + ReplaceableWrappedTextRenderer renderer = new ReplaceableWrappedTextRenderer(gui, width, title); + int height = 33 + renderer.getRequiredHeight(width - 10); + gui.addWindow(new GuiConfirmationDialog(gui, (gui.getXSize() - width) / 2, (gui.getYSize() - height) / 2, width, height, renderer, onConfirm, type)); } @Override diff --git a/src/main/java/mekanism/client/render/IFancyFontRenderer.java b/src/main/java/mekanism/client/render/IFancyFontRenderer.java index 79c73b792c9..0c061ce3959 100644 --- a/src/main/java/mekanism/client/render/IFancyFontRenderer.java +++ b/src/main/java/mekanism/client/render/IFancyFontRenderer.java @@ -1,15 +1,15 @@ package mekanism.client.render; import com.mojang.blaze3d.vertex.PoseStack; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import mekanism.api.text.TextComponentUtil; import mekanism.client.SpecialColors; import net.minecraft.Util; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.network.chat.Component; +import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.Mth; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; @@ -45,10 +45,6 @@ default int inactiveButtonTextColor() { return SpecialColors.TEXT_INACTIVE_BUTTON.argb(); } - private int drawString(GuiGraphics guiGraphics, Component component, float x, float y, int color, boolean shadow) { - return guiGraphics.drawString(font(), component.getVisualOrderText(), x, y, color, shadow); - } - default int getStringWidth(Component component) { return font().width(component); } @@ -98,7 +94,7 @@ default void drawScrollingString(GuiGraphics guiGraphics, Component text, int mi } else { targetX = alignment.getTarget(minX, maxX, areaWidth, textWidth); } - drawString(guiGraphics, text, targetX, targetY, color, shadow); + guiGraphics.drawString(font(), text.getVisualOrderText(), targetX, targetY, color, shadow); if (isScrolling) { guiGraphics.disableScissor(); } @@ -137,7 +133,7 @@ default void drawScaledScrollingString(GuiGraphics guiGraphics, Component text, targetX = alignment.getTarget(minX, maxX, areaWidth, textWidth); } PoseStack pose = prepTextScale(guiGraphics, targetX, targetY, scale); - drawString(guiGraphics, text, 0, 0, color, shadow); + guiGraphics.drawString(font(), text, 0, 0, color, shadow); pose.popPose(); if (isScrolling) { guiGraphics.disableScissor(); @@ -200,113 +196,74 @@ public float getTarget(int minX, int maxX, float areaWidth, float textWidth) { } } - // efficient tool to draw word-by-word wrapped text based on a horizontal bound. looks intimidating but runs in O(n) class WrappedTextRenderer { - private final List linesToDraw = new ArrayList<>(); - private final IFancyFontRenderer font; - private final String text; + private final Component text; + private List linesToDraw = Collections.emptyList(); + + IFancyFontRenderer fontRenderer; @Nullable private Font lastFont; private int lastMaxLength = -1; - private int lineLength = 0; - public WrappedTextRenderer(IFancyFontRenderer font, Component text) { - this(font, text.getString()); - } - - public WrappedTextRenderer(IFancyFontRenderer font, String text) { - this.font = font; + public WrappedTextRenderer(IFancyFontRenderer fontRenderer, Component text) { + this.fontRenderer = fontRenderer; this.text = text; } public void renderCentered(GuiGraphics guiGraphics, int x, int y, int color, int maxLength) { calculateLines(maxLength); - int startY = y; - int lineHeight = font.getLineHeight(); - int width = font.getXSize(); - for (LineData line : linesToDraw) { - font.drawString(guiGraphics, line.component(), x + (width - line.length()) / 2F, startY, color, false); - startY += lineHeight; - } + drawLines(guiGraphics, x, y, color, true); } public int renderWithScale(GuiGraphics guiGraphics, int x, int y, int color, int maxLength, float scale) { //Divide by scale for calculating actual max length so that when the text is scaled it has the proper total space available calculateLines(Mth.floor(maxLength / scale)); - PoseStack pose = font.prepTextScale(guiGraphics, x, y, scale); - int startY = 0; - int lineHeight = font.getLineHeight(); - for (LineData line : linesToDraw) { - font.drawString(guiGraphics, line.component(), 0, startY, color, false); - startY += lineHeight; - } + PoseStack pose = fontRenderer.prepTextScale(guiGraphics, x, y, scale); + drawLines(guiGraphics, 0, 0, color, false); pose.popPose(); return linesToDraw.size(); } - void calculateLines(int maxLength) { + private void drawLines(GuiGraphics guiGraphics, int x, int startY, int color, boolean center) { + Font font = fontRenderer.font(); + for (FormattedCharSequence line : linesToDraw) { + float targetX; + if (center) { + targetX = x + (fontRenderer.getXSize() - font.width(line)) / 2F; + } else { + targetX = x; + } + guiGraphics.drawString(font, line, targetX, startY, color, false); + startY += font.lineHeight; + } + } + + private void calculateLines(int maxLength) { //If something changed since the last time we calculated it - Font font = this.font.font(); + Font font = fontRenderer.font(); if (font != null && (lastFont != font || lastMaxLength != maxLength)) { lastFont = font; lastMaxLength = maxLength; - linesToDraw.clear(); - StringBuilder lineBuilder = new StringBuilder(); - StringBuilder wordBuilder = new StringBuilder(); - int spaceLength = font.width(" "); - int wordLength = 0; - for (char c : text.toCharArray()) { - if (c == ' ') { - lineBuilder = addWord(lineBuilder, wordBuilder, maxLength, spaceLength, wordLength); - wordBuilder = new StringBuilder(); - wordLength = 0; - continue; - } - wordBuilder.append(c); - wordLength += font.width(Character.toString(c)); - } - if (!wordBuilder.isEmpty()) { - lineBuilder = addWord(lineBuilder, wordBuilder, maxLength, spaceLength, wordLength); - } - if (!lineBuilder.isEmpty()) { - linesToDraw.add(new LineData(lineBuilder, lineLength)); - } + linesToDraw = font.split(text, maxLength); } } - StringBuilder addWord(StringBuilder lineBuilder, StringBuilder wordBuilder, int maxLength, int spaceLength, int wordLength) { - // ignore spacing if this is the first word of the line - int spacingLength = lineBuilder.isEmpty() ? 0 : spaceLength; - if (lineLength + spacingLength + wordLength > maxLength) { - linesToDraw.add(new LineData(lineBuilder, lineLength)); - lineBuilder = new StringBuilder(wordBuilder); - lineLength = wordLength; - } else { - if (spacingLength > 0) { - lineBuilder.append(' '); - } - lineBuilder.append(wordBuilder); - lineLength += spacingLength + wordLength; - } - return lineBuilder; + public int getRequiredHeight(int maxLength) { + calculateLines(maxLength); + return fontRenderer.getLineHeight() * linesToDraw.size(); } + } - public static int calculateHeightRequired(Font font, Component text, int width, int maxLength) { - //TODO: Come up with a better way of doing this (maybe allow it to somehow replace what the stored font is - // that way we can calculate, and then use the calculated values in our actual renderer without having to calculate once more - WrappedTextRenderer wrappedTextRenderer = new WrappedTextRenderer(new SimpleFancyFontRenderer(font, width), text); - wrappedTextRenderer.calculateLines(maxLength); - return font.lineHeight * wrappedTextRenderer.linesToDraw.size(); - } + class ReplaceableWrappedTextRenderer extends WrappedTextRenderer { - private record LineData(Component component, int length) { + public ReplaceableWrappedTextRenderer(IFancyFontRenderer parent, int width, Component text) { + super(new SimpleFancyFontRenderer(parent.font(), width), text); + } - private LineData(StringBuilder lineBuilder, int length) { - //TODO - 1.21: Make use of the style of the passed in parent component that we split during? - // Similar to StringSplitter$LineBreakFinder - this(TextComponentUtil.getString(lineBuilder.toString()), length); - } + public WrappedTextRenderer replaceFont(IFancyFontRenderer font) { + this.fontRenderer = font; + return this; } private record SimpleFancyFontRenderer(Font font, int getXSize) implements IFancyFontRenderer {