diff --git a/README.md b/README.md index 7604ac5..c881a78 100644 --- a/README.md +++ b/README.md @@ -2,113 +2,68 @@ A curses-like library for node.js. -Blessed was originally written to only support the xterm terminfo, but can -now parse and compile any terminfo to be completely portable accross all -terminals. See the `tput` example below. +## Example -Blessed also includes an extremely high-level widget library. - - -## Example Usage - -This will actually parse the xterm terminfo and compile every -string capability to a javascript function: - -``` js -var Tput = require('blessed').Tput - , tput = Tput('xterm'); - -console.log(tput.setaf(4) + 'hello' + tput.sgr0()); -``` - -To play around with it on the command line, it works just like tput: - -``` bash -$ tput.js setaf 2 -$ tput.js sgr0 -$ echo "$(tput.js setaf 2)hello world$(tput.js sgr0)" -``` - -The main functionality is exposed in the main `blessed` module: - -``` js -var blessed = require('blessed') - , program = blessed(); - -program.key('q', function(ch, key) { - program.clear(); - program.disableMouse(); - program.showCursor(); - program.normalBuffer(); - process.exit(0); -}); - -program.on('mouse', function(data) { - if (data.action === 'mousemove') { - program.move(data.x, data.y); - program.bg('red'); - program.write('x'); - program.bg('!red'); - } -}); - -program.alternateBuffer(); -program.enableMouse(); -program.hideCursor(); -program.clear(); - -program.move(1, 1); -program.bg('black'); -program.write('Hello world', 'blue fg'); -program.setx((program.cols / 2 | 0) - 4); -program.down(5); -program.write('Hi again!'); -program.bg('!black'); -program.feed(); -``` - - -## High-level Documentation - -### Example - -This will render a box with ascii borders containing the text 'Hello world!', +This will render a box with ascii borders containing the text `'Hello world!'`, perfectly centered horizontally and vertically. ``` js -var blessed = require('blessed') - , screen = new blessed.Screen; +var blessed = require('blessed'); -var box = new blessed.Box({ +// Create a screen object. +var screen = blessed.screen(); + +// Create a box perfectly centered horizontally and vertically. +var box = blessed.box({ top: 'center', left: 'center', width: '50%', height: '50%', border: { type: 'ascii', - fg: 'white' + fg: '#ffffff' }, fg: 'white', bg: 'magenta', - content: 'Hello world!', - tags: true + content: 'Hello {bold}world{/bold}!', + tags: true, + hoverEffects: { + bg: 'green' + } }); +// Append our box to the screen. screen.append(box); +// If our box is clicked, change the content. box.on('click', function(data) { box.setContent('{center}Some different {red-fg}content{/red-fg}.{/center}'); screen.render(); }); -screen.key('escape', function(ch, key) { +// If box is focused, handle `enter` and give us some more content. +box.key('enter', function() { + box.setContent('{right}Even different {black-fg}content{/black-fg}.{/right}\n'); + box.setLine(1, 'bar'); + box.insertLine(1, 'foo'); + screen.render(); +}); + +// Quit on Escape, q, or Control-C. +screen.key(['escape', 'q', 'C-c'], function(ch, key) { return process.exit(0); }); +// Focus our element. +box.focus(); + +// Render the screen. screen.render(); ``` +## High-level Documentation + ### Widgets Blessed comes with a number of high-level widgets so you can avoid all the @@ -770,6 +725,65 @@ Outputting: - For a less interactive position testing, see `test/widget-pos.js`. +## Lower-Level Usage + +This will actually parse the xterm terminfo and compile every +string capability to a javascript function: + +``` js +var Tput = require('blessed').Tput + , tput = Tput('xterm'); + +console.log(tput.setaf(4) + 'hello' + tput.sgr0()); +``` + +To play around with it on the command line, it works just like tput: + +``` bash +$ tput.js setaf 2 +$ tput.js sgr0 +$ echo "$(tput.js setaf 2)hello world$(tput.js sgr0)" +``` + +The main functionality is exposed in the main `blessed` module: + +``` js +var blessed = require('blessed') + , program = blessed(); + +program.key('q', function(ch, key) { + program.clear(); + program.disableMouse(); + program.showCursor(); + program.normalBuffer(); + process.exit(0); +}); + +program.on('mouse', function(data) { + if (data.action === 'mousemove') { + program.move(data.x, data.y); + program.bg('red'); + program.write('x'); + program.bg('!red'); + } +}); + +program.alternateBuffer(); +program.enableMouse(); +program.hideCursor(); +program.clear(); + +program.move(1, 1); +program.bg('black'); +program.write('Hello world', 'blue fg'); +program.setx((program.cols / 2 | 0) - 4); +program.down(5); +program.write('Hi again!'); +program.bg('!black'); +program.feed(); +``` + + ## License Copyright (c) 2013, Christopher Jeffrey. (MIT License) diff --git a/lib/widget.js b/lib/widget.js index 68a4243..0e1c9a6 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -1535,16 +1535,16 @@ Element.prototype.__defineGetter__('detached', function() { }); Element.prototype.key = function() { - return Screen.prototype.key.apply(this, arguments); + return this.screen.program.key.apply(this, arguments); }; Element.prototype.onceKey = function() { - return Screen.prototype.onceKey.apply(this, arguments); + return this.screen.program.onceKey.apply(this, arguments); }; Element.prototype.unkey = Element.prototype.removeKey = function() { - return Screen.prototype.unkey.apply(this, arguments); + return this.screen.program.unkey.apply(this, arguments); }; Element.prototype.clearPos = function() { @@ -2168,75 +2168,99 @@ outer: // Maybe _lastPos could be updated on .left, .right, etc setters? Box.prototype.insertLine = function(i, line) { + var pos = this._lastPos || 0; + if (typeof line === 'string') line = [line]; - if (this.screen.cleanSides(this)) { - i = Math.max(i, 0); - i = Math.min(i, this._lastPos.yl - this._lastPos.yi - (this.border ? 2 : 0) - this.padding * 2); + if (i > this._clines.length) { + for (var j = this._clines.length; j < i; j++) this._clines.push(''); + } + + i = Math.max(i, 0); + //i = Math.min(i, this._clines.length); + + var height = pos.yl - pos.yi - (this.border ? 2 : 0) - this.padding * 2 + , base = this.childBase || 0 + , visible = i >= base && i - base < height; + + if (pos && visible && this.screen.cleanSides(this)) { this.screen.insertLine(line.length, - this._lastPos.yi + (this.border ? 1 : 0) + this.padding + i, - this._lastPos.yi, - this._lastPos.yl - (this.border ? 1 : 0) - this.padding - 1); + pos.yi + (this.border ? 1 : 0) + this.padding + i - base, + pos.yi, + pos.yl - (this.border ? 1 : 0) - this.padding - 1); } line.forEach(function(line, j) { - this._clines.splice((this.childBase || 0) + (this.border ? 1 : 0) + this.padding + i + j, 0, line); + this._clines.splice(i + j, 0, line); }, this); this.setContent(this._clines.join('\n'), true); }; Box.prototype.deleteLine = function(i, n) { + var pos = this._lastPos || 0; + var reset = true , n = n || 1; - if (this.screen.cleanSides(this)) { - i = Math.max(i, 0); - i = Math.min(i, this._lastPos.yl - this._lastPos.yi - (this.border ? 2 : 0) - this.padding * 2); + i = Math.max(i, 0); + i = Math.min(i, this._clines.length - 1); + + var height = pos.yl - pos.yi - (this.border ? 2 : 0) - this.padding * 2 + , base = this.childBase || 0 + , visible = i >= base && i - base < height; + + if (pos && visible && this.screen.cleanSides(this)) { this.screen.deleteLine(n, - this._lastPos.yi + (this.border ? 1 : 0) + this.padding + i, - this._lastPos.yi, - this._lastPos.yl - (this.border ? 1 : 0) - this.padding - 1); + pos.yi + (this.border ? 1 : 0) + this.padding + i - base, + pos.yi, + pos.yl - (this.border ? 1 : 0) - this.padding - 1); reset = false; } while (n--) { - this._clines.splice((this.childBase || 0) + (this.border ? 1 : 0) + this.padding + i, 1); + this._clines.splice(i, 1); } this.setContent(this._clines.join('\n'), reset); }; Box.prototype.insertTop = function(line) { - return this.insertLine(0, line); + return this.insertLine((this.childBase || 0) + 0, line); }; Box.prototype.insertBottom = function(line) { - return this.insertLine(this.height - (this.border ? 2 : 0) - this.padding * 2, line); + return this.insertLine((this.childBase || 0) + + this.height - (this.border ? 2 : 0) - this.padding * 2, line); }; Box.prototype.deleteTop = function() { - return this.deleteLine(0); + return this.deleteLine((this.childBase || 0) + 0); }; Box.prototype.deleteBottom = function() { - return this.deleteLine(this.height - (this.border ? 2 : 0) - this.padding * 2); + return this.deleteLine((this.childBase || 0) + + this.height - (this.border ? 2 : 0) - this.padding * 2); }; Box.prototype.setLine = function(i, line) { + if (i > this._clines.length) { + for (var j = this._clines.length; j < i; j++) this._clines.push(''); + } i = Math.max(i, 0); - i = Math.min(i, this._lastPos.yl - this._lastPos.yi - (this.border ? 2 : 0) - this.padding * 2); - this._clines[(this.childBase || 0) + (this.border ? 1 : 0) + this.padding + i] = line; + //i = Math.min(i, this._clines.length); + this._clines[(this.childBase || 0) + i] = line; return this.setContent(this._clines.join('\n'), true); }; Box.prototype.getLine = function(i) { i = Math.max(i, 0); - i = Math.min(i, this._lastPos.yl - this._lastPos.yi - (this.border ? 2 : 0) - this.padding * 2); - return this._clines[(this.childBase || 0) + (this.border ? 1 : 0) + this.padding + i]; + i = Math.min(i, this._clines.length); + return this._clines[(this.childBase || 0) + i]; }; Box.prototype.clearLine = function(i) { + i = Math.min(i, this._clines.length - 1); return this.setLine(i, ''); }; @@ -4095,7 +4119,8 @@ function wrapContent(content, width, tags, state) { while (line[i] && line[i++] !== 'm'); } if (!line[i]) break; - if (++total === width) { + //if (++total === width) { + if (++total > width) { // Try to find a space to break on: if (line[i] !== ' ') { var j = i; @@ -4122,6 +4147,10 @@ function wrapContent(content, width, tags, state) { line = line.substring(i - 1); out.push(sp(part, width, align)); } + + // Make sure we didn't wrap the line to the very end, otherwise + // we get a pointless empty line after a newline. + if (line === '') return; } // If only an escape code got cut off, at it to `part`.