From 607cb75c76066865b46b9c2a73ba140069182261 Mon Sep 17 00:00:00 2001 From: Alex Dmitriev <mr.alex.dmitriev@icloud.com> Date: Thu, 23 May 2024 10:47:49 +0300 Subject: [PATCH] Implement Help window --- .../com/norsedreki/dogcat/app/AppState.kt | 12 ++++- .../com/norsedreki/dogcat/app/di/AppModule.kt | 5 +- .../dogcat/app/ui/help/HelpPresenter.kt | 51 +++++++++++++++---- .../norsedreki/dogcat/app/ui/help/HelpView.kt | 42 +++++++++++++-- .../app/ui/logLines/LogLinesPresenter.kt | 1 + 5 files changed, 95 insertions(+), 16 deletions(-) diff --git a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/AppState.kt b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/AppState.kt index 3bb7cf2..d650887 100644 --- a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/AppState.kt +++ b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/AppState.kt @@ -13,7 +13,8 @@ data class AppStateHolder( val autoscroll: Boolean, val packageFilter: Pair<ByPackage?, Boolean>, val userInputLocation: Pair<Int, Int>, - val isCursorHeld: Boolean + val isCursorHeld: Boolean, + val isUiHeld: Boolean ) interface AppState { @@ -27,6 +28,8 @@ interface AppState { fun setUserInputLocation(x: Int, y: Int) fun holdCursor(hold: Boolean) + + fun holdUi(hold: Boolean) } class InternalAppState : AppState { @@ -38,7 +41,8 @@ class InternalAppState : AppState { null to false, 0 to 0, false, - ) + false, + ), ) override fun autoscroll(on: Boolean) { @@ -56,4 +60,8 @@ class InternalAppState : AppState { override fun holdCursor(hold: Boolean) { state.value = state.value.copy(isCursorHeld = hold) } + + override fun holdUi(hold: Boolean) { + state.value = state.value.copy(isUiHeld = hold) + } } diff --git a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/di/AppModule.kt b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/di/AppModule.kt index 33f9e82..cc7e632 100644 --- a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/di/AppModule.kt +++ b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/di/AppModule.kt @@ -65,7 +65,10 @@ class AppModule { ) } bindSingleton<HelpPresenter> { - HelpPresenter() + HelpPresenter( + instance(), + instance(), + ) } } diff --git a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpPresenter.kt b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpPresenter.kt index 8211e3a..0b2a5a7 100644 --- a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpPresenter.kt +++ b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpPresenter.kt @@ -5,28 +5,61 @@ package com.norsedreki.dogcat.app.ui.help +import com.norsedreki.dogcat.app.AppState +import com.norsedreki.dogcat.app.Keymap +import com.norsedreki.dogcat.app.Keymap.Actions.HELP import com.norsedreki.dogcat.app.ui.HasLifecycle +import com.norsedreki.dogcat.app.ui.Input import kotlin.coroutines.coroutineContext import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch -class HelpPresenter : HasLifecycle { +class HelpPresenter( + private val input: Input, + private val appState: AppState, +) : HasLifecycle { private lateinit var view: HelpView - override suspend fun start() { - view = HelpView() - view.start() - - view.state = HelpView.State("") + private var showing = false + override suspend fun start() { val scope = CoroutineScope(coroutineContext) - // scope.launch { collectAutoscroll() } + scope.launch { collectKeypresses() } } override suspend fun stop() { - if (this::view.isInitialized) { - view.stop() + // No op since views come and go along with hotkey for help. + } + + private suspend fun collectKeypresses() { + input.keypresses.collect { key -> + when (Keymap.bindings[key]) { + HELP -> { + val h = Keymap.bindings.entries.map { "${it.value.name} -- '${Char(it.key)}'" } + + if (!showing) { + appState.holdUi(true) + + view = HelpView() + view.start() + + view.state = HelpView.State(h) + + showing = true + } else { + showing = false + view.stop() + + appState.holdUi(false) + } + } + + else -> { + // Other keys are handled elsewhere + } + } } } } diff --git a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpView.kt b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpView.kt index c856755..73495ec 100644 --- a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpView.kt +++ b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/help/HelpView.kt @@ -5,23 +5,29 @@ package com.norsedreki.dogcat.app.ui.help -import com.norsedreki.dogcat.app.AppConfig.STATUS_VIEW_BOTTOM_MARGIN import com.norsedreki.dogcat.app.ui.HasLifecycle +import com.norsedreki.logger.Logger import kotlin.properties.Delegates import kotlinx.cinterop.CPointer import kotlinx.cinterop.ExperimentalForeignApi import ncurses.WINDOW +import ncurses.box import ncurses.delwin +import ncurses.getmaxx import ncurses.getmaxy +import ncurses.mvwaddstr +import ncurses.mvwin import ncurses.newwin import ncurses.stdscr +import ncurses.werase import ncurses.wrefresh +import ncurses.wresize @OptIn(ExperimentalForeignApi::class) class HelpView : HasLifecycle { data class State( - val packageName: String = "", + val text: List<String> = listOf() ) var state: State by Delegates.observable(State()) { _, _, newValue -> updateView(newValue) } @@ -29,15 +35,43 @@ class HelpView : HasLifecycle { private lateinit var window: CPointer<WINDOW> override suspend fun start() { - val sy = getmaxy(stdscr) - window = newwin(0, 0, sy - STATUS_VIEW_BOTTOM_MARGIN, 0)!! + val sy = getmaxy(stdscr) / 2 + val sx = getmaxx(stdscr) / 2 + + + window = newwin(1, 1, sy, sx)!! + werase(window) // clear the window + wrefresh(window) // refresh the window to apply the clearing + } override suspend fun stop() { + //werase(window) delwin(window) } private fun updateView(state: State) { + val padding = 2 // adjust this value as needed + val maxWidth = state.text.maxOf { it.length } + padding * 2 + val height = state.text.size + padding * 2 + + val sy = getmaxy(stdscr) + val sx = getmaxx(stdscr) + + val startY = (sy - height) / 2 + val startX = (sx - maxWidth) / 2 + + wresize(window, height, maxWidth) + mvwin(window, startY, startX) + Logger.d("UPDATE HELP VIEW: $startX, $startY, $maxWidth, $height") + box(window, 0U, 0U) // draw a box around the window + + state.text.forEachIndexed { index, line -> + mvwaddstr(window, index + padding, padding, line) + } + wrefresh(window) + + Logger.d("HELP view refreshed") } } diff --git a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/logLines/LogLinesPresenter.kt b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/logLines/LogLinesPresenter.kt index c2c85e3..29def14 100644 --- a/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/logLines/LogLinesPresenter.kt +++ b/src/nativeMain/kotlin/com/norsedreki/dogcat/app/ui/logLines/LogLinesPresenter.kt @@ -79,6 +79,7 @@ class LogLinesPresenter( tagWidth = appArguments.tagWidth ?: DEFAULT_TAG_WIDTH, isCursorHeld = it.isCursorHeld, cursorReturnLocation = it.userInputLocation, + isUiHeld = it.isUiHeld, ) } }