package net.minecraft.client.gui.font; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.StringSplitter; import net.minecraft.client.gui.screens.Screen; import net.minecraft.util.Mth; import net.minecraft.util.StringUtil; @Environment(EnvType.CLIENT) public class TextFieldHelper { private final Supplier getMessageFn; private final Consumer setMessageFn; private final Supplier getClipboardFn; private final Consumer setClipboardFn; private final Predicate stringValidator; private int cursorPos; private int selectionPos; public TextFieldHelper( Supplier getMessage, Consumer setMessage, Supplier getClipboard, Consumer setClipboard, Predicate stringValidator ) { this.getMessageFn = getMessage; this.setMessageFn = setMessage; this.getClipboardFn = getClipboard; this.setClipboardFn = setClipboard; this.stringValidator = stringValidator; this.setCursorToEnd(); } public static Supplier createClipboardGetter(Minecraft minecraft) { return () -> getClipboardContents(minecraft); } public static String getClipboardContents(Minecraft minecraft) { return ChatFormatting.stripFormatting(minecraft.keyboardHandler.getClipboard().replaceAll("\\r", "")); } public static Consumer createClipboardSetter(Minecraft minecraft) { return string -> setClipboardContents(minecraft, string); } public static void setClipboardContents(Minecraft minecraft, String text) { minecraft.keyboardHandler.setClipboard(text); } public boolean charTyped(char character) { if (StringUtil.isAllowedChatCharacter(character)) { this.insertText((String)this.getMessageFn.get(), Character.toString(character)); } return true; } public boolean keyPressed(int key) { if (Screen.isSelectAll(key)) { this.selectAll(); return true; } else if (Screen.isCopy(key)) { this.copy(); return true; } else if (Screen.isPaste(key)) { this.paste(); return true; } else if (Screen.isCut(key)) { this.cut(); return true; } else { TextFieldHelper.CursorStep cursorStep = Screen.hasControlDown() ? TextFieldHelper.CursorStep.WORD : TextFieldHelper.CursorStep.CHARACTER; if (key == 259) { this.removeFromCursor(-1, cursorStep); return true; } else { if (key == 261) { this.removeFromCursor(1, cursorStep); } else { if (key == 263) { this.moveBy(-1, Screen.hasShiftDown(), cursorStep); return true; } if (key == 262) { this.moveBy(1, Screen.hasShiftDown(), cursorStep); return true; } if (key == 268) { this.setCursorToStart(Screen.hasShiftDown()); return true; } if (key == 269) { this.setCursorToEnd(Screen.hasShiftDown()); return true; } } return false; } } } private int clampToMsgLength(int textIndex) { return Mth.clamp(textIndex, 0, ((String)this.getMessageFn.get()).length()); } private void insertText(String text, String clipboardText) { if (this.selectionPos != this.cursorPos) { text = this.deleteSelection(text); } this.cursorPos = Mth.clamp(this.cursorPos, 0, text.length()); String string = new StringBuilder(text).insert(this.cursorPos, clipboardText).toString(); if (this.stringValidator.test(string)) { this.setMessageFn.accept(string); this.selectionPos = this.cursorPos = Math.min(string.length(), this.cursorPos + clipboardText.length()); } } public void insertText(String text) { this.insertText((String)this.getMessageFn.get(), text); } private void resetSelectionIfNeeded(boolean keepSelection) { if (!keepSelection) { this.selectionPos = this.cursorPos; } } public void moveBy(int direction, boolean keepSelection, TextFieldHelper.CursorStep cursorStep) { switch (cursorStep) { case CHARACTER: this.moveByChars(direction, keepSelection); break; case WORD: this.moveByWords(direction, keepSelection); } } public void moveByChars(int direction) { this.moveByChars(direction, false); } public void moveByChars(int direction, boolean keepSelection) { this.cursorPos = Util.offsetByCodepoints((String)this.getMessageFn.get(), this.cursorPos, direction); this.resetSelectionIfNeeded(keepSelection); } public void moveByWords(int direction) { this.moveByWords(direction, false); } public void moveByWords(int direction, boolean keepSelection) { this.cursorPos = StringSplitter.getWordPosition((String)this.getMessageFn.get(), direction, this.cursorPos, true); this.resetSelectionIfNeeded(keepSelection); } public void removeFromCursor(int direction, TextFieldHelper.CursorStep step) { switch (step) { case CHARACTER: this.removeCharsFromCursor(direction); break; case WORD: this.removeWordsFromCursor(direction); } } public void removeWordsFromCursor(int direction) { int i = StringSplitter.getWordPosition((String)this.getMessageFn.get(), direction, this.cursorPos, true); this.removeCharsFromCursor(i - this.cursorPos); } public void removeCharsFromCursor(int direction) { String string = (String)this.getMessageFn.get(); if (!string.isEmpty()) { String string2; if (this.selectionPos != this.cursorPos) { string2 = this.deleteSelection(string); } else { int i = Util.offsetByCodepoints(string, this.cursorPos, direction); int j = Math.min(i, this.cursorPos); int k = Math.max(i, this.cursorPos); string2 = new StringBuilder(string).delete(j, k).toString(); if (direction < 0) { this.selectionPos = this.cursorPos = j; } } this.setMessageFn.accept(string2); } } public void cut() { String string = (String)this.getMessageFn.get(); this.setClipboardFn.accept(this.getSelected(string)); this.setMessageFn.accept(this.deleteSelection(string)); } public void paste() { this.insertText((String)this.getMessageFn.get(), (String)this.getClipboardFn.get()); this.selectionPos = this.cursorPos; } public void copy() { this.setClipboardFn.accept(this.getSelected((String)this.getMessageFn.get())); } public void selectAll() { this.selectionPos = 0; this.cursorPos = ((String)this.getMessageFn.get()).length(); } private String getSelected(String text) { int i = Math.min(this.cursorPos, this.selectionPos); int j = Math.max(this.cursorPos, this.selectionPos); return text.substring(i, j); } private String deleteSelection(String text) { if (this.selectionPos == this.cursorPos) { return text; } else { int i = Math.min(this.cursorPos, this.selectionPos); int j = Math.max(this.cursorPos, this.selectionPos); String string = text.substring(0, i) + text.substring(j); this.selectionPos = this.cursorPos = i; return string; } } public void setCursorToStart() { this.setCursorToStart(false); } public void setCursorToStart(boolean keepSelection) { this.cursorPos = 0; this.resetSelectionIfNeeded(keepSelection); } public void setCursorToEnd() { this.setCursorToEnd(false); } public void setCursorToEnd(boolean keepSelection) { this.cursorPos = ((String)this.getMessageFn.get()).length(); this.resetSelectionIfNeeded(keepSelection); } public int getCursorPos() { return this.cursorPos; } public void setCursorPos(int textIndex) { this.setCursorPos(textIndex, true); } public void setCursorPos(int textIndex, boolean keepSelection) { this.cursorPos = this.clampToMsgLength(textIndex); this.resetSelectionIfNeeded(keepSelection); } public int getSelectionPos() { return this.selectionPos; } public void setSelectionPos(int textIndex) { this.selectionPos = this.clampToMsgLength(textIndex); } public void setSelectionRange(int selectionStart, int selectionEnd) { int i = ((String)this.getMessageFn.get()).length(); this.cursorPos = Mth.clamp(selectionStart, 0, i); this.selectionPos = Mth.clamp(selectionEnd, 0, i); } public boolean isSelecting() { return this.cursorPos != this.selectionPos; } @Environment(EnvType.CLIENT) public static enum CursorStep { CHARACTER, WORD; } }