From 397853c8449958565a5d83ef6984a23e4e49dfae Mon Sep 17 00:00:00 2001 From: pier-bezuhoff Date: Wed, 24 Jan 2024 05:44:10 +0300 Subject: [PATCH] implemented redo (to be tested) --- .../commonMain/kotlin/ui/EditClusterScreen.kt | 3 ++ .../kotlin/ui/EditClusterViewModel.kt | 34 +++++++++++++++---- .../kotlin/data/io/OpenFile.wasmJs.kt | 3 +- .../kotlin/data/io/SaveFile.wasmJs.kt | 2 ++ gradle/libs.versions.toml | 3 ++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/ui/EditClusterScreen.kt b/composeApp/src/commonMain/kotlin/ui/EditClusterScreen.kt index 9b750de2..88a44b58 100644 --- a/composeApp/src/commonMain/kotlin/ui/EditClusterScreen.kt +++ b/composeApp/src/commonMain/kotlin/ui/EditClusterScreen.kt @@ -104,6 +104,9 @@ fun EditClusterTopBar( IconButton(onClick = viewModel::undo) { Icon(painterResource("icons/undo.xml"), contentDescription = "Undo") } + IconButton(onClick = viewModel::redo) { + Icon(painterResource("icons/redo.xml"), contentDescription = "Redo") + } // IconButton(onClick = viewModel::cancelAndGoBack) { // Icon(Icons.Default.Close, contentDescription = "Cancel") // } diff --git a/composeApp/src/commonMain/kotlin/ui/EditClusterViewModel.kt b/composeApp/src/commonMain/kotlin/ui/EditClusterViewModel.kt index 0c7e5d61..da2af94d 100644 --- a/composeApp/src/commonMain/kotlin/ui/EditClusterViewModel.kt +++ b/composeApp/src/commonMain/kotlin/ui/EditClusterViewModel.kt @@ -49,11 +49,13 @@ class EditClusterViewModel( val handleIsDown = mutableStateOf(false) val grabbedCircleIx = mutableStateOf(null) + // tagged & grouped gap buffer private val commands = ArrayDeque(HISTORY_SIZE) + private val redoCommands = ArrayDeque(HISTORY_SIZE) // we group history by commands and record it only when the new command differs from the previous one // NOTE: history doesn't survive background app kill - // TODO: redo stack private val history = ArrayDeque(HISTORY_SIZE) + private val redoHistory = ArrayDeque(HISTORY_SIZE) private val _decayingCircles = MutableSharedFlow() val decayingCircles = _decayingCircles.asSharedFlow() @@ -94,6 +96,7 @@ class EditClusterViewModel( fun undo() { if (history.size > 1) { val previousState = history.removeLast() + val previousCommand = commands.removeLast() selection.clear() parts.clear() circles.clear() @@ -101,7 +104,24 @@ class EditClusterViewModel( parts.addAll(previousState.parts) switchSelectionMode(previousState.selectionMode) selection.addAll(previousState.selection) - commands.removeLast() + redoCommands.addFirst(previousCommand) + redoHistory.addFirst(previousState) + } + } + + fun redo() { + if (redoHistory.isNotEmpty()) { + val nextState = redoHistory.removeFirst() + val nextCommand = redoCommands.removeFirst() + selection.clear() + parts.clear() + circles.clear() + circles.addAll(nextState.circles) + parts.addAll(nextState.parts) + switchSelectionMode(nextState.selectionMode) + selection.addAll(nextState.selection) + commands.addLast(nextCommand) + history.addLast(nextState) } } @@ -111,9 +131,11 @@ class EditClusterViewModel( history.removeFirst() commands.removeFirst() } - history.add(UiState.save(this)) - commands.add(command) + history.addLast(UiState.save(this)) + commands.addLast(command) } + redoCommands.clear() + redoHistory.clear() } suspend fun createNewCircle() { @@ -198,11 +220,11 @@ class EditClusterViewModel( selection.clear() } else if (selectionMode.value == SelectionMode.SelectRegion && newMode == SelectionMode.SelectRegion) { if (parts.isEmpty()) { - recordCommand(Command.SELECT_PART) +// recordCommand(Command.SELECT_PART) // select interlacing, todo: proper 2^n -> even # of 1's -> {0101001} -> parts parts.add(Cluster.Part(emptySet(), circles.indices.toSet())) } else { - recordCommand(Command.SELECT_PART) +// recordCommand(Command.SELECT_PART) parts.clear() } } diff --git a/composeApp/src/wasmJsMain/kotlin/data/io/OpenFile.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/data/io/OpenFile.wasmJs.kt index 992a8623..6b5fb8f3 100644 --- a/composeApp/src/wasmJsMain/kotlin/data/io/OpenFile.wasmJs.kt +++ b/composeApp/src/wasmJsMain/kotlin/data/io/OpenFile.wasmJs.kt @@ -36,7 +36,8 @@ actual fun OpenFileButton( fun queryFile(callback: (file: File?) -> Unit) { val input = document.createElement("input") as HTMLInputElement input.type = "file" - input.accept = "text/plain" +// input.accept = "text/plain" // doesnt detect custom formats +// input.accept = "*/*" input.onchange = { event -> val file = input.files?.get(0) // val file = extractFileFromEvent(event) diff --git a/composeApp/src/wasmJsMain/kotlin/data/io/SaveFile.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/data/io/SaveFile.wasmJs.kt index ae587d63..19e2260a 100644 --- a/composeApp/src/wasmJsMain/kotlin/data/io/SaveFile.wasmJs.kt +++ b/composeApp/src/wasmJsMain/kotlin/data/io/SaveFile.wasmJs.kt @@ -37,6 +37,8 @@ actual fun SaveFileButton( } } +// showSaveFilePicker() is still experimental, cmon js bros... + // global js function external fun encodeURIComponent(str: String): String diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 11a75cb7..3fd0e054 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ kotlinx-serialization = "1.6.2" compose = "1.5.4" compose-compiler = "1.5.6" compose-plugin = "1.6.0-alpha01" +material3 = "1.1.2" junit = "4.13.2" #noinspection GradleDependency kotlin = "1.9.21" @@ -34,6 +35,8 @@ compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" } compose-material = { module = "androidx.compose.material:material", version.ref = "compose" } +compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" } +compose-material3-window-size-klass = { module = "androidx.compose.material3:material3-window-size-class", version.ref = "material3" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization"} [plugins]