Re-implement GuiTextField without depending on the Minecraft Text Field
This commit is contained in:
@@ -8,6 +8,7 @@ import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||
import de.johni0702.minecraft.gui.element.GuiPasswordField;
|
||||
import de.johni0702.minecraft.gui.element.GuiTextField;
|
||||
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||
import eu.crushedpixel.replaymod.gui.GuiConstants;
|
||||
|
||||
public class GuiLoginPrompt extends AbstractGuiScreen<GuiLoginPrompt> {
|
||||
@@ -72,9 +73,9 @@ public class GuiLoginPrompt extends AbstractGuiScreen<GuiLoginPrompt> {
|
||||
new GuiRegister(apiClient, GuiLoginPrompt.this).display();
|
||||
}
|
||||
});
|
||||
Runnable contentValidation = new Runnable() {
|
||||
Consumer<String> contentValidation = new Consumer<String>() {
|
||||
@Override
|
||||
public void run() {
|
||||
public void consume(String obj) {
|
||||
loginButton.setEnabled(!username.getText().isEmpty() && !password.getText().isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import de.johni0702.minecraft.gui.element.*;
|
||||
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import de.johni0702.minecraft.gui.utils.Consumers;
|
||||
import eu.crushedpixel.replaymod.gui.GuiConstants;
|
||||
import eu.crushedpixel.replaymod.utils.EmailAddressUtils;
|
||||
import eu.crushedpixel.replaymod.utils.RegexUtils;
|
||||
@@ -137,7 +138,7 @@ public class GuiRegister extends AbstractGuiScreen<GuiRegister> {
|
||||
}
|
||||
}
|
||||
};
|
||||
inputs.forEach(IGuiTextField.class).onTextChanged(contentValidation);
|
||||
inputs.forEach(IGuiTextField.class).onTextChanged(Consumers.from(contentValidation));
|
||||
|
||||
cancelButton.onClick(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -165,9 +165,9 @@ public class GuiReplayViewer extends GuiScreen {
|
||||
popup.getYesButton().onClick();
|
||||
}
|
||||
}
|
||||
}).onTextChanged(new Runnable() {
|
||||
}).onTextChanged(new Consumer<String>() {
|
||||
@Override
|
||||
public void run() {
|
||||
public void consume(String obj) {
|
||||
popup.getYesButton().setEnabled(!nameField.getText().isEmpty());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
package de.johni0702.minecraft.gui.element;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||
import de.johni0702.minecraft.gui.RenderInfo;
|
||||
@@ -30,84 +31,401 @@ import de.johni0702.minecraft.gui.function.Clickable;
|
||||
import de.johni0702.minecraft.gui.function.Focusable;
|
||||
import de.johni0702.minecraft.gui.function.Tickable;
|
||||
import de.johni0702.minecraft.gui.function.Typeable;
|
||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import lombok.NonNull;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.ChatAllowedCharacters;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.util.Dimension;
|
||||
import org.lwjgl.util.ReadableColor;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
import org.lwjgl.util.ReadablePoint;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.util.*;
|
||||
|
||||
import static net.minecraft.client.renderer.GlStateManager.*;
|
||||
import static net.minecraft.client.renderer.GlStateManager.disableColorLogic;
|
||||
import static net.minecraft.client.renderer.GlStateManager.enableTexture2D;
|
||||
import static net.minecraft.util.MathHelper.clamp_int;
|
||||
|
||||
public abstract class AbstractGuiTextField<T extends AbstractGuiTextField<T>>
|
||||
extends AbstractGuiElement<T> implements Clickable, Tickable, Typeable, IGuiTextField<T> {
|
||||
private final net.minecraft.client.gui.GuiTextField wrapped;
|
||||
|
||||
private Runnable textChanged;
|
||||
private Runnable onEnter;
|
||||
private static final ReadableColor BORDER_COLOR = new Color(160, 160, 160);
|
||||
private static final ReadableColor CURSOR_COLOR = new Color(240, 240, 240);
|
||||
private static final int BORDER = 4;
|
||||
|
||||
// Focus
|
||||
@Getter
|
||||
private boolean focused;
|
||||
@Getter
|
||||
private Focusable next, previous;
|
||||
|
||||
// Content
|
||||
@Getter
|
||||
private int maxLength = 32;
|
||||
|
||||
@Getter
|
||||
@NonNull
|
||||
private String text = "";
|
||||
|
||||
private int cursorPos;
|
||||
private int selectionPos;
|
||||
|
||||
@Getter
|
||||
private String hint;
|
||||
|
||||
@Getter
|
||||
private ReadableColor textColor;
|
||||
// Rendering
|
||||
private int currentOffset;
|
||||
private int blinkCursorTick;
|
||||
public ReadableColor textColorEnabled = new Color(224, 224, 224);
|
||||
public ReadableColor textColorDisabled = new Color(112, 112, 112);
|
||||
private ReadableDimension size = new Dimension(0, 0); // Size of last render
|
||||
|
||||
private Consumer<String> textChanged;
|
||||
private Runnable onEnter;
|
||||
|
||||
public AbstractGuiTextField() {
|
||||
this.wrapped = new net.minecraft.client.gui.GuiTextField(0, Minecraft.getMinecraft().fontRendererObj, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public AbstractGuiTextField(GuiContainer container) {
|
||||
super(container);
|
||||
this.wrapped = new net.minecraft.client.gui.GuiTextField(0, Minecraft.getMinecraft().fontRendererObj, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
|
||||
ReadablePoint position = renderer.getOpenGlOffset();
|
||||
wrapped.xPosition = position.getX() + 1;
|
||||
wrapped.yPosition = position.getY() + 1;
|
||||
wrapped.width = size.getWidth() - 2;
|
||||
wrapped.height = size.getHeight() - 2;
|
||||
|
||||
if (wrapped.text.isEmpty() && !isFocused() && !Strings.isNullOrEmpty(hint)) {
|
||||
wrapped.setEnabled(false);
|
||||
wrapped.setDisabledTextColour(0xff404040);
|
||||
wrapped.text = hint;
|
||||
wrapped.drawTextBox();
|
||||
wrapped.text = "";
|
||||
wrapped.setDisabledTextColour(0xff707070);
|
||||
wrapped.setEnabled(isEnabled());
|
||||
public T setText(String text) {
|
||||
if (text.length() > maxLength) {
|
||||
this.text = text.substring(0, maxLength);
|
||||
} else {
|
||||
wrapped.drawTextBox();
|
||||
this.text = text;
|
||||
}
|
||||
selectionPos = cursorPos = text.length();
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setI18nText(@NonNull String text, @NonNull Object... args) {
|
||||
return setText(I18n.format(text, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setMaxLength(int maxLength) {
|
||||
Preconditions.checkArgument(maxLength >= 0, "maxLength must not be negative");
|
||||
this.maxLength = maxLength;
|
||||
if (text.length() > maxLength) {
|
||||
setText(text);
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteText(int from, int to) {
|
||||
Preconditions.checkArgument(from <= to, "from must not be greater than to");
|
||||
Preconditions.checkArgument(from >= 0, "from must be greater than zero");
|
||||
Preconditions.checkArgument(to < text.length(), "to must be less than test.length()");
|
||||
|
||||
String deleted = text.substring(from, to + 1);
|
||||
text = text.substring(0, from) + text.substring(to + 1);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionFrom() {
|
||||
return cursorPos > selectionPos ? selectionPos : cursorPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionTo() {
|
||||
return cursorPos > selectionPos ? cursorPos : selectionPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectedText() {
|
||||
return text.substring(getSelectionFrom(), getSelectionTo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteSelectedText() {
|
||||
if (cursorPos == selectionPos) {
|
||||
return ""; // Nothing selected
|
||||
}
|
||||
int from = getSelectionFrom();
|
||||
String deleted = deleteText(from, getSelectionTo() - 1);
|
||||
cursorPos = selectionPos = from;
|
||||
updateCurrentOffset();
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current text offset to make sure the cursor is always visible.
|
||||
*/
|
||||
private void updateCurrentOffset() {
|
||||
currentOffset = Math.min(currentOffset, cursorPos);
|
||||
String line = text.substring(currentOffset, cursorPos);
|
||||
FontRenderer fontRenderer = getMinecraft().fontRendererObj;
|
||||
int currentWidth = fontRenderer.getStringWidth(line);
|
||||
if (currentWidth > size.getWidth() - 2*BORDER) {
|
||||
currentOffset = cursorPos - fontRenderer.trimStringToWidth(line, size.getWidth() - 2*BORDER, true).length();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableDimension calcMinSize() {
|
||||
FontRenderer fontRenderer = getMinecraft().fontRendererObj;
|
||||
return new Dimension(0, fontRenderer.FONT_HEIGHT);
|
||||
public T writeText(String append) {
|
||||
for (char c : append.toCharArray()) {
|
||||
writeChar(c);
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T writeChar(char c) {
|
||||
if (!ChatAllowedCharacters.isAllowedCharacter(c)) {
|
||||
return getThis();
|
||||
}
|
||||
|
||||
deleteSelectedText();
|
||||
|
||||
if (text.length() >= maxLength) {
|
||||
return getThis();
|
||||
}
|
||||
|
||||
text = text.substring(0, cursorPos) + c + text.substring(cursorPos);
|
||||
selectionPos = ++cursorPos;
|
||||
|
||||
updateCurrentOffset();
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deleteNextChar() {
|
||||
if (cursorPos < text.length()) {
|
||||
text = text.substring(0, cursorPos) + text.substring(cursorPos + 1);
|
||||
}
|
||||
selectionPos = cursorPos;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the amount of characters to the next word (excluding).
|
||||
* If this is the last word in the line, return the amount of characters remaining to till the end.
|
||||
* Everything except the Space character is considered part of a word.
|
||||
* @return Length in characters
|
||||
*/
|
||||
protected int getNextWordLength() {
|
||||
int length = 0;
|
||||
boolean inWord = true;
|
||||
for (int i = cursorPos; i < text.length(); i++) {
|
||||
if (inWord) {
|
||||
if (text.charAt(i) == ' ') {
|
||||
inWord = false;
|
||||
}
|
||||
} else {
|
||||
if (text.charAt(i) != ' ') {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteNextWord() {
|
||||
int worldLength = getNextWordLength();
|
||||
if (worldLength > 0) {
|
||||
return deleteText(cursorPos, cursorPos + worldLength);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deletePreviousChar() {
|
||||
if (cursorPos > 0) {
|
||||
text = text.substring(0, cursorPos - 1) + text.substring(cursorPos);
|
||||
selectionPos = --cursorPos;
|
||||
updateCurrentOffset();
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the amount of characters to the previous word (including).
|
||||
* If this is the first word in the line, return the amount of characters till the start.
|
||||
* Everything except the Space character is considered part of a word.
|
||||
* @return Length in characters
|
||||
*/
|
||||
protected int getPreviousWordLength() {
|
||||
int length = 0;
|
||||
boolean inWord = false;
|
||||
for (int i = cursorPos - 1; i >= 0; i--) {
|
||||
if (inWord) {
|
||||
if (text.charAt(i) == ' ') {
|
||||
return length;
|
||||
}
|
||||
} else {
|
||||
if (text.charAt(i) != ' ') {
|
||||
inWord = true;
|
||||
}
|
||||
}
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deletePreviousWord() {
|
||||
int worldLength = getPreviousWordLength();
|
||||
String deleted = "";
|
||||
if (worldLength > 0) {
|
||||
deleted = deleteText(cursorPos - worldLength, cursorPos);
|
||||
selectionPos = cursorPos -= worldLength;
|
||||
updateCurrentOffset();
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setCursorPosition(int pos) {
|
||||
Preconditions.checkArgument(pos >= 0 && pos <= text.length());
|
||||
selectionPos = cursorPos = pos;
|
||||
updateCurrentOffset();
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts all colors on the screen.
|
||||
* @param guiRenderer The GUI Renderer
|
||||
* @param right Right border of the inverted rectangle
|
||||
* @param bottom Bottom border of the inverted rectangle
|
||||
* @param left Left border of the inverted rectangle
|
||||
* @param top Top border of the inverted rectangle
|
||||
*/
|
||||
private void invertColors(GuiRenderer guiRenderer, int right, int bottom, int left, int top) {
|
||||
int x = guiRenderer.getOpenGlOffset().getX();
|
||||
int y = guiRenderer.getOpenGlOffset().getY();
|
||||
right+=x;
|
||||
left+=x;
|
||||
bottom+=y;
|
||||
top+=y;
|
||||
|
||||
color(0, 0, 255, 255);
|
||||
disableTexture2D();
|
||||
enableColorLogic();
|
||||
colorLogicOp(GL11.GL_OR_REVERSE);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
WorldRenderer renderer = tessellator.getWorldRenderer();
|
||||
renderer.startDrawingQuads();
|
||||
renderer.addVertex(right, top, 0);
|
||||
renderer.addVertex(left, top, 0);
|
||||
renderer.addVertex(left, bottom, 0);
|
||||
renderer.addVertex(right, bottom, 0);
|
||||
tessellator.draw();
|
||||
|
||||
disableColorLogic();
|
||||
enableTexture2D();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReadableDimension calcMinSize() {
|
||||
return new Dimension(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClick(ReadablePoint position, int button) {
|
||||
wrapped.mouseClicked(position.getX(), position.getY(), button);
|
||||
return false;
|
||||
if (getContainer() != null) {
|
||||
getContainer().convertFor(this, (Point) (position = new Point(position)));
|
||||
}
|
||||
boolean hovering = isMouseHovering(position);
|
||||
|
||||
if (hovering && isFocused() && button == 0) {
|
||||
int mouseX = position.getX() - BORDER;
|
||||
FontRenderer fontRenderer = getMinecraft().fontRendererObj;
|
||||
String text = this.text.substring(currentOffset);
|
||||
int textX = fontRenderer.trimStringToWidth(text, mouseX).length() + currentOffset;
|
||||
setCursorPosition(textX);
|
||||
}
|
||||
|
||||
setFocused(hovering);
|
||||
return hovering;
|
||||
}
|
||||
|
||||
protected boolean isMouseHovering(ReadablePoint pos) {
|
||||
return pos.getX() > 0 && pos.getY() > 0
|
||||
&& pos.getX() < size.getWidth() && pos.getY() < size.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setFocused(boolean isFocused) {
|
||||
if (isFocused && !this.focused) {
|
||||
this.blinkCursorTick = 0; // Restart blinking to indicate successful focus
|
||||
}
|
||||
this.focused = isFocused;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setNext(Focusable next) {
|
||||
this.next = next;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setPrevious(Focusable previous) {
|
||||
this.previous = previous;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
|
||||
this.size = size;
|
||||
int width = size.getWidth(), height = size.getHeight();
|
||||
FontRenderer fontRenderer = getMinecraft().fontRendererObj;
|
||||
int posY = height / 2 - fontRenderer.FONT_HEIGHT / 2;
|
||||
|
||||
// Draw black rect once pixel smaller than gray rect
|
||||
renderer.drawRect(0, 0, width, height, BORDER_COLOR);
|
||||
renderer.drawRect(1, 1, width - 2, height - 2, ReadableColor.BLACK);
|
||||
|
||||
if (text.isEmpty() && !isFocused() && !Strings.isNullOrEmpty(hint)) {
|
||||
// Draw hint
|
||||
String text = fontRenderer.trimStringToWidth(hint, width - 2*BORDER);
|
||||
renderer.drawString(BORDER, posY, textColorDisabled, text);
|
||||
} else {
|
||||
// Draw text
|
||||
String renderText = text.substring(currentOffset);
|
||||
renderText = fontRenderer.trimStringToWidth(renderText, width - 2*BORDER);
|
||||
ReadableColor color = isEnabled() ? textColorEnabled : textColorDisabled;
|
||||
int lineEnd = renderer.drawString(BORDER, height / 2 - fontRenderer.FONT_HEIGHT / 2, color, renderText);
|
||||
|
||||
// Draw selection
|
||||
int from = getSelectionFrom();
|
||||
int to = getSelectionTo();
|
||||
String leftStr = text.substring(0, clamp_int(from - currentOffset, 0, text.length()));
|
||||
String rightStr = text.substring(clamp_int(to - currentOffset, 0, text.length()));
|
||||
int left = BORDER + fontRenderer.getStringWidth(leftStr);
|
||||
int right = lineEnd - fontRenderer.getStringWidth(rightStr) - 1;
|
||||
invertColors(renderer, right, height - 2, left, 2);
|
||||
|
||||
// Draw cursor
|
||||
if (blinkCursorTick / 6 % 2 == 0 && focused) {
|
||||
String beforeCursor = text.substring(0, cursorPos - currentOffset);
|
||||
int posX = BORDER + fontRenderer.getStringWidth(beforeCursor);
|
||||
if (cursorPos == text.length()) {
|
||||
renderer.drawString(posX, posY, CURSOR_COLOR, "_", true);
|
||||
} else {
|
||||
renderer.drawRect(posX, posY - 1, 1, 1 + fontRenderer.FONT_HEIGHT, CURSOR_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||
if (!isFocused()) {
|
||||
if (!this.focused) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyCode == Keyboard.KEY_RETURN) {
|
||||
onEnter();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == Keyboard.KEY_TAB) {
|
||||
Focusable other = shiftDown ? previous : next;
|
||||
if (other != null) {
|
||||
@@ -117,106 +435,131 @@ public abstract class AbstractGuiTextField<T extends AbstractGuiTextField<T>>
|
||||
return true;
|
||||
}
|
||||
|
||||
String before = wrapped.getText();
|
||||
wrapped.textboxKeyTyped(keyChar, keyCode);
|
||||
String after = wrapped.getText();
|
||||
if (!before.equals(after)) {
|
||||
if (textChanged != null) {
|
||||
textChanged.run();
|
||||
if (keyCode == Keyboard.KEY_RETURN) {
|
||||
onEnter();
|
||||
return true;
|
||||
}
|
||||
|
||||
String textBefore = text;
|
||||
try {
|
||||
if (GuiScreen.isCtrlKeyDown()) {
|
||||
switch (keyCode) {
|
||||
case Keyboard.KEY_A: // Select all
|
||||
cursorPos = 0;
|
||||
selectionPos = text.length();
|
||||
updateCurrentOffset();
|
||||
return true;
|
||||
case Keyboard.KEY_C: // Copy
|
||||
GuiScreen.setClipboardString(getSelectedText());
|
||||
return true;
|
||||
case Keyboard.KEY_V: // Paste
|
||||
if (isEnabled()) {
|
||||
writeText(GuiScreen.getClipboardString());
|
||||
}
|
||||
return true;
|
||||
case Keyboard.KEY_X: // Cut
|
||||
if (isEnabled()) {
|
||||
GuiScreen.setClipboardString(deleteSelectedText());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean words = GuiScreen.isCtrlKeyDown();
|
||||
boolean select = GuiScreen.isShiftKeyDown();
|
||||
switch (keyCode) {
|
||||
case Keyboard.KEY_HOME:
|
||||
cursorPos = 0;
|
||||
break;
|
||||
case Keyboard.KEY_END:
|
||||
cursorPos = text.length();
|
||||
break;
|
||||
case Keyboard.KEY_LEFT:
|
||||
if (cursorPos != 0) {
|
||||
if (words) {
|
||||
cursorPos -= getPreviousWordLength();
|
||||
} else {
|
||||
cursorPos--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Keyboard.KEY_RIGHT:
|
||||
if (cursorPos != text.length()) {
|
||||
if (words) {
|
||||
cursorPos += getNextWordLength();
|
||||
} else {
|
||||
cursorPos++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Keyboard.KEY_BACK:
|
||||
if (isEnabled()) {
|
||||
if (getSelectedText().length() > 0) {
|
||||
deleteSelectedText();
|
||||
} else if (words) {
|
||||
deletePreviousWord();
|
||||
} else {
|
||||
deletePreviousChar();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case Keyboard.KEY_DELETE:
|
||||
if (isEnabled()) {
|
||||
if (getSelectedText().length() > 0) {
|
||||
deleteSelectedText();
|
||||
} else if (words) {
|
||||
deleteNextWord();
|
||||
} else {
|
||||
deleteNextChar();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
if (isEnabled()) {
|
||||
if (keyChar == '\r') {
|
||||
keyChar = '\n';
|
||||
}
|
||||
writeChar(keyChar);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
updateCurrentOffset();
|
||||
|
||||
if (!select) {
|
||||
selectionPos = cursorPos;
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (!textBefore.equals(text)) {
|
||||
onTextChanged(textBefore);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setText(String text) {
|
||||
if (text.length() > wrapped.getMaxStringLength()) {
|
||||
wrapped.text = text.substring(0, wrapped.getMaxStringLength());
|
||||
} else {
|
||||
wrapped.text = text;
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setI18nText(String text, Object... args) {
|
||||
return setText(I18n.format(text, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return wrapped.text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return wrapped.isFocused();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setFocused(boolean focused) {
|
||||
wrapped.setFocused(focused);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Focusable getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setNext(Focusable next) {
|
||||
if (this.next == next) return getThis();
|
||||
if (this.next != null) {
|
||||
this.next.setPrevious(null);
|
||||
}
|
||||
this.next = next;
|
||||
if (this.next != null) {
|
||||
this.next.setPrevious(this);
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Focusable getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setPrevious(Focusable previous) {
|
||||
if (this.previous == previous) return getThis();
|
||||
if (this.previous != null) {
|
||||
this.previous.setNext(null);
|
||||
}
|
||||
this.previous = previous;
|
||||
if (this.previous != null) {
|
||||
this.previous.setNext(this);
|
||||
}
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLength() {
|
||||
return wrapped.getMaxStringLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setMaxLength(int maxLength) {
|
||||
wrapped.setMaxStringLength(maxLength);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
wrapped.updateCursorCounter();
|
||||
blinkCursorTick++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user presses the Enter/Return key while this text field is focused.
|
||||
*/
|
||||
protected void onEnter() {
|
||||
if (onEnter != null) {
|
||||
onEnter.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the text has changed due to user input.
|
||||
*/
|
||||
protected void onTextChanged(String from) {
|
||||
if (textChanged != null) {
|
||||
textChanged.consume(from);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T onEnter(Runnable onEnter) {
|
||||
this.onEnter = onEnter;
|
||||
@@ -224,7 +567,7 @@ public abstract class AbstractGuiTextField<T extends AbstractGuiTextField<T>>
|
||||
}
|
||||
|
||||
@Override
|
||||
public T onTextChanged(Runnable textChanged) {
|
||||
public T onTextChanged(Consumer<String> textChanged) {
|
||||
this.textChanged = textChanged;
|
||||
return getThis();
|
||||
}
|
||||
@@ -240,10 +583,20 @@ public abstract class AbstractGuiTextField<T extends AbstractGuiTextField<T>>
|
||||
return setHint(I18n.format(hint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableColor getTextColor() {
|
||||
return textColorEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setTextColor(ReadableColor textColor) {
|
||||
this.textColor = textColor;
|
||||
wrapped.setTextColor(textColor.getAlpha() << 24 | textColor.getRed() << 16 | textColor.getGreen() << 8 | textColor.getBlue());
|
||||
this.textColorEnabled = textColor;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T setTextColorDisabled(ReadableColor textColorDisabled) {
|
||||
this.textColorDisabled = textColorDisabled;
|
||||
return getThis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,24 +23,144 @@
|
||||
package de.johni0702.minecraft.gui.element;
|
||||
|
||||
import de.johni0702.minecraft.gui.function.Focusable;
|
||||
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||
import lombok.NonNull;
|
||||
import org.lwjgl.util.ReadableColor;
|
||||
|
||||
public interface IGuiTextField<T extends IGuiTextField<T>> extends GuiElement<T>, Focusable<T> {
|
||||
T setText(String text);
|
||||
T setI18nText(String text, Object... args);
|
||||
String getText();
|
||||
/**
|
||||
* Set the text to the specified string.
|
||||
* If the string is longer than {@link #getMaxLength()} it is truncated from the end.
|
||||
* This method positions the cursor at the end of the text and removes any selections.
|
||||
* @param text The new text
|
||||
* @return {@code this} for chaining
|
||||
*/
|
||||
@NonNull T setText(@NonNull String text);
|
||||
|
||||
/**
|
||||
* Set the text to the specified string.
|
||||
* If the string is longer than {@link #getMaxLength()} it is truncated from the end.
|
||||
* This method positions the cursor at the end of the text and removes any selections.
|
||||
* @param text The language key for the new text
|
||||
* @param args The arguments used in translating the language key
|
||||
* @return {@code this} for chaining
|
||||
*/
|
||||
@NonNull T setI18nText(@NonNull String text, @NonNull Object... args);
|
||||
|
||||
/**
|
||||
* Return the whole text in this text field.
|
||||
* @return The text, may be empty
|
||||
*/
|
||||
@NonNull String getText();
|
||||
|
||||
/**
|
||||
* Return the maximum allowed length of the text in this text field.
|
||||
* @return Maximum number of characters
|
||||
*/
|
||||
int getMaxLength();
|
||||
|
||||
/**
|
||||
* Set the maximum allowed length of the text in this text field.
|
||||
* If the current test is longer than the new limit, it is truncated from the end (the cursor and selection
|
||||
* are reset in that process, see {@link #setText(String)}).
|
||||
* @param maxLength Maximum number of characters
|
||||
* @return {@code this} for chaining
|
||||
* @throws IllegalArgumentException When {@code maxLength} is negative
|
||||
*/
|
||||
T setMaxLength(int maxLength);
|
||||
|
||||
/**
|
||||
* Deletes the text between {@code from} and {@code to} (inclusive).
|
||||
* @param from Index at which to start
|
||||
* @param to Index to which to delete
|
||||
* @return The deleted text
|
||||
* @throws IllegalArgumentException If {@code from} is greater than {@code to} or either is out of bounds
|
||||
*/
|
||||
@NonNull String deleteText(int from, int to);
|
||||
|
||||
/**
|
||||
* Return the index at which the selection starts (inclusive)
|
||||
* @return Index of first character
|
||||
*/
|
||||
int getSelectionFrom();
|
||||
|
||||
/**
|
||||
* Return the index at which the selection ends (exclusive)
|
||||
* @return Index after the last character
|
||||
*/
|
||||
int getSelectionTo();
|
||||
|
||||
/**
|
||||
* Return the selected text.
|
||||
* @return The selected text
|
||||
*/
|
||||
@NonNull String getSelectedText();
|
||||
|
||||
/**
|
||||
* Delete the selected text. Positions the cursor at the beginning of the selection and clears the selection.
|
||||
* @return The deleted text
|
||||
*/
|
||||
@NonNull String deleteSelectedText();
|
||||
|
||||
/**
|
||||
* Appends the specified string to this text field character by character.
|
||||
* Excess characters are ignored.
|
||||
* @param append String to append
|
||||
* @return {@code this} for chaining
|
||||
* @see #writeChar(char)
|
||||
*/
|
||||
@NonNull T writeText(@NonNull String append);
|
||||
|
||||
/**
|
||||
* Appends the specified character to this text field replacing the current selection (if any).
|
||||
* This does nothing if the maximum character limit is reached.
|
||||
* @param c Character to append
|
||||
* @return {@code this} for chaining
|
||||
*/
|
||||
@NonNull T writeChar(char c);
|
||||
|
||||
/**
|
||||
* Delete the nex character (if any).
|
||||
* Clears the selection.
|
||||
* @return {@code this} for chaining
|
||||
*/
|
||||
T deleteNextChar();
|
||||
|
||||
/**
|
||||
* Delete everything from the cursor (inclusive) to the beginning of the next word (exclusive).
|
||||
* If there are no more words, delete everything until the end of the line.
|
||||
* @return The deleted text
|
||||
*/
|
||||
String deleteNextWord();
|
||||
|
||||
/**
|
||||
* Delete the previous character (if any).
|
||||
* @return {@code this} for chaining
|
||||
*/
|
||||
@NonNull T deletePreviousChar();
|
||||
|
||||
/**
|
||||
* Delete everything from cursor to the first character of the previous word (or the start of the line).
|
||||
* @return The deleted text
|
||||
*/
|
||||
@NonNull String deletePreviousWord();
|
||||
|
||||
/**
|
||||
* Set the cursor position.
|
||||
* @param pos Position of the cursor
|
||||
* @return {@code this} for chaining
|
||||
* @throws IllegalArgumentException If {@code pos} < 0 or pos > length
|
||||
*/
|
||||
@NonNull T setCursorPosition(int pos);
|
||||
|
||||
T onEnter(Runnable onEnter);
|
||||
T onTextChanged(Runnable textChanged);
|
||||
T onTextChanged(Consumer<String> textChanged);
|
||||
|
||||
String getHint();
|
||||
T setHint(String hint);
|
||||
|
||||
T setI18nHint(String hint, Object... args);
|
||||
|
||||
ReadableColor getTextColor();
|
||||
T setTextColor(ReadableColor textColor);
|
||||
T setTextColorDisabled(ReadableColor textColorDisabled);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2015 johni0702
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package de.johni0702.minecraft.gui.utils;
|
||||
|
||||
public class Consumers {
|
||||
public static <U> Consumer<U> from(final Runnable runnable) {
|
||||
return new Consumer<U>() {
|
||||
@Override
|
||||
public void consume(U obj) {
|
||||
runnable.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user