try to fix some selection bugs
This commit is contained in:
parent
518b7e67fe
commit
9c762d50fa
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user