« get me outta code hell

tui-lib - Pure Node.js library for making visual command-line programs (ala vim, ncdu)
about summary refs log tree commit diff
path: root/ui/Root.js
diff options
context:
space:
mode:
Diffstat (limited to 'ui/Root.js')
-rw-r--r--ui/Root.js57
1 files changed, 48 insertions, 9 deletions
diff --git a/ui/Root.js b/ui/Root.js
index 1933323..3bd4767 100644
--- a/ui/Root.js
+++ b/ui/Root.js
@@ -1,8 +1,9 @@
 const ansi = require('../util/ansi')
+const telc = require('../util/telchars')
 
 const DisplayElement = require('./DisplayElement')
 
-const FocusElement = require('./form/FocusElement')
+const Form = require('./form/Form')
 
 module.exports = class Root extends DisplayElement {
   // An element to be used as the root of a UI. Handles lots of UI and
@@ -27,17 +28,42 @@ module.exports = class Root extends DisplayElement {
   }
 
   handleData(buffer) {
-    if (this.selectedElement) {
-      const els = [
-        this.selectedElement, ...this.selectedElement.directAncestors]
-      for (const el of els) {
-        if (el instanceof FocusElement) {
+    if (telc.isMouse(buffer)) {
+      const { button, line, col } = telc.parseMouse(buffer)
+      const topEl = this.getElementAt(col - 1, line - 1)
+      if (topEl) {
+        //console.log('Clicked', topEl.constructor.name, 'of', topEl.parent.constructor.name)
+        this.eachAncestor(topEl, el => {
+          if (typeof el.clicked === 'function') {
+            return el.clicked(button) === false
+          }
+        })
+      }
+    } else {
+      this.eachAncestor(this.selectedElement, el => {
+        if (typeof el.keyPressed === 'function') {
           const shouldBreak = (el.keyPressed(buffer) === false)
           if (shouldBreak) {
-            break
+            return true
           }
           el.emit('keypressed', buffer)
         }
+      })
+    }
+  }
+
+  eachAncestor(topEl, func) {
+    // Handy function for doing something to an element and all its ancestors,
+    // allowing for the passed function to return false to break the loop and
+    // stop propagation.
+
+    if (topEl) {
+      const els = [topEl, ...topEl.directAncestors]
+      for (const el of els) {
+        const shouldBreak = func(el)
+        if (shouldBreak) {
+          break
+        }
       }
     }
   }
@@ -51,6 +77,7 @@ module.exports = class Root extends DisplayElement {
     // Render the cursor, based on the cursorX and cursorY of the currently
     // selected element.
     if (this.selectedElement && this.selectedElement.cursorVisible) {
+      /*
       if ((Date.now() - this.cursorBlinkOffset) % 1000 < 500) {
         writable.write(ansi.moveCursor(
           this.selectedElement.absCursorY, this.selectedElement.absCursorX))
@@ -58,9 +85,10 @@ module.exports = class Root extends DisplayElement {
         writable.write('I')
         writable.write(ansi.resetAttributes())
       }
+      */
 
       writable.write(ansi.showCursor())
-      writable.write(ansi.moveCursor(
+      writable.write(ansi.moveCursorRaw(
         this.selectedElement.absCursorY, this.selectedElement.absCursorX))
     } else {
       writable.write(ansi.hideCursor())
@@ -74,10 +102,21 @@ module.exports = class Root extends DisplayElement {
     this.cursorBlinkOffset = Date.now()
   }
 
-  select(el) {
+  select(el, {fromForm = false} = {}) {
     // Select an element. Calls the unfocus method on the already-selected
     // element, if there is one.
 
+    // If the element is part of a form, just be lazy and pass control to that
+    // form...unless the form itself asked us to select the element!
+    // TODO: This is so that if an element is selected, its parent form will
+    // automatically see that and correctly update its curIndex... but what if
+    // the element is an input of a form which is NOT its parent?
+    const parent = el.parent
+    if (!fromForm && parent instanceof Form && parent.inputs.includes(el)) {
+      parent.selectInput(el)
+      return
+    }
+
     const oldSelected = this.selectedElement
     const newSelected = el