diff --git a/lib/tput.js b/lib/tput.js index 615d4de..b403918 100644 --- a/lib/tput.js +++ b/lib/tput.js @@ -60,6 +60,9 @@ function Tput(options) { } catch (e) { // If there was an error, fallback // to an internally stored terminfo/cap. + if (this.debug) { + throw e; + } this._useXtermI(); } } diff --git a/lib/widget.js b/lib/widget.js index 7b12ce1..d2d40df 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -2880,10 +2880,6 @@ function Button(options) { } }); - this.on('click', function() { - self.press(); - }); - if (this.options.defaultEffects) { this.on('mouseover', function() { self.inverse = !self.options.inverse; @@ -3160,6 +3156,650 @@ FileManager.prototype.reset = function(cwd, callback) { this.refresh(callback); }; +/** + * Checkbox + */ + +function Checkbox(options) { + var self = this; + + if (!(this instanceof Checkbox)) { + return new Checkbox(options); + } + + Input.call(this, options); + + this.value = options.value || ''; + this.checked = options.checked || false; + + this.on('keypress', function(ch, key) { + if (key.name === 'enter' || key.name === 'space') { + self.check(); + } + }); + + if (this.options.mouse) { + this.on('click', function() { + self.check(); + }); + } + + this.on('focus', function() { + self.program.saveCursor(); + self.program.cup(this.top, this.left + 1); + self.program.showCursor(); + }); + + this.on('blur', function() { + self.program.hideCursor(); + self.program.restoreCursor(); + }); +} + +Checkbox.prototype.__proto__ = Input.prototype; + +Checkbox.prototype.type = 'checkbox'; + +Checkbox.prototype._render = Checkbox.prototype.render; +Checkbox.prototype.render = function(stop) { + this.setContent('[' + (this.checked ? 'x' : ' ') + '] ' + this.value); + return this._render(stop); +}; + +Checkbox.prototype.check = function() { + if (this.checked) return; + this.checked = true; + this.emit('check'); +}; + +Checkbox.prototype.uncheck = function() { + if (!this.checked) return; + this.checked = false; + this.emit('uncheck'); +}; + +Checkbox.prototype.toggle = function() { + return this.checked + ? this.uncheck() + : this.check(); +}; + +Checkbox.prototype.setChecked = function(val) { + val = !!val; + if (this.checked === val) return; + this.checked = val; + this.emit('check', val); +}; + +/** + * RadioButton + */ + +function RadioButton(options) { + var self = this; + + if (!(this instanceof RadioButton)) { + return new RadioButton(options); + } + + Checkbox.call(this, options); + + self.group = options.group || []; + + this.on('check', function() { + self.group.forEach(function(el) { + el.uncheck(); + }); + }); +} + +RadioButton.prototype.__proto__ = Checkbox.prototype; + +RadioButton.prototype.type = 'radio-button'; + +/** + * Prompt + */ + +function Prompt(options) { + var self = this; + + if (!(this instanceof Prompt)) { + return new Prompt(options); + } + + Box.call(this, options); + + /*prompt = createPrompt.prompt = blessed.box({ + parent: screen, + hidden: true, + content: '', + width: 'half', + height: 7, + left: 'center', + top: 'center', + border: { + type: 'ascii' + }, + tags: true + });*/ + + this._.input = new Textbox({ + parent: this, + top: 3, + height: 1, + left: 2, + right: 2, + bg: 'black' + }); + + this._.okay = new Button({ + parent: this, + top: 5, + height: 1, + left: 2, + width: 6, + content: 'Okay', + align: 'center', + bg: 'black', + hoverBg: 'blue', + autoFocus: false, + mouse: true + }); + + this._.cancel = new Button({ + parent: this, + top: 5, + height: 1, + shrink: true, + left: 10, + width: 8, + content: 'Cancel', + align: 'center', + bg: 'black', + hoverBg: 'blue', + autoFocus: false, + mouse: true + }); +} + +Prompt.prototype.__proto__ = Box.prototype; + +Prompt.prototype.type = 'prompt'; + +Prompt.prototype.type = function(text, value, callback) { + var self = this; + var okay, cancel; + + if (!callback) { + callback = value; + value = ''; + } + + this.show(); + this.setContent(' ' + text); + + if (value) this._.input.value = value; + + this.screen.saveFocus(); + + this._.okay.on('press', okay = function() { + self._.input.submit(); + }); + + this._.cancel.on('press', cancel = function() { + self._.input.cancel(); + }); + + this._.input.readInput(function(err, data) { + self.hide(); + self.screen.restoreFocus(); + self._.okay.removeListener('press', okay); + self._.cancel.removeListener('press', cancel); + return callback(err, data); + }); + + this.screen.render(); +}; + +/** + * Question + */ + +function Question(options) { + var self = this; + + if (!(this instanceof Question)) { + return new Question(options); + } + + Box.call(this, options); + + /*question = createQuestion.question = blessed.box({ + parent: screen, + hidden: true, + content: '', + width: 'half', + height: 5, + left: 'center', + top: 'center', + border: { + type: 'ascii' + }, + tags: true + });*/ + + this._.okay = new Button({ + parent: this, + top: 3, + height: 1, + left: 2, + width: 6, + content: 'Okay', + align: 'center', + bg: 'black', + hoverBg: 'blue', + autoFocus: false, + mouse: true + }); + + this._.cancel = new Button({ + parent: this, + top: 3, + height: 1, + shrink: true, + left: 10, + width: 8, + content: 'Cancel', + align: 'center', + bg: 'black', + hoverBg: 'blue', + autoFocus: false, + mouse: true + }); +} + +Question.prototype.__proto__ = Box.prototype; + +Question.prototype.type = 'question'; + +Question.prototype.ask = function(text, callback) { + var self = this; + var press, okay, cancel; + + this.show(); + this.setContent(' ' + text); + + this.screen.on('keypress', press = function(ch, key) { + if (key.name === 'mouse') return; + if (key.name !== 'enter' + && key.name !== 'escape' + && key.name !== 'q' + && key.name !== 'y' + && key.name !== 'n') { + return; + } + done(null, key.name === 'enter' || key.name === 'y'); + }); + + this._.okay.on('press', okay = function() { + done(null, true); + }); + + this._.cancel.on('press', cancel = function() { + done(null, false); + }); + + this.screen.saveFocus(); + this.focus(); + + function done(err, data) { + self.hide(); + self.screen.restoreFocus(); + self.screen.removeListener('keypress', press); + self._.okay.removeListener('press', okay); + self._.cancel.removeListener('press', cancel); + return callback(err, data); + } + + this.screen.render(); +}; + +/** + * Message / Error + */ + +function Message(options) { + var self = this; + + if (!(this instanceof Message)) { + return new Message(options); + } + + options.tags = true; + + Box.call(this, options); + + /*var msg = blessed.box({ + parent: screen, + hidden: true, + top: 'center', + left: 'center', + width: 'half', + tags: true, + align: 'center', + shrink: true, + border: { + type: 'ascii' + } + });*/ +} + +Message.prototype.__proto__ = Box.prototype; + +Message.prototype.type = 'message'; + +Message.prototype.log = +Message.prototype.display = function(text, time, callback) { + var self = this; + if (typeof time === 'function') { + callback = time; + time = null; + } + time = time || 3; + this.show(); + this.setContent(text); + this.screen.render(); + setTimeout(function() { + self.hide(); + self.screen.render(); + if (callback) callback(); + }, time * 1000); +}; + +Message.prototype.error = function(text, callback) { + return this.display('{red-fg}Error: ' + text + '{/}', callback); +}; + +/** + * Info - TODO: Merge display method into Message? + */ + +function Info(options) { + var self = this; + + if (!(this instanceof Info)) { + return new Info(options); + } + + Box.call(this, options); + + /*var info = blessed.box({ + parent: screen, + hidden: true, + top: 'center', + left: 'center', + width: 'half', + tags: true, + shrink: true, + border: { + type: 'ascii' + } + });*/ +} + +Info.prototype.__proto__ = Box.prototype; + +Info.prototype.type = 'info'; + +Info.prototype.display = function(text, callback) { + this.show(); + + this.setContent(text); + + this.screen.render(); + + function end() { + if (end.done) return; + end.done = true; + self.hide(); + self.screen.render(); + if (callback) callback(); + } + + return setTimeout(function() { + self.screen.on('keypress', function fn(ch, key) { + if (key.name === 'mouse') return; + self.screen.removeListener('keypress', fn); + end(); + }); + screen.on('mouse', function fn(data) { + if (data.action === 'mousemove') return; + self.screen.removeListener('mouse', fn); + end(); + }); + }, 10); +}; + +/** + * Loading + */ + +function Loading(options) { + var self = this; + + if (!(this instanceof Loading)) { + return new Loading(options); + } + + Box.call(this, options); + + this._.icon = new Text({ + parent: this, + align: 'center', + top: 2, + left: 1, + right: 1, + height: 1, + content: '|' + }); +} + +Loading.prototype.__proto__ = Box.prototype; + +Loading.prototype.type = 'loading'; + +Loading.prototype.load = function(text) { + var self = this; + + this.show(); + this.setContent(text); + + if (this._.timer) { + this._.stop(); + } + + this.screen.lockKeys = true; + + this._.timer = setInterval(function() { + if (self._.icon.content === '|') { + self._.icon.setContent('/'); + } else if (self._.icon.content === '/') { + self._.icon.setContent('-'); + } else if (self._.icon.content === '-') { + self._.icon.setContent('\\'); + } else if (self._.icon.content === '\\') { + self._.icon.setContent('|'); + } + self.screen.render(); + }, 200); +}; + +Loading.prototype.stop = function() { + this.screen.lockKeys = false; + this.hide(); + if (this._.timer) { + clearInterval(this._.timer); + delete this._.timer; + } + this.screen.render(); +}; + +/** + * PickList - TODO Merge into List + */ + +function PickList(options) { + var self = this; + + if (!(this instanceof PickList)) { + return new PickList(options); + } + + List.call(this, options); +} + +PickList.prototype.__proto__ = List.prototype; + +PickList.prototype.type = 'popup-menu'; + +PickList.prototype.pick = function(callback) { + this.screen.saveFocus(); + this.focus(); + this.show(); + this.screen.render(); + this.once('action', function(el, selected) { + self.screen.restoreFocus(); + self.hide(); + self.screen.render(); + if (!el) return callback(); + return callback(null, selected); + }); +}; + +/** + * Listbar / HorizontalList + */ + +function Listbar(options) { + var self = this; + + if (!(this instanceof Listbar)) { + return new Listbar(options); + } + + this.items = []; + + Box.call(this, options); +} + +Listbar.prototype.__proto__ = Box.prototype; + +Listbar.prototype.type = 'menubar'; + +Listbar.prototype.setOptions = +Listbar.prototype.setCommands = +Listbar.prototype.setItems = function(commands) { + this.children.forEach(function(el) { + el.detach(); + }); + + var self = this + , drawn = 0; + + this.commands = commands; + + Object.keys(commands).forEach(function(name) { + var cmd = commands[name] + , title + , len + , button; + + title = '{light-black-fg}' + + cmd.prefix + + '{/light-black-fg}' + + ':' + + name; + + len = (cmd.prefix + ':' + name).length; + + button = new Button({ + parent: self, + top: 0, + left: drawn + 1, + height: 1, + content: title, + width: len + 2, + align: 'center', + tags: true, + autoFocus: false, + hoverEffects: { + bg: 'blue' + }, + focusEffects: { + bg: 'blue' + }, + mouse: true + }); + + self._[name] = button; + cmd.element = button; + self.items.push(button); + + if (cmd.callback) { + button.on('press', cmd.callback); + if (cmd.keys) { + screen.key(cmd.keys[0], cmd.callback); + } + } + + drawn += len + 3; + }); +}; + +/** + * DirManager - Merge into FileManager? + */ + +function DirManager(options) { + var self = this; + + if (!(this instanceof DirManager)) { + return new DirManager(options); + } + + FileManager.call(this, options); + + this.on('cd', function(dir) { + if (dir === self.cwd) return; + self.emit('file', dir); + }); +} + +DirManager.prototype.__proto__ = FileManager.prototype; + +DirManager.prototype.type = 'dir-manager'; + +/** + * Passbox - Useless + */ + +function Passbox(options) { + var self = this; + + if (!(this instanceof Passbox)) { + return new Passbox(options); + } + + options.censor = true; + + Textbox.call(this, options); +} + +Passbox.prototype.__proto__ = Textbox.prototype; + +Passbox.prototype.type = 'passbox'; + /** * Helpers */ @@ -3443,3 +4083,15 @@ exports.Textarea = exports.textarea = Textarea; exports.Button = exports.button = Button; exports.ProgressBar = exports.progressbar = ProgressBar; exports.FileManager = exports.filemanager = FileManager; + +exports.Checkbox = exports.checkbox = Checkbox; +exports.RadioButton = exports.radiobutton = RadioButton; +exports.Prompt = exports.prompt = Prompt; +exports.Question = exports.question = Question; +exports.Message = exports.message = Message; +exports.Info = exports.info = Info; +exports.Loading = exports.loading = Loading; +exports.PickList = exports.picklist = PickList; +exports.Listbar = exports.listbar = Listbar; +exports.DirManager = exports.dirmanager = DirManager; +exports.Passbox = exports.passbox = Passbox;