« get me outta code hell

Initial commit - 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/form/TextInput.js
diff options
context:
space:
mode:
authorLiam <towerofnix@gmail.com>2017-01-07 18:26:02 -0400
committerLiam <towerofnix@gmail.com>2017-01-07 18:26:02 -0400
commit16da7fb310198851c2e4b02abedfb24979287242 (patch)
treed7546f7c1a3c3833e6450ea1e10af388f8848bb5 /ui/form/TextInput.js
Initial commit
Diffstat (limited to 'ui/form/TextInput.js')
-rw-r--r--ui/form/TextInput.js114
1 files changed, 114 insertions, 0 deletions
diff --git a/ui/form/TextInput.js b/ui/form/TextInput.js
new file mode 100644
index 0000000..d09480f
--- /dev/null
+++ b/ui/form/TextInput.js
@@ -0,0 +1,114 @@
+const ansi = require('../../ansi')
+const unic = require('../../unichars')
+const telc = require('../../telchars')
+
+const FocusElement = require('./FocusElement')
+
+module.exports = class TextInput extends FocusElement {
+  // An element that the user can type in.
+
+  constructor() {
+    super()
+
+    this.value = ''
+    this.cursorIndex = 0
+    this.scrollChars = 0
+  }
+
+  drawTo(writable) {
+    // There should be room for the cursor so move the "right edge" left a
+    // single character.
+
+    const startRange = this.scrollChars
+    const endRange = this.scrollChars + this.w - 3
+
+    let str = this.value.slice(startRange, endRange)
+
+    writable.write(ansi.moveCursor(this.absTop, this.absLeft + 1))
+    writable.write(str)
+
+    // Ellipsis on left side, if there's more characters behind the visible
+    // area.
+    if (startRange > 0) {
+      writable.write(ansi.moveCursor(this.absTop, this.absLeft))
+      writable.write(unic.ELLIPSIS)
+    }
+
+    // Ellipsis on the right side, if there's more characters ahead of the
+    // visible area.
+    if (endRange < this.value.length) {
+      writable.write(ansi.moveCursor(this.absTop, this.absRight - 1))
+      writable.write(unic.ELLIPSIS.repeat(2))
+    }
+
+    this.cursorX = this.cursorIndex - this.scrollChars + 1
+
+    super.drawTo(writable)
+  }
+
+  keyPressed(keyBuf) {
+    if (keyBuf[0] === 127) {
+      this.value = (
+        this.value.slice(0, this.cursorIndex - 1) +
+        this.value.slice(this.cursorIndex)
+      )
+      this.cursorIndex--
+      this.root.cursorMoved()
+    } else if (keyBuf[0] === 13) {
+      this.emit('value', this.value)
+    } else if (keyBuf[0] === 0x1b && keyBuf[1] === 0x5b) {
+      // Keyboard navigation
+      if (keyBuf[2] === 0x44) {
+        this.cursorIndex--
+        this.root.cursorMoved()
+      } else if (keyBuf[2] === 0x43) {
+        this.cursorIndex++
+        this.root.cursorMoved()
+      }
+    } else if (telc.isEscape(keyBuf)) {
+      // ESC is bad and we don't want that in the text input!
+      return
+    } else {
+      // console.log(keyBuf, keyBuf[0], keyBuf[1], keyBuf[2])
+      this.value = (
+        this.value.slice(0, this.cursorIndex) + keyBuf.toString() +
+        this.value.slice(this.cursorIndex)
+      )
+      this.cursorIndex++
+      this.root.cursorMoved()
+    }
+
+    this.keepCursorInRange()
+  }
+
+  keepCursorInRange() {
+    // Keep the cursor inside or at the end of the input value.
+
+    if (this.cursorIndex < 0) {
+      this.cursorIndex = 0
+    }
+
+    if (this.cursorIndex > this.value.length) {
+      this.cursorIndex = this.value.length
+    }
+
+    // Scroll right, if the cursor is past the right edge of where text is
+    // displayed.
+    if (this.cursorIndex - this.scrollChars > this.w - 3) {
+      this.scrollChars++
+    }
+
+    // Scroll left, if the cursor is behind the left edge of where text is
+    // displayed.
+    if (this.cursorIndex - this.scrollChars < 0) {
+      this.scrollChars--
+    }
+
+    // Scroll left, if we can see past the end of the text.
+    if (this.scrollChars > 0 && (
+      this.scrollChars + this.w - 3 > this.value.length)
+    ) {
+      this.scrollChars--
+    }
+  }
+}