try to fix some selection bugs

This commit is contained in:
DBotThePony 2023-01-26 17:21:53 +07:00
parent 518b7e67fe
commit 9c762d50fa
Signed by: DBot
GPG Key ID: DCC23B5715498507

View File

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