Reputation: 1765
I need to implement redo/undo for TextField in Jetpack Compose. For an EditText I used this one and it worked well. However, for Jetpack Compose there is no such a listener. I would implement an own one based on that one for EditText, but I'm missing these two listener methods, which are not available for TextField:
doOnTextChanged { text, start, before, count -> }
doBeforeTextChanged { text, start, count, after -> }
In TextField is only one listener to use
onValuesChange = { }
that only string without start and count returns.
How would I achieve a redo/undo to implement for a TextField in Jetpack Compose?
This is what I did so far. Would be great to make it functionable:
class EditTextDo {
private var mIsUndoOrRedo = false
private val editHistory: EditHistory? = null
fun redo() {
val edit = editHistory?.getNext() ?: return
// Do Redo
}
fun undo() {
val edit = editHistory?.getPrevious() ?: return
// Do Undo
}
fun canUndo(): Boolean {
editHistory?.let {
return it.position > 0
}
return false
}
fun canRedo(): Boolean {
editHistory?.let {
return it.position < it.history.size
}
return false
}
}
class EditHistory {
var position = 0
private var maxHistorySize = -1
val history = LinkedList<EditItem>()
private fun clear() {
position = 0
history.clear()
}
fun add(item: EditItem) {
while (history.size > position) {
history.removeLast()
}
history.add(item)
position++
if (maxHistorySize >= 0)
trimHistory()
}
fun getNext(): EditItem? {
if (position >= history.size) {
return null
}
val item = history[position]
position++
return item
}
fun getPrevious(): EditItem? {
if (position == 0) {
return null
}
position--
return history[position]
}
private fun setMaxHistorySize(maxHistorySize: Int) {
this.maxHistorySize = maxHistorySize
if (maxHistorySize >= 0)
trimHistory()
}
private fun trimHistory() {
while (history.size > maxHistorySize) {
history.removeFirst()
position--
}
if (position < 0)
position = 0
}
}
data class EditItem(val start: Int, val before: CharSequence, val after: CharSequence)
Upvotes: 4
Views: 1014
Reputation: 6207
This may not exactly address your post but hopefully it help. I have a simple undo/redo
Textfield
on my project using Queue
structure to keep track of the input history, I'm not specifying history size though.
@Composable
fun TextFieldWithHistory() {
val undoRedoState = remember { UndoRedoState() }
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
TextField(
value = undoRedoState.input,
onValueChange = {
undoRedoState.onInput(it)
}
)
Button(
onClick = {
undoRedoState.undo()
}) {
Text(text = "Undo")
}
Button(
onClick = {
undoRedoState.redo()
}) {
Text(text = "Redo")
}
}
}
class UndoRedoState {
var input by mutableStateOf(TextFieldValue(""))
var undoHistory = ArrayDeque<TextFieldValue?>()
var redoHistory = ArrayDeque<TextFieldValue?>()
init {
undoHistory.add(input)
}
fun onInput(value: TextFieldValue) {
// always set the cursor at the end (selection = text length)
val updatedValue = value.copy(value.text, selection = TextRange(value.text.length))
undoHistory.add(updatedValue)
input = updatedValue
}
fun undo() {
if (undoHistory.size > 1) {
// pop the last
val pop = undoHistory.removeLastOrNull()
pop?.let {
if (it.text.isNotEmpty()) {
redoHistory.add(it)
}
}
// peek the last
val peek = undoHistory.lastOrNull()
peek?.let{
input = it
}
}
}
fun redo() {
val pop = redoHistory.removeLastOrNull()
pop?.let {
if (it.text.isNotEmpty()) {
undoHistory.add(it)
input = it
}
}
}
}
Upvotes: 2