From bb09feca7e80046adbc13a697a796e0d82e84e4e Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Sep 2022 22:27:18 +0200 Subject: [PATCH] Rewrite dynamic page contents to allow adding more types of elements --- .../unicopia/container/DynamicContent.java | 155 +++++++++++++----- .../container/ParagraphWrappingVisitor.java | 8 +- .../container/SpellbookChapterList.java | 8 +- 3 files changed, 124 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/container/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/container/DynamicContent.java index 91232854..00f34e6e 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/container/DynamicContent.java @@ -6,7 +6,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.container.SpellbookChapterList.Content; -import com.minelittlepony.unicopia.container.SpellbookChapterList.Draw; +import com.minelittlepony.unicopia.container.SpellbookChapterList.Drawable; import com.minelittlepony.unicopia.entity.player.Pony; import com.mojang.blaze3d.systems.RenderSystem; @@ -66,16 +66,17 @@ public class DynamicContent implements Content { }); } - class Page implements Draw { + class Page implements Drawable { private final List paragraphs = new ArrayList<>(); private final List images = new ArrayList<>(); - private boolean compiled = false; - private final List wrappedText = new ArrayList<>(); + private final List elements = new ArrayList<>(); private final Text title; private final int level; + private boolean compiled; + public Page(JsonObject json) { title = Text.Serializer.fromJson(json.get("title")); level = JsonHelper.getInt(json, "level", 0); @@ -93,7 +94,8 @@ public class DynamicContent implements Content { JsonHelper.getInt(image, "x", 0), JsonHelper.getInt(image, "width", 0), JsonHelper.getInt(image, "height", 0) - ) + ), + Flow.valueOf(JsonHelper.getString(image, "flow", "RIGHT")) )); } else { paragraphs.add(new Paragraph(lineNumber[0], Text.Serializer.fromJson(element))); @@ -104,33 +106,21 @@ public class DynamicContent implements Content { }); } - public void compile() { - if (!compiled) { - compiled = true; - wrappedText.clear(); - ParagraphWrappingVisitor visitor = new ParagraphWrappingVisitor(this, yPosition -> { - return (bounds.width / 2 - 10) - images.stream() - .map(Image::bounds) - .filter(b -> b.contains(b.left + b.width / 2, yPosition)) - .mapToInt(b -> b.width) - .max() - .orElse(0); - }, wrappedText::add); - paragraphs.forEach(paragraph -> { - paragraph.text().visit(visitor, Style.EMPTY); - visitor.forceAdvance(); - }); - } - } - public void reset() { compiled = false; } @Override public void draw(MatrixStack matrices, int mouseX, int mouseY) { + if (!compiled) { + compiled = true; + int relativeY = 0; + for (PageElement element : elements.stream().filter(PageElement::isInline).toList()) { + element.compile(relativeY); + relativeY += element.bounds().height; + } + } - compile(); TextRenderer font = MinecraftClient.getInstance().textRenderer; boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; @@ -148,21 +138,22 @@ public class DynamicContent implements Content { matrices.pop(); matrices.push(); - matrices.translate(0, 16, 0); + matrices.translate(bounds.left, bounds.top + 16, 0); + elements.stream().filter(PageElement::isFloating).forEach(element -> { + Bounds bounds = element.bounds(); + matrices.push(); + matrices.translate(bounds.left, bounds.top, 0); + element.draw(matrices, mouseX, mouseY); + matrices.pop(); + }); + matrices.push(); - for (int y = 0; y < wrappedText.size(); y++) { - Text line = wrappedText.get(y); - if (needsMoreXp) { - line = line.copy().formatted(Formatting.OBFUSCATED); - } - font.draw(matrices, line, - bounds.left, - bounds.top + (y * font.fontHeight), - 0 - ); - } + elements.stream().filter(PageElement::isInline).forEach(element -> { + element.draw(matrices, mouseX, mouseY); + matrices.translate(0, element.bounds().height, 0); + }); + matrices.pop(); - matrices.translate(bounds.left, bounds.top, 0); images.forEach(image -> image.draw(matrices, mouseX, mouseY)); matrices.pop(); } @@ -171,7 +162,8 @@ public class DynamicContent implements Content { public record Image( Identifier texture, - Bounds bounds) implements Draw { + Bounds bounds, + Flow flow) implements PageElement { @Override public void draw(MatrixStack matrices, int mouseX, int mouseY) { RenderSystem.setShaderTexture(0, texture); @@ -179,5 +171,90 @@ public class DynamicContent implements Content { RenderSystem.setShaderTexture(0, SpellbookScreen.TEXTURE); } } + + protected int getLineLimitAt(int yPosition) { + return (bounds.width / 2 - 10) - elements.stream() + .filter(PageElement::isFloating) + .map(PageElement::bounds) + .filter(b -> b.contains(b.left + b.width / 2, yPosition)) + .mapToInt(b -> b.width) + .sum(); + } + + protected int getLeftMarginAt(int yPosition) { + return elements.stream() + .filter(p -> p.flow() == Flow.LEFT) + .map(PageElement::bounds) + .filter(b -> b.contains(b.left + b.width / 2, yPosition)) + .mapToInt(b -> b.width) + .sum(); + } + + public class TextBlock implements PageElement { + private final Text unwrappedText; + private final List wrappedText = new ArrayList<>(); + private final Bounds bounds = Bounds.empty(); + + public TextBlock(Text text) { + unwrappedText = text; + } + + @Override + public void compile(int y) { + wrappedText.clear(); + ParagraphWrappingVisitor visitor = new ParagraphWrappingVisitor(yPosition -> { + return getLineLimitAt(y + yPosition); + }, (line, yPosition) -> { + wrappedText.add(new Line(line, getLeftMarginAt(y + yPosition))); + }); + unwrappedText.visit(visitor, Style.EMPTY); + visitor.forceAdvance(); + bounds.height = MinecraftClient.getInstance().textRenderer.fontHeight * (wrappedText.size() + 1); + } + + @Override + public void draw(MatrixStack matrices, int mouseX, int mouseY) { + TextRenderer font = MinecraftClient.getInstance().textRenderer; + boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; + matrices.push(); + wrappedText.forEach(line -> { + font.draw(matrices, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text(), line.x(), 0, 0); + matrices.translate(0, font.fontHeight, 0); + }); + matrices.pop(); + } + + @Override + public Bounds bounds() { + return bounds; + } + + @Override + public Flow flow() { + return Flow.NONE; + } + + private record Line(Text text, int x) { } + } + + private interface PageElement extends Drawable { + Bounds bounds(); + + Flow flow(); + + default boolean isInline() { + return flow() == Flow.NONE; + } + + default boolean isFloating() { + return !isInline(); + } + + default void compile(int y) {} + } + + private enum Flow { + NONE, LEFT, RIGHT + } } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/container/ParagraphWrappingVisitor.java b/src/main/java/com/minelittlepony/unicopia/container/ParagraphWrappingVisitor.java index 5190b116..22c5c317 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/ParagraphWrappingVisitor.java +++ b/src/main/java/com/minelittlepony/unicopia/container/ParagraphWrappingVisitor.java @@ -1,7 +1,7 @@ package com.minelittlepony.unicopia.container; import java.util.*; -import java.util.function.Consumer; +import java.util.function.BiConsumer; import it.unimi.dsi.fastutil.ints.Int2IntFunction; import net.minecraft.client.MinecraftClient; @@ -22,9 +22,9 @@ class ParagraphWrappingVisitor implements StyledVisitor { private boolean progressedNonEmpty; private final Int2IntFunction widthSupplier; - private final Consumer lineConsumer; + private final BiConsumer lineConsumer; - ParagraphWrappingVisitor(DynamicContent.Page page, Int2IntFunction widthSupplier, Consumer lineConsumer) { + ParagraphWrappingVisitor(Int2IntFunction widthSupplier, BiConsumer lineConsumer) { this.widthSupplier = widthSupplier; this.lineConsumer = lineConsumer; pageWidth = widthSupplier.applyAsInt((line) * font.fontHeight); @@ -87,7 +87,7 @@ class ParagraphWrappingVisitor implements StyledVisitor { public void advance() { if (progressedNonEmpty || currentLineCollectedLength > 0) { progressedNonEmpty = true; - lineConsumer.accept(currentLine); + lineConsumer.accept(currentLine, (++line) * font.fontHeight); } pageWidth = widthSupplier.applyAsInt((++line) * font.fontHeight); currentLine = Text.empty(); diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterList.java b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterList.java index 732c470c..ba45053d 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterList.java +++ b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterList.java @@ -65,12 +65,12 @@ public class SpellbookChapterList { RIGHT } - public interface Content extends Draw { + public interface Content extends Drawable { void init(SpellbookScreen screen); default void copyStateFrom(Content old) {} - static Optional of(Consumer init, Draw draw) { + static Optional of(Consumer init, Drawable obj) { return Optional.of(new Content() { @Override public void init(SpellbookScreen screen) { @@ -79,13 +79,13 @@ public class SpellbookChapterList { @Override public void draw(MatrixStack matrices, int mouseX, int mouseY) { - draw.draw(matrices, mouseX, mouseY); + obj.draw(matrices, mouseX, mouseY); } }); } } - public interface Draw { + public interface Drawable { void draw(MatrixStack matrices, int mouseX, int mouseY); } }