Rewrite dynamic page contents to allow adding more types of elements

This commit is contained in:
Sollace 2022-09-01 22:27:18 +02:00
parent af385ac671
commit bb09feca7e
3 changed files with 124 additions and 47 deletions

View file

@ -6,7 +6,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.common.client.gui.dimension.Bounds;
import com.minelittlepony.unicopia.container.SpellbookChapterList.Content; 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.minelittlepony.unicopia.entity.player.Pony;
import com.mojang.blaze3d.systems.RenderSystem; 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<Page.Paragraph> paragraphs = new ArrayList<>(); private final List<Page.Paragraph> paragraphs = new ArrayList<>();
private final List<Page.Image> images = new ArrayList<>(); private final List<Page.Image> images = new ArrayList<>();
private boolean compiled = false; private final List<PageElement> elements = new ArrayList<>();
private final List<Text> wrappedText = new ArrayList<>();
private final Text title; private final Text title;
private final int level; private final int level;
private boolean compiled;
public Page(JsonObject json) { public Page(JsonObject json) {
title = Text.Serializer.fromJson(json.get("title")); title = Text.Serializer.fromJson(json.get("title"));
level = JsonHelper.getInt(json, "level", 0); level = JsonHelper.getInt(json, "level", 0);
@ -93,7 +94,8 @@ public class DynamicContent implements Content {
JsonHelper.getInt(image, "x", 0), JsonHelper.getInt(image, "x", 0),
JsonHelper.getInt(image, "width", 0), JsonHelper.getInt(image, "width", 0),
JsonHelper.getInt(image, "height", 0) JsonHelper.getInt(image, "height", 0)
) ),
Flow.valueOf(JsonHelper.getString(image, "flow", "RIGHT"))
)); ));
} else { } else {
paragraphs.add(new Paragraph(lineNumber[0], Text.Serializer.fromJson(element))); 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() { public void reset() {
compiled = false; compiled = false;
} }
@Override @Override
public void draw(MatrixStack matrices, int mouseX, int mouseY) { 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; TextRenderer font = MinecraftClient.getInstance().textRenderer;
boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level;
@ -148,21 +138,22 @@ public class DynamicContent implements Content {
matrices.pop(); matrices.pop();
matrices.push(); matrices.push();
matrices.translate(0, 16, 0); matrices.translate(bounds.left, bounds.top + 16, 0);
elements.stream().filter(PageElement::isFloating).forEach(element -> {
for (int y = 0; y < wrappedText.size(); y++) { Bounds bounds = element.bounds();
Text line = wrappedText.get(y); matrices.push();
if (needsMoreXp) {
line = line.copy().formatted(Formatting.OBFUSCATED);
}
font.draw(matrices, line,
bounds.left,
bounds.top + (y * font.fontHeight),
0
);
}
matrices.translate(bounds.left, bounds.top, 0); matrices.translate(bounds.left, bounds.top, 0);
element.draw(matrices, mouseX, mouseY);
matrices.pop();
});
matrices.push();
elements.stream().filter(PageElement::isInline).forEach(element -> {
element.draw(matrices, mouseX, mouseY);
matrices.translate(0, element.bounds().height, 0);
});
matrices.pop();
images.forEach(image -> image.draw(matrices, mouseX, mouseY)); images.forEach(image -> image.draw(matrices, mouseX, mouseY));
matrices.pop(); matrices.pop();
} }
@ -171,7 +162,8 @@ public class DynamicContent implements Content {
public record Image( public record Image(
Identifier texture, Identifier texture,
Bounds bounds) implements Draw { Bounds bounds,
Flow flow) implements PageElement {
@Override @Override
public void draw(MatrixStack matrices, int mouseX, int mouseY) { public void draw(MatrixStack matrices, int mouseX, int mouseY) {
RenderSystem.setShaderTexture(0, texture); RenderSystem.setShaderTexture(0, texture);
@ -179,5 +171,90 @@ public class DynamicContent implements Content {
RenderSystem.setShaderTexture(0, SpellbookScreen.TEXTURE); 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<Line> 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
}
} }
} }

View file

@ -1,7 +1,7 @@
package com.minelittlepony.unicopia.container; package com.minelittlepony.unicopia.container;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.BiConsumer;
import it.unimi.dsi.fastutil.ints.Int2IntFunction; import it.unimi.dsi.fastutil.ints.Int2IntFunction;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@ -22,9 +22,9 @@ class ParagraphWrappingVisitor implements StyledVisitor<Object> {
private boolean progressedNonEmpty; private boolean progressedNonEmpty;
private final Int2IntFunction widthSupplier; private final Int2IntFunction widthSupplier;
private final Consumer<Text> lineConsumer; private final BiConsumer<Text, Integer> lineConsumer;
ParagraphWrappingVisitor(DynamicContent.Page page, Int2IntFunction widthSupplier, Consumer<Text> lineConsumer) { ParagraphWrappingVisitor(Int2IntFunction widthSupplier, BiConsumer<Text, Integer> lineConsumer) {
this.widthSupplier = widthSupplier; this.widthSupplier = widthSupplier;
this.lineConsumer = lineConsumer; this.lineConsumer = lineConsumer;
pageWidth = widthSupplier.applyAsInt((line) * font.fontHeight); pageWidth = widthSupplier.applyAsInt((line) * font.fontHeight);
@ -87,7 +87,7 @@ class ParagraphWrappingVisitor implements StyledVisitor<Object> {
public void advance() { public void advance() {
if (progressedNonEmpty || currentLineCollectedLength > 0) { if (progressedNonEmpty || currentLineCollectedLength > 0) {
progressedNonEmpty = true; progressedNonEmpty = true;
lineConsumer.accept(currentLine); lineConsumer.accept(currentLine, (++line) * font.fontHeight);
} }
pageWidth = widthSupplier.applyAsInt((++line) * font.fontHeight); pageWidth = widthSupplier.applyAsInt((++line) * font.fontHeight);
currentLine = Text.empty(); currentLine = Text.empty();

View file

@ -65,12 +65,12 @@ public class SpellbookChapterList {
RIGHT RIGHT
} }
public interface Content extends Draw { public interface Content extends Drawable {
void init(SpellbookScreen screen); void init(SpellbookScreen screen);
default void copyStateFrom(Content old) {} default void copyStateFrom(Content old) {}
static Optional<Content> of(Consumer<SpellbookScreen> init, Draw draw) { static Optional<Content> of(Consumer<SpellbookScreen> init, Drawable obj) {
return Optional.of(new Content() { return Optional.of(new Content() {
@Override @Override
public void init(SpellbookScreen screen) { public void init(SpellbookScreen screen) {
@ -79,13 +79,13 @@ public class SpellbookChapterList {
@Override @Override
public void draw(MatrixStack matrices, int mouseX, int mouseY) { 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); void draw(MatrixStack matrices, int mouseX, int mouseY);
} }
} }