mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-01 11:36:43 +01:00
Rewrite dynamic page contents to allow adding more types of elements
This commit is contained in:
parent
af385ac671
commit
bb09feca7e
3 changed files with 124 additions and 47 deletions
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue