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,
                 )
         }
     }