Line and row scrolling

This commit is contained in:
DBotThePony 2023-01-28 17:26:04 +07:00
parent fd84c40c1e
commit 64a54add95
Signed by: DBot
GPG Key ID: DCC23B5715498507

View File

@ -27,6 +27,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.milliTime
import kotlin.math.roundToInt
open class TextInputPanel<out S : Screen>(
screen: S,
@ -158,6 +159,18 @@ open class TextInputPanel<out S : Screen>(
private val undo = ArrayDeque<Snapshot>()
private val redo = ArrayDeque<Snapshot>()
/**
* scroll in rows (up-down)
*/
private var scrollLines = 0
/**
* scroll in pixels (left-right)
*/
private var scrollPixels = 0f
var rowSpacing = 2f
private fun triggerChangeCallback() {
if (oldText != lines) {
textCache = null
@ -1018,13 +1031,59 @@ open class TextInputPanel<out S : Screen>(
return true
}
private val characterWidthCache = Char2IntOpenHashMap()
private fun width(char: Char): Int {
return characterWidthCache.computeIfAbsent(char, Char2IntFunction { font.width(it.toString()) })
}
private fun width(text: String, beginning: Int = 0, end: Int = text.length - 1): Int {
var accumulate = 0
for (i in beginning.coerceAtLeast(0) .. end.coerceAtMost(text.length - 1)) {
accumulate += width(text[i])
}
return accumulate
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (!backgroundColor.isFullyTransparent)
drawRect(stack, 0f, 0f, width, height, backgroundColor)
if (multiLine) {
val heightInLines = ((height - dockPadding.top - dockPadding.bottom) / (font.lineHeight + rowSpacing)).toInt()
if (heightInLines > 0) {
if (cursorLine < scrollLines) {
scrollLines = (cursorLine - 1).coerceAtLeast(0)
} else if (heightInLines + scrollLines < cursorLine) {
scrollLines = (cursorLine - heightInLines).coerceIn(0, lines.size - 2)
}
}
} else {
scrollLines = 0
}
val selectedLine = this[cursorLine]
if (selectedLine != null) {
val w = width(selectedLine, end = cursorRow) - scrollPixels
if (w < 0f) {
scrollPixels = (scrollPixels - 30f).coerceAtLeast(0f)
} else if (w >= width - dockPadding.right) {
scrollPixels += 30f
}
}
stack.pushPose()
stack.translate(-scrollPixels, 0f, 0f)
var y = dockPadding.top
for ((i, line) in lines.withIndex()) {
for (i in scrollLines until lines.size) {
val line = lines[i]
val selection = selections[i]
font.drawAligned(
@ -1046,7 +1105,7 @@ open class TextInputPanel<out S : Screen>(
x += font.width(before).toFloat()
}
val width = if (selection.coversNewline(line) && i != lines.size - 1) this.width - x else font.width(selected).toFloat()
val width = if (selection.coversNewline(line) && i != lines.size - 1) this.width - x + scrollPixels else font.width(selected).toFloat()
RenderSystem.setShader(GameRenderer::getPositionShader)
RenderSystem.setShaderColor(cursorColor.red, cursorColor.green, cursorColor.blue, 0.4f)
@ -1060,8 +1119,8 @@ open class TextInputPanel<out S : Screen>(
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION)
builder.vertex(stack.last().pose(), x, y + font.lineHeight + 2f, 0f).endVertex()
builder.vertex(stack.last().pose(), x + width, y + font.lineHeight + 2f, 0f).endVertex()
builder.vertex(stack.last().pose(), x, y + font.lineHeight + rowSpacing, 0f).endVertex()
builder.vertex(stack.last().pose(), x + width, y + font.lineHeight + rowSpacing, 0f).endVertex()
builder.vertex(stack.last().pose(), x + width, y, 0f).endVertex()
builder.vertex(stack.last().pose(), x, y, 0f).endVertex()
@ -1073,7 +1132,7 @@ open class TextInputPanel<out S : Screen>(
RenderSystem.enableDepthTest()
}
y += font.lineHeight + 2f
y += font.lineHeight + rowSpacing
if (y > height - dockPadding.bottom)
break
@ -1089,7 +1148,7 @@ open class TextInputPanel<out S : Screen>(
text = "_",
align = TextAlign.TOP_LEFT,
x = dockPadding.left + (if (activeLine == null) 0f else font.width(activeLine).toFloat()),
y = dockPadding.top + cursorLine * (font.lineHeight + 2f),
y = dockPadding.top + (cursorLine - scrollLines) * (font.lineHeight + rowSpacing),
color = cursorColor
)
} else {
@ -1099,12 +1158,14 @@ open class TextInputPanel<out S : Screen>(
text = "|",
align = TextAlign.TOP_LEFT,
x = dockPadding.left + font.width(activeLine.substring(0, cursorRow)).toFloat() - 1f,
y = dockPadding.top + cursorLine * (font.lineHeight + 2f),
y = dockPadding.top + (cursorLine - scrollLines) * (font.lineHeight + rowSpacing),
color = cursorColor
)
}
}
stack.popPose()
if (debugDraw) {
font.drawAligned(
poseStack = stack,
@ -1122,7 +1183,7 @@ open class TextInputPanel<out S : Screen>(
text = cursorRow.toString(),
align = TextAlign.TOP_RIGHT,
x = width - dockPadding.right,
y = dockPadding.top + font.lineHeight + 2f,
y = dockPadding.top + font.lineHeight + rowSpacing,
color = cursorColor
)
@ -1132,7 +1193,7 @@ open class TextInputPanel<out S : Screen>(
text = lines.size.toString(),
align = TextAlign.TOP_RIGHT,
x = width - dockPadding.right,
y = dockPadding.top + font.lineHeight * 2 + 4f,
y = dockPadding.top + font.lineHeight * 2f + rowSpacing * 2f,
color = cursorColor
)
}
@ -1181,19 +1242,20 @@ open class TextInputPanel<out S : Screen>(
if (x > width - dockPadding.right)
x = width - dockPadding.right
val line = (y / (font.lineHeight + 2f)).toInt().coerceIn(0, lines.size - 1)
x += scrollPixels
val line = (scrollLines + (y / (font.lineHeight + rowSpacing)).toInt()).coerceIn(0, lines.size - 1)
val sLine = this[line] ?: return Vector2i(0, line)
if (x <= 0f)
return Vector2i(0, line)
else if (x >= font.width(sLine))
else if (x >= width(sLine))
return Vector2i(sLine.length, line)
val cache = Char2IntOpenHashMap()
var accumulatedWidth = 0f
for ((i, char) in sLine.withIndex()) {
val width = cache.computeIfAbsent(char, Char2IntFunction { font.width(it.toString()) })
val width = width(char)
if (x in accumulatedWidth .. accumulatedWidth + width) {
if (x - accumulatedWidth < width / 2)