major changes. better content formatting and parsing.

This commit is contained in:
Christopher Jeffrey 2013-06-20 06:43:56 -05:00
parent 5b8a16df23
commit f431e98c03
3 changed files with 241 additions and 17 deletions

View File

@ -6,8 +6,7 @@ Blessed was originally written to only support the xterm terminfo, but can
now parse and compile any terminfo to be completely portable accross all now parse and compile any terminfo to be completely portable accross all
terminals. See the `tput` example below. terminals. See the `tput` example below.
I want this library to eventually become a high-level library for terminal Blessed also includes an extremely high-level widget library.
widgets.
## Example Usage ## Example Usage
@ -29,7 +28,7 @@ $ tput.js sgr0
$ echo "$(tput.js setaf 2)hello world$(tput.js sgr0)" $ echo "$(tput.js setaf 2)hello world$(tput.js sgr0)"
``` ```
The higher level functionality is exposed in the main `blessed` module: The main functionality is exposed in the main `blessed` module:
``` js ``` js
var blessed = require('blessed') var blessed = require('blessed')
@ -135,6 +134,10 @@ The base node which everything inherits from.
- **adopt** - received when node is added to a parent. - **adopt** - received when node is added to a parent.
- **remove** - received when node is removed from it's current parent. - **remove** - received when node is removed from it's current parent.
- **reparent** - received when node gains a new parent. - **reparent** - received when node gains a new parent.
- **attach** - received when node is attached to the screen directly or
somewhere in its ancestry.
- **detach** - received when node is detached from the screen directly or
somewhere in its ancestry.
##### Methods: ##### Methods:

View File

