From 1f434c1ef11fa55bab1718ea4e3ca8d115c0dfb1 Mon Sep 17 00:00:00 2001 From: Florrie Date: Sat, 8 Dec 2018 02:37:15 -0400 Subject: Mouse support Not exactly the most elegant implementation, but it definitely works and isn't really difficult to code around! --- ui/Root.js | 57 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 9 deletions(-) (limited to 'ui/Root.js') diff --git a/ui/Root.js b/ui/Root.js index 1933323..3bd4767 100644 --- a/ui/Root.js +++ b/ui/Root.js @@ -1,8 +1,9 @@ const ansi = require('../util/ansi') +const telc = require('../util/telchars') const DisplayElement = require('./DisplayElement') -const FocusElement = require('./form/FocusElement') +const Form = require('./form/Form') module.exports = class Root extends DisplayElement { // An element to be used as the root of a UI. Handles lots of UI and @@ -27,17 +28,42 @@ module.exports = class Root extends DisplayElement { } handleData(buffer) { - if (this.selectedElement) { - const els = [ - this.selectedElement, ...this.selectedElement.directAncestors] - for (const el of els) { - if (el instanceof FocusElement) { + if (telc.isMouse(buffer)) { + const { button, line, col } = telc.parseMouse(buffer) + const topEl = this.getElementAt(col - 1, line - 1) + if (topEl) { + //console.log('Clicked', topEl.constructor.name, 'of', topEl.parent.constructor.name) + this.eachAncestor(topEl, el => { + if (typeof el.clicked === 'function') { + return el.clicked(button) === false + } + }) + } + } else { + this.eachAncestor(this.selectedElement, el => { + if (typeof el.keyPressed === 'function') { const shouldBreak = (el.keyPressed(buffer) === false) if (shouldBreak) { - break + return true } el.emit('keypressed', buffer) } + }) + } + } + + eachAncestor(topEl, func) { + // Handy function for doing something to an element and all its ancestors, + // allowing for the passed function to return false to break the loop and + // stop propagation. + + if (topEl) { + const els = [topEl, ...topEl.directAncestors] + for (const el of els) { + const shouldBreak = func(el) + if (shouldBreak) { + break + } } } } @@ -51,6 +77,7 @@ module.exports = class Root extends DisplayElement { // Render the cursor, based on the cursorX and cursorY of the currently // selected element. if (this.selectedElement && this.selectedElement.cursorVisible) { + /* if ((Date.now() - this.cursorBlinkOffset) % 1000 < 500) { writable.write(ansi.moveCursor( this.selectedElement.absCursorY, this.selectedElement.absCursorX)) @@ -58,9 +85,10 @@ module.exports = class Root extends DisplayElement { writable.write('I') writable.write(ansi.resetAttributes()) } + */ writable.write(ansi.showCursor()) - writable.write(ansi.moveCursor( + writable.write(ansi.moveCursorRaw( this.selectedElement.absCursorY, this.selectedElement.absCursorX)) } else { writable.write(ansi.hideCursor()) @@ -74,10 +102,21 @@ module.exports = class Root extends DisplayElement { this.cursorBlinkOffset = Date.now() } - select(el) { + select(el, {fromForm = false} = {}) { // Select an element. Calls the unfocus method on the already-selected // element, if there is one. + // If the element is part of a form, just be lazy and pass control to that + // form...unless the form itself asked us to select the element! + // TODO: This is so that if an element is selected, its parent form will + // automatically see that and correctly update its curIndex... but what if + // the element is an input of a form which is NOT its parent? + const parent = el.parent + if (!fromForm && parent instanceof Form && parent.inputs.includes(el)) { + parent.selectInput(el) + return + } + const oldSelected = this.selectedElement const newSelected = el -- cgit 1.3.0-6-gf8a5