input element: improve some things, chat: don't allow spaces at the beginning

This commit is contained in:
Bixilon 2022-02-11 15:26:07 +01:00
parent 38d018e9f5
commit 54a70e6665
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
2 changed files with 43 additions and 28 deletions

View File

@ -36,24 +36,27 @@ class TextInputElement(
val maxLength: Int = Int.MAX_VALUE, val maxLength: Int = Int.MAX_VALUE,
val cursorStyles: TextCursorStyles = TextCursorStyles.CLICKED, val cursorStyles: TextCursorStyles = TextCursorStyles.CLICKED,
var editable: Boolean = true, var editable: Boolean = true,
var onChange: () -> Unit = {},
) : Element(guiRenderer) { ) : Element(guiRenderer) {
private val cursor = ColorElement(guiRenderer, size = Vec2i(1, Font.TOTAL_CHAR_HEIGHT)) private val cursor = ColorElement(guiRenderer, size = Vec2i(1, Font.TOTAL_CHAR_HEIGHT))
private val textElement = MarkTextElement(guiRenderer, "", background = false, parent = this) private val textElement = MarkTextElement(guiRenderer, "", background = false, parent = this)
private val background = ColorElement(guiRenderer, Vec2i.EMPTY, RenderConstants.TEXT_BACKGROUND_COLOR) private val background = ColorElement(guiRenderer, Vec2i.EMPTY, RenderConstants.TEXT_BACKGROUND_COLOR)
private var cursorOffset: Vec2i = Vec2i.EMPTY private var cursorOffset: Vec2i = Vec2i.EMPTY
private val _value = StringBuffer(256) val _value = StringBuffer(256)
var value: String var value: String
get() = _value.toString() get() = _value.toString()
set(value) { set(value) {
pointer = 0 _pointer = 0
if (_value.equals(value)) { if (_value.equals(value)) {
return return
} }
_value.replace(0, _value.length, value) _value.replace(0, _value.length, value)
onChange()
textUpToDate = false
forceApply() forceApply()
} }
private var textUpToDate = false private var textUpToDate = false
private var pointer = 0 var _pointer = 0
private var cursorTick = 0 private var cursorTick = 0
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
@ -75,13 +78,13 @@ class TextInputElement(
} }
background.size = Vec2i(prefMaxSize.x, prefMaxSize.y) background.size = Vec2i(prefMaxSize.x, prefMaxSize.y)
cursorOffset = if (pointer == 0) { cursorOffset = if (_pointer == 0) {
Vec2i.EMPTY Vec2i.EMPTY
} else { } else {
val preCursorText = if (pointer == value.length) { val preCursorText = if (_pointer == value.length) {
textElement textElement
} else { } else {
TextElement(guiRenderer, value.substring(0, pointer), parent = this) TextElement(guiRenderer, value.substring(0, _pointer), parent = this)
} }
Vec2i(preCursorText.renderInfo.lines.lastOrNull()?.width ?: 0, maxOf(preCursorText.renderInfo.lines.size - 1, 0) * preCursorText.charHeight) Vec2i(preCursorText.renderInfo.lines.lastOrNull()?.width ?: 0, maxOf(preCursorText.renderInfo.lines.size - 1, 0) * preCursorText.charHeight)
} }
@ -104,14 +107,15 @@ class TextInputElement(
val insert = string.replace("\n", "").replace("\r", "").replace('§', '&') val insert = string.replace("\n", "").replace("\r", "").replace('§', '&')
if (textElement.marked) { if (textElement.marked) {
_value.delete(textElement.markStartPosition, textElement.markEndPosition) _value.delete(textElement.markStartPosition, textElement.markEndPosition)
if (pointer > textElement.markStartPosition) { if (_pointer > textElement.markStartPosition) {
pointer = textElement.markStartPosition _pointer = textElement.markStartPosition
} }
} }
val appendLength = minOf(insert.length, maxLength - _value.length) val appendLength = minOf(insert.length, maxLength - _value.length)
_value.insert(pointer, insert.substring(0, appendLength)) _value.insert(_pointer, insert.substring(0, appendLength))
pointer += appendLength _pointer += appendLength
textUpToDate = false textUpToDate = false
onChange()
} }
override fun onCharPress(char: Int) { override fun onCharPress(char: Int) {
@ -132,32 +136,32 @@ class TextInputElement(
var start: Int = textElement.markStartPosition var start: Int = textElement.markStartPosition
var end: Int = textElement.markEndPosition var end: Int = textElement.markEndPosition
if (right) { if (right) {
if (start == pointer) { if (start == _pointer) {
start += modify start += modify
} else { } else {
if (start < 0) { if (start < 0) {
start = pointer start = _pointer
end = start end = start
} }
end += modify end += modify
} }
} else { } else {
if (end == pointer) { if (end == _pointer) {
end += modify end += modify
} else { } else {
if (start < 0) { if (start < 0) {
end = pointer end = _pointer
start = end start = end
} }
start += modify start += modify
} }
} }
textElement.mark(start, end) textElement.mark(start, end)
pointer += modify _pointer += modify
return return
} }
pointer = if (marked) if (right) textElement.markEndPosition else textElement.markStartPosition else if (right) minOf(_value.length, pointer + modify) else maxOf(0, pointer + modify) _pointer = if (marked) if (right) textElement.markEndPosition else textElement.markStartPosition else if (right) minOf(_value.length, _pointer + modify) else maxOf(0, _pointer + modify)
textElement.unmark() textElement.unmark()
} }
@ -186,12 +190,12 @@ class TextInputElement(
} }
if (textElement.marked) { if (textElement.marked) {
insert("") insert("")
} else if (pointer == 0) { } else if (_pointer == 0) {
return return
} else { } else {
val delete = if (controlDown) calculateWordPointer(false) else -1 val delete = if (controlDown) calculateWordPointer(false) else -1
_value.delete(pointer + delete, pointer) _value.delete(_pointer + delete, _pointer)
pointer += delete _pointer += delete
textUpToDate = false textUpToDate = false
} }
} }
@ -201,16 +205,16 @@ class TextInputElement(
} }
if (textElement.marked) { if (textElement.marked) {
insert("") insert("")
} else if (pointer == _value.length) { } else if (_pointer == _value.length) {
return return
} else { } else {
val delete = if (controlDown) calculateWordPointer(true) else 1 val delete = if (controlDown) calculateWordPointer(true) else 1
_value.delete(pointer, pointer + delete) _value.delete(_pointer, _pointer + delete)
textUpToDate = false textUpToDate = false
} }
} }
KeyCodes.KEY_LEFT -> { KeyCodes.KEY_LEFT -> {
if (pointer == 0) { if (_pointer == 0) {
if (!shiftDown) { if (!shiftDown) {
textElement.unmark() textElement.unmark()
} }
@ -224,8 +228,8 @@ class TextInputElement(
mark(shiftDown, false, modify) mark(shiftDown, false, modify)
} }
KeyCodes.KEY_RIGHT -> { KeyCodes.KEY_RIGHT -> {
if (pointer == _value.length) { if (_pointer == _value.length) {
if (!shiftDown && pointer == _value.length) { if (!shiftDown && _pointer == _value.length) {
textElement.unmark() textElement.unmark()
} }
return return
@ -240,11 +244,11 @@ class TextInputElement(
} }
KeyCodes.KEY_HOME -> { KeyCodes.KEY_HOME -> {
textElement.unmark() textElement.unmark()
pointer = 0 _pointer = 0
} }
KeyCodes.KEY_END -> { KeyCodes.KEY_END -> {
textElement.unmark() textElement.unmark()
pointer = value.length _pointer = value.length
} }
else -> return textElement.onKey(key, type) else -> return textElement.onKey(key, type)
} }
@ -257,8 +261,8 @@ class TextInputElement(
private fun calculateWordPointer(right: Boolean): Int { private fun calculateWordPointer(right: Boolean): Int {
var modify = if (right) 1 else -1 var modify = if (right) 1 else -1
while (pointer + modify in 1 until _value.length) { while (_pointer + modify in 1 until _value.length) {
val char = _value[pointer + modify] val char = _value[_pointer + modify]
if (char in WORD_SEPARATORS) { if (char in WORD_SEPARATORS) {
break break
} }
@ -272,6 +276,10 @@ class TextInputElement(
return modify return modify
} }
override fun onOpen() {
cursorTick = 19 // make cursor visible
}
companion object { companion object {
private const val CURSOR_TICK_ON_ACTION = 10 private const val CURSOR_TICK_ON_ACTION = 10
private val WORD_SEPARATORS = CharOpenHashSet(charArrayOf(' ', ',', ';', '-', '\'', '`', '"', '“', '„', '.', '&', '@', '^', '/', '\\', '…', '*', '⁂', '=', '?', '!', '‽', '¡', '¿', '⸮', '#', '№', '%', '‰', '‱', '°', '⌀', '+', '', '×', '÷', '~', '±', '∓', '', '⁀', '|', '¦', '‖', '•', '·', '©', '©', '℗', '®', '', '', '“', '”', '"', '"', '', '', '«', '»', '(', ')', '[', ']', '{', '}', '⟨', '⟩', '”', '〃', '†', '‡', '❧', '☞', '◊', '¶', '⸿', '፠', '๛', '※', '§')) private val WORD_SEPARATORS = CharOpenHashSet(charArrayOf(' ', ',', ';', '-', '\'', '`', '"', '“', '„', '.', '&', '@', '^', '/', '\\', '…', '*', '⁂', '=', '?', '!', '‽', '¡', '¿', '⸮', '#', '№', '%', '‰', '‱', '°', '⌀', '+', '', '×', '÷', '~', '±', '∓', '', '⁀', '|', '¦', '‖', '•', '·', '©', '©', '℗', '®', '', '', '“', '”', '"', '"', '', '', '«', '»', '(', ')', '[', ']', '{', '}', '⟨', '⟩', '”', '〃', '†', '‡', '❧', '☞', '◊', '¶', '⸿', '፠', '๛', '※', '§'))

View File

@ -69,6 +69,12 @@ class ChatElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedElem
chatProfile::width.profileWatchRendering(this, profile = profile) { messages.prefMaxSize = Vec2i(it, messages.prefMaxSize.y) } chatProfile::width.profileWatchRendering(this, profile = profile) { messages.prefMaxSize = Vec2i(it, messages.prefMaxSize.y) }
chatProfile::height.profileWatchRendering(this, profile = profile) { messages.prefMaxSize = Vec2i(messages.prefMaxSize.x, it) } chatProfile::height.profileWatchRendering(this, profile = profile) { messages.prefMaxSize = Vec2i(messages.prefMaxSize.x, it) }
forceSilentApply() forceSilentApply()
input.onChange = {
while (input._value.startsWith(' ')) {
input._value.deleteCharAt(0)
input._pointer--
}
}
} }
@ -115,6 +121,7 @@ class ChatElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedElem
override fun onOpen() { override fun onOpen() {
active = true active = true
input.onOpen()
} }
override fun onClose() { override fun onClose() {