diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/TextInputPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/TextInputPanel.kt index d78276a78..51be6d77a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/TextInputPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/TextInputPanel.kt @@ -6,7 +6,6 @@ import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.DefaultVertexFormat import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.VertexFormat -import it.unimi.dsi.fastutil.chars.Char2IntAVLTreeMap import it.unimi.dsi.fastutil.chars.Char2IntFunction import it.unimi.dsi.fastutil.chars.Char2IntOpenHashMap import it.unimi.dsi.fastutil.chars.CharOpenHashSet @@ -16,7 +15,6 @@ import net.minecraft.client.gui.screens.Screen import net.minecraft.client.renderer.GameRenderer import org.joml.Vector2i import ru.dbotthepony.mc.otm.client.isCtrlDown -import ru.dbotthepony.mc.otm.client.isKeyDown import ru.dbotthepony.mc.otm.client.isShiftDown import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource @@ -35,40 +33,35 @@ open class TextInputPanel( width: Float = 10f, height: Float = 10f, ) : EditablePanel(screen, parent, x, y, width, height) { - private data class TextSelection(val start: Int, val end: Int) { - val isInversed get() = start > end - val isNotEmpty get() = start != end - val isValid get() = start != end + private data class TextSelection(val cursor: Int, val shift: Int) { + val isLeft get() = shift < 0 + val isRight get() = shift > 0 - val actualStart get() = if (isInversed) this.end else this.start - val actualEnd get() = if (isInversed) this.start else this.end + fun move(value: Int, lineLength: Int): TextSelection { + return TextSelection(cursor, (shift + value).coerceIn(-cursor, lineLength - cursor + 1)) + } + + val isValid get() = shift != 0 + val start get() = if (isRight) cursor else cursor + shift + val end get() = if (isRight) cursor + shift else cursor fun sub(line: String): Pair { - val before = line.substring(0, actualStart.coerceIn(0, line.length)) - val selected = line.substring(actualStart.coerceIn(0, line.length), actualEnd.coerceAtMost(line.length)) + val before = line.substring(0, start.coerceIn(0, line.length)) + val selected = line.substring(start.coerceIn(0, line.length), end.coerceAtMost(line.length)) return before to selected } - fun coversEntireString(value: String?): Boolean { - if (value == null) - return false - - return value.length <= actualEnd && actualStart <= 0 + fun coversEntireString(value: String): Boolean { + return start <= 0 && value.length <= end } - fun coversEntireLine(value: String?): Boolean { - if (value == null) - return true - - return value.length < actualEnd && actualStart <= 0 + fun coversEntireLine(value: String): Boolean { + return start <= 0 && value.length < end } - fun coversNewline(value: String?): Boolean { - if (value == null) - return actualEnd == Int.MAX_VALUE - - return value.length < actualEnd + fun coversNewline(value: String): Boolean { + return value.length < end } } @@ -306,7 +299,8 @@ open class TextInputPanel( val selection = selections[line] if (selection != null) { - val (start, end) = selection + val start = selection.start + val end = selection.end if (character < start) { putSelection(line, TextSelection(start + moveBy, end + moveBy)) @@ -345,7 +339,7 @@ open class TextInputPanel( } else if (selection.coversEntireString(line)) { this[lineNumber] = "" } else if (selection.coversNewline(line)) { - val before = line.substring(0, selection.actualStart.coerceIn(0, line.length)) + val before = line.substring(0, selection.start.coerceIn(0, line.length)) val next = this[lineNumber + 1] if (next == null) { @@ -355,22 +349,20 @@ open class TextInputPanel( removeLine(lineNumber + 1) } } else { - val before = line.substring(0, selection.actualStart.coerceIn(0, line.length)) - val after = line.substring(selection.actualEnd.coerceIn(0, line.length)) + val before = line.substring(0, selection.start.coerceIn(0, line.length)) + val after = line.substring(selection.end.coerceIn(0, line.length)) this[lineNumber] = before + after } if (lineNumber < cursorLine) { cursorLine = lineNumber - cursorCharacter = selection.actualStart - } else if (lineNumber == cursorLine && cursorCharacter > selection.actualStart) { - cursorCharacter = selection.actualStart + cursorCharacter = selection.start + } else if (lineNumber == cursorLine && cursorCharacter > selection.start) { + cursorCharacter = selection.start } } } - protected open fun textChanged(oldText: String, newText: String) {} - var text: String get() { var textCache = textCache @@ -410,14 +402,17 @@ open class TextInputPanel( fun advanceCursorLeft(greedy: Boolean = false): CursorAdvanceResult { val oldLine = cursorLine - val oldChar = cursorCharacter val line = this[cursorLine] var couldHaveChangedLine = false if (line != null && cursorCharacter > line.length) { - cursorCharacter = line.length - 1 + cursorCharacter = line.length + } else if (cursorCharacter < 0) { + cursorCharacter = 0 } + val oldChar = cursorCharacter + if (cursorCharacter > 0) { if (greedy && line != null) { cursorCharacter = greedyAdvanceLeft(line, cursorCharacter) @@ -444,11 +439,18 @@ open class TextInputPanel( fun advanceCursorRight(greedy: Boolean = false): CursorAdvanceResult { val oldLine = cursorLine - val oldChar = cursorCharacter var couldHaveChangedLine = false val line = this[cursorLine] + if (line != null && cursorCharacter > line.length) { + cursorCharacter = line.length + } else if (cursorCharacter < 0) { + cursorCharacter = 0 + } + + val oldChar = cursorCharacter + if (greedy && line != null && cursorCharacter + 1 < line.length) { cursorCharacter = greedyAdvanceRight(line, cursorCharacter) } else { @@ -472,47 +474,39 @@ open class TextInputPanel( } private fun simulateSelectLeft(greedy: Boolean) { - val thisLine = this[cursorLine] ?: return - - val existing = selections[cursorLine] + val line = this[cursorLine] ?: return + val existing = selections[cursorLine] ?: TextSelection(cursorCharacter, 0) val result = advanceCursorLeft(greedy) - if (!result.linesChanged) - putSelection(result.oldLine, TextSelection( - existing?.start ?: result.oldCharacter, - result.newCharacter)) - else { - this[result.newLine]?.let { - val existingNewline = selections[result.newLine] + if (result.couldHaveChangedLine) { + putSelection(result.oldLine, existing.move(-line.length, line.length)) + } else { + putSelection(result.oldLine, existing.move(result.newCharacter - result.oldCharacter, line.length)) + } - if (existingNewline == null) { - putSelection(result.newLine, TextSelection( - Int.MAX_VALUE, - it.length)) - } else { - if (!existingNewline.isInversed) { - putSelection(result.newLine, TextSelection( - existingNewline.start, - result.newCharacter)) - } - } - } + if (result.linesChanged) { + val existingNew = selections[result.newLine] + val lineNew = this[result.newLine] ?: return - if (existing != null && existing.actualStart == 0 && thisLine.isEmpty() && !existing.isInversed) { - selections.remove(result.oldLine) + if (existingNew == null) { + putSelection(result.newLine, TextSelection(result.newCharacter + 1, -1)) + } else { + putSelection(result.newLine, existingNew.move(-1, lineNew.length)) } } } private fun simulateSelectRight(greedy: Boolean) { - this[cursorLine] ?: return - - val existing = selections[cursorLine] + val line = this[cursorLine] ?: return + val existing = selections[cursorLine] ?: TextSelection(cursorCharacter, 0) val result = advanceCursorRight(greedy) - putSelection(result.oldLine, TextSelection( - existing?.start ?: result.oldCharacter, - if (result.couldHaveChangedLine) Int.MAX_VALUE else result.newCharacter)) + if (result.couldHaveChangedLine) { + if (result.oldLine != lines.size - 1) + putSelection(result.oldLine, existing.move(line.length.coerceAtLeast(1), line.length)) + } else { + putSelection(result.oldLine, existing.move(result.newCharacter - result.oldCharacter, line.length)) + } } private fun simulateSelectionUp(): Boolean { @@ -560,13 +554,21 @@ open class TextInputPanel( simulateSelectRight(false) } } else { - val store = this.cursorLine + if (line.isEmpty()) { + val existing = selections[this.cursorLine] - for (i in 0 .. line.length) { - simulateSelectRight(false) + if (existing != null && existing.isLeft) { + selections.remove(this.cursorLine) + } + } else { + val store = this.cursorLine + + for (i in 0 .. line.length) { + simulateSelectRight(false) + } + + this.cursorLine = store } - - this.cursorLine = store } this.cursorCharacter = cursorCharacter @@ -842,7 +844,7 @@ open class TextInputPanel( color = textColor ) - if (selection != null && selection.isValid && (selection.isNotEmpty || selection.coversEntireLine(line))) { + if (selection != null && selection.isValid) { val (before, selected) = selection.sub(line) var x = 0f @@ -851,7 +853,7 @@ open class TextInputPanel( x = font.width(before).toFloat() } - val width = if (selection.coversNewline(line)) this.width - x else font.width(selected).toFloat() + val width = if (selection.coversNewline(line) && i != lines.size - 1) this.width - x else font.width(selected).toFloat() RenderSystem.setShader(GameRenderer::getPositionShader) RenderSystem.setShaderColor(0.0f, 0.0f, 1.0f, 1.0f)