From f866877399c35d43288acf1927d5fecf45f5c382 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Feb 2024 13:36:37 +0300 Subject: [PATCH] Draft for respecting cursor hold position --- src/nativeMain/kotlin/AppConfig.kt | 2 +- .../kotlin/{AppState.kt => AppStateHolder.kt} | 31 +++++++---- src/nativeMain/kotlin/di/AppModule.kt | 8 ++- src/nativeMain/kotlin/di/DogcatModule.kt | 2 - src/nativeMain/kotlin/ui/AppPresenter.kt | 15 +++--- .../kotlin/ui/logLines/LogLinesPresenter.kt | 25 ++++++--- .../kotlin/ui/logLines/LogLinesView.kt | 53 ++++++++++++++++--- .../kotlin/ui/status/StatusPresenter.kt | 13 ++--- src/nativeMain/kotlin/ui/status/StatusView.kt | 3 ++ src/nativeMain/kotlin/userInput/Input.kt | 26 ++++++--- 10 files changed, 122 insertions(+), 56 deletions(-) rename src/nativeMain/kotlin/{AppState.kt => AppStateHolder.kt} (53%) diff --git a/src/nativeMain/kotlin/AppConfig.kt b/src/nativeMain/kotlin/AppConfig.kt index 899289c..ffc28de 100644 --- a/src/nativeMain/kotlin/AppConfig.kt +++ b/src/nativeMain/kotlin/AppConfig.kt @@ -1,6 +1,6 @@ object AppConfig { - const val INPUT_KEY_DELAY_MILLIS = 10L + const val INPUT_KEY_DELAY_MILLIS = 30L const val DEFAULT_TAG_WIDTH = 23 diff --git a/src/nativeMain/kotlin/AppState.kt b/src/nativeMain/kotlin/AppStateHolder.kt similarity index 53% rename from src/nativeMain/kotlin/AppState.kt rename to src/nativeMain/kotlin/AppStateHolder.kt index e42478f..24563b1 100644 --- a/src/nativeMain/kotlin/AppState.kt +++ b/src/nativeMain/kotlin/AppStateHolder.kt @@ -2,31 +2,40 @@ import dogcat.LogFilter.ByPackage import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -data class AppState( +data class AppStateHolder( val autoscroll: Boolean, val packageFilter: Pair, - val inputFilterLocation: Pair + val inputFilterLocation: Pair, - //val linesCount: Int, + val isCursorHeld: Boolean ) -interface AppStateFlow { +interface AppState { - val state: StateFlow + val state: StateFlow fun autoscroll(on: Boolean) fun filterByPackage(f: ByPackage?, enable: Boolean) fun setInputFilterLocation(x: Int, y: Int) + + fun holdCursor(hold: Boolean) } -class InternalAppStateFlow : AppStateFlow { +class InternalAppState : AppState { - override val state = MutableStateFlow(AppState(false, null to false, 0 to 0)) + override val state = MutableStateFlow( + AppStateHolder( + false, + null to false, + 0 to 0, + false + ) + ) override fun autoscroll(on: Boolean) { state.value = state.value.copy(autoscroll = on) @@ -36,12 +45,16 @@ class InternalAppStateFlow : AppStateFlow { //if (f != null) { // state.value = state.value.copy(packageFilter = f to true) //} else { - //state.value = state.value.copy(packageFilter = state.value.packageFilter.first to enable) - state.value = state.value.copy(packageFilter = f to enable) + //state.value = state.value.copy(packageFilter = state.value.packageFilter.first to enable) + state.value = state.value.copy(packageFilter = f to enable) //} } override fun setInputFilterLocation(x: Int, y: Int) { state.value = state.value.copy(inputFilterLocation = x to y) } + + override fun holdCursor(hold: Boolean) { + state.value = state.value.copy(isCursorHeld = hold) + } } diff --git a/src/nativeMain/kotlin/di/AppModule.kt b/src/nativeMain/kotlin/di/AppModule.kt index 2765ee7..aee4553 100644 --- a/src/nativeMain/kotlin/di/AppModule.kt +++ b/src/nativeMain/kotlin/di/AppModule.kt @@ -1,13 +1,11 @@ package di -import AppStateFlow +import AppState import BuildConfig import FileLogger -import InternalAppStateFlow +import InternalAppState import di.DogcatModule.dogcatModule import kotlinx.coroutines.CloseableCoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO import logger.CanLog import logger.NoOpLogger import org.kodein.di.DI @@ -31,7 +29,7 @@ class AppModule(ui: CloseableCoroutineDispatcher) { } //bindSingleton { DefaultInput(Dispatchers.IO) } - bindSingleton { InternalAppStateFlow() } + bindSingleton { InternalAppState() } bindSingleton { DefaultInput(instance()) } bindSingleton { AppPresenter( diff --git a/src/nativeMain/kotlin/di/DogcatModule.kt b/src/nativeMain/kotlin/di/DogcatModule.kt index dcf322c..0168c63 100644 --- a/src/nativeMain/kotlin/di/DogcatModule.kt +++ b/src/nativeMain/kotlin/di/DogcatModule.kt @@ -1,8 +1,6 @@ package di import AdbShell -import AppStateFlow -import InternalAppStateFlow import dogcat.* import dogcat.state.DefaultAppliedFiltersState import kotlinx.coroutines.Dispatchers diff --git a/src/nativeMain/kotlin/ui/AppPresenter.kt b/src/nativeMain/kotlin/ui/AppPresenter.kt index 3b00b56..a2e48c9 100644 --- a/src/nativeMain/kotlin/ui/AppPresenter.kt +++ b/src/nativeMain/kotlin/ui/AppPresenter.kt @@ -1,13 +1,12 @@ package ui -import AppStateFlow +import AppState import dogcat.Command.* import dogcat.Dogcat import dogcat.LogFilter.* import dogcat.LogLevel.* import dogcat.state.PublicState import dogcat.state.PublicState.Active -import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* @@ -24,7 +23,7 @@ import kotlin.coroutines.coroutineContext class AppPresenter( private val dogcat: Dogcat, - private val appStateFlow: AppStateFlow, + private val appState: AppState, private val input: Input, private val logLinesPresenter: LogLinesPresenter, private val statusPresenter: StatusPresenter, @@ -38,7 +37,7 @@ class AppPresenter( else -> dogcat(Start.PickAllApps) } - appStateFlow.setInputFilterLocation("Filter: ".length, 49) + appState.setInputFilterLocation("Filter: ".length, 49) view.start() @@ -108,7 +107,7 @@ class AppPresenter( when (Keymap.bindings[keyCode]) { Autoscroll -> { - appStateFlow.autoscroll(!appStateFlow.state.value.autoscroll) + appState.autoscroll(!appState.state.value.autoscroll) } ClearLogs -> { @@ -116,16 +115,16 @@ class AppPresenter( } ToggleFilterByPackage -> { - val f = appStateFlow.state.value.packageFilter + val f = appState.state.value.packageFilter if (f.second) { Logger.d("${context()} !DeselectSelectAppByPackage") - appStateFlow.filterByPackage(f.first, false) + appState.filterByPackage(f.first, false) dogcat(ResetFilter(ByPackage::class)) } else if (f.first != null) { Logger.d("${context()} !SelectAppByPackage") dogcat(Start.PickAppPackage(f.first!!.packageName)) - appStateFlow.filterByPackage(f.first, true) + appState.filterByPackage(f.first, true) } } diff --git a/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt b/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt index 80b2ea1..6edc1ab 100644 --- a/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt +++ b/src/nativeMain/kotlin/ui/logLines/LogLinesPresenter.kt @@ -1,6 +1,6 @@ package ui.logLines -import AppStateFlow +import AppState import dogcat.Dogcat import dogcat.Unparseable import dogcat.state.PublicState.* @@ -9,6 +9,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import logger.Logger import logger.context @@ -20,7 +21,7 @@ import kotlin.coroutines.coroutineContext class LogLinesPresenter( private val dogcat: Dogcat, - private val appStateFlow: AppStateFlow, + private val appState: AppState, private val input: Input, ) : HasLifecycle { //views can come and go, when input disappears @@ -38,6 +39,14 @@ class LogLinesPresenter( scope.launch { collectKeypresses() } + + scope.launch { + appState.state + .map { it.isCursorHeld } + .collect { + view.isCursorHeld = it + } + } } override suspend fun stop() { @@ -99,36 +108,36 @@ class LogLinesPresenter( Logger.d("${context()} Log lines key") when (Keymap.bindings[it]) { Home -> { - appStateFlow.autoscroll(false) + appState.autoscroll(false) view.autoscroll = false view.home() } End -> { - appStateFlow.autoscroll(true) + appState.autoscroll(true) view.autoscroll = true view.end() } LineUp -> { - appStateFlow.autoscroll(false) + appState.autoscroll(false) view.autoscroll = false view.lineUp() } LineDown -> { - appStateFlow.autoscroll(false) + appState.autoscroll(false) view.autoscroll = false //? view.lineDown(1) } PageDown -> { - val a = appStateFlow.state.value.autoscroll + val a = appState.state.value.autoscroll view.pageDown() } PageUp -> { - appStateFlow.autoscroll(false) + appState.autoscroll(false) view.autoscroll = false view.pageUp() } diff --git a/src/nativeMain/kotlin/ui/logLines/LogLinesView.kt b/src/nativeMain/kotlin/ui/logLines/LogLinesView.kt index 871db33..1b35dd1 100644 --- a/src/nativeMain/kotlin/ui/logLines/LogLinesView.kt +++ b/src/nativeMain/kotlin/ui/logLines/LogLinesView.kt @@ -10,6 +10,8 @@ import kotlin.math.min @OptIn(ExperimentalForeignApi::class) class LogLinesView { + var isCursorHeld: Boolean = false + private val sx = getmaxx(stdscr) private val sy = getmaxy(stdscr) @@ -182,18 +184,53 @@ class LogLinesView { fun refresh() { //logger.Logger.d("FVL $firstVisibleLine") - curs_set(0) + val notSeeingLastLine = firstVisibleLine <= linesCount - pageSize + + /**/ + +/* when { + isCursorHeld -> { + Logger.d("cursor is held") + //curs_set(0) + } + notSeeingLastLine -> { + curs_set(0) + } + else -> { + curs_set(1) + } + }*/ + prefresh(pad, firstVisibleLine, 0, position.startY, position.startX, position.endY, position.endX) + //call doupdate with pnoutrefresh + + when { + isCursorHeld -> { + wmove(stdscr, 49, "Filter: ".length) + curs_set(1) + wrefresh(stdscr) + } + + !notSeeingLastLine -> { + curs_set(1) + } + + else -> { + curs_set(0) + } + } - val notSeeingLastLine = firstVisibleLine <= linesCount - pageSize - if (notSeeingLastLine) { - //curs_set(0) - //Logger.d("Cursor is hidden") + /*if (!isCursorHeld && !notSeeingLastLine) { + curs_set(1) } else { - //wmove(pad, firstVisibleLine, 0) - //curs_set(1) - //Logger.d("Cursor is visible") + curs_set(0) } + + if (isCursorHeld) { + wmove(stdscr, 49, "Filter: ".length) + curs_set(1) + wrefresh(stdscr) + }*/ } suspend fun recordLine(count: Int = 1) { diff --git a/src/nativeMain/kotlin/ui/status/StatusPresenter.kt b/src/nativeMain/kotlin/ui/status/StatusPresenter.kt index a8eb778..9613d7a 100644 --- a/src/nativeMain/kotlin/ui/status/StatusPresenter.kt +++ b/src/nativeMain/kotlin/ui/status/StatusPresenter.kt @@ -1,9 +1,7 @@ package ui.status -import AppStateFlow +import AppState import userInput.Input -import userInput.Keymap.Actions.* -import dogcat.Command import dogcat.Command.FilterBy import dogcat.Dogcat import dogcat.LogFilter @@ -13,12 +11,11 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import logger.Logger import logger.context -import userInput.Keymap import kotlin.coroutines.coroutineContext class StatusPresenter( private val dogcat: Dogcat, - private val appStateFlow: AppStateFlow, + private val appState: AppState, private val input: Input ) { //views can come and go, when input disappears @@ -40,7 +37,7 @@ class StatusPresenter( Logger.d("${context()} Update filters in pres") it[LogFilter.ByPackage::class]?.let { - appStateFlow.filterByPackage(it as LogFilter.ByPackage, true) + appState.filterByPackage(it as LogFilter.ByPackage, true) } } .launchIn(scope) @@ -51,7 +48,7 @@ class StatusPresenter( .filterIsInstance() .mapLatest { it } .onEach { - view.updateAutoscroll(appStateFlow.state.value.autoscroll) + view.updateAutoscroll(appState.state.value.autoscroll) Logger.d("${context()} !Emulator in pres ${it.deviceName}") view.updateDevice(it.deviceName, true) @@ -79,7 +76,7 @@ class StatusPresenter( - appStateFlow + appState .state .onEach { Logger.d("${context()} autoscroll in pres ${it.autoscroll}") diff --git a/src/nativeMain/kotlin/ui/status/StatusView.kt b/src/nativeMain/kotlin/ui/status/StatusView.kt index 615c35e..6218134 100644 --- a/src/nativeMain/kotlin/ui/status/StatusView.kt +++ b/src/nativeMain/kotlin/ui/status/StatusView.kt @@ -95,6 +95,7 @@ class StatusView { return bytePtr.toKString() } + //return cursor on input mode! fun updateAutoscroll(autoscroll: Boolean) { wattron(window, COLOR_PAIR(12)) mvwprintw(window, 0, 10, "Autoscroll ${autoscroll}") @@ -102,6 +103,7 @@ class StatusView { wrefresh(window) } + //return cursor on input mode! fun updateDevice(device: String?, running: Boolean) { device?.let { curs_set(0) @@ -115,6 +117,7 @@ class StatusView { } } + //return cursor on input mode! fun updatePackageName(packageName: String) { wattron(window, COLOR_PAIR(12)) mvwprintw(window, 0, getmaxx(window) - packageName.length, packageName) diff --git a/src/nativeMain/kotlin/userInput/Input.kt b/src/nativeMain/kotlin/userInput/Input.kt index 351d592..13650e4 100644 --- a/src/nativeMain/kotlin/userInput/Input.kt +++ b/src/nativeMain/kotlin/userInput/Input.kt @@ -1,7 +1,7 @@ package userInput import AppConfig.INPUT_KEY_DELAY_MILLIS -import AppStateFlow +import AppState import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow @@ -19,7 +19,7 @@ interface Input : HasLifecycle { } class DefaultInput( - private val appStateFlow: AppStateFlow + private val appState: AppState ) : Input { private val keypressesSubject = MutableSharedFlow() @@ -33,8 +33,8 @@ class DefaultInput( @OptIn(ExperimentalForeignApi::class) override suspend fun start() { - val x = appStateFlow.state.value.inputFilterLocation.first - val y = appStateFlow.state.value.inputFilterLocation.second + val x = appState.state.value.inputFilterLocation.first + val y = appState.state.value.inputFilterLocation.second CoroutineScope(coroutineContext) .launch { @@ -44,11 +44,12 @@ class DefaultInput( val key = wgetch(stdscr) if (key == ERR) { - if (inputMode) { - curs_set(1) + /*if (inputMode) { wmove(stdscr, y , cursorPosition) + curs_set(1) + wrefresh(stdscr) - } + }*/ delay(INPUT_KEY_DELAY_MILLIS) @@ -56,9 +57,17 @@ class DefaultInput( } if (Keymap.bindings[key] == Keymap.Actions.InputFilterBySubstring && !inputMode) { + appState.holdCursor(true) inputMode = true mvwaddstr(stdscr, y, 0, "Filter: ") + wclrtoeol(stdscr) + + //move not needed? + wmove(stdscr, y , cursorPosition) + curs_set(1) + + wrefresh(stdscr) continue } @@ -84,8 +93,11 @@ class DefaultInput( val input = inputBuffer.toString() inputBuffer.clear() cursorPosition = x + //do not just disable, maybe log lines want it back curs_set(0) + inputMode = false + appState.holdCursor(false) stringsSubject.emit(input) }