protected boolean insertCharacter(char charTyped) {

    if (isAllowedCharacter(charTyped)) {

      if (selectionStart != selectionEnd) {
        if (caret == selectionStart) {
          ++caret;
        }
        text[selectionStart++] = charTyped;
        return true;
      }

      if ((caretInsert && caret == text.length) || textLength == text.length) {
        return false;
      }

      if (!caretInsert) {
        if (caret < textLength) {
          System.arraycopy(text, caret, text, caret + 1, textLength - caret);
        }
        ++textLength;
      }
      text[caret++] = charTyped;
      return true;
    } else {
      return true;
    }
  }
 public ElementTextField setMaxLength(short limit) {
   char[] oldText = text;
   text = new char[limit];
   textLength = Math.min(limit, textLength);
   if (oldText != null) {
     System.arraycopy(oldText, 0, text, 0, textLength);
   }
   findRenderStart();
   return this;
 }
  protected void clearSelection() {

    if (selectionStart != selectionEnd) {
      if (selectionEnd < textLength) {
        System.arraycopy(text, selectionEnd, text, selectionStart, textLength - selectionEnd);
      }
      textLength -= selectionEnd - selectionStart;

      selectionEnd = caret = selectionStart;
      findRenderStart();
    }
  }
  @Override
  public boolean onKeyTyped(char charTyped, int keyTyped) {
    if (!isFocused()) {
      return false;
    }

    switch (charTyped) {
      case 1: // ^A
        selectionEnd = caret = textLength;
        selectionStart = 0;
        findRenderStart();
        return true;
      case 3: // ^C
        if (selectionStart != selectionEnd) {
          GuiScreen.setClipboardString(getSelectedText());
        }
        return true;
      case 24: // ^X
        if (selectionStart != selectionEnd) {
          GuiScreen.setClipboardString(getSelectedText());
          clearSelection();
        }

        return true;
      case 22: // ^V
        writeText(GuiScreen.getClipboardString());

        return true;
      default:
        switch (keyTyped) {
          case Keyboard.KEY_ESCAPE:
            setFocused(false);
            return !isFocused();
          case Keyboard.KEY_RETURN:
          case Keyboard.KEY_NUMPADENTER:
            return onEnter();
          case Keyboard.KEY_INSERT:
            if (GuiScreen.isShiftKeyDown()) {
              writeText(GuiScreen.getClipboardString());
            } else {
              caretInsert = !caretInsert;
            }

            return true;
          case Keyboard.KEY_CLEAR: // mac only (clear selection)
            clearSelection();

            return true;
          case Keyboard.KEY_DELETE: // delete
            if (!GuiScreen.isShiftKeyDown()) {
              if (selectionStart != selectionEnd) {
                clearSelection();
              } else if (GuiScreen.isCtrlKeyDown()) {
                int size = seekNextCaretLocation(caret, true) - caret;
                selectionStart = caret;
                selectionEnd = caret + size;
                clearSelection();
              } else {
                if (caret < textLength && textLength > 0) {
                  --textLength;
                  System.arraycopy(text, caret + 1, text, caret, textLength - caret);
                }
              }
              if (caret <= renderStart) {
                renderStart = MathHelper.clampI(caret - 3, 0, textLength);
              }
              findRenderStart();
              onCharacterEntered(true);
              return true;
            }
            // continue.. (shift+delete = backspace)
          case Keyboard.KEY_BACK: // backspace
            if (selectionStart != selectionEnd) {
              clearSelection();
            } else if (GuiScreen.isCtrlKeyDown()) {
              int size = seekNextCaretLocation(caret, false) - caret;
              selectionStart = caret + size;
              selectionEnd = caret;
              clearSelection();
            } else {
              if (caret > 0 && textLength > 0) {
                --caret;
                System.arraycopy(text, caret + 1, text, caret, textLength - caret);
                --textLength;
              }
            }
            if (caret <= renderStart) {
              renderStart = MathHelper.clampI(caret - 3, 0, textLength);
            }
            findRenderStart();
            onCharacterEntered(true);
            return true;
          case Keyboard.KEY_HOME: // home
            if (GuiScreen.isShiftKeyDown()) {
              if (caret > selectionEnd) {
                selectionEnd = selectionStart;
              }
              selectionStart = 0;
            } else {
              selectionStart = selectionEnd = 0;
            }
            renderStart = caret = 0;

            return true;
          case Keyboard.KEY_END: // end
            if (GuiScreen.isShiftKeyDown()) {
              if (caret < selectionStart) {
                selectionStart = selectionEnd;
              }
              selectionEnd = textLength;
            } else {
              selectionStart = selectionEnd = textLength;
            }
            caret = textLength;
            findRenderStart();

            return true;
          case Keyboard.KEY_LEFT: // left arrow
          case Keyboard.KEY_RIGHT: // right arrow
            int size = keyTyped == 203 ? -1 : 1;
            if (GuiScreen.isCtrlKeyDown()) {
              size = seekNextCaretLocation(caret, keyTyped == 205) - caret;
            }

            if (selectionStart == selectionEnd || !GuiScreen.isShiftKeyDown()) {
              selectionStart = selectionEnd = caret;
            }

            {
              int t = caret;
              caret = MathHelper.clampI(caret + size, 0, textLength);
              size = caret - t;
            }
            findRenderStart();

            if (GuiScreen.isShiftKeyDown()) {
              if (caret == selectionStart + size) {
                selectionStart = caret;
              } else if (caret == selectionEnd + size) {
                selectionEnd = caret;
              }
              // this logic is 'broken' in that the selection doesn't wrap
              // such that a|bc|def becomes abc|def| but it will highlight
              // the rest of the word the caret is on   i.e., a|bc|def -> a|bcdef|
              // i don't know that it matters (home+end exhibit the former)

              if (selectionStart > selectionEnd) {
                int t = selectionStart;
                selectionStart = selectionEnd;
                selectionEnd = t;
              }
            }

            return true;
          default:
            if (isAllowedCharacter(charTyped)) {
              boolean typed = insertCharacter(charTyped);
              clearSelection();
              findRenderStart();
              onCharacterEntered(typed);
              return true;
            } else {
              return false;
            }
        }
    }
  }