« get me outta code hell

(!!) Only render when draw-dependency props change - 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:
authorFlorrie <towerofnix@gmail.com>2019-09-15 16:55:53 -0300
committerFlorrie <towerofnix@gmail.com>2019-09-15 17:09:31 -0300
commit3f76094c554c23ee3519f41458a04d348f4f75a3 (patch)
tree5b87c588651b52aec6136e651e73d9f1d747c638 /ui/Root.js
parent878e55e7c2a203d89fb1dad83ba6d6d8751b521a (diff)
(!!) Only render when draw-dependency props change
This is a very large change and probably breaks most applications not
built to work with it. (Obviously, I'm not really being that responsible
with this sort of thing.) I've tested with mtui and it works fine, but
some elements may need tweaks before being 100% adjusted to the new
scheduled-render system we're using with this commit. Also, any elements
which have custom draw behavior will likely need updating so that they
appropriately schedule renders.
Diffstat (limited to 'ui/Root.js')
-rw-r--r--ui/Root.js65
1 files changed, 61 insertions, 4 deletions
diff --git a/ui/Root.js b/ui/Root.js
index 8242f7a..fd81fed 100644
--- a/ui/Root.js
+++ b/ui/Root.js
@@ -9,10 +9,11 @@ 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) {
+  constructor(interfacer, writable = null) {
     super()
 
     this.interfacer = interfacer
+    this.writable = writable || interfacer
 
     this.selectedElement = null
 
@@ -21,10 +22,8 @@ module.exports = class Root extends DisplayElement {
     this.oldSelectionStates = []
 
     interfacer.on('inputData', buf => this.handleData(buf))
-  }
 
-  render() {
-    this.renderTo(this.interfacer)
+    this.renderCount = 0
   }
 
   handleData(buffer) {
@@ -74,7 +73,60 @@ module.exports = class Root extends DisplayElement {
     writable.write(' '.repeat(this.w * this.h))
   }
 
+  scheduleRender() {
+    if (!this.scheduledRender) {
+      setTimeout(() => {
+        this.scheduledRender = false
+        this.render()
+      })
+      this.scheduledRender = true
+    }
+  }
+
+  render() {
+    this.renderTo(this.writable)
+  }
+
+  renderNow() {
+    this.renderNowTo(this.writable)
+  }
+
+  renderTo(writable) {
+    if (this.shouldRenderTo(writable)) {
+      this.renderNowTo(writable)
+    }
+  }
+
+  renderNowTo(writable) {
+    if (writable) {
+      this.renderCount++
+      super.renderTo(writable)
+    }
+  }
+
+  shouldRenderTo(writable) {
+    let render = false
+    this.eachDescendant(el => {
+      // If we already know we're going to render, checking the element's
+      // scheduled-draw status (which involves iterating over each of its draw
+      // dependency properties) is redundant.
+      if (render) {
+        el.unscheduleDraw()
+      } else if (el.hasScheduledDraw()) {
+        render = true
+        el.unscheduleDraw()
+      }
+      el.updateLastDrawValues()
+    })
+    return render
+  }
+
   didRenderTo(writable) {
+    /*
+    writable.write(ansi.moveCursorRaw(1, 1))
+    writable.write('Renders: ' + this.renderCount)
+    */
+
     // Render the cursor, based on the cursorX and cursorY of the currently
     // selected element.
     if (this.selectedElement && this.selectedElement.cursorVisible) {
@@ -94,6 +146,8 @@ module.exports = class Root extends DisplayElement {
     } else {
       writable.write(ansi.hideCursor())
     }
+
+    this.emit('rendered')
   }
 
   cursorMoved() {
@@ -209,4 +263,7 @@ module.exports = class Root extends DisplayElement {
     if (this.selectedElement.directAncestors.includes(el)) return true
     return false
   }
+
+  get selectedElement() { return this.getDep('selectedElement') }
+  set selectedElement(v) { return this.setDep('selectedElement', v) }
 }