« get me outta code hell

Automatically show/hide ListScrollForm scrollbar - tui-lib - Pure Node.js library for making visual command-line programs (ala vim, ncdu)
about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2019-05-12 17:17:53 -0300
committerFlorrie <towerofnix@gmail.com>2019-05-12 17:17:53 -0300
commitd569cc0db9994295860f48d254f630211c1806ad (patch)
tree747b19efeeeca1775f8f4014cdd3d9639a05586e
parent8bc775cfea02e8b1986393918ed7720b5764c5f2 (diff)
Automatically show/hide ListScrollForm scrollbar
...depending on whether there is enough content that it cannot all be
displayed in the form's space or not.
-rw-r--r--ui/form/ListScrollForm.js53
1 files changed, 43 insertions, 10 deletions
diff --git a/ui/form/ListScrollForm.js b/ui/form/ListScrollForm.js
index d748d40..64214ab 100644
--- a/ui/form/ListScrollForm.js
+++ b/ui/form/ListScrollForm.js
@@ -8,19 +8,21 @@ const Form = require('./Form')
 module.exports = class ListScrollForm extends Form {
   // A form that lets the user scroll through a list of items. It
   // automatically adjusts to always allow the selected item to be visible.
+  // Unless disabled in the constructor, a scrollbar is automatically displayed
+  // if there are more items than can be shown in the height of the form at a
+  // single time.
 
-  constructor(layoutType = 'vertical', scrollBarShown = true) {
+  constructor(layoutType = 'vertical', enableScrollBar = true) {
     super()
 
     this.layoutType = layoutType
 
     this.scrollItems = 0
 
+    this.scrollBarEnabled = enableScrollBar
+
     this.scrollBar = new ScrollBar(this)
-    this.scrollBarShown = scrollBarShown
-    if (scrollBarShown) {
-      this.addChild(this.scrollBar)
-    }
+    this.scrollBarShown = false
   }
 
   fixLayout() {
@@ -79,6 +81,10 @@ module.exports = class ListScrollForm extends Form {
     }
 
     delete this._scrollItemsLength
+
+    if (this.scrollBarEnabled) {
+      this.showScrollbarIfNecessary()
+    }
   }
 
   keyPressed(keyBuf) {
@@ -245,6 +251,17 @@ module.exports = class ListScrollForm extends Form {
     return pos
   }
 
+  showScrollbarIfNecessary() {
+    this.scrollBarShown = this.scrollBar.canScrollAtAll()
+
+    const isChild = this.children.includes(this.scrollBar)
+    if (this.scrollBarShown) {
+      if (!isChild) this.addChild(this.scrollBar)
+    } else {
+      if (isChild) this.removeChild(this.scrollBar)
+    }
+  }
+
   get scrollSize() {
     // Gets the actual length made up by all of the items currently scrolled
     // past.
@@ -308,10 +325,13 @@ class ScrollBar extends DisplayElement {
     // TODO: Horizontal layout! Not functionally a lot different, but I'm too
     // lazy to write a test UI for it right now.
 
+    const {
+      backwards: canScrollBackwards,
+      forwards: canScrollForwards
+    } = this.getScrollableDirections()
+
     // - 2 for extra UI elements (arrows)
     const totalLength = this.h - 2
-    const totalScroll = this.listScrollForm.getScrollItemsLength()
-    const currentScroll = this.listScrollForm.scrollItems
 
     // ..[-----]..
     //   ^start|
@@ -323,15 +343,13 @@ class ScrollBar extends DisplayElement {
     // handle should be 10% down, and it should extend 60% of the scrollbar
     // length, to the 70% mark.
 
+    const currentScroll = this.listScrollForm.scrollItems
     const edgeLength = this.listScrollForm.contentH
     const totalItems = this.listScrollForm.inputs.length
     const itemsVisibleAtOnce = Math.min(totalItems, edgeLength)
     const handleLength = itemsVisibleAtOnce / totalItems * totalLength
     let handlePosition = Math.floor(totalLength / totalItems * currentScroll)
 
-    const canScrollBackwards = (currentScroll > 0)
-    const canScrollForwards = (currentScroll < totalScroll)
-
     // Silly peeve of mine: The handle should only be visibly touching the top
     // or bottom of the scrollbar area if you're actually scrolled all the way
     // to the start or end. Otherwise, it shouldn't be touching! There should
@@ -364,4 +382,19 @@ class ScrollBar extends DisplayElement {
       }
     }
   }
+
+  getScrollableDirections() {
+    const currentScroll = this.listScrollForm.scrollItems
+    const totalScroll = this.listScrollForm.getScrollItemsLength()
+
+    return {
+      backwards: (currentScroll > 0),
+      forwards: (currentScroll < totalScroll)
+    }
+  }
+
+  canScrollAtAll() {
+    const {backwards, forwards} = this.getScrollableDirections()
+    return backwards || forwards
+  }
 }