SimpleJavaEngine/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java

724 lines
17 KiB
Java

package speiger.src.coreengine.rendering.gui.components;
import java.util.function.Predicate;
import org.lwjgl.glfw.GLFW;
import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.rendering.gui.GuiComponent;
import speiger.src.coreengine.rendering.gui.base.IButtonComponent;
import speiger.src.coreengine.rendering.gui.base.IKeyComponent;
import speiger.src.coreengine.rendering.gui.helper.Align;
import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox;
import speiger.src.coreengine.rendering.gui.helper.box.ParentBox;
import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain.Target;
import speiger.src.coreengine.rendering.gui.helper.constrains.Constrains;
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata;
import speiger.src.coreengine.rendering.gui.renderer.lexer.Word;
import speiger.src.coreengine.rendering.gui.renderer.lexer.WordType;
import speiger.src.coreengine.rendering.input.Keyboard;
import speiger.src.coreengine.rendering.utils.Cursor;
import speiger.src.coreengine.utils.functions.Functions;
public class TextFieldComponent extends GuiComponent
implements IButtonComponent, IKeyComponent
{
public static final int FLAG_FOCUS = 1 << 20;
public static final int FLAG_CAN_LOSE_FOCUS = 1 << 21;
public static final int FLAG_DOUBLE_CLICK = 1 << 22;
public static final int FLAG_INFINITE_WIDTH = 1 << 23;
public static final int FLAG_AUTO_VALIDATE = 1 << 24;
TextComponent text = new TextComponent().horizontal(Align.LEFT_TOP).singleLine(true).special(false).cast();
int color;
int curserPosition = 0;
int selectionPosition = 0;
long lastClickTime = 0;
int maxTextLength = 32;
Predicate<String> validator = Functions.getAlwaysTrue();
int lineOffset = 0;
boolean direction = false;
int largestPos = 0;
IGuiBox viewPort = new ParentBox(1F);
public TextFieldComponent(int color)
{
super(0F, 0F, 0F, 0F);
setFlag(FLAG_CAN_LOSE_FOCUS);
this.color = color;
}
public TextFieldComponent(float x, float y, float width, float height, int color)
{
super(x, y, width, height);
setFlag(FLAG_CAN_LOSE_FOCUS);
this.color = color;
}
@Override
public void init()
{
addBox(viewPort);
addChild(text, createConstraints());
if(text.getText().length() > 0 && isFlagNotSet(FLAG_INFINITE_WIDTH))
{
String s = text.getText();
float scale = text.getTextScale();
float width = text.getBox().getWidth();
while(text.getFont().width(s) * scale > width && s.length() > 0)
{
s = s.substring(0, s.length() - 1);
}
text.setText(s);
}
}
private Constrains createConstraints()
{
return isFlagNotSet(FLAG_INFINITE_WIDTH) ? Constrains.parent(1F) : Constrains.dynamic(this::getOffset, Target.X).parent(1F, Target.Y).parent(1F, Target.WIDTH).parent(1F, Target.HEIGHT).build();
}
public TextFieldComponent setValidator(Predicate<String> validator)
{
this.validator = validator;
return this;
}
public TextFieldComponent setMaxTextLength(int charLimit)
{
maxTextLength = charLimit;
return this;
}
public TextFieldComponent setCanLoseFocus(boolean value)
{
setFlag(FLAG_CAN_LOSE_FOCUS, value);
return this;
}
public TextFieldComponent setFocused(boolean value)
{
setFlag(FLAG_FOCUS, value);
return this;
}
public TextFieldComponent setAutoUpdating(boolean value)
{
setFlag(FLAG_AUTO_VALIDATE, value);
return this;
}
public TextFieldComponent setColor(int color)
{
this.color = color;
return this;
}
public final TextFieldComponent setInfiniteText(boolean value)
{
if(setFlag(FLAG_INFINITE_WIDTH, value))
{
text.limit(!value);
if(getGui() != null)
{
addConstrains(text, createConstraints());
}
}
return this;
}
public TextComponent getRawText()
{
return text;
}
public String getText()
{
return text.getText();
}
public String getSelectedText()
{
return text.getText(Math.min(getCurserPosition(), getSelectionPosition()), Math.max(getCurserPosition(), getSelectionPosition()));
}
public TextFieldComponent setText(String s)
{
if(s == null)
{
return this;
}
if(validator.test(s))
{
if(s.length() > maxTextLength)
{
s = s.substring(0, maxTextLength);
}
if(isFlagNotSet(FLAG_INFINITE_WIDTH) && getGui() != null)
{
float scale = text.getTextScale();
float width = text.getBox().getWidth();
while(text.getFont().width(s) * scale > width)
{
s = s.substring(0, s.length() - 1);
}
}
text.setText(s);
}
return this;
}
@Override
protected boolean updateSelf(int mouseX, int mouseY, float particalTicks)
{
if(text.isTopHovered(mouseX, mouseY))
{
bindCursor(Cursor.CURSOR_IBEAM);
}
return true;
}
@Override
protected boolean renderSelf(int mouseX, int mouseY, float particalTicks)
{
float brightness = getActiveBrightness();
UIRenderer render = getRenderer();
render.setBrightness(brightness * 0.7F).drawQuad(getBox(), color).setBrightness(brightness);
IGuiBox box = text.getBox();
render.drawQuad(viewPort, 0.001F, color);
if(isFlagSet(FLAG_INFINITE_WIDTH))
{
enableScissorsBox(getBox().getMinX(1F), getBox().getMinY(1F), getBox().getMaxX(-1F), getBox().getMaxY(-1F));
}
if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0)
{
TextMetadata data = text.getMetadata();
if(hasSelectedText())
{
float extra = text.getHorizontal().align(box.getWidth(), data.getMaxWidth());
render.drawQuad(box.getMinX() + extra + data.getWidth(Math.min(getCurserPosition(), getSelectionPosition())), box.getMinY(), box.getMinX() + extra + data.getWidth(Math.max(getCurserPosition(), getSelectionPosition())), box.getMaxY(), 0.02F, text.getTextColor());
}
else
{
float width = data.getWidth(curserPosition) + text.getHorizontal().align(box.getWidth(), data.getMaxWidth());
render.drawQuad(box.getMinX() + width, box.getMinY(), box.getMinX() + width + text.getTextScale(), box.getMaxY(), 0.02F, text.getTextColor());
}
}
if(isFlagSet(FLAG_INFINITE_WIDTH))
{
renderChildren(mouseX, mouseY, particalTicks);
disableScissors();
return false;
}
return true;
}
@Override
public boolean onClick(int button, int mouseX, int mouseY)
{
setFocused(true);
if(hasSelectedText() && isFlagNotSet(FLAG_DOUBLE_CLICK))
{
setSelectionPosition(-1);
setCurserPosition(getMousePosition(mouseX));
return true;
}
int pos = getMousePosition(mouseX);
if(pos == getCurserPosition() || isFlagSet(FLAG_DOUBLE_CLICK))
{
if(System.currentTimeMillis() - lastClickTime < 500L)
{
if(isFlagSet(FLAG_DOUBLE_CLICK))
{
clearFlag(FLAG_DOUBLE_CLICK);
setSelectionPosition(0);
setCurserPosition(text.length());
}
else
{
setFlag(FLAG_DOUBLE_CLICK);
handleDoubleClick(pos);
}
}
else
{
clearFlag(FLAG_DOUBLE_CLICK);
}
lastClickTime = System.currentTimeMillis();
return true;
}
clearFlag(FLAG_DOUBLE_CLICK);
setCurserPosition(pos);
lastClickTime = System.currentTimeMillis();
return true;
}
@Override
public boolean onDrag(int mouseX, int mouseY)
{
if(!hasSelectedText())
{
setSelectionPosition(getCurserPosition());
}
setCurserPosition(getMousePosition(mouseX));
return true;
}
@Override
public boolean isAcceptingInput()
{
return isFlagSet(FLAG_FOCUS);
}
@Override
public boolean isBlockingMovement()
{
return true;
}
@Override
public boolean onKeyPressed(int key)
{
if(isFlagNotSet(FLAG_FOCUS))
{
return false;
}
if(key == GLFW.GLFW_KEY_ENTER)
{
notifyListeners(LISTENER_USER_ACTION);
if(isFlagSet(FLAG_CAN_LOSE_FOCUS))
{
setFocused(false);
return true;
}
}
if(key == GLFW.GLFW_KEY_BACKSPACE)
{
if(Keyboard.isCtrlDown())
{
Word word = text.getMetadata().getWord(getCurserPosition());
if(word == null)
{
return true;
}
if(getCurserPosition() == word.getStartIndex())
{
if(word.getPrev() != null)
{
deleteAtCurser(word.getPrev().getStartIndex() - word.getStartIndex());
}
}
else
{
setCurserPosition(word.getStartIndex());
deleteAtCurser(word.getEndIndex() - word.getStartIndex());
}
return true;
}
else if(deleteAtCurser(-1))
{
return true;
}
}
else if(key == GLFW.GLFW_KEY_DELETE)
{
if(Keyboard.isCtrlDown())
{
Word word = text.getMetadata().getWord(getCurserPosition());
if(word == null)
{
return true;
}
if(getCurserPosition() == word.getEndIndex())
{
if(word.getNext() != null)
{
deleteAtCurser(word.getNext().getEndIndex() - word.getEndIndex());
}
}
else
{
setCurserPosition(word.getStartIndex());
deleteAtCurser(word.getEndIndex() - word.getStartIndex());
}
return true;
}
else if(deleteAtCurser(1))
{
return true;
}
}
else if(key == GLFW.GLFW_KEY_LEFT)
{
if(getCurserPosition() >= 0)
{
if(Keyboard.isShiftDown() && getSelectionPosition() == -1)
{
setSelectionPosition(getCurserPosition());
}
else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1)
{
setSelectionPosition(-1);
}
if(Keyboard.isCtrlDown())
{
Word word = text.getMetadata().getWord(getCurserPosition());
if(word.getStartIndex() == getCurserPosition())
{
if(word.getPrev() != null)
{
setCurserPosition(word.getPrev().getStartIndex());
}
}
else
{
setCurserPosition(word.getStartIndex());
}
}
else
{
setCurserPosition(getCurserPosition() - 1);
}
return true;
}
}
else if(key == GLFW.GLFW_KEY_RIGHT)
{
if(getCurserPosition() < text.length())
{
if(Keyboard.isShiftDown() && getSelectionPosition() == -1)
{
setSelectionPosition(getCurserPosition());
}
else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1)
{
setSelectionPosition(-1);
}
if(Keyboard.isCtrlDown())
{
Word word = text.getMetadata().getWord(getCurserPosition());
if(word.getEndIndex() == getCurserPosition())
{
if(word.getNext() != null)
{
setCurserPosition(word.getNext().getEndIndex());
}
}
else
{
setCurserPosition(word.getEndIndex());
}
}
else
{
setCurserPosition(getCurserPosition() + 1);
}
return true;
}
else
{
if(!Keyboard.isShiftDown() && hasSelectedText())
{
setSelectionPosition(-1);
}
return true;
}
}
else if(key == GLFW.GLFW_KEY_END)
{
if(Keyboard.isShiftDown())
{
if(!hasSelectedText())
{
setSelectionPosition(getCurserPosition());
}
}
else
{
setSelectionPosition(-1);
}
setCurserToEnd();
return true;
}
else if(key == GLFW.GLFW_KEY_HOME)
{
if(Keyboard.isShiftDown())
{
if(getSelectionPosition() == -1)
{
setSelectionPosition(getCurserPosition());
}
}
else
{
setSelectionPosition(-1);
}
setCurserPosition(0);
return true;
}
else if(key == GLFW.GLFW_KEY_TAB)
{
writeText(Character.toString('\t'));
return true;
}
else if(isSelect(key))
{
setSelectionPosition(0);
setCurserToEnd();
return true;
}
else if(isCopy(key))
{
if(hasSelectedText())
{
getWindow().setClipboardString(getSelectedText());
}
return true;
}
else if(isPaste(key))
{
String text = getWindow().getClipboardString();
if(text != null)
{
writeText(text);
}
return true;
}
else if(isCut(key))
{
if(hasSelectedText())
{
getWindow().setClipboardString(getSelectedText());
writeText("");
}
return true;
}
else if(Keyboard.isPrintableKey(key))
{
return true;
}
return false;
}
@Override
public boolean onKeyTyped(char letter, int codepoint)
{
return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(codepoint) && writeText(new String(Character.toChars(codepoint)));
}
public boolean deleteAtCurser(int amount)
{
if(text.length() > 0)
{
if(hasSelectedText())
{
int startPos = Math.min(getCurserPosition(), getSelectionPosition());
writeText("");
setCurserPosition(startPos);
}
else
{
int startPos = Math.min(getCurserPosition(), getCurserPosition() + amount);
int endPos = Math.max(getCurserPosition(), getCurserPosition() + amount);
StringBuilder builder = new StringBuilder();
if(startPos >= 0)
{
builder.append(text.getText(0, startPos));
}
if(endPos < text.length())
{
builder.append(text.getText(endPos));
}
String s = builder.toString();
if(validator.test(s))
{
setText(s);
setCurserPosition(Math.min(startPos, text.length()));
if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION);
}
}
return true;
}
return false;
}
public boolean writeText(String toWrite)
{
toWrite = text.getFont().clearInvalidLetters(toWrite);
StringBuilder builder = new StringBuilder();
int startPos = hasSelectedText() ? Math.min(getSelectionPosition(), getCurserPosition()) : getCurserPosition();
int endPos = hasSelectedText() ? Math.max(getSelectionPosition(), getCurserPosition()) : getCurserPosition();
int room = maxTextLength - text.length() - (startPos - endPos);
if(text.length() > 0) builder.append(text.getText(0, startPos));
int moved = 0;
if(room < toWrite.length())
{
builder.append(toWrite.substring(0, room));
moved = room;
}
else
{
builder.append(toWrite);
moved = toWrite.length();
}
if(text.length() > 0 && endPos < text.length()) builder.append(text.getText(endPos));
String s = builder.toString();
if(validator.test(s))
{
setText(s);
setCurserPosition(Math.min(endPos + moved, text.length()));
setSelectionPosition(-1);
if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION);
return true;
}
return false;
}
@Override
public void onFocusLost()
{
if(isFlagSet(FLAG_CAN_LOSE_FOCUS))
{
if(isFlagSet(FLAG_FOCUS))
{
notifyListeners(LISTENER_USER_ACTION);
}
setFocused(false);
}
}
protected void handleDoubleClick(int position)
{
Word word = text.getMetadata().getWord(position);
if(word == null)
{
return;
}
if(!word.isSpecial())
{
setSelectionPosition(word.getStartIndex());
setCurserPosition(word.getEndIndex());
return;
}
WordType type = word.getType();
if(type.isStartWord())
{
Word other = type.findEndWord(word);
if(other != null)
{
setSelectionPosition(word.getStartIndex());
setCurserPosition(other.getEndIndex());
}
}
else if(type.isEndWord())
{
Word other = type.findStartWord(word);
if(other != null)
{
setSelectionPosition(other.getStartIndex());
setCurserPosition(word.getEndIndex());
}
}
else if(type.isDualWord())
{
if(word.getStartIndex() == position && word.getPrev() != null)
{
Word other = type.findStartWord(word);
if(other != null)
{
setSelectionPosition(other.getStartIndex());
setCurserPosition(word.getEndIndex());
}
}
else
{
Word other = type.findEndWord(word);
if(other != null)
{
setSelectionPosition(word.getStartIndex());
setCurserPosition(other.getEndIndex());
}
}
}
else
{
setSelectionPosition(word.getStartIndex());
setCurserPosition(word.getEndIndex());
}
}
protected int getMousePosition(int mouseX)
{
return text.getMetadata().getIndex(mouseX - (text.getBox().getMinX()+text.getHorizontal().align(text.getBox().getWidth(), text.getMetadata().getMaxWidth())));
}
public void setCurserToEnd()
{
setCurserPosition(text.length());
}
public void setCurserPosition(int curserPosition)
{
this.curserPosition = Math.max(0, curserPosition);
if(isFlagSet(FLAG_INFINITE_WIDTH) && text.getFont() != null)
{
int lastLine = lineOffset;
if(lineOffset > text.length())
{
lineOffset = text.length();
}
String s = text.getFont().trimToWidth(text.getText(lineOffset), text.getWidth());
int k = s.length() + lineOffset;
if(curserPosition > k)
{
lineOffset += curserPosition - k;
direction = true;
largestPos = curserPosition;
}
else if(curserPosition < largestPos)
{
int diff = largestPos - curserPosition;
lineOffset -= diff;
largestPos -= diff;
if(lineOffset <= 0) direction = false;
}
lineOffset = MathUtils.clamp(0, text.length(), lineOffset);
if(lastLine != lineOffset)
{
text.onChanged(false);
}
}
}
public int getCurserPosition()
{
return curserPosition;
}
public void setSelectionPosition(int selectionPosition)
{
this.selectionPosition = selectionPosition;
}
public int getSelectionPosition()
{
return selectionPosition;
}
public boolean hasSelectedText()
{
return selectionPosition != -1;
}
protected float getOffset()
{
if(direction && text.getFont() != null)
{
return -(text.getMetadata().getWidth(largestPos)) + text.getBox().getWidth() - 1F;
}
return 1F - (text.getMetadata().getWidth(lineOffset));
}
}