minecraft-src/net/minecraft/client/gui/components/MultiLineEditBox.java
2025-09-18 12:37:33 +00:00

308 lines
9.2 KiB
Java

package net.minecraft.client.gui.components;
import java.util.function.Consumer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.Util;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.MultilineTextField.StringView;
import net.minecraft.client.gui.narration.NarratedElementType;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.util.StringUtil;
@Environment(EnvType.CLIENT)
public class MultiLineEditBox extends AbstractTextAreaWidget {
private static final int CURSOR_INSERT_WIDTH = 1;
private static final int CURSOR_COLOR = -3092272;
private static final String CURSOR_APPEND_CHARACTER = "_";
private static final int TEXT_COLOR = -2039584;
private static final int PLACEHOLDER_TEXT_COLOR = -857677600;
private static final int CURSOR_BLINK_INTERVAL_MS = 300;
private final Font font;
private final Component placeholder;
private final MultilineTextField textField;
private final int textColor;
private final boolean textShadow;
private final int cursorColor;
private long focusedTime = Util.getMillis();
MultiLineEditBox(
Font font,
int x,
int y,
int width,
int height,
Component placeholder,
Component message,
int textColor,
boolean textShadow,
int cursorColor,
boolean showBackground,
boolean showDecorations
) {
super(x, y, width, height, message, showBackground, showDecorations);
this.font = font;
this.textShadow = textShadow;
this.textColor = textColor;
this.cursorColor = cursorColor;
this.placeholder = placeholder;
this.textField = new MultilineTextField(font, width - this.totalInnerPadding());
this.textField.setCursorListener(this::scrollToCursor);
}
public void setCharacterLimit(int characterLimit) {
this.textField.setCharacterLimit(characterLimit);
}
public void setLineLimit(int lineLimit) {
this.textField.setLineLimit(lineLimit);
}
public void setValueListener(Consumer<String> valueListener) {
this.textField.setValueListener(valueListener);
}
public void setValue(String value) {
this.setValue(value, false);
}
public void setValue(String value, boolean force) {
this.textField.setValue(value, force);
}
public String getValue() {
return this.textField.value();
}
@Override
public void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {
narrationElementOutput.add(NarratedElementType.TITLE, Component.translatable("gui.narrate.editBox", this.getMessage(), this.getValue()));
}
@Override
public void onClick(double mouseX, double mouseY) {
this.textField.setSelecting(Screen.hasShiftDown());
this.seekCursorScreen(mouseX, mouseY);
}
@Override
protected void onDrag(double mouseX, double mouseY, double dragX, double dragY) {
this.textField.setSelecting(true);
this.seekCursorScreen(mouseX, mouseY);
this.textField.setSelecting(Screen.hasShiftDown());
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
return this.textField.keyPressed(keyCode);
}
@Override
public boolean charTyped(char codePoint, int modifiers) {
if (this.visible && this.isFocused() && StringUtil.isAllowedChatCharacter(codePoint)) {
this.textField.insertText(Character.toString(codePoint));
return true;
} else {
return false;
}
}
@Override
protected void renderContents(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
String string = this.textField.value();
if (string.isEmpty() && !this.isFocused()) {
guiGraphics.drawWordWrap(this.font, this.placeholder, this.getInnerLeft(), this.getInnerTop(), this.width - this.totalInnerPadding(), -857677600);
} else {
int i = this.textField.cursor();
boolean bl = this.isFocused() && (Util.getMillis() - this.focusedTime) / 300L % 2L == 0L;
boolean bl2 = i < string.length();
int j = 0;
int k = 0;
int l = this.getInnerTop();
for (StringView stringView : this.textField.iterateLines()) {
boolean bl3 = this.withinContentAreaTopBottom(l, l + 9);
int m = this.getInnerLeft();
if (bl && bl2 && i >= stringView.beginIndex() && i < stringView.endIndex()) {
if (bl3) {
String string2 = string.substring(stringView.beginIndex(), i);
guiGraphics.drawString(this.font, string2, m, l, this.textColor, this.textShadow);
j = m + this.font.width(string2);
guiGraphics.fill(j, l - 1, j + 1, l + 1 + 9, this.cursorColor);
guiGraphics.drawString(this.font, string.substring(i, stringView.endIndex()), j, l, this.textColor, this.textShadow);
}
} else {
if (bl3) {
String string2 = string.substring(stringView.beginIndex(), stringView.endIndex());
guiGraphics.drawString(this.font, string2, m, l, this.textColor, this.textShadow);
j = m + this.font.width(string2) - 1;
}
k = l;
}
l += 9;
}
if (bl && !bl2 && this.withinContentAreaTopBottom(k, k + 9)) {
guiGraphics.drawString(this.font, "_", j, k, this.cursorColor, this.textShadow);
}
if (this.textField.hasSelection()) {
StringView stringView2 = this.textField.getSelected();
int n = this.getInnerLeft();
l = this.getInnerTop();
for (StringView stringView3 : this.textField.iterateLines()) {
if (stringView2.beginIndex() > stringView3.endIndex()) {
l += 9;
} else {
if (stringView3.beginIndex() > stringView2.endIndex()) {
break;
}
if (this.withinContentAreaTopBottom(l, l + 9)) {
int o = this.font.width(string.substring(stringView3.beginIndex(), Math.max(stringView2.beginIndex(), stringView3.beginIndex())));
int p;
if (stringView2.endIndex() > stringView3.endIndex()) {
p = this.width - this.innerPadding();
} else {
p = this.font.width(string.substring(stringView3.beginIndex(), stringView2.endIndex()));
}
guiGraphics.textHighlight(n + o, l, n + p, l + 9);
}
l += 9;
}
}
}
}
}
@Override
protected void renderDecorations(GuiGraphics guiGraphics) {
super.renderDecorations(guiGraphics);
if (this.textField.hasCharacterLimit()) {
int i = this.textField.characterLimit();
Component component = Component.translatable("gui.multiLineEditBox.character_limit", this.textField.value().length(), i);
guiGraphics.drawString(this.font, component, this.getX() + this.width - this.font.width(component), this.getY() + this.height + 4, -6250336);
}
}
@Override
public int getInnerHeight() {
return 9 * this.textField.getLineCount();
}
@Override
protected double scrollRate() {
return 9.0 / 2.0;
}
private void scrollToCursor() {
double d = this.scrollAmount();
StringView stringView = this.textField.getLineView((int)(d / 9.0));
if (this.textField.cursor() <= stringView.beginIndex()) {
d = this.textField.getLineAtCursor() * 9;
} else {
StringView stringView2 = this.textField.getLineView((int)((d + this.height) / 9.0) - 1);
if (this.textField.cursor() > stringView2.endIndex()) {
d = this.textField.getLineAtCursor() * 9 - this.height + 9 + this.totalInnerPadding();
}
}
this.setScrollAmount(d);
}
private void seekCursorScreen(double mouseX, double mouseY) {
double d = mouseX - this.getX() - this.innerPadding();
double e = mouseY - this.getY() - this.innerPadding() + this.scrollAmount();
this.textField.seekCursorToPoint(d, e);
}
@Override
public void setFocused(boolean focused) {
super.setFocused(focused);
if (focused) {
this.focusedTime = Util.getMillis();
}
}
public static MultiLineEditBox.Builder builder() {
return new MultiLineEditBox.Builder();
}
@Environment(EnvType.CLIENT)
public static class Builder {
private int x;
private int y;
private Component placeholder = CommonComponents.EMPTY;
private int textColor = -2039584;
private boolean textShadow = true;
private int cursorColor = -3092272;
private boolean showBackground = true;
private boolean showDecorations = true;
public MultiLineEditBox.Builder setX(int x) {
this.x = x;
return this;
}
public MultiLineEditBox.Builder setY(int y) {
this.y = y;
return this;
}
public MultiLineEditBox.Builder setPlaceholder(Component placeholder) {
this.placeholder = placeholder;
return this;
}
public MultiLineEditBox.Builder setTextColor(int textColor) {
this.textColor = textColor;
return this;
}
public MultiLineEditBox.Builder setTextShadow(boolean textShadow) {
this.textShadow = textShadow;
return this;
}
public MultiLineEditBox.Builder setCursorColor(int cursorColor) {
this.cursorColor = cursorColor;
return this;
}
public MultiLineEditBox.Builder setShowBackground(boolean showBackground) {
this.showBackground = showBackground;
return this;
}
public MultiLineEditBox.Builder setShowDecorations(boolean showDecorations) {
this.showDecorations = showDecorations;
return this;
}
public MultiLineEditBox build(Font font, int width, int height, Component message) {
return new MultiLineEditBox(
font,
this.x,
this.y,
width,
height,
this.placeholder,
message,
this.textColor,
this.textShadow,
this.cursorColor,
this.showBackground,
this.showDecorations
);
}
}
}