diff --git a/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala b/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala index 4516b58..fc16c2f 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/TextInput.scala @@ -11,7 +11,7 @@ import ocelot.desktop.ui.event.{DoubleClickEvent, DragEvent, KeyEvent, MouseEven import ocelot.desktop.ui.widget.TextInput.{Cursor, Selection, Text} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.traits.HoverAnimation -import ocelot.desktop.util.{DrawUtils, Register, Watcher} +import ocelot.desktop.util.{BaseWatcher, DrawUtils, Register, Watcher} import ocelot.desktop.util.animation.ColorAnimation import org.lwjgl.input.Keyboard @@ -28,7 +28,7 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w // model private val _text: Text = new Text(initialText.codePoints().toArray) private val cursor: Cursor = new Cursor() - private val selectionWatcher = new Watcher[Option[Selection]](None) + private val selectionWatcher = Watcher[Option[Selection]](None) // updated after all events are processed so that event handlers can refer to the previous position. private val prevCursorPosition = Register.sampling(cursor.position) @@ -47,13 +47,13 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w selectionWatcher.value = newValue } - cursor.onChange(position => { + cursor.onChange = { position => cursorOffset = charsWidth(_text.chars, 0, position) - blinkTimer = 0 - adjustScroll() - }) + blinkTimer = 0 + adjustScroll() + } - selectionWatcher.onChange(newValue => { + selectionWatcher.onChange = { newValue => selectionOffsets = newValue.map { case Selection.Ordered(start, end) => ( @@ -61,7 +61,7 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w charsWidth(_text.chars, 0, end), ) } - }) + } private val foregroundAnimation = new ColorAnimation(targetForegroundColor, 7f) private val borderAnimation = new ColorAnimation(targetBorderColor, 7f) @@ -81,7 +81,10 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w def text_=(value: String): Unit = { _text.chars = value.codePoints().toArray selection = None - cursor.position = cursor.position max 0 min _text.chars.length + + val desiredPosition = cursor.desiredPosition + cursor.position = desiredPosition max 0 min _text.chars.length + cursor.desiredPosition = desiredPosition } private def selectedText: String = selection match { @@ -448,14 +451,20 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w } object TextInput { - class Text(initialValue: Array[Int]) extends Watcher(initialValue) { + class Text(initialValue: Array[Int]) extends BaseWatcher(initialValue) { def chars: Array[Int] = value def chars_=(newValue: Array[Int]): Unit = value = newValue } - class Cursor(initialValue: Int = 0) extends Watcher(initialValue) { + class Cursor(initialValue: Int = 0) extends BaseWatcher(initialValue) { + var desiredPosition: Int = initialValue + def position: Int = value - def position_=(newValue: Int): Unit = value = newValue + + def position_=(newValue: Int): Unit = { + value = newValue + desiredPosition = newValue + } } case class Selection(start: Int, end: Int) { diff --git a/src/main/scala/ocelot/desktop/util/Watcher.scala b/src/main/scala/ocelot/desktop/util/Watcher.scala index 0e8831a..959d8b4 100644 --- a/src/main/scala/ocelot/desktop/util/Watcher.scala +++ b/src/main/scala/ocelot/desktop/util/Watcher.scala @@ -1,24 +1,27 @@ package ocelot.desktop.util /** - * Keeps a reference to an object - * and tells whether there were any changes to the value since the last check. + * Keeps a value and tracks its changes. */ -class Watcher[T](initialValue: T) { +abstract class BaseWatcher[T](initialValue: T) { private var dirty = false private var _callback: Option[T => Unit] = None private var _value: T = initialValue - def value: T = _value - def value_=(newValue: T): Unit = { + protected def value: T = _value + protected def value_=(newValue: T): Unit = { dirty = _value != newValue if (dirty) { _callback.foreach(_(newValue)) } _value = newValue } + + def onChange: Option[T => Unit] = _callback - def onChange(callback: T => Unit): Unit = _callback = Some(callback) + def onChange_=(callback: T => Unit): Unit = { + _callback = Some(callback) + } def changed(): Boolean = { if (dirty) { @@ -29,5 +32,10 @@ class Watcher[T](initialValue: T) { } object Watcher { - def apply[T](value: T) = new Watcher(value) + def apply[T](initialValue: T) = new Watcher(initialValue) + + class Watcher[T](initialValue: T) extends BaseWatcher(initialValue) { + override def value: T = super.value + override def value_=(newValue: T): Unit = super.value_=(newValue) + } }