From b6d5cc402426ca1df55817681b069b4974d507a8 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 26 Jan 2023 15:21:34 +0700 Subject: [PATCH] Moving text cursor using mice --- .../client/screen/panels/TextInputPanel.kt | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) 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 d50effbf4..66ee71717 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,11 +6,15 @@ 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 import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import it.unimi.dsi.fastutil.ints.Int2ObjectMap 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 @@ -929,9 +933,69 @@ open class TextInputPanel( override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { selections.clear() requestFocus() + + val (lx, ly) = screenToLocal(x, y) + val pos = localToText(lx, ly) + cursorLine = pos.y + cursorCharacter = pos.x + return true } + fun localToText(x: Float, y: Float): Vector2i { + if (lines.isEmpty()) + return Vector2i(0, 0) + + val line = (y / (font.lineHeight + 2f)).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)) + return Vector2i(sLine.length, line) + + // binary search attempt + // simply: sucks + /*@Suppress("name_shadowing") + var x = x + var start = 0 + var end = sLine.length - 1 + + while (start < end - 1) { + val split = (end - start) / 2 + val before = font.width(sLine.substring(start, start + split)) + + if (x < before) { + end = start + split + } else if (x > before) { + x -= before + start += split + } else { + break + } + } + + return Vector2i(start, line)*/ + + val cache = Char2IntOpenHashMap() + var accumulatedWidth = 0f + + for ((i, char) in sLine.withIndex()) { + val width = cache.computeIfAbsent(char, Char2IntFunction { font.width(it.toString()) }) + + if (x in accumulatedWidth .. accumulatedWidth + width) { + if (x - accumulatedWidth < width / 2) + return Vector2i(i, line) + else + return Vector2i(i + 1, line) + } else { + accumulatedWidth += width + } + } + + return Vector2i(sLine.length, line) + } + private enum class CharType { SPACES { override fun contains(input: Char): Boolean {