diff options
Diffstat (limited to 'ui/form/ListScrollForm.js')
-rw-r--r-- | ui/form/ListScrollForm.js | 303 |
1 files changed, 0 insertions, 303 deletions
diff --git a/ui/form/ListScrollForm.js b/ui/form/ListScrollForm.js deleted file mode 100644 index e4f4249..0000000 --- a/ui/form/ListScrollForm.js +++ /dev/null @@ -1,303 +0,0 @@ -const ansi = require('../../util/ansi') -const telc = require('../../util/telchars') - -const Form = require('./Form') -const ScrollBar = require('./ScrollBar') - -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', enableScrollBar = true) { - super() - - this.layoutType = layoutType - this.wheelMode = 'scroll' // scroll, selection - - this.scrollItems = 0 - - this.scrollBarEnabled = enableScrollBar - - this.scrollBar = new ScrollBar({ - getLayoutType: () => this.layoutType, - getCurrentScroll: () => this.scrollItems, - getMaximumScroll: () => this.getScrollItemsLength(), - getTotalItems: () => this.inputs.length - }) - this.scrollBarShown = false - } - - fixLayout() { - this.keepScrollInBounds() - - const scrollItems = this.scrollItems - - // The scrollItems property represents the item to the very left of where - // we've scrolled, so we know right away that none of those will be - // visible and we won't bother iterating over them. We do need to hide - // them, though. - for (let i = 0; i < Math.min(scrollItems, this.inputs.length); i++) { - this.inputs[i].visible = false - } - - // This variable stores how far along the respective axis (implied by - // layoutType) the next element should be. - let nextPos = 0 - - let formEdge - if (this.layoutType === 'horizontal') { - formEdge = this.contentW - } else { - formEdge = this.contentH - } - - for (let i = scrollItems; i < this.inputs.length; i++) { - const item = this.inputs[i] - item.fixLayout() - - const curPos = nextPos - let curSize - if (this.layoutType === 'horizontal') { - item.x = curPos - curSize = item.w - } else { - item.y = curPos - curSize = item.h - } - nextPos += curSize - - // By default, the item should be visible.. - item.visible = true - - // ..but the item's far edge is past the form's far edge, it isn't - // fully visible and should be hidden. - if (curPos + curSize > formEdge) { - item.visible = false - } - - // Same deal goes for the close edge. We can check it against 0 since - // the close edge of the form's content is going to be 0, of course! - if (curPos < 0) { - item.visible = false - } - } - - delete this._scrollItemsLength - - if (this.scrollBarEnabled) { - this.showScrollbarIfNecessary() - } - } - - keyPressed(keyBuf) { - let ret - - handleKeyPress: { - if (this.layoutType === 'horizontal') { - if (telc.isLeft(keyBuf)) { - this.previousInput() - ret = false; break handleKeyPress - } else if (telc.isRight(keyBuf)) { - this.nextInput() - ret = false; break handleKeyPress - } - } else if (this.layoutType === 'vertical') { - if (telc.isUp(keyBuf)) { - this.previousInput() - ret = false; break handleKeyPress - } else if (telc.isDown(keyBuf)) { - this.nextInput() - ret = false; break handleKeyPress - } - } - - ret = super.keyPressed(keyBuf) - } - - this.scrollSelectedElementIntoView() - - return ret - } - - clicked(button) { - if (this.wheelMode === 'selection') { - // Change the actual selected item. - if (button === 'scroll-up') { - this.previousInput() - this.scrollSelectedElementIntoView() - } else if (button === 'scroll-down') { - this.nextInput() - this.scrollSelectedElementIntoView() - } - } else if (this.wheelMode === 'scroll') { - // Scrolling is typically pretty slow with a mouse wheel when it's by - // a single line, so scroll at 3x that speed. - for (let i = 0; i < 3; i++) { - if (button === 'scroll-up') { - this.scrollItems-- - } else if (button === 'scroll-down') { - this.scrollItems++ - } else { - return - } - } - } - - this.fixLayout() - } - - scrollSelectedElementIntoView() { - const sel = this.inputs[this.curIndex] - - if (!sel) { - return - } - - let formEdge - if (this.layoutType === 'horizontal') { - formEdge = this.contentW - } else { - formEdge = this.contentH - } - - // If the item is ahead of our view (either to the right of or below), - // we should move the view so that the item is the farthest right (of all - // the visible items). - if (this.getItemPos(sel) > formEdge + this.scrollSize) { - this.scrollElementIntoEndOfView(sel) - } - - // Adjusting the number of scroll items is much simpler to deal with if - // the item is behind our view. Since the item's behind, we need to move - // the scroll to be immediately behind it, which is simple since we - // already have its index. - if (this.getItemPos(sel) <= this.scrollSize) { - this.scrollItems = this.curIndex - } - - this.fixLayout() - } - - firstInput(...args) { - this.scrollItems = 0 - - super.firstInput(...args) - - this.fixLayout() - } - - getScrollPositionOfElementAtEndOfView(element) { - // We can decide how many items to scroll past by moving forward until - // the item's far edge is visible. - const pos = this.getItemPos(element) - - let edge - if (this.layoutType === 'horizontal') { - edge = this.contentW - } else { - edge = this.contentH - } - - for (let i = 0; i < this.inputs.length; i++) { - if (pos <= edge) { - return i - } - - if (this.layoutType === 'horizontal') { - edge += this.inputs[i].w - } else { - edge += this.inputs[i].h - } - } - // No result? Well, it's at the end. - return this.inputs.length - } - - scrollElementIntoEndOfView(element) { - this.scrollItems = this.getScrollPositionOfElementAtEndOfView(element) - } - - scrollToBeginning() { - this.scrollItems = 0 - this.fixLayout() - } - - scrollToEnd() { - this.scrollElementIntoEndOfView(this.inputs[this.inputs.length - 1]) - this.fixLayout() - } - - keepScrollInBounds() { - this.scrollItems = Math.max(this.scrollItems, 0) - this.scrollItems = Math.min(this.scrollItems, this.getScrollItemsLength()) - } - - getScrollItemsLength() { - if (typeof this._scrollItemsLength === 'undefined') { - const lastInput = this.inputs[this.inputs.length - 1] - this._scrollItemsLength = this.getScrollPositionOfElementAtEndOfView(lastInput) - } - - return this._scrollItemsLength - } - - getItemPos(item) { - // Gets the position of the item in an unscrolled view. - - const index = this.inputs.indexOf(item) - let pos = 0 - for (let i = 0; i <= index; i++) { - if (this.layoutType === 'horizontal') { - pos += this.inputs[i].w - } else { - pos += this.inputs[i].h - } - } - 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. - - let size = 0 - for (let i = 0; i < Math.min(this.scrollItems, this.inputs.length); i++) { - if (this.layoutType === 'horizontal') { - size += this.inputs[i].w - } else { - size += this.inputs[i].h - } - } - return size - } - - get contentW() { - if (this.scrollBarShown && this.layoutType === 'vertical') { - return this.w - 1 - } else { - return this.w - } - } - - get contentH() { - if (this.scrollBarShown && this.layoutType === 'horizontal') { - return this.h - 1 - } else { - return this.h - } - } -} |