package net.minecraft.client.gui.screens.inventory; import com.mojang.blaze3d.platform.Lighting; import java.util.stream.IntStream; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.font.TextFieldHelper; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.AbstractSignRenderer; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket; import net.minecraft.util.ARGB; import net.minecraft.world.level.block.SignBlock; import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.level.block.entity.SignText; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.WoodType; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; @Environment(EnvType.CLIENT) public abstract class AbstractSignEditScreen extends Screen { /** * Reference to the sign object. */ protected final SignBlockEntity sign; private SignText text; private final String[] messages; private final boolean isFrontText; protected final WoodType woodType; /** * Counts the number of screen updates. */ private int frame; /** * The index of the line that is being edited. */ private int line; @Nullable private TextFieldHelper signField; public AbstractSignEditScreen(SignBlockEntity sign, boolean isFrontText, boolean isFiltered) { this(sign, isFrontText, isFiltered, Component.translatable("sign.edit")); } public AbstractSignEditScreen(SignBlockEntity sign, boolean isFrontText, boolean isFiltered, Component title) { super(title); this.sign = sign; this.text = sign.getText(isFrontText); this.isFrontText = isFrontText; this.woodType = SignBlock.getWoodType(sign.getBlockState().getBlock()); this.messages = (String[])IntStream.range(0, 4).mapToObj(i -> this.text.getMessage(i, isFiltered)).map(Component::getString).toArray(String[]::new); } @Override protected void init() { this.addRenderableWidget( Button.builder(CommonComponents.GUI_DONE, button -> this.onDone()).bounds(this.width / 2 - 100, this.height / 4 + 144, 200, 20).build() ); this.signField = new TextFieldHelper( () -> this.messages[this.line], this::setMessage, TextFieldHelper.createClipboardGetter(this.minecraft), TextFieldHelper.createClipboardSetter(this.minecraft), string -> this.minecraft.font.width(string) <= this.sign.getMaxTextLineWidth() ); } @Override public void tick() { this.frame++; if (!this.isValid()) { this.onDone(); } } private boolean isValid() { return this.minecraft != null && this.minecraft.player != null && !this.sign.isRemoved() && !this.sign.playerIsTooFarAwayToEdit(this.minecraft.player.getUUID()); } @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (keyCode == 265) { this.line = this.line - 1 & 3; this.signField.setCursorToEnd(); return true; } else if (keyCode == 264 || keyCode == 257 || keyCode == 335) { this.line = this.line + 1 & 3; this.signField.setCursorToEnd(); return true; } else { return this.signField.keyPressed(keyCode) ? true : super.keyPressed(keyCode, scanCode, modifiers); } } @Override public boolean charTyped(char codePoint, int modifiers) { this.signField.charTyped(codePoint); return true; } @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { super.render(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.flush(); Lighting.setupForFlatItems(); guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 40, 16777215); this.renderSign(guiGraphics); guiGraphics.flush(); Lighting.setupFor3DItems(); } @Override public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { this.renderTransparentBackground(guiGraphics); } @Override public void onClose() { this.onDone(); } @Override public void removed() { ClientPacketListener clientPacketListener = this.minecraft.getConnection(); if (clientPacketListener != null) { clientPacketListener.send( new ServerboundSignUpdatePacket(this.sign.getBlockPos(), this.isFrontText, this.messages[0], this.messages[1], this.messages[2], this.messages[3]) ); } } @Override public boolean isPauseScreen() { return false; } protected abstract void renderSignBackground(GuiGraphics guiGraphics); protected abstract Vector3f getSignTextScale(); protected void offsetSign(GuiGraphics guiGraphics, BlockState state) { guiGraphics.pose().translate(this.width / 2.0F, 90.0F, 50.0F); } private void renderSign(GuiGraphics guiGraphics) { guiGraphics.pose().pushPose(); this.offsetSign(guiGraphics, this.sign.getBlockState()); guiGraphics.pose().pushPose(); this.renderSignBackground(guiGraphics); guiGraphics.pose().popPose(); this.renderSignText(guiGraphics); guiGraphics.pose().popPose(); } private void renderSignText(GuiGraphics guiGraphics) { guiGraphics.pose().translate(0.0F, 0.0F, 4.0F); Vector3f vector3f = this.getSignTextScale(); guiGraphics.pose().scale(vector3f.x(), vector3f.y(), vector3f.z()); int i = this.text.hasGlowingText() ? this.text.getColor().getTextColor() : AbstractSignRenderer.getDarkColor(this.text); boolean bl = this.frame / 6 % 2 == 0; int j = this.signField.getCursorPos(); int k = this.signField.getSelectionPos(); int l = 4 * this.sign.getTextLineHeight() / 2; int m = this.line * this.sign.getTextLineHeight() - l; for (int n = 0; n < this.messages.length; n++) { String string = this.messages[n]; if (string != null) { if (this.font.isBidirectional()) { string = this.font.bidirectionalShaping(string); } int o = -this.font.width(string) / 2; guiGraphics.drawString(this.font, string, o, n * this.sign.getTextLineHeight() - l, i, false); if (n == this.line && j >= 0 && bl) { int p = this.font.width(string.substring(0, Math.max(Math.min(j, string.length()), 0))); int q = p - this.font.width(string) / 2; if (j >= string.length()) { guiGraphics.drawString(this.font, "_", q, m, i, false); } } } } for (int nx = 0; nx < this.messages.length; nx++) { String string = this.messages[nx]; if (string != null && nx == this.line && j >= 0) { int o = this.font.width(string.substring(0, Math.max(Math.min(j, string.length()), 0))); int p = o - this.font.width(string) / 2; if (bl && j < string.length()) { guiGraphics.fill(p, m - 1, p + 1, m + this.sign.getTextLineHeight(), ARGB.opaque(i)); } if (k != j) { int q = Math.min(j, k); int r = Math.max(j, k); int s = this.font.width(string.substring(0, q)) - this.font.width(string) / 2; int t = this.font.width(string.substring(0, r)) - this.font.width(string) / 2; int u = Math.min(s, t); int v = Math.max(s, t); guiGraphics.fill(RenderType.guiTextHighlight(), u, m, v, m + this.sign.getTextLineHeight(), -16776961); } } } } private void setMessage(String message) { this.messages[this.line] = message; this.text = this.text.setMessage(this.line, Component.literal(message)); this.sign.setText(this.text, this.isFrontText); } private void onDone() { this.minecraft.setScreen(null); } }