Skip to content

Commit

Permalink
Merge pull request #100 from HollowHorizon/main
Browse files Browse the repository at this point in the history
Better word selection & Text area line customization
  • Loading branch information
fabmax authored Dec 6, 2024
2 parents 468b2a8 + 38badb3 commit 46c23c9
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@ 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
}

fun moveWordLeft(text: String, caretPos: Int): Int {
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)
}
}
Expand All @@ -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)
}
}
Expand Down

0 comments on commit 46c23c9

Please sign in to comment.