LibJF/libjf-config-ui-tiny/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java

297 lines
12 KiB
Java

package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.*;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.*;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.text.*;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
@Environment(EnvType.CLIENT)
public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEntry> {
private final TextRenderer textRenderer;
private final boolean background;
public EntryListWidget(MinecraftClient client, TextRenderer tr, int width, int height, int top, int bottom) {
super(client, width, height, top, bottom, 25);
this.centerListVertically = false;
textRenderer = tr;
background = client.world == null;
setRenderBackground(background);
}
@Override
public int getScrollbarPositionX() {
return this.width -7;
}
public void addUnknown(ClickableWidget resetButton, BooleanSupplier resetVisible, Text text, Reflowable reflow) {
this.addEntry(new ConfigUnknownEntry(text, resetVisible, resetButton, reflow));
}
public void addButton(@Nullable ClickableWidget button, ClickableWidget resetButton, BooleanSupplier resetVisible, Text text, Reflowable reflow) {
this.addEntry(new ConfigScreenEntry(button, text, resetVisible, resetButton, reflow));
}
public void addReference(Text text, Supplier<Screen> targetScreen) {
this.addEntry(new ConfigReferenceEntry(width, text, targetScreen));
}
public void addText(Text text) {
for (ConfigEntry entry : ConfigTextEntry.create(this, text, width)) this.addEntry(entry);
}
@Override
public int getRowWidth() {
return 10000;
}
public Optional<Text> getHoveredEntryTitle(double mouseY) {
for (ConfigEntry abstractEntry : this.children()) {
if (abstractEntry instanceof ConfigScreenEntry entry
&& entry.button.visible
&& mouseY >= entry.button.getY() && mouseY < entry.button.getY() + itemHeight) {
return Optional.of(entry.getText());
} else if (abstractEntry instanceof ConfigUnknownEntry entry
&& mouseY >= entry.resetButton.getY() && mouseY < entry.resetButton.getY() + itemHeight) {
return Optional.of(entry.getText());
}
}
return Optional.empty();
}
public void refreshGrid(ScreenRect tabArea) {
updateSize(tabArea.width(), tabArea.height(), tabArea.getTop(), tabArea.getBottom());
setLeftPos(tabArea.getLeft());
for (int len = getEntryCount() - 1, i = len; i >= 0; i--) {
getEntry(i).reflow(width, height);
}
}
@Override
protected void renderDecorations(DrawContext context, int mouseX, int mouseY) {
super.renderDecorations(context, mouseX, mouseY);
if (background) return;
// Background texture at the top of the screen
context.setShaderColor(0.25f, 0.25f, 0.25f, 1.0f);
context.drawTexture(Screen.OPTIONS_BACKGROUND_TEXTURE, left, 0, 0.0f, 0.0f, width, top, 32, 32);
context.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
// Gradient at the top of the screen
context.fillGradient(RenderLayer.getGuiOverlay(), left, top, right, top + 4, 0xff000000, 0x00000000, 0);
// Gradient at the bottom of the screen
if (getScrollAmount() < getMaxScroll()) context.fillGradient(RenderLayer.getGuiOverlay(), left, bottom - 4, right, bottom, 0x00000000, 0xff000000, 0);
}
@Environment(EnvType.CLIENT)
public abstract static class ConfigEntry extends Entry<ConfigEntry> implements Reflowable {
@Override
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
if (hovered) {
context.fill(x, y, x + entryWidth, y + entryHeight, 0x24FFFFFF);
}
}
}
@Environment(EnvType.CLIENT)
public class ConfigTextEntry extends ConfigEntry {
private int width;
private final OrderedText text;
private final TextRenderer renderer;
private final ConfigTextEntry next;
public StringVisitable originalText = null;
public ConfigTextEntry(int width, OrderedText text, TextRenderer renderer, ConfigTextEntry next) {
this.width = width;
this.text = text;
this.renderer = renderer;
this.next = next;
}
public static List<ConfigTextEntry> create(EntryListWidget parent, StringVisitable text, int width) {
TextRenderer renderer = MinecraftClient.getInstance().textRenderer;
List<ConfigTextEntry> entries = new ArrayList<>();
ConfigTextEntry next = null;
List<OrderedText> wrappedLines = new ArrayList<>(renderer.wrapLines(text, width));
Collections.reverse(wrappedLines);
for (OrderedText line : wrappedLines) {
ConfigTextEntry[] tmp = new ConfigTextEntry[1];
entries.add(next = tmp[0] = parent.new ConfigTextEntry(width, line, renderer, next));
}
if (!entries.isEmpty()) entries.get(entries.size() - 1).originalText = text;
Collections.reverse(entries);
return entries;
}
@Override
public List<? extends Selectable> selectableChildren() {
return List.of();
}
@Override
public List<? extends Element> children() {
return List.of();
}
@Override
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
super.render(context, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta);
int textX = (width - renderer.getWidth(text)) / 2;
int textY = y + (20 - renderer.fontHeight) / 2;
context.drawTextWithShadow(renderer, text, textX, textY, 0xFFFFFF);
}
@Override
public void reflow(int width, int height) {
this.width = width;
if (originalText != null) {
List<ConfigEntry> children = EntryListWidget.this.children();
int i = children.indexOf(this);
EntryListWidget.this.removeEntry(this);
for (ConfigTextEntry entry : create(EntryListWidget.this, originalText, width)) {
children.add(i++, entry);
}
} else {
EntryListWidget.this.removeEntry(this);
}
}
}
@Environment(EnvType.CLIENT)
public static class ConfigReferenceEntry extends ConfigEntry {
private final ClickableWidget button;
public ConfigReferenceEntry(int width, Text text, Supplier<Screen> targetScreen) {
this.button = ButtonWidget.builder(text, btn -> MinecraftClient.getInstance().setScreen(targetScreen.get()))
.dimensions(width / 2 - 154, 0, 308, 20)
.build();
}
@Override
public List<? extends Selectable> selectableChildren() {
return List.of(button);
}
@Override
public List<? extends Element> children() {
return List.of(button);
}
@Override
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
super.render(context, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta);
button.setY(y);
button.render(context, mouseX, mouseY, tickDelta);
}
@Override
public void reflow(int width, int height) {
button.setX(width / 2 - 154);
}
}
@Environment(EnvType.CLIENT)
public static class ConfigScreenEntry extends ConfigEntry {
private static final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
public final ClickableWidget button;
private final BooleanSupplier resetVisible;
private final ClickableWidget resetButton;
private final Text text;
private final Reflowable reflow;
public ConfigScreenEntry(ClickableWidget button, Text text, BooleanSupplier resetVisible, ClickableWidget resetButton, Reflowable reflow) {
this.button = button;
this.resetVisible = resetVisible;
this.resetButton = resetButton;
this.text = text;
this.reflow = reflow;
}
@Override
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
super.render(context, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta);
button.setY(y);
button.render(context, mouseX, mouseY, tickDelta);
context.drawTextWithShadow(textRenderer, text, 12, y + 5, 0xFFFFFF);
if (resetVisible.getAsBoolean()) {
resetButton.setY(y);
resetButton.render(context, mouseX, mouseY, tickDelta);
}
}
@Override
public void reflow(int width, int height) {
reflow.reflow(width, height);
}
@Override
public List<? extends Element> children() {
return List.of(button, resetButton);
}
@Override
public List<? extends Selectable> selectableChildren() {
return List.of(button);
}
public Text getText() {
return text;
}
}
@Environment(EnvType.CLIENT)
public static class ConfigUnknownEntry extends ConfigEntry {
private static final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
private final BooleanSupplier resetVisible;
private final ClickableWidget resetButton;
private final Text text;
private final Reflowable reflow;
public ConfigUnknownEntry(Text text, BooleanSupplier resetVisible, ClickableWidget resetButton, Reflowable reflow) {
this.resetVisible = resetVisible;
this.resetButton = resetButton;
this.text = text;
this.reflow = reflow;
}
@Override
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
super.render(context, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta);
context.drawTextWithShadow(textRenderer, text, 12, y + 5, 0xFFFFFF);
if (resetVisible.getAsBoolean()) {
resetButton.setY(y);
resetButton.render(context, mouseX, mouseY, tickDelta);
}
}
@Override
public void reflow(int width, int height) {
reflow.reflow(width, height);
}
@Override
public List<? extends Element> children() {
return List.of(resetButton);
}
@Override
public List<? extends Selectable> selectableChildren() {
return List.of();
}
public Text getText() {
return text;
}
}
}