Skip to content

Instantly share code, notes, and snippets.

@Caellian
Last active August 29, 2015 14:26
Show Gist options
  • Save Caellian/3c0fa5b6dc053924a855 to your computer and use it in GitHub Desktop.
Save Caellian/3c0fa5b6dc053924a855 to your computer and use it in GitHub Desktop.
TextArea Showcase
package com.caellian.codeBlocks.client.gui;
import com.google.common.collect.Lists;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.util.ChatAllowedCharacters;
import org.apache.commons.lang3.ArrayUtils;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
/**
* @author Caellian
*/
public class GUITextArea extends Gui
{
/**
* Font renderer used by this GUITextArea.
*/
public final FontRenderer fontRendererInstance;
/**
* The x position of this text field.
*/
public int xPosition;
/**
* The y position of this text field.
*/
public int yPosition;
/**
* The width of this text field.
*/
public int width;
/**
* The height of this text field.
*/
public int height;
/**
* Text area content.
*/
private ArrayList<String> text = new ArrayList<>(0);
/**
* Default string length limit.
*/
public int maxLineCount = Short.MAX_VALUE;
/**
* Default string length limit.
*/
public int maxLineLength = Short.MAX_VALUE;
/**
* Number of pixels to put between lines.
*/
public int lineMargin = 2;
/**
* True if text area background should be drawn.
*/
public boolean enableBackgroundDrawing = true;
/**
* If true the text area can lose focus by clicking elsewhere on the screen.
*/
public boolean canLoseFocus = true;
/**
* If this value is true along with isEnabled, keyTyped will process the keys.
*/
public boolean isFocused = false;
/**
* If this value is true along with isFocused, keyTyped will process the keys.
*/
public boolean isEnabled = true;
/**
* The current character index that should be used as start of the rendered text on the top right corner of GUI.
*/
public int[] offsetDisplay = new int[]{0, 0};
/**
* Current cursor position.
*/
public int[] cursor = new int[]{0, 0};
/**
* Selection.
*/
public int[][] selection = new int[][]{{0, 0}, {0, 0}};
/**
* Color used to display text inside of enabled text area.
*/
public Color enabledColor = new Color(224, 224, 224);
/**
* Color used to display text inside of disabled text area.
*/
public Color disabledColor = new Color(112, 112, 112);
/**
* Color used to display cursor.
*/
public Color cursorColor = new Color(255, 255, 255);
/**
* Color used to display text behind selection.
*/
public Color highlightColor = new Color(0, 0, 255);
/**
* Color used to display text behind selection.
*/
public int highlightColorOperator = GL11.GL_OR_REVERSE;
/**
* True if this text area is visible.
*/
public boolean visible = true;
/**
* Characters that are considered whitespace running trough text.
*/
public char[] whitespaceCharacters = new char[]{' ', '_'};
/**
* Map of custom key bindings.
*/
private final HashMap<Integer, Action> keyBindings = new HashMap<>(0);
public GUITextArea(FontRenderer fontRendererInstance, int width, int height, boolean isEnabled)
{
this(fontRendererInstance, (Minecraft.getMinecraft().displayWidth - width) / 2, (Minecraft.getMinecraft().displayHeight - height) / 2, width, height, isEnabled);
}
public GUITextArea(FontRenderer fontRendererInstance, int xPosition, int yPosition, int width, int height, boolean isEnabled)
{
this(fontRendererInstance, xPosition, yPosition, width, height, 64, 32, isEnabled);
}
public GUITextArea(FontRenderer fontRendererInstance, int xPosition, int yPosition, int width, int height, int maxLineCount, int maxLineLength, boolean isEnabled)
{
this.fontRendererInstance = fontRendererInstance;
this.xPosition = xPosition;
this.yPosition = yPosition;
this.width = width;
this.height = height;
this.maxLineCount = maxLineCount;
this.maxLineLength = maxLineLength;
this.isEnabled = isEnabled;
this.text.add("");
}
public void addKeyBinding(int characterKey, Action action)
{
keyBindings.put(characterKey, action);
}
/**
* This method returns text using line break characters ('\n') in between the lines.
*
* @return Text string.
*/
public String getRawText()
{
String result = "";
if (!isEmpty())
{
for (String line : this.text)
{
result += line + "\n";
}
}
return result.substring(0, result.length() - 1);
}
/**
* This method returns selected text.
*
* @return Selected text.
*/
public String getSelectedText()
{
String result = "";
if (!isEmpty())
{
for (int line = selection[0][1]; line <= selection[1][1]; line++)
{
if (line == selection[0][1])
{
result += text.get(line).substring(selection[0][0]);
}
else if (line == selection[1][1])
{
result += text.get(line).substring(0, selection[1][0]);
}
else
{
result += text.get(line);
}
}
}
return result;
}
/**
* This method replaces selected area with specified text or puts the specified text at cursor position if there is
* no area selected.
*
* @param input
* Text to replace selected area with or put at cursor position if no area is selected.
*
* @return True if area was selected.
*/
public boolean writeText(String... input)
{
ArrayList<String> inputManager = new ArrayList<>(0);
for (String line : input)
{
inputManager.addAll(Lists.newArrayList(line.split("\n")));
}
String[] inputModified = inputManager.toArray(new String[inputManager.size()]);
boolean replace = false;
if (selection[0] != selection[1])
{
replace = true;
}
int firstLine;
int lastLine;
String lineStart;
String lineEnd;
if (replace)
{
firstLine = selection[0][1];
lastLine = selection[1][1];
lineStart = text.get(firstLine).substring(0, selection[0][0]);
lineEnd = text.get(lastLine).substring(selection[1][0]);
}
else
{
firstLine = lastLine = cursor[1];
lineStart = text.get(firstLine).substring(0, cursor[0]);
lineEnd = text.get(lastLine).substring(cursor[0]);
}
for (int line = firstLine; line < lastLine; line++)
{
this.text.remove(firstLine);
}
inputModified[0] = lineStart + inputModified[0];
inputModified[inputModified.length - 1] = inputModified[inputModified.length - 1] + lineEnd;
this.text.addAll(firstLine, Lists.newArrayList(inputModified));
this.moveCursor(new int[]{1, 0});
return replace;
}
/**
* This method moves the cursor around on specified axis. Both arguments can be negative.
*
* @param amount
* This is number to characters/words to move the cursor from specified position.
* This argument members can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
*/
public void moveCursor(int[] amount)
{
if (!isEmpty())
{
if (this.text.size() - 1 < this.cursor[1] + amount[1])
{
this.cursor[1] = this.text.size() - 1;
}
else if (this.cursor[1] + amount[1] < 0)
{
this.cursor[1] = 0;
}
else
{
this.cursor[1] = this.cursor[1] + amount[1];
}
if (this.text.get(this.cursor[1]).length() - 1 < this.cursor[0] + amount[0])
{
int tmp = this.text.get(this.cursor[1]).length() - 1;
this.cursor[0] = tmp < 0 ? 0 : tmp;
}
else if (this.cursor[0] + amount[0] < 0)
{
this.cursor[0] = 0;
}
else
{
this.cursor[0] = this.cursor[0] + amount[0];
}
if (this.offsetDisplay[0] > this.cursor[0])
{
this.offsetDisplay[0] = this.cursor[0];
}
else if (this.offsetDisplay[0] + this.width < this.cursor[0])
{
this.offsetDisplay[0] = this.cursor[0] - this.width;
}
if (this.offsetDisplay[1] > this.cursor[1])
{
this.offsetDisplay[1] = this.cursor[1];
}
else if (this.offsetDisplay[1] + this.height < this.cursor[1])
{
this.offsetDisplay[1] = this.cursor[1] - this.height;
}
}
}
/**
* This method moves the cursor around on specified axis. Both arguments can be negative.
*
* @param amount
* This is number to characters/words to move the cursor from specified position.
* This argument members can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param whiteSpaces
* If true method will iterate over whitespace characters instead of all characters.</br>
* This allows skipping over words instead of individual characters.
*/
public void moveCursor(int[] amount, boolean whiteSpaces)
{
if (whiteSpaces)
{
int[] newPosition = this.relativePos(amount, this.cursor, true);
moveCursor(new int[]{newPosition[0] - this.cursor[0], newPosition[1] - this.cursor[1]});
}
else
{
moveCursor(amount);
}
}
/**
* This method moves the cursor to specified position.
*
* @param newPosition
* Position to put cursor at.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
*/
public void setCursorSafely(int[] newPosition)
{
moveCursor(new int[]{newPosition[0] - this.cursor[0], newPosition[1] - this.cursor[1]});
}
/**
* @param moveAmount
* This is number to characters/words to move from specified position. This argument can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param position
* This is the starting position used by this method.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param whiteSpaces
* If true method will iterate over whitespace characters instead of all characters.</br>
* This allows skipping over words instead of individual characters.
*
* @return New position relative to specified on by specified amount of characters/words.
*/
public int[] relativePos(int[] moveAmount, int[] position, boolean whiteSpaces)
{
return relativePos(moveAmount, position, whiteSpaces, this.text.toArray(new String[this.text.size()]));
}
/**
* Returns position relative to specified one by specified one in direction dependant on amount sign.
*
* @param moveAmount
* This is number to characters/words to move from specified position.<br/>
* This argument can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param position
* This is the starting position used by this method.<br/>
* If X coordinate of this argument is -1,
* the coordinate of the last letter in line defined by Y coordinate will be used.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param whiteSpaces
* If true method will iterate over whitespace characters instead of all characters.<br/>
* This allows skipping over words instead of individual characters.
* @param text
* Text to iterate over.
*
* @return New position relative to specified on by specified amount of characters/words.
*/
public int[] relativePos(int[] moveAmount, int[] position, boolean whiteSpaces, String... text)
{
//noinspection deprecation
return relativePos(moveAmount, position, whiteSpaces, false, text);
}
//TODO: Test wrapLineSearch.
//TODO: See if relativePos code can be compacted further.
/**
* Returns position relative to specified one by specified one in direction dependant on amount sign.
*
* @param moveAmount
* This is number to characters/words to move from specified position.<br/>
* This argument can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param position
* This is the starting position used by this method.<br/>
* If X coordinate of this argument is -1,
* the coordinate of the last letter in line defined by Y coordinate will be used.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param whiteSpaces
* If true method will iterate over whitespace characters instead of all characters.<br/>
* This allows skipping over words instead of individual characters.
* @param wrapLineSearch
* If true method will use exhaustive/multiline X coordinate mode.<br/>
* This means that if X coordinate of moveAmount is bigger than resulting line in specified direction,
* this method will recursively return value from line below/above this one.<br/>
* <p>
* <b>This is untested and could possibly create an infinite loop.<b/>
* @param text
* Text to iterate over.
*
* @return New position relative to specified on by specified amount of characters/words.
*/
@Deprecated
public int[] relativePos(int[] moveAmount, int[] position, boolean whiteSpaces, boolean wrapLineSearch, String... text)
{
if (!isEmpty())
{
if (position[0] == -1)
{
position[0] = text[position[1]].length() - 1;
}
if (whiteSpaces)
{
int[] result = position.clone();
if (text.length < result[1] + moveAmount[1])
{
result[1] = text.length;
}
else if (result[1] + moveAmount[1] < 0)
{
result[1] = 0;
}
else
{
result[1] = result[1] + moveAmount[1];
}
if (text[result[1]].length() - 1 < result[0] + moveAmount[0])
{
if (wrapLineSearch && position != new int[]{text[text.length - 1].length() - 1, text.length - 1})
{
//noinspection deprecation
this.relativePos(new int[]{moveAmount[0] - text[result[1]].substring(position[0], (text[result[1]].length() - 1)).length(), 0}, this.relativePos(new int[]{0, 1}, new int[]{0, ++position[1]}, true, true, text), true, true, text);
}
else
{
result[0] = text[result[1]].length() - 1;
}
}
else if (result[0] + moveAmount[0] < 0)
{
if (wrapLineSearch && position != new int[]{0, 0})
{
//noinspection deprecation
this.relativePos(new int[]{moveAmount[0] + text[result[1]].substring(0, position[0]).length(), 0}, this.relativePos(new int[]{0, -1}, new int[]{-1, --position[1]}, true, true, text), true, true, text);
}
else
{
result[0] = 0;
}
}
else
{
result[0] = result[0] + moveAmount[0];
}
return result;
}
else
{
int[] result = position.clone();
if (text.length < result[1] + moveAmount[1])
{
result[1] = text.length;
}
else if (result[1] + moveAmount[1] < 0)
{
result[1] = 0;
}
else
{
result[1] = result[1] + moveAmount[1];
}
int movementLeft = moveAmount[0];
while (movementLeft != 0)
{
try
{
while (!ArrayUtils.contains(this.whitespaceCharacters, text[result[1]].charAt(result[0])))
{
result[0] += movementLeft < 0 ? -1 : 1;
}
movementLeft = movementLeft < 0 ? movementLeft + 1 : movementLeft - 1;
} catch (IndexOutOfBoundsException ignored)
{
//Code has reached the end of line.
result[1] += movementLeft < 0 ? -1 : 1;
if (result[1] < 0)
{
return new int[]{0, 0};
}
else if (result[1] > text.length)
{
return new int[]{text[text.length - 1].length(), text.length - 1};
}
else
{
result[0] = movementLeft < 0 ? text[result[1]].length() - 1 : 0;
}
}
}
}
}
return new int[]{0, 0};
}
/**
* This method will remove specified selection from specified text.
*
* @param selection
* Selection to remove from text.<br/>
* <p>
* <b>This integer array of arrays must contain two arrays of integers representing first and second coordinate locations,<br/>
* and both coordinate locations must contain two integers representing X and Y coordinate.<b/>
* @param text
* Text to modify.
*
* @return Removed text from selection.
*/
public String[] removeText(int[][] selection, String... text)
{
ArrayList<String> result = Lists.newArrayList(text);
int firstLine = selection[0][1];
int lastLine = selection[1][1];
String start = "";
String end = "";
for (int line = firstLine; line <= lastLine; line++)
{
if (line == firstLine)
{
start = text[firstLine].substring(0, selection[0][0]);
}
else if (line == lastLine)
{
end = text[lastLine].substring(selection[1][0], text[lastLine].length() - 1);
}
result.remove(firstLine);
}
if (!Objects.equals(start + end, ""))
{
result.add(firstLine, start + end);
}
return result.toArray(new String[result.size()]);
}
/**
* Selects all text in text area.
*/
public void selectEverything()
{
this.selection[0][0] = 0;
this.selection[0][1] = 0;
this.selection[1][1] = this.text.size() - 1;
this.selection[1][0] = this.text.get(this.selection[1][1]).length() - 1;
this.cursor = this.selection[1];
}
/**
* @return True if text doesn't contain anything.
*/
public boolean isEmpty()
{
return this.text.size() == 0 || this.text == new ArrayList<String>(0);
}
/**
* Removes characters from specified string.
*
* @param amount
* Number of characters to delete. This argument components can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param currentPosition
* Current position.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param text
* Text to remove characters currentPosition.
*/
public void deleteCharacters(int[] amount, int[] currentPosition, String[] text)
{
this.removeText(new int[][]{this.relativePos(amount, currentPosition, false, text), currentPosition}, text);
}
/**
* Removes words from specified string in direction defined by amount signs.
*
* @param amount
* Number of words to delete. This argument components can be negative.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param currentPosition
* Current position.<br/>
* <p>
* <b>This integer array must contain two integers representing X and Y coordinate.<b/>
* @param text
* Text to remove characters currentPosition.
*/
public void deleteWords(int[] amount, int[] currentPosition, String[] text)
{
this.removeText(new int[][]{this.relativePos(amount, currentPosition, true, text), currentPosition}, text);
}
/**
* Call this method from your GUI if you want keys to be handled automatically by this class.
*
* @param typedCharacter
* Character typed.
* @param keyCode
* Integer value of key typed.
*
* @return True if key was handled properly.
*/
public boolean keyTyped(char typedCharacter, int keyCode)
{
if (!this.isFocused)
{
return false;
}
else
{
if (isControlDown())
{
switch (keyCode)
{
case Keyboard.KEY_A:
selectEverything();
return true;
case Keyboard.KEY_C:
GuiScreen.setClipboardString(this.getSelectedText());
return true;
case Keyboard.KEY_V:
if (this.isEnabled)
{
this.writeText(GuiScreen.getClipboardString());
}
return true;
case Keyboard.KEY_X:
GuiScreen.setClipboardString(this.getSelectedText());
if (this.isEnabled)
{
this.writeText("");
}
return true;
}
}
switch (keyCode)
{
case Keyboard.KEY_BACK:
if (this.isEnabled)
{
if (this.cursor[0] == 0 && this.cursor[1] != 0)
{
this.text.set(this.cursor[1] - 1, this.text.get(this.cursor[1] - 1) + this.text.remove(this.cursor[1]));
}
else if (isControlDown())
{
this.deleteWords(new int[]{-1, 0}, this.cursor, this.text.toArray(new String[this.text.size()]));
}
else
{
if (selection[0] == selection[1])
{
this.deleteCharacters(new int[]{-1, 0}, this.cursor, this.text.toArray(new String[this.text.size()]));
}
else
{
this.text = Lists.newArrayList(this.removeText(selection, this.text.toArray(new String[this.text.size()])));
}
}
}
return true;
case Keyboard.KEY_DELETE:
if (this.isEnabled)
{
if (isControlDown())
{
this.deleteWords(new int[]{1, 0}, this.cursor, this.text.toArray(new String[this.text.size()]));
}
else
{
this.deleteCharacters(new int[]{1, 0}, this.cursor, this.text.toArray(new String[this.text.size()]));
}
}
return true;
case Keyboard.KEY_HOME:
if (isShiftDown())
{
this.selection = new int[][]{{0, this.cursor[1]}, this.cursor};
}
else
{
this.cursor = new int[]{0, this.cursor[1]};
}
return true;
case Keyboard.KEY_END:
if (isShiftDown())
{
this.selection = new int[][]{this.cursor, {0, this.text.get(this.cursor[1]).length() - 1}};
}
else
{
this.cursor = new int[]{this.text.get(this.cursor[1]).length() - 1, this.cursor[1]};
}
return true;
case Keyboard.KEY_LEFT:
if (isShiftDown())
{
if (isControlDown())
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{-1, 0}, this.selection[1], true, (String[]) this.text.toArray(new String[this.text.size()]))};
}
else
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{-1, 0}, this.selection[1], false, (String[]) this.text.toArray(new String[this.text.size()]))};
}
}
else if (isControlDown())
{
this.moveCursor(new int[]{-1, 0}, true);
}
else
{
this.moveCursor(new int[]{-1, 0});
}
return true;
case Keyboard.KEY_RIGHT:
if (isShiftDown())
{
if (isControlDown())
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{1, 0}, this.selection[1], true, (String[]) this.text.toArray(new String[this.text.size()]))};
}
else
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{1, 0}, this.selection[1], false, (String[]) this.text.toArray(new String[this.text.size()]))};
}
}
else if (isControlDown())
{
this.moveCursor(new int[]{1, 0}, true);
}
else
{
this.moveCursor(new int[]{1, 0});
}
return true;
case Keyboard.KEY_DOWN:
if (isShiftDown())
{
if (isControlDown())
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{1, 1}, this.selection[1], true, (String[]) this.text.toArray(new String[this.text.size()]))};
}
else
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{0, 1}, this.selection[1], false, (String[]) this.text.toArray(new String[this.text.size()]))};
}
}
else if (isControlDown())
{
this.moveCursor(new int[]{0, 1}, true);
}
else
{
this.moveCursor(new int[]{0, 1});
}
return true;
case Keyboard.KEY_UP:
if (isShiftDown())
{
if (isControlDown())
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{-1, -1}, this.selection[1], true, (String[]) this.text.toArray(new String[this.text.size()]))};
}
else
{
this.selection = new int[][]{this.cursor, this.relativePos(new int[]{0, -1}, this.selection[1], false, (String[]) this.text.toArray(new String[this.text.size()]))};
}
}
else if (isControlDown())
{
this.moveCursor(new int[]{0, -1}, true);
}
else
{
this.moveCursor(new int[]{0, -1});
}
return true;
default:
if (keyBindings.containsKey(keyCode))
{
if (!keyBindings.get(keyCode).execute())
{
return handleInput(typedCharacter);
}
}
else
{
return handleInput(typedCharacter);
}
}
}
return false;
}
protected boolean handleInput(char typedCharacter)
{
if (ChatAllowedCharacters.isAllowedCharacter(typedCharacter))
{
if (this.isEnabled)
{
this.writeText(Character.toString(typedCharacter));
}
return true;
}
else
{
return false;
}
}
/**
* Call this method from your GUI if you want mouse to be handled automatically by this class.
*
* @param xPos
* Clicked X position.
* @param yPos
* Clicked Y position.
* @param buttonClicked
* Clicked mouse button code.
*/
public void mouseClicked(int xPos, int yPos, int buttonClicked)
{
boolean inside = xPos >= this.xPosition &&
xPos < this.xPosition + this.width &&
yPos >= this.yPosition &&
yPos < this.yPosition + this.height;
if (this.canLoseFocus)
{
this.isFocused = inside;
}
if (this.isFocused && inside && buttonClicked == 0 && !isEmpty())
{
int[] posClicked = new int[2];
posClicked[1] = this.height - (xPos - this.xPosition) / this.fontRendererInstance.FONT_HEIGHT;
if (this.text.size() >= posClicked[1] + offsetDisplay[1])
{
String clickedLine = this.fontRendererInstance.trimStringToWidth(this.text.get(posClicked[1] + offsetDisplay[1]).substring(this.offsetDisplay[0]), this.width);
int length = xPos - this.xPosition;
if (this.enableBackgroundDrawing)
{
length -= 4;
}
posClicked[0] = this.fontRendererInstance.trimStringToWidth(clickedLine, length).length() + this.offsetDisplay[0];
this.setCursorSafely(posClicked);
this.selection[0] = this.cursor;
}
else
{
this.setCursorSafely(new int[]{this.text.get(this.text.size() - 1).length(), this.text.size() - 1});
this.selection[0] = this.cursor;
}
}
}
/**
* @param mouseX
* Current mouse X position.
* @param mouseY
* Current mouse Y position.
* @param clickedMouseButton
* Clicked mouse button code.
* @param timeSinceLastClick
* Value representing ticks mouse button was held for.
*/
public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick)
{
boolean inside = mouseX >= this.xPosition &&
mouseX < this.xPosition + this.width &&
mouseY >= this.yPosition &&
mouseY < this.yPosition + this.height;
if (this.canLoseFocus)
{
this.isFocused = inside;
}
if (this.isFocused && inside && clickedMouseButton == 0 && !isEmpty())
{
int[] posDragged = new int[2];
posDragged[1] = this.height - (mouseX - this.xPosition) / this.fontRendererInstance.FONT_HEIGHT;
if (this.text.size() >= posDragged[1] + offsetDisplay[1])
{
String currentLine = this.fontRendererInstance.trimStringToWidth(this.text.get(posDragged[1] + offsetDisplay[1]).substring(this.offsetDisplay[0]), this.width - this.lineMargin * 2);
int length = mouseX - this.xPosition;
if (this.enableBackgroundDrawing)
{
length -= 4;
}
posDragged[0] = this.fontRendererInstance.trimStringToWidth(currentLine, length).length() + this.offsetDisplay[0];
this.selection[1] = posDragged;
}
else
{
this.selection[1] = new int[]{this.text.get(this.text.size() - 1).length(), this.text.size() - 1};
}
}
}
public void render()
{
if (this.visible)
{
if (this.enableBackgroundDrawing)
{
drawRect(this.xPosition - 1, this.yPosition - 1, this.xPosition + this.width + 1, this.yPosition + this.height + 1, new Color(95, 95, 95).getRGB());
drawRect(this.xPosition, this.yPosition, this.xPosition + this.width, this.yPosition + this.height, new Color(16, 16, 16).getRGB());
}
if (!isEmpty())
{
int textColor = this.isEnabled ? this.enabledColor.getRGB() : this.disabledColor.getRGB();
int firstLine = this.offsetDisplay[1];
int lineCount = this.height % (this.fontRendererInstance.FONT_HEIGHT + this.lineMargin) >= this.lineMargin ? (this.height / (this.fontRendererInstance.FONT_HEIGHT + this.lineMargin)) : (this.height / (this.fontRendererInstance.FONT_HEIGHT + this.lineMargin)) - 1;
int lastLine = firstLine + lineCount;
for (int line = firstLine; line < lastLine && line <= this.text.size() - 1; line++)
{
String renderedLine = this.fontRendererInstance.trimStringToWidth(this.text.get(line).substring(this.offsetDisplay[0]), this.width - this.lineMargin * 2);
if (renderedLine != null && !Objects.equals(renderedLine, ""))
{
int xPos = this.xPosition + this.lineMargin;
int yPos = this.yPosition + (this.lineMargin + (line * (this.fontRendererInstance.FONT_HEIGHT + this.lineMargin)));
this.fontRendererInstance.drawString(renderedLine, xPos, yPos, textColor);
//DEBUGGING
this.fontRendererInstance.drawString(String.valueOf(line), xPos - 15, yPos, textColor);
//DEBUGGING
this.fontRendererInstance.drawString("CHAR:" + String.valueOf(this.cursor[0]) + ", LINE:" + String.valueOf(this.cursor[1]), 50, 50, textColor);
}
if (cursorLogic() && this.cursor[1] == line)
{
int cursorX;
if (this.text.get(line).length() > 0)
{
//DEBUGGING
System.out.println(this.text.get(line));
cursorX = this.xPosition + this.lineMargin + this.fontRendererInstance.getStringWidth(this.text.get(line).substring(this.offsetDisplay[0], this.cursor[0]));
}
else
{
cursorX = this.xPosition + this.lineMargin;
}
int cursorY = this.yPosition + (this.lineMargin + (line * (this.fontRendererInstance.FONT_HEIGHT + this.lineMargin)));
Gui.drawRect(cursorX, cursorY, cursorX, cursorY + (this.lineMargin * 2) + this.fontRendererInstance.FONT_HEIGHT, cursorColor.getRGB());
}
}
if (this.selection[0] != this.selection[1])
{
for (int line = this.selection[0][1]; line < this.selection[1][1]; line++)
{
int drawnWidth;
int lineX;
int lineY = this.yPosition + this.height - (this.lineMargin + ((firstLine - line) * (this.fontRendererInstance.FONT_HEIGHT + this.lineMargin)));
if (line == this.selection[0][1])
{
drawnWidth = this.fontRendererInstance.getStringWidth(this.fontRendererInstance.trimStringToWidth(this.text.get(line).substring(this.offsetDisplay[0]), this.width - this.lineMargin * 2).substring(this.selection[0][0]));
lineX = this.xPosition + this.width - (drawnWidth + this.lineMargin);
}
else if (line == this.selection[1][1])
{
drawnWidth = this.fontRendererInstance.getStringWidth(this.text.get(line).substring(this.offsetDisplay[0], this.selection[1][0]));
lineX = this.xPosition + this.lineMargin;
}
else
{
drawnWidth = this.width - this.lineMargin * 2;
lineX = this.xPosition + this.lineMargin;
}
this.drawBoxOverlay(new int[][]{{lineX, lineY}, {lineX + drawnWidth, lineY + this.lineMargin + this.fontRendererInstance.FONT_HEIGHT}});
}
}
}
}
}
private int cursorTimer;
private boolean cursorLogic()
{
cursorTimer = cursorTimer > 40 ? 0 : cursorTimer + 1;
return this.isFocused && this.cursorTimer / 6 % 2 == 0;
}
public void updateCursorTimer()
{
cursorTimer++;
}
/**
* Draws the vertical line cursor in the text area.
*
* @param selection
* This is an integer array of arrays containing box display vertices.<br/>
* <p>
* <b>This integer array of arrays must contain two arrays of integers representing first and second coordinate locations,<br/>
* and both coordinate locations must contain two integers representing X and Y coordinate.<b/>
*/
private void drawBoxOverlay(int[][] selection)
{
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer worldrenderer = tessellator.getWorldRenderer();
GlStateManager.color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 255.0F);
GlStateManager.disableTexture2D();
GlStateManager.enableColorLogic();
GlStateManager.colorLogicOp(highlightColorOperator);
worldrenderer.startDrawingQuads();
worldrenderer.addVertex((double) selection[0][0], (double) selection[1][0], 0.0D);
worldrenderer.addVertex((double) selection[1][0], (double) selection[1][1], 0.0D);
worldrenderer.addVertex((double) selection[1][1], (double) selection[0][1], 0.0D);
worldrenderer.addVertex((double) selection[0][1], (double) selection[0][0], 0.0D);
tessellator.draw();
GlStateManager.disableColorLogic();
GlStateManager.enableTexture2D();
}
/**
* @return True if control key is being held down.
*/
private static boolean isControlDown()
{
return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
}
/**
* @return True if shift key is being held down.
*/
private static boolean isShiftDown()
{
return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
}
/**
* @return True if alt key is being held down.
*/
private static boolean isAltDown()
{
return Keyboard.isKeyDown(Keyboard.KEY_LMENU);
}
public FontRenderer getFontRendererInstance()
{
return fontRendererInstance;
}
public int getXPosition()
{
return xPosition;
}
public void setXPosition(int xPosition)
{
this.xPosition = xPosition;
}
public int getYPosition()
{
return yPosition;
}
public void setYPosition(int yPosition)
{
this.yPosition = yPosition;
}
public int getWidth()
{
return width;
}
public void setWidth(int width)
{
this.width = width;
}
public int getHeight()
{
return height;
}
public void setHeight(int height)
{
this.height = height;
}
public ArrayList<String> getText()
{
return text;
}
public void setText(ArrayList<String> text)
{
this.text = text;
}
public int getMaxLineCount()
{
return maxLineCount;
}
public void setMaxLineCount(int maxLineCount)
{
this.maxLineCount = maxLineCount;
}
public int getMaxLineLength()
{
return maxLineLength;
}
public void setMaxLineLength(int maxLineLength)
{
this.maxLineLength = maxLineLength;
}
public int getLineMargin()
{
return lineMargin;
}
public void setLineMargin(int lineMargin)
{
this.lineMargin = lineMargin;
}
public boolean isBackgroundDrawingEnabled()
{
return enableBackgroundDrawing;
}
public void setEnableBackgroundDrawing(boolean enableBackgroundDrawing)
{
this.enableBackgroundDrawing = enableBackgroundDrawing;
}
public boolean canLoseFocus()
{
return canLoseFocus;
}
public void setCanLoseFocus(boolean canLoseFocus)
{
this.canLoseFocus = canLoseFocus;
}
public boolean isFocused()
{
return isFocused;
}
public void setIsFocused(boolean isFocused)
{
this.isFocused = isFocused;
}
public boolean isEnabled()
{
return isEnabled;
}
public void setIsEnabled(boolean isEnabled)
{
this.isEnabled = isEnabled;
}
public int[] getOffsetDisplay()
{
return offsetDisplay;
}
public void setOffsetDisplay(int[] offsetDisplay)
{
this.offsetDisplay = offsetDisplay;
}
public int[] getCursor()
{
return cursor;
}
public void setCursor(int[] cursor)
{
this.cursor = cursor;
}
public int[][] getSelection()
{
return selection;
}
public void setSelection(int[][] selection)
{
this.selection = selection;
}
public Color getEnabledColor()
{
return enabledColor;
}
public void setEnabledColor(Color enabledColor)
{
this.enabledColor = enabledColor;
}
public Color getDisabledColor()
{
return disabledColor;
}
public void setDisabledColor(Color disabledColor)
{
this.disabledColor = disabledColor;
}
public boolean isVisible()
{
return visible;
}
public void setVisible(boolean visible)
{
this.visible = visible;
}
public char[] getWhitespaceCharacters()
{
return whitespaceCharacters;
}
public void setWhitespaceCharacters(char[] whitespaceCharacters)
{
this.whitespaceCharacters = whitespaceCharacters;
}
public HashMap<Integer, Action> getKeyBindings()
{
return keyBindings;
}
public Color getCursorColor()
{
return cursorColor;
}
public void setCursorColor(Color cursorColor)
{
this.cursorColor = cursorColor;
}
public Color getHighlightColor()
{
return highlightColor;
}
public void setHighlightColor(Color highlightColor)
{
this.highlightColor = highlightColor;
}
public int getHighlightColorOperator()
{
return highlightColorOperator;
}
public void setHighlightColorOperator(int highlightColorOperator)
{
this.highlightColorOperator = highlightColorOperator;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment