From 38badb316d777c7c54b622cd0614c4ac421d0583 Mon Sep 17 00:00:00 2001 From: HollowHorizon Date: Fri, 6 Dec 2024 22:20:03 +0300 Subject: [PATCH] better word selection & text area line customization --- .../de/fabmax/kool/modules/ui2/TextArea.kt | 65 ++++++++++--------- .../fabmax/kool/util/TextCaretNavigation.kt | 17 +++-- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/kool-core/src/commonMain/kotlin/de/fabmax/kool/modules/ui2/TextArea.kt b/kool-core/src/commonMain/kotlin/de/fabmax/kool/modules/ui2/TextArea.kt index 49b0e10cc..0087d176c 100644 --- a/kool-core/src/commonMain/kotlin/de/fabmax/kool/modules/ui2/TextArea.kt +++ b/kool-core/src/commonMain/kotlin/de/fabmax/kool/modules/ui2/TextArea.kt @@ -172,38 +172,45 @@ open class TextAreaNode(parent: UiNode?, surface: UiSurface) : BoxNode(parent, s selectionHandler.updateSelectionRange() linesHolder.indices(lineProvider.size) { lineIndex -> val line = lineProvider[lineIndex] - AttributedText(line) { - modifier.width(Grow.MinFit) - - if (this@TextAreaNode.modifier.onSelectionChanged != null) { - modifier - .onClick { - when (it.pointer.leftButtonRepeatedClickCount) { - 1 -> selectionHandler.onSelectStart(this, lineIndex, it, false) - 2 -> selectionHandler.selectWord(this, line.text, lineIndex, it) - 3 -> selectionHandler.selectLine(this, line.text, lineIndex) - } - } - .onDragStart { selectionHandler.onSelectStart(this, lineIndex, it, true) } - .onDrag { selectionHandler.onDrag(it) } - .onDragEnd { selectionHandler.onSelectEnd() } - .onPointer { selectionHandler.onPointer(this, lineIndex, it) } - - modifier.padding(start = textAreaMod.lineStartPadding, end = textAreaMod.lineEndPadding) - if (lineIndex == 0) { - modifier - .textAlignY(AlignmentY.Bottom) - .padding(top = textAreaMod.firstLineTopPadding) - } - if (lineIndex == lineProvider.lastIndex) { - modifier - .textAlignY(AlignmentY.Top) - .padding(bottom = textAreaMod.lastLineBottomPadding) - } + setupTextLine(line, lineIndex, textAreaMod, lineProvider) + } + } - selectionHandler.applySelectionRange(this, line, lineIndex) + protected fun UiScope.setupTextLine( + line: TextLine, + lineIndex: Int, + textAreaMod: TextAreaModifier, + lineProvider: TextLineProvider, + ) = AttributedText(line) { + modifier.width(Grow.MinFit) + + if (this@TextAreaNode.modifier.onSelectionChanged != null) { + modifier + .onClick { + when (it.pointer.leftButtonRepeatedClickCount) { + 1 -> selectionHandler.onSelectStart(this, lineIndex, it, false) + 2 -> selectionHandler.selectWord(this, line.text, lineIndex, it) + 3 -> selectionHandler.selectLine(this, line.text, lineIndex) + } } + .onDragStart { selectionHandler.onSelectStart(this, lineIndex, it, true) } + .onDrag { selectionHandler.onDrag(it) } + .onDragEnd { selectionHandler.onSelectEnd() } + .onPointer { selectionHandler.onPointer(this, lineIndex, it) } + + modifier.padding(start = textAreaMod.lineStartPadding, end = textAreaMod.lineEndPadding) + if (lineIndex == 0) { + modifier + .textAlignY(AlignmentY.Bottom) + .padding(top = textAreaMod.firstLineTopPadding) } + if (lineIndex == lineProvider.lastIndex) { + modifier + .textAlignY(AlignmentY.Top) + .padding(bottom = textAreaMod.lastLineBottomPadding) + } + + selectionHandler.applySelectionRange(this, line, lineIndex) } } diff --git a/kool-core/src/commonMain/kotlin/de/fabmax/kool/util/TextCaretNavigation.kt b/kool-core/src/commonMain/kotlin/de/fabmax/kool/util/TextCaretNavigation.kt index 979752297..bb75b10e4 100644 --- a/kool-core/src/commonMain/kotlin/de/fabmax/kool/util/TextCaretNavigation.kt +++ b/kool-core/src/commonMain/kotlin/de/fabmax/kool/util/TextCaretNavigation.kt @@ -3,17 +3,20 @@ package de.fabmax.kool.util import de.fabmax.kool.math.clamp object TextCaretNavigation { + private val LIMITING_CHARS = charArrayOf(' ', '(', '{', '[', '<', ')', '}', ']', '>', ',', '.') + + private fun Char.isLimitingChar() = this in LIMITING_CHARS fun startOfWord(text: String, caretPos: Int): Int { var i = caretPos.clamp(0, text.lastIndex) - while (i > 0 && !text[i].isWhitespace()) i-- - if (text[i].isWhitespace()) i++ + while (i > 0 && !text[i].isLimitingChar()) i-- + if (text[i].isLimitingChar()) i++ return i } fun endOfWord(text: String, caretPos: Int): Int { var i = caretPos.clamp(0, text.lastIndex) - while (i < text.length && !text[i].isWhitespace()) i++ + while (i < text.length && !text[i].isLimitingChar()) i++ return i } @@ -21,9 +24,9 @@ object TextCaretNavigation { var i = (caretPos - 1).clamp(0, text.lastIndex) return when { i == 0 -> 0 - !text[i].isWhitespace() -> startOfWord(text, i) + !text[i].isLimitingChar() -> startOfWord(text, i) else -> { - while (i > 0 && text[i].isWhitespace()) i-- + while (i > 0 && text[i].isLimitingChar()) i-- startOfWord(text, i) } } @@ -33,9 +36,9 @@ object TextCaretNavigation { var i = (caretPos + 1).clamp(0, text.length) return when { i == text.length -> text.length - !text[i].isWhitespace() -> endOfWord(text, i) + !text[i].isLimitingChar() -> endOfWord(text, i) else -> { - while (i < text.length && text[i].isWhitespace()) i++ + while (i < text.length && text[i].isLimitingChar()) i++ endOfWord(text, i) } }