« get me outta code hell

Root.js « ui - 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
blob: 16b2fc212f6fcceb64146bd822dbad5b40dfe744 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const ansi = require('../util/ansi')

const DisplayElement = require('./DisplayElement')

const FocusElement = require('./form/FocusElement')

module.exports = class Root extends DisplayElement {
  // An element to be used as the root of a UI. Handles lots of UI and
  // socket stuff.

  constructor(interfacer) {
    super()

    this.interfacer = interfacer

    this.selected = null

    this.cursorBlinkOffset = Date.now()

    interfacer.on('inputData', buf => this.handleData(buf))
  }

  render() {
    this.renderTo(this.interfacer)
  }

  handleData(buffer) {
    if (this.selected) {
      const els = this.selected.directAncestors.concat([this.selected])
      for (const el of els) {
        if (el instanceof FocusElement) {
          const shouldBreak = (el.keyPressed(buffer) === false)
          if (shouldBreak) {
            break
          }
          el.emit('keypressed', buffer)
        }
      }
    }
  }

  drawTo(writable) {
    writable.write(ansi.moveCursor(0, 0))
    writable.write(' '.repeat(this.w * this.h))
  }

  didRenderTo(writable) {
    // Render the cursor, based on the cursorX and cursorY of the currently
    // selected element.
    if (this.selected && this.selected.cursorVisible) {
      if ((Date.now() - this.cursorBlinkOffset) % 1000 < 500) {
        writable.write(
          ansi.moveCursor(this.selected.absCursorY, this.selected.absCursorX)
        )
        writable.write(ansi.invert())
        writable.write('I')
        writable.write(ansi.resetAttributes())
      }

      writable.write(ansi.showCursor())
      writable.write(
        ansi.moveCursor(this.selected.absCursorY, this.selected.absCursorX)
      )
    } else {
      writable.write(ansi.hideCursor())
    }

    if (this.selected && this.selected.cursorVisible) {}
  }

  cursorMoved() {
    // Resets the blinking animation for the cursor. Call this whenever you
    // move the cursor.

    this.cursorBlinkOffset = Date.now()
  }

  select(el) {
    // Select an element. Calls the unfocus method on the already-selected
    // element, if there is one.

    if (this.selected) {
      this.selected.unfocused()
    }

    this.selected = el
    this.selected.focused()

    this.cursorMoved()
  }

  isChildOrSelfSelected(el) {
    if (!this.selected) return false
    if (this.selected === el) return true
    if (this.selected.directAncestors.includes(el)) return true
    return false
  }
}