Skip to content

Commit

Permalink
Make use of vanilla's line splitter to ensure we properly maintain an…
Browse files Browse the repository at this point in the history
…y styles the component might have
  • Loading branch information
pupnewfster committed Oct 12, 2024
1 parent 0896283 commit 69de805
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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
Expand Down
125 changes: 41 additions & 84 deletions src/main/java/mekanism/client/render/IFancyFontRenderer.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<LineData> linesToDraw = new ArrayList<>();
private final IFancyFontRenderer font;
private final String text;
private final Component text;
private List<FormattedCharSequence> 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 {
Expand Down

0 comments on commit 69de805

Please sign in to comment.