688 lines
16 KiB
Java
688 lines
16 KiB
Java
package speiger.src.coreengine.rendering.gui.components;
|
|
|
|
import java.util.function.Predicate;
|
|
|
|
import org.lwjgl.glfw.GLFW;
|
|
|
|
import speiger.src.coreengine.math.vector.ints.Vec2i;
|
|
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.constrains.Constrains;
|
|
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
|
|
import speiger.src.coreengine.rendering.gui.renderer.lexer.Line;
|
|
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 TextPanelComponent extends GuiComponent implements IButtonComponent, IKeyComponent
|
|
{
|
|
public static final int FLAG_FOCUS = 1024;
|
|
public static final int FLAG_CAN_LOSE_FOCUS = 2048;
|
|
|
|
TextComponent text = new TextComponent().align(Align.LEFT_TOP, Align.LEFT_TOP).singleLine(true).special(false).cast();
|
|
int color;
|
|
int curserPosition = 0;
|
|
Vec2i curserPos = Vec2i.mutable();
|
|
int selectionPosition = 0;
|
|
Vec2i selectionPos = Vec2i.mutable();
|
|
long lastClickTime = 0;
|
|
int maxTextLength = 64;
|
|
int clickCount = 0;
|
|
Predicate<String> validator = Functions.getAlwaysTrue();
|
|
|
|
public TextPanelComponent(int color)
|
|
{
|
|
super(0F, 0F, 0F, 0F);
|
|
this.color = color;
|
|
}
|
|
|
|
public TextPanelComponent(float x, float y, float width, float height, int color)
|
|
{
|
|
super(x, y, width, height);
|
|
this.color = color;
|
|
}
|
|
|
|
@Override
|
|
public void init()
|
|
{
|
|
addChild(text, Constrains.parent(1F));
|
|
}
|
|
|
|
public TextPanelComponent setValidator(Predicate<String> validator)
|
|
{
|
|
this.validator = validator;
|
|
return this;
|
|
}
|
|
|
|
public TextPanelComponent setMaxTextLength(int charLimit)
|
|
{
|
|
maxTextLength = charLimit;
|
|
return this;
|
|
}
|
|
|
|
public TextPanelComponent setColor(int color)
|
|
{
|
|
this.color = color;
|
|
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 TextPanelComponent setText(String s)
|
|
{
|
|
if(s == null)
|
|
{
|
|
return this;
|
|
}
|
|
if(validator.test(s))
|
|
{
|
|
if(s.length() > maxTextLength)
|
|
{
|
|
s = s.substring(0, maxTextLength);
|
|
}
|
|
float height = text.getBox().getHeight();
|
|
float scale = text.getTextScale();
|
|
while(s.length() > 0 && text.getFont().height(s) * scale > height)
|
|
{
|
|
s = s.substring(0, s.length() - 1);
|
|
}
|
|
text.setText(s);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
protected boolean updateSelf(int mouseX, int mouseY, float particalTicks)
|
|
{
|
|
if(text.isMouseOver(mouseX, mouseY) && !text.isOverChild(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(box, 0.001F, color);
|
|
if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0)
|
|
{
|
|
TextMetadata data = text.getMetadata();
|
|
float height = text.getFont().height() * text.getTextScale();
|
|
if(hasSelectedText())
|
|
{
|
|
if(selectionPos.getY() == curserPos.getY())
|
|
{
|
|
render.drawQuad(box.getMinX() + data.getWidth(Math.min(curserPos.getX(), selectionPos.getX()), curserPos.getY()), box.getMinY() + (height * curserPos.getY()), box.getMinX() + data.getWidth(Math.max(curserPos.getX(), selectionPos.getX()), curserPos.getY()), box.getMinY() + (height * (curserPos.getY()+1)), 0.02F, text.getTextColor());
|
|
}
|
|
else
|
|
{
|
|
Vec2i min = selectionPos.getY() < curserPos.getY() ? selectionPos : curserPos;
|
|
Vec2i max = selectionPos.getY() > curserPos.getY() ? selectionPos : curserPos;
|
|
for(int i = min.getY(),m=max.getY();i<=m;i++)
|
|
{
|
|
if(i == min.getY())
|
|
{
|
|
render.drawQuad(box.getMinX() + data.getWidth(min.getX()), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor());
|
|
}
|
|
else if(i == max.getY())
|
|
{
|
|
render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getWidth(max.getX()), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor());
|
|
}
|
|
else
|
|
{
|
|
render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor());
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
float width = data.getWidth(curserPos.getX(), curserPos.getY());
|
|
render.drawQuad(box.getMinX()+width, box.getMinY() + (height * curserPos.getY()), box.getMinX()+width+text.getTextScale(), box.getMinY() + (height * (curserPos.getY()+1)), 0.02F, text.getTextColor());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onClick(int button, int mouseX, int mouseY)
|
|
{
|
|
setFlag(FLAG_FOCUS);
|
|
if(hasSelectedText() && clickCount <= 0)
|
|
{
|
|
setSelectionPosition(-1);
|
|
setCurserPosition(getMousePosition(mouseX, mouseY));
|
|
return true;
|
|
}
|
|
int pos = getMousePosition(mouseX, mouseY);
|
|
if(pos == getCurserPosition())
|
|
{
|
|
if(System.currentTimeMillis() - lastClickTime < 500L)
|
|
{
|
|
if(clickCount == 1)
|
|
{
|
|
Line line = text.getMetadata().getLine(curserPos.getY());
|
|
setSelectionPosition(line.getStart());
|
|
setCurserPosition(line.getEnd());
|
|
clickCount = 2;
|
|
}
|
|
else if(clickCount == 2)
|
|
{
|
|
clickCount = 3;
|
|
setSelectionPosition(0);
|
|
setCurserToEnd();
|
|
}
|
|
else
|
|
{
|
|
clickCount = 1;
|
|
handleDoubleClick(pos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
clickCount = 0;
|
|
}
|
|
lastClickTime = System.currentTimeMillis();
|
|
return true;
|
|
}
|
|
lastClickTime = System.currentTimeMillis();
|
|
setCurserPosition(pos);
|
|
setSelectionPosition(-1);
|
|
clickCount = 0;
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onDrag(int mouseX, int mouseY)
|
|
{
|
|
if(!hasSelectedText())
|
|
{
|
|
setSelectionPosition(getCurserPosition());
|
|
}
|
|
setCurserPosition(getMousePosition(mouseX, mouseY));
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onFocusLost()
|
|
{
|
|
clearFlag(FLAG_FOCUS);
|
|
}
|
|
|
|
@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_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 == null)
|
|
{
|
|
return true;
|
|
}
|
|
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 == null)
|
|
{
|
|
return true;
|
|
}
|
|
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_UP)
|
|
{
|
|
if(Keyboard.isShiftDown() && !hasSelectedText())
|
|
{
|
|
setSelectionPosition(getCurserPosition());
|
|
}
|
|
else if(!Keyboard.isShiftDown() && hasSelectedText())
|
|
{
|
|
setSelectionPosition(-1);
|
|
}
|
|
setCurserPosition(text.getMetadata().moveDown(curserPos));
|
|
}
|
|
else if(key == GLFW.GLFW_KEY_DOWN)
|
|
{
|
|
if(Keyboard.isShiftDown() && !hasSelectedText())
|
|
{
|
|
setSelectionPosition(getCurserPosition());
|
|
}
|
|
else if(!Keyboard.isShiftDown() && hasSelectedText())
|
|
{
|
|
setSelectionPosition(-1);
|
|
}
|
|
setCurserPosition(text.getMetadata().moveUp(curserPos));
|
|
}
|
|
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(key == GLFW.GLFW_KEY_ENTER)
|
|
{
|
|
writeText(Character.toString('\n'));
|
|
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()));
|
|
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);
|
|
notifyListeners(LISTENER_USER_ACTION);
|
|
return true;
|
|
}
|
|
return 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());
|
|
}
|
|
}
|
|
|
|
public int getMousePosition(int mouseX, int mouseY)
|
|
{
|
|
return text.getMetadata().getIndex(mouseX - text.getBox().getMinX(), mouseY - text.getBox().getMinY());
|
|
}
|
|
|
|
public void setCurserToEnd()
|
|
{
|
|
setCurserPosition(curserPosition);
|
|
}
|
|
|
|
public void setCurserPosition(int curserPosition)
|
|
{
|
|
if(this.curserPosition == curserPosition)
|
|
{
|
|
return;
|
|
}
|
|
this.curserPosition = curserPosition;
|
|
text.getMetadata().convert(curserPosition, curserPos);
|
|
}
|
|
|
|
public int getCurserPosition()
|
|
{
|
|
return curserPosition;
|
|
}
|
|
|
|
public void setSelectionPosition(int selectionPosition)
|
|
{
|
|
if(this.selectionPosition == selectionPosition)
|
|
{
|
|
return;
|
|
}
|
|
this.selectionPosition = selectionPosition;
|
|
if(selectionPosition == -1)
|
|
{
|
|
selectionPos.set(Vec2i.MINUS_ONE);
|
|
return;
|
|
}
|
|
text.getMetadata().convert(selectionPosition, selectionPos);
|
|
}
|
|
|
|
public int getSelectionPosition()
|
|
{
|
|
return selectionPosition;
|
|
}
|
|
|
|
public boolean hasSelectedText()
|
|
{
|
|
return selectionPosition != -1;
|
|
}
|
|
}
|