@ -122,6 +122,10 @@ Program.prototype.listen = function() {
} }
return; return;
} }
if (key.name === 'undefined' && key.code === '[M') {
// A mouse sequence. The readline module doesn't understand these.
return;
}
self.emit('keypress', ch, key); self.emit('keypress', ch, key);
}); });
@ -1816,6 +1820,7 @@ Program.prototype.showCursor = function() {
Program.prototype.alternate = Program.prototype.alternate =
Program.prototype.alternateBuffer = function() { Program.prototype.alternateBuffer = function() {
if (this.term('vt') || this.term('linux')) return;
//return this.setMode('?47'); //return this.setMode('?47');
//return this.setMode('?1047'); //return this.setMode('?1047');
return this.setMode('?1049'); return this.setMode('?1049');
@ -1966,6 +1971,9 @@ Program.prototype.setMouse = function(opt) {
opt.allMotion = opt.normalMouse; opt.allMotion = opt.normalMouse;
} }
// Make sure we're not a vtNNN
if (this.term('vt')) return;
// Ps = 9 -> Send Mouse X & Y on button press. See the sec- // Ps = 9 -> Send Mouse X & Y on button press. See the sec-
// tion Mouse Tracking. // tion Mouse Tracking.
// Ps = 9 -> Don't send Mouse X & Y on button press. // Ps = 9 -> Don't send Mouse X & Y on button press.
@ -1985,6 +1993,10 @@ Program.prototype.setMouse = function(opt) {
else this.resetMode('?1000'); else this.resetMode('?1000');
} }
// Linux Console actually *does* support mouse reporting.
// See: `$ man console_codes`.
if (this.term('linux')) return;
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. // Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
if (opt.hiliteTracking != null) { if (opt.hiliteTracking != null) {

View File

@ -33,6 +33,10 @@ function Node(options) {
this.parent.append(this); this.parent.append(this);
} }
if (!this.parent) {
this._detached = true;
}
(options.children || []).forEach(this.append.bind(this)); (options.children || []).forEach(this.append.bind(this));
if (this._isScreen && !this.focused) { if (this._isScreen && !this.focused) {
@ -43,6 +47,8 @@ function Node(options) {
Node.prototype.__proto__ = EventEmitter.prototype; Node.prototype.__proto__ = EventEmitter.prototype;
Node.prototype.prepend = function(element) { Node.prototype.prepend = function(element) {
var old = element.parent;
element.parent = this; element.parent = this;
if (this._isScreen && !this.focused) { if (this._isScreen && !this.focused) {
@ -55,9 +61,18 @@ Node.prototype.prepend = function(element) {
element.emit('reparent', this); element.emit('reparent', this);
this.emit('adopt', element); this.emit('adopt', element);
//if (!old) {
(function emit(el) {
el._detached = false;
el.emit('attach');
if (el.children) el.children.forEach(emit);
})(element);
}; };
Node.prototype.append = function(element) { Node.prototype.append = function(element) {
var old = element.parent;
element.parent = this; element.parent = this;
if (this._isScreen && !this.focused) { if (this._isScreen && !this.focused) {
@ -70,6 +85,13 @@ Node.prototype.append = function(element) {
element.emit('reparent', this); element.emit('reparent', this);
this.emit('adopt', element); this.emit('adopt', element);
//if (!old) {
(function emit(el) {
el._detached = false;
el.emit('attach');
if (el.children) el.children.forEach(emit);
})(element);
}; };
Node.prototype.remove = function(element) { Node.prototype.remove = function(element) {
@ -93,6 +115,12 @@ Node.prototype.remove = function(element) {
element.emit('reparent', null); element.emit('reparent', null);
this.emit('remove', element); this.emit('remove', element);
(function emit(el) {
el._detached = true;
el.emit('detach');
if (el.children) el.children.forEach(emit);
})(element);
}; };
Node.prototype.detach = function(element) { Node.prototype.detach = function(element) {
@ -736,6 +764,28 @@ function Element(options) {
self.screen._listenKeys(self); self.screen._listenKeys(self);
} }
}); });
this.screen.on('resize', function() {
self.parseContent();
});
this.on('resize', function() {
self.parseContent();
});
this.on('attach', function() {
self.parseContent();
});
this.parseContent();
//if (this.parent) {
// this.parseContent();
//} else {
// this.once('reparent', function() {
// self.parseContent();
// });
//}
} }
Element.prototype.__proto__ = Node.prototype; Element.prototype.__proto__ = Node.prototype;
@ -757,6 +807,20 @@ Element.prototype.emit = function(type) {
}; };
*/ */
Element.prototype.parseContent = function() {
if (!this.content) return;
if (this.detached) return;
var w = this.width - (this.border ? 2 : 0);
if (this._clines == null
|| this._clines.width !== w
|| this._clines.content !== this.content) {
this._clines = wrapContent(this.content, w, this.align);
this._clines.width = w;
this._clines.content = this.content;
this._pcontent = this._clines.join('\n');
}
};
Element.prototype.hide = function() { Element.prototype.hide = function() {
if (this.hidden) return; if (this.hidden) return;
this.hidden = true; this.hidden = true;
@ -800,6 +864,7 @@ Element.prototype.setContent = function(content) {
// TODO: Maybe simply set _pcontent with _parseTags result. // TODO: Maybe simply set _pcontent with _parseTags result.
// text = text.replace(/\x1b(?!\[[\d;]*m)/g, ''); // text = text.replace(/\x1b(?!\[[\d;]*m)/g, '');
this.content = this._parseTags(content || ''); this.content = this._parseTags(content || '');
this.parseContent();
if (ret) { if (ret) {
//if (ret && !this.hidden) { //if (ret && !this.hidden) {
this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl); this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl);
@ -833,6 +898,15 @@ Element.prototype.__defineGetter__('visible', function() {
return true; return true;
}); });
Element.prototype.__defineGetter__('detached', function() {
var el = this;
do {
if (el._isScreen) return false;
if (!el.parent) return true;
} while (el = el.parent);
return false;
});
/** /**
* Positioning * Positioning
*/ */
@ -1120,6 +1194,10 @@ Element.prototype.__defineSetter__('rbottom', function(val) {
return this.options.bottom = this.position.bottom = val; return this.options.bottom = this.position.bottom = val;
}); });
Element.prototype.calcShrink = function(xi_, xl, yi_, yl) {
return [xi_, xl, yi_, yl];
};
/** /**
* Box * Box
*/ */
@ -1135,6 +1213,17 @@ Box.prototype.__proto__ = Element.prototype;
// TODO: Optimize. Move elsewhere. // TODO: Optimize. Move elsewhere.
Box.prototype._getShrinkSize = function(content) { Box.prototype._getShrinkSize = function(content) {
if (this._clines) {
return {
height: this._clines.length,
width: this._clines.reduce(function(current, line) {
line = line.replace(/\x1b\[[\d;]*m/g, '');
return line.length > current
? line.length //+ (lines.length > 1 ? 1 : 0) // for newlines
: current;
}, 0)
};
}
var lines = content.replace(/\x1b\[[\d;]*m/g, '').split('\n'); var lines = content.replace(/\x1b\[[\d;]*m/g, '').split('\n');
return { return {
height: lines.length, height: lines.length,
@ -1195,6 +1284,13 @@ Box.prototype.render = function(stop) {
// NOTE: Could simply change some offsets around and move this below the // NOTE: Could simply change some offsets around and move this below the
// shrink block. Could also stop passing a string to _getShrinkSize. // shrink block. Could also stop passing a string to _getShrinkSize.
/*
if (this.align === 'center' || this.align === 'right') {
content = '{' + this.align + '}' + content + '{/' + this.align + '}';
} else
*/
/*
if (this.align === 'center' || this.align === 'right') { if (this.align === 'center' || this.align === 'right') {
var ncontent = content.replace(/\x1b\[[\d;]*m/g, '') var ncontent = content.replace(/\x1b\[[\d;]*m/g, '')
, width = this.width - (this.border ? 2 : 0) - this.padding * 2; , width = this.width - (this.border ? 2 : 0) - this.padding * 2;
@ -1216,7 +1312,58 @@ Box.prototype.render = function(stop) {
} }
} }
} }
*/
//if (!this._pcontent && this.content) {
// this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
// this._pcontent = this._clines.join('\n');
// content = this._pcontent;
// //content = wrapContent(this.content, this.width - (this.border ? 2 : 0)).join('\n');
//}
/*
if (this.tags || this.align) {
var self = this;
content = content.replace(/(^|\n){(center|right)}([^\0]+?){\/\2}(?=\n|$)/g, function(_, start, align, text) {
var lines = text.trim().split('\n')
, line
, i = 0
, width = self.width - (self.border ? 2 : 0) - self.padding * 2
, out = ''
, len
, s;
for (; i < lines.length; i++) {
line = lines[i];
len = line.replace(/\x1b\[[\d;]*m/g, '').length;
s = width - len;
if (s < 0) {
out += '\n' + line;
continue;
}
if (align === 'center') {
s = Array(((s / 2 | 0) - (self.border ? 1 : 0)) + 1).join(' ');
out += '\n' + s + line + (self.shrink ? s : '');
} else {
s -= self.left; // this shouldn't be necessary
s = Array((s - (self.border ? 2 : 0)) + 1).join(' ');
out += '\n' + s + line;
}
}
if (out[0] === '\n' && !start) {
out = out.substring(1);
}
return out;
});
}
*/
// TODO: Check for 'center', recalculate yi, and xi. Better yet, simply
// move this check into this.left/width/etc.
if (this.shrink) { if (this.shrink) {
var hw = this._getShrinkSize(content) var hw = this._getShrinkSize(content)
, h = hw.height , h = hw.height
@ -1235,6 +1382,10 @@ Box.prototype.render = function(stop) {
} }
} }
//if (this.options.vshrink && this._clines) {
// yl = yi_ + this._clines.length + (this.border ? 2 : 0) + this.padding;
//}
var ret = this._lastPos = { var ret = this._lastPos = {
xi: xi_, xi: xi_,
xl: xl, xl: xl,
@ -1785,6 +1936,7 @@ function ScrollableText(options) {
}); });
} }
/*
this.screen.on('resize', function() { this.screen.on('resize', function() {
self._recalculateIndex(); self._recalculateIndex();
}); });
@ -1806,6 +1958,7 @@ function ScrollableText(options) {
self._recalculateIndex(); self._recalculateIndex();
}); });
} }
*/
} }
ScrollableText.prototype.__proto__ = ScrollableBox.prototype; ScrollableText.prototype.__proto__ = ScrollableBox.prototype;
@ -1827,11 +1980,13 @@ ScrollableText.prototype.scroll = function(offset) {
// feeds. This allows us to take preformatted text output from other programs // feeds. This allows us to take preformatted text output from other programs
// and put it in a scrollable text box. // and put it in a scrollable text box.
if (this.content != null) { if (this.content != null) {
w = this.width - (this.border ? 2 : 0); this._parseContent();
if (this._clines == null || this._clines.width !== w) { //w = this.width - (this.border ? 2 : 0);
this._clines = wrapContent(this.content, w); //if (this._clines == null || this._clines.width !== w) {
this._pcontent = this._clines.join('\n'); // this._clines = wrapContent(this.content, w);
} // this._clines.content = this.content;
// this._pcontent = this._clines.join('\n');
//}
max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0)); max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0));
if (max < 0) max = 0; if (max < 0) max = 0;
@ -1854,14 +2009,14 @@ ScrollableText.prototype.scroll = function(offset) {
ScrollableText.prototype._setContent = ScrollableText.prototype.setContent; ScrollableText.prototype._setContent = ScrollableText.prototype.setContent;
ScrollableText.prototype.setContent = function(content) { ScrollableText.prototype.setContent = function(content) {
var ret = this._setContent(content); var ret = this._setContent(content);
this._recalculateIndex(); //this._recalculateIndex();
return ret; return ret;
}; };
ScrollableText.prototype._recalculateIndex = function() { ScrollableText.prototype._recalculateIndex = function() {
if (!this.parent) return; if (this.detached) return;
this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0)); //this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
this._pcontent = this._clines.join('\n'); //this._pcontent = this._clines.join('\n');
var max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0)); var max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0));
if (max < 0) max = 0; if (max < 0) max = 0;
@ -1876,6 +2031,14 @@ ScrollableText.prototype._recalculateIndex = function() {
this.contentIndex = t; this.contentIndex = t;
}; };
ScrollableText.prototype._parseContent = ScrollableText.prototype.parseContent;
ScrollableText.prototype.parseContent = function(content) {
var ret = this._parseContent();
this._recalculateIndex();
return ret;
};
/** /**
* Input * Input
*/ */
@ -2264,11 +2427,44 @@ function readEditor(callback) {
}); });
} }
function wrapContent(content, width) { function sp(line, width, align) {
if (!align) return line;
var len = line.replace(/\x1b\[[\d;]*m/g, '').length
, s = width - len;
if (len === 0) return line;
if (s < 0) return line;
if (align === 'center') {
s = Array(((s / 2) | 0) + 1).join(' ');
return s + line + s;
} else if (align === 'right') {
s = Array(s + 1).join(' ');
return s + line;
}
return line;
}
function wrapContent(content, width, state) {
var lines = content.split('\n') var lines = content.split('\n')
, out = []; , out = [];
lines.forEach(function(line) { lines.forEach(function(line) {
var align = state
, cap;
if (cap = /^{(center|right)}/.exec(line)) {
line = line.substring(cap[0].length);
align = state = cap[1];
}
if (cap = /{\/(center|right)}$/.exec(line)) {
line = line.slice(0, -cap[0].length);
state = null;
}
var total var total
, i , i
, part , part
@ -2283,7 +2479,17 @@ function wrapContent(content, width) {
while (line[i] && line[i++] !== 'm'); while (line[i] && line[i++] !== 'm');
} }
if (!line[i]) break; if (!line[i]) break;
if (++total === width) break; if (++total === width) {
// Try to find a space to break on:
if (line[i] !== ' ') {
var j = i;
while (j > i - 10 && j > 0 && line[j] !== ' ') j--;
if (line[j] === ' ') i = j + 1;
} else {
i++;
}
break;
}
} }
part = line.substring(0, i - 1); part = line.substring(0, i - 1);
@ -2292,10 +2498,12 @@ function wrapContent(content, width) {
if (esc) { if (esc) {
part = part.slice(0, -esc[0].length); part = part.slice(0, -esc[0].length);
line = line.substring(i - 1 - esc[0].length); line = line.substring(i - 1 - esc[0].length);
out.push(part); //out.push(part);
out.push(sp(part, width, align));
} else { } else {
line = line.substring(i - 1); line = line.substring(i - 1);
out.push(part); //out.push(part);
out.push(sp(part, width, align));
} }
} }
@ -2304,7 +2512,8 @@ function wrapContent(content, width) {
out[out.length-1] += line; out[out.length-1] += line;
return; return;
} }
out.push(line); //out.push(line);
out.push(sp(line, width, align));
}); });
out.width = width; out.width = width;