aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authornea <romangraef@gmail.com>2021-10-30 02:49:38 +0200
committernea <romangraef@gmail.com>2021-10-30 02:49:38 +0200
commit70447a8719b6d8d5ad3ac0da2a5610eef77df9c4 (patch)
tree7daa5a9b8f8ba11978932b4a0a003d2447a585b5 /src
parente97b093c21755a44474fa0d43e4dca2d275de499 (diff)
downloadLibGui-70447a8719b6d8d5ad3ac0da2a5610eef77df9c4.tar.gz
LibGui-70447a8719b6d8d5ad3ac0da2a5610eef77df9c4.tar.bz2
LibGui-70447a8719b6d8d5ad3ac0da2a5610eef77df9c4.zip
Refactor rendering of WTextField and add text scrolling
Diffstat (limited to 'src')
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WTextField.java384
1 files changed, 195 insertions, 189 deletions
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WTextField.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WTextField.java
index e9e2692..c0ef7c2 100644
--- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WTextField.java
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WTextField.java
@@ -33,63 +33,82 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
public class WTextField extends WWidget {
- public static final int OFFSET_X_TEXT = 4;
- //public static final int OFFSET_Y_TEXT = 6;
-
+ public static final int TEXT_PADDING_X = 4;
+ public static final int TEXT_PADDING_Y = 6;
+ public static final int CURSOR_PADDING_Y = 4;
+ public static final int CURSOR_HEIGHT = 12;
+
@Environment(EnvType.CLIENT)
private TextRenderer font;
private String text = "";
private int maxLength = 16;
private boolean editable = true;
+ private int tickCount = 0;
+
+ private int disabledColor = 0x707070;
private int enabledColor = 0xE0E0E0;
- private int uneditableColor = 0x707070;
-
+ private int suggestionColor = 0x808080;
+
+ private static final int BACKGROUND_COLOR = 0xFF000000;
+ private static final int BORDER_COLOR_SELECTED = 0xFFFFFFA0;
+ private static final int BORDER_COLOR_UNSELECTED = 0xFFA0A0A0;
+ private static final int CURSOR_COLOR = 0xFFD0D0D0;
+
@Nullable
private Text suggestion = null;
+
+ // Index of the leftmost character to be rendered.
+ private int scrollOffset = 0;
+
private int cursor = 0;
/**
- * If not -1, select is the "anchor point" of a selection. That is, if you hit shift+left with
- * no existing selection, the selection will be anchored to where you were, but the cursor will
- * move left, expanding the selection as you continue to move left. If you move to the right,
- * eventually you'll overtake the anchor, drop the anchor at the same place and start expanding
- * the selection rightwards instead.
+ * If not -1, select is the "anchor point" of a selection. That is, if you hit shift+left with no existing
+ * selection, the selection will be anchored to where you were, but the cursor will move left, expanding the
+ * selection as you continue to move left. If you move to the right, eventually you'll overtake the anchor, drop the
+ * anchor at the same place and start expanding the selection rightwards instead.
*/
private int select = -1;
private Consumer<String> onChanged;
private Predicate<String> textPredicate;
-
+
@Environment(EnvType.CLIENT)
@Nullable
private BackgroundPainter backgroundPainter;
public WTextField() {
}
-
+
public WTextField(Text suggestion) {
this.suggestion = suggestion;
}
-
+
public void setText(String s) {
- if (this.textPredicate==null || this.textPredicate.test(s)) {
- this.text = (s.length()>maxLength) ? s.substring(0,maxLength) : s;
- if (onChanged!=null) onChanged.accept(this.text);
+ if (this.textPredicate == null || this.textPredicate.test(s)) {
+ this.text = (s.length() > maxLength) ? s.substring(0, maxLength) : s;
+ if (onChanged != null) onChanged.accept(this.text);
}
}
public String getText() {
return this.text;
}
-
+
@Override
public boolean canResize() {
return true;
}
-
+
+ @Override
+ public void tick() {
+ super.tick();
+ this.tickCount++;
+ }
+
@Override
public void setSize(int x, int y) {
super.setSize(x, 20);
@@ -97,6 +116,7 @@ public class WTextField extends WWidget {
public void setCursorPos(int location) {
this.cursor = MathHelper.clamp(location, 0, this.text.length());
+ scrollCursorIntoView();
}
public int getMaxLength() {
@@ -106,112 +126,103 @@ public class WTextField extends WWidget {
public int getCursor() {
return this.cursor;
}
-
+
+ public void scrollCursorIntoView() {
+ if (scrollOffset > cursor) {
+ scrollOffset = cursor;
+ }
+ if (scrollOffset < cursor && font.trimToWidth(text.substring(scrollOffset), width - TEXT_PADDING_X * 2).length() + scrollOffset < cursor) {
+ scrollOffset = cursor;
+ }
+
+ int rightMostScrollOffset = text.length() - font.trimToWidth(text, width - TEXT_PADDING_X * 2, true).length();
+ scrollOffset = Math.min(rightMostScrollOffset, scrollOffset);
+ }
+
@Nullable
public String getSelection() {
- if (select<0) return null;
- if (select==cursor) return null;
-
+ if (select < 0) return null;
+ if (select == cursor) return null;
+
//Tidy some things
- if (select>text.length()) select = text.length();
- if (cursor<0) cursor = 0;
- if (cursor>text.length()) cursor = text.length();
-
+ if (select > text.length()) select = text.length();
+ if (cursor < 0) cursor = 0;
+ if (cursor > text.length()) cursor = text.length();
+
int start = Math.min(select, cursor);
int end = Math.max(select, cursor);
-
+
return text.substring(start, end);
}
-
+
public boolean isEditable() {
return this.editable;
}
@Environment(EnvType.CLIENT)
- protected void renderTextField(MatrixStack matrices, int x, int y) {
- if (this.font==null) this.font = MinecraftClient.getInstance().textRenderer;
-
- int borderColor = (this.isFocused()) ? 0xFF_FFFFA0 : 0xFF_A0A0A0;
- ScreenDrawing.coloredRect(matrices, x-1, y-1, width+2, height+2, borderColor);
- ScreenDrawing.coloredRect(matrices, x, y, width, height, 0xFF000000);
-
-
- int textColor = this.editable ? this.enabledColor : this.uneditableColor;
-
- //TODO: Scroll offset
- String trimText = font.trimToWidth(this.text, this.width-OFFSET_X_TEXT);
-
- boolean selection = (select!=-1);
- boolean focused = this.isFocused(); //this.isFocused() && this.focusedTicks / 6 % 2 == 0 && boolean_1; //Blinks the cursor
-
- //int textWidth = font.getStringWidth(trimText);
- //int textAnchor = (font.isRightToLeft()) ?
- // x + OFFSET_X_TEXT + textWidth :
- // x + OFFSET_X_TEXT;
-
- int textX = x + OFFSET_X_TEXT;
- //(font.isRightToLeft()) ?
- //textAnchor - textWidth :
- //textAnchor;
-
- int textY = y + (height - 8) / 2;
-
- //TODO: Adjust by scroll offset
- int adjustedCursor = this.cursor;
- if (adjustedCursor > trimText.length()) {
- adjustedCursor = trimText.length();
- }
-
- int preCursorAdvance = textX;
- if (!trimText.isEmpty()) {
- String string_2 = trimText.substring(0,adjustedCursor);
- preCursorAdvance = font.drawWithShadow(matrices, string_2, textX, textY, textColor);
- }
+ protected void renderBox(MatrixStack matrices, int x, int y) {
+ int borderColor = this.isFocused() ? BORDER_COLOR_SELECTED : BORDER_COLOR_UNSELECTED;
+ ScreenDrawing.coloredRect(matrices, x - 1, y - 1, width + 2, height + 2, borderColor);
+ ScreenDrawing.coloredRect(matrices, x, y, width, height, BACKGROUND_COLOR);
+ }
- if (adjustedCursor<trimText.length()) {
- font.drawWithShadow(matrices, trimText.substring(adjustedCursor), preCursorAdvance-1, (float)textY, textColor);
- }
-
+ @Environment(EnvType.CLIENT)
+ protected void renderText(MatrixStack matrices, int x, int y, String visibleText) {
+ int textColor = this.editable ? this.enabledColor : this.disabledColor;
+ this.font.drawWithShadow(matrices, visibleText, x + TEXT_PADDING_X, y + TEXT_PADDING_Y, textColor);
+ }
- if (text.length()==0 && this.suggestion != null) {
- font.drawWithShadow(matrices, this.suggestion, textX, textY, 0xFF808080);
- }
+ @Environment(EnvType.CLIENT)
+ protected void renderCursor(MatrixStack matrices, int x, int y, String visibleText) {
+ if (this.tickCount / 6 % 2 == 0) return;
+ if (this.cursor < this.scrollOffset) return;
+ if (this.cursor > this.scrollOffset + visibleText.length()) return;
+ int cursorOffset = this.font.getWidth(visibleText.substring(0, this.cursor - this.scrollOffset));
+ ScreenDrawing.coloredRect(matrices, x + TEXT_PADDING_X + cursorOffset, y + CURSOR_PADDING_Y, 1, CURSOR_HEIGHT, CURSOR_COLOR);
+ }
- //int var10002;
- //int var10003;
- if (focused && !selection) {
- if (adjustedCursor<trimText.length()) {
- //int caretLoc = WTextField.getCaretOffset(text, cursor);
- //if (caretLoc<0) {
- // caretLoc = textX+MinecraftClient.getInstance().textRenderer.getStringWidth(trimText)-caretLoc;
- //} else {
- // caretLoc = textX+caretLoc-1;
- //}
- ScreenDrawing.coloredRect(matrices, preCursorAdvance-1, textY-2, 1, 12, 0xFFD0D0D0);
- //if (boolean_3) {
- // int var10001 = int_7 - 1;
- // var10002 = int_9 + 1;
- // var10003 = int_7 + 1;
- //
- // DrawableHelper.fill(int_9, var10001, var10002, var10003 + 9, -3092272);
-
- } else {
- font.drawWithShadow(matrices, "_", preCursorAdvance, textY, textColor);
- }
- }
+ @Environment(EnvType.CLIENT)
+ protected void renderSuggestion(MatrixStack matrices, int x, int y) {
+ if (this.suggestion == null) return;
+ this.font.drawWithShadow(matrices, this.suggestion, x + TEXT_PADDING_X, y + TEXT_PADDING_Y, this.suggestionColor);
+ }
- if (selection) {
- int a = WTextField.getCaretOffset(text, cursor);
- int b = WTextField.getCaretOffset(text, select);
- if (b<a) {
- int tmp = b;
- b = a;
- a = tmp;
- }
- invertedRect(matrices, textX+a-1, textY-1, Math.min(b-a, width - OFFSET_X_TEXT), 12);
+ @Environment(EnvType.CLIENT)
+ protected void renderSelection(MatrixStack matrices, int x, int y, String visibleText) {
+ if (select == cursor || select == -1) return;
+
+ int textLength = visibleText.length();
+
+ int left = Math.min(cursor, select);
+ int right = Math.max(cursor, select);
+
+ if (right < scrollOffset || left > scrollOffset + textLength) return;
+
+ int normalizedLeft = Math.max(scrollOffset, left) - scrollOffset;
+ int normalizedRight = Math.min(scrollOffset + textLength, right) - scrollOffset;
+
+ int leftCaret = font.getWidth(visibleText.substring(0, normalizedLeft));
+ int selectionWidth = font.getWidth(visibleText.substring(normalizedLeft, normalizedRight));
+
+ invertedRect(matrices, x + TEXT_PADDING_X + leftCaret, y + CURSOR_PADDING_Y, selectionWidth, CURSOR_HEIGHT);
+ }
+
+ @Environment(EnvType.CLIENT)
+ protected void renderTextField(MatrixStack matrices, int x, int y) {
+ if (this.font == null) this.font = MinecraftClient.getInstance().textRenderer;
+
+ String visibleText = font.trimToWidth(this.text.substring(this.scrollOffset), this.width - 2 * TEXT_PADDING_X);
+ renderBox(matrices, x, y);
+ renderText(matrices, x, y, visibleText);
+ if (this.text.isEmpty() && !this.isFocused()) {
+ renderSuggestion(matrices, x, y);
}
+ if (this.isFocused()) {
+ renderCursor(matrices, x, y, visibleText);
+ }
+ renderSelection(matrices, x, y, visibleText);
}
-
+
@Environment(EnvType.CLIENT)
private void invertedRect(MatrixStack matrices, int x, int y, int width, int height) {
Tessellator tessellator = Tessellator.getInstance();
@@ -223,10 +234,10 @@ public class WTextField extends WWidget {
RenderSystem.enableColorLogicOp();
RenderSystem.logicOp(GlStateManager.LogicOp.OR_REVERSE);
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION);
- buffer.vertex(model, x, y+height, 0).next();
- buffer.vertex(model, x+width, y+height, 0).next();
- buffer.vertex(model, x+width, y, 0).next();
- buffer.vertex(model, x, y, 0).next();
+ buffer.vertex(model, x, y + height, 0).next();
+ buffer.vertex(model, x + width, y + height, 0).next();
+ buffer.vertex(model, x + width, y, 0).next();
+ buffer.vertex(model, x, y, 0).next();
buffer.end();
BufferRenderer.draw(buffer);
RenderSystem.disableColorLogicOp();
@@ -237,12 +248,12 @@ public class WTextField extends WWidget {
this.textPredicate = predicate_1;
return this;
}
-
+
public WTextField setChangedListener(Consumer<String> listener) {
this.onChanged = listener;
return this;
}
-
+
public WTextField setMaxLength(int max) {
this.maxLength = max;
if (this.text.length() > max) {
@@ -251,17 +262,22 @@ public class WTextField extends WWidget {
}
return this;
}
-
+
public WTextField setEnabledColor(int col) {
this.enabledColor = col;
return this;
}
-
+
+ public WTextField setSuggestionColor(int suggestionColor) {
+ this.suggestionColor = suggestionColor;
+ return this;
+ }
+
public WTextField setDisabledColor(int col) {
- this.uneditableColor = col;
+ this.disabledColor = col;
return this;
}
-
+
public WTextField setEditable(boolean editable) {
this.editable = editable;
return this;
@@ -281,17 +297,17 @@ public class WTextField extends WWidget {
this.suggestion = suggestion;
return this;
}
-
+
@Environment(EnvType.CLIENT)
public WTextField setBackgroundPainter(BackgroundPainter painter) {
this.backgroundPainter = painter;
return this;
}
-
+
public boolean canFocus() {
return true;
}
-
+
@Override
public void onFocusGained() {
}
@@ -306,26 +322,28 @@ public class WTextField extends WWidget {
@Override
public InputResult onClick(int x, int y, int button) {
requestFocus();
- cursor = getCaretPos(this.text, x-OFFSET_X_TEXT);
+ cursor = getCaretPos(this.text, x - TEXT_PADDING_X);
+ scrollCursorIntoView();
return InputResult.PROCESSED;
}
@Environment(EnvType.CLIENT)
@Override
public void onCharTyped(char ch) {
- if (this.text.length()<this.maxLength) {
+ if (this.text.length() < this.maxLength) {
//snap cursor into bounds if it went astray
- if (cursor<0) cursor=0;
- if (cursor>this.text.length()) cursor = this.text.length();
-
+ if (cursor < 0) cursor = 0;
+ if (cursor > this.text.length()) cursor = this.text.length();
+
String before = this.text.substring(0, cursor);
String after = this.text.substring(cursor, this.text.length());
- this.text = before+ch+after;
+ this.text = before + ch + after;
cursor++;
+ scrollCursorIntoView();
if (onChanged != null) onChanged.accept(text);
}
}
-
+
@Environment(EnvType.CLIENT)
@Override
public void onKeyPressed(int ch, int key, int modifiers) {
@@ -333,40 +351,40 @@ public class WTextField extends WWidget {
if (Screen.isCopy(ch)) {
String selection = getSelection();
- if (selection!=null) {
+ if (selection != null) {
MinecraftClient.getInstance().keyboard.setClipboard(selection);
}
-
+
return;
} else if (Screen.isPaste(ch)) {
- if (select!=-1) {
+ if (select != -1) {
int a = select;
int b = cursor;
- if (b<a) {
+ if (b < a) {
int tmp = b;
b = a;
a = tmp;
}
String before = this.text.substring(0, a);
String after = this.text.substring(b);
-
+
String clip = MinecraftClient.getInstance().keyboard.getClipboard();
- text = before+clip+after;
+ text = before + clip + after;
select = -1;
- cursor = (before+clip).length();
+ cursor = (before + clip).length();
} else {
String before = this.text.substring(0, cursor);
String after = this.text.substring(cursor, this.text.length());
-
+
String clip = MinecraftClient.getInstance().keyboard.getClipboard();
text = before + clip + after;
cursor += clip.length();
- if (text.length()>this.maxLength) {
+ if (text.length() > this.maxLength) {
text = text.substring(0, maxLength);
- if (cursor>text.length()) cursor = text.length();
+ if (cursor > text.length()) cursor = text.length();
}
}
-
+ scrollCursorIntoView();
if (onChanged != null) onChanged.accept(text);
return;
} else if (Screen.isSelectAll(ch)) {
@@ -374,66 +392,67 @@ public class WTextField extends WWidget {
cursor = text.length();
return;
}
-
+
//System.out.println("Ch: "+ch+", Key: "+key+", Mod: "+modifiers);
-
- if (modifiers==0) {
- if (ch==GLFW.GLFW_KEY_DELETE || ch==GLFW.GLFW_KEY_BACKSPACE) {
- if (text.length()>0 && cursor>0) {
- if (select>=0 && select!=cursor) {
+
+ if (modifiers == 0) {
+ if (ch == GLFW.GLFW_KEY_DELETE || ch == GLFW.GLFW_KEY_BACKSPACE) {
+ if (text.length() > 0 && cursor > 0) {
+ if (select >= 0 && select != cursor) {
int a = select;
int b = cursor;
- if (b<a) {
+ if (b < a) {
int tmp = b;
b = a;
a = tmp;
}
String before = this.text.substring(0, a);
String after = this.text.substring(b);
- text = before+after;
- if (cursor==b) cursor = a;
+ text = before + after;
+ if (cursor == b) cursor = a;
select = -1;
} else {
String before = this.text.substring(0, cursor);
String after = this.text.substring(cursor, this.text.length());
-
- before = before.substring(0,before.length()-1);
- text = before+after;
+
+ before = before.substring(0, before.length() - 1);
+ text = before + after;
cursor--;
}
if (onChanged != null) onChanged.accept(text);
}
- } else if (ch==GLFW.GLFW_KEY_LEFT) {
- if (select!=-1) {
+ } else if (ch == GLFW.GLFW_KEY_LEFT) {
+ if (select != -1) {
cursor = Math.min(cursor, select);
select = -1; //Clear the selection anchor
} else {
- if (cursor>0) cursor--;
+ if (cursor > 0) cursor--;
}
- } else if (ch==GLFW.GLFW_KEY_RIGHT) {
- if (select!=-1) {
+ } else if (ch == GLFW.GLFW_KEY_RIGHT) {
+ if (select != -1) {
cursor = Math.max(cursor, select);
select = -1; //Clear the selection anchor
} else {
- if (cursor<text.length()) cursor++;
+ if (cursor < text.length()) cursor++;
}
} else {
//System.out.println("Ch: "+ch+", Key: "+key);
}
} else {
- if (modifiers==GLFW.GLFW_MOD_SHIFT) {
- if (ch==GLFW.GLFW_KEY_LEFT) {
- if (select==-1) select = cursor;
- if (cursor>0) cursor--;
- if (select==cursor) select = -1;
- } else if (ch==GLFW.GLFW_KEY_RIGHT) {
- if (select==-1) select = cursor;
- if (cursor<text.length()) cursor++;
- if (select==cursor) select = -1;
+ if (modifiers == GLFW.GLFW_MOD_SHIFT) {
+ if (ch == GLFW.GLFW_KEY_LEFT) {
+ if (select == -1) select = cursor;
+ if (cursor > 0) cursor--;
+ if (select == cursor) select = -1;
+ } else if (ch == GLFW.GLFW_KEY_RIGHT) {
+ if (select == -1) select = cursor;
+ if (cursor < text.length()) cursor++;
+ if (select == cursor) select = -1;
}
}
}
+ scrollCursorIntoView();
}
@Override
@@ -446,41 +465,28 @@ public class WTextField extends WWidget {
}
/**
- * From an X offset past the left edge of a TextRenderer.draw, finds out what the closest caret
- * position (division between letters) is.
+ * From an X offset past the left edge of a TextRenderer.draw, finds out what the closest caret position (division
+ * between letters) is.
+ *
* @param s
* @param x
+ *
* @return
*/
@Environment(EnvType.CLIENT)
public static int getCaretPos(String s, int x) {
- if (x<=0) return 0;
-
+ if (x <= 0) return 0;
+
TextRenderer font = MinecraftClient.getInstance().textRenderer;
int lastAdvance = 0;
- for(int i=0; i<s.length()-1; i++) {
- int advance = font.getWidth(s.substring(0,i+1));
- int charAdvance = advance-lastAdvance;
- if (x<advance + (charAdvance/2)) return i+1;
-
+ for (int i = 0; i < s.length() - 1; i++) {
+ int advance = font.getWidth(s.substring(0, i + 1));
+ int charAdvance = advance - lastAdvance;
+ if (x < advance + (charAdvance / 2)) return i + 1;
+
lastAdvance = advance;
}
-
+
return s.length();
}
-
- /**
- * From a caret position, finds out what the x-offset to draw the caret is.
- * @param s
- * @param pos
- * @return
- */
- @Environment(EnvType.CLIENT)
- public static int getCaretOffset(String s, int pos) {
- if (pos==0) return 0;//-1;
-
- TextRenderer font = MinecraftClient.getInstance().textRenderer;
- int ofs = font.getWidth(s.substring(0, pos))+1;
- return ofs; //(font.isRightToLeft()) ? -ofs : ofs;
- }
}