diff --git a/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala b/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala index a03387e..b974f12 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala @@ -52,9 +52,8 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w def text: String = chars.mkString def text_=(value: String): Unit = { chars = value.toCharArray - cursorPos = 0 - cursorOffset = 0 - textChanged = true + adjustCursor(0) + // note: we are not setting `textChanged = true` here to avoid potential recursion with `onInput` callback } protected var placeholder: Array[Char] = "".toCharArray @@ -96,7 +95,7 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w private def charsWidth(chars: Array[Char], first: Int, last: Int): Int = { var width = 0 - for (index <- first to last) { + for (index <- (first max 0) to (last min (chars.length - 1))) { width += font().charWidth(chars(index)) } width @@ -117,53 +116,32 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w width += charWidth(chars(pos)) if (width < absoluteX) pos += 1 } - cursorPos = chars.length.min(pos).max(0) - cursorOffset = if (cursorPos > 0) charsWidth(chars, 0, (cursorPos - 1).max(0)) else 0 - blinkTimer = 0 - adjustScroll() + adjustCursor(chars.length.min(pos).max(0)) case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_LEFT, _) if isFocused => - if (cursorPos != 0) { - cursorOffset -= charWidth(chars(cursorPos - 1)) - cursorPos -= 1 - blinkTimer = 0 - adjustScroll() - } + if (cursorPos > 0) adjustCursor(cursorPos - 1) event.consume() case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_RIGHT, _) if isFocused => - if (cursorPos < chars.length) { - cursorOffset += charWidth(chars(cursorPos)) - cursorPos += 1 - blinkTimer = 0 - adjustScroll() - } + if (cursorPos < chars.length) adjustCursor(cursorPos + 1) event.consume() case event @ KeyEvent(KeyEvent.State.Press, Keyboard.KEY_HOME, _) if isFocused => - cursorPos = 0 - cursorOffset = 0 - blinkTimer = 0 - scroll = 0 + adjustCursor(0) event.consume() case event @ KeyEvent(KeyEvent.State.Press, Keyboard.KEY_END, _) if isFocused => - val textWidth = charsWidth(chars) - cursorPos = chars.length - cursorOffset = textWidth - blinkTimer = 0 - scroll = (textWidth - size.width + 16).max(0) + adjustCursor(chars.length) event.consume() case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_BACK, _) if isFocused => val (lhs, rhs) = chars.splitAt(cursorPos) if (!lhs.isEmpty) { - val cw = charWidth(lhs.last) chars = lhs.take(lhs.length - 1) ++ rhs textChanged = true - cursorOffset -= cw - cursorPos -= 1 - blinkTimer = 0 + adjustCursor(cursorPos - 1) + // if the text overflows - scroll it into visibility, it will feel nicer + val cw = charWidth(lhs.last) scroll = (scroll - cw).max(0) } event.consume() @@ -182,18 +160,12 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w event.consume() case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_INSERT, _) if isFocused => - // TODO: insert the whole clipboard string at once instead of going char-by-char. - for (char <- UiHandler.clipboard.toCharArray) { - writeChar(char) - } + writeString(UiHandler.clipboard) event.consume() case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_V, _) if isFocused && KeyEvents.isControlDown => - // TODO: insert the whole clipboard string at once instead of going char-by-char. - for (char <- UiHandler.clipboard.toCharArray) { - writeChar(char) - } + writeString(UiHandler.clipboard) event.consume() case event @ KeyEvent(KeyEvent.State.Press, Keyboard.KEY_RETURN, _) if isFocused => @@ -206,13 +178,26 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w } + private def writeString(string: String): Unit = { + val (lhs, rhs) = chars.splitAt(cursorPos) + val array = string.toCharArray + chars = lhs ++ array ++ rhs + textChanged = true + adjustCursor(cursorPos + string.length) + } + private def writeChar(char: Char): Unit = { val (lhs, rhs) = chars.splitAt(cursorPos) chars = lhs ++ Array(char) ++ rhs textChanged = true - cursorOffset += charWidth(chars(cursorPos)) - cursorPos += 1 + adjustCursor(cursorPos + 1) + } + + private def adjustCursor(position: Int): Unit = { + cursorPos = position + cursorOffset = charsWidth(chars, 0, cursorPos - 1) blinkTimer = 0 + adjustScroll() } private def adjustScroll(): Unit = {