major changes. better content formatting and parsing.
This commit is contained in:
parent
5b8a16df23
commit
f431e98c03
|
@ -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
|
||||
terminals. See the `tput` example below.
|
||||
|
||||
I want this library to eventually become a high-level library for terminal
|
||||
widgets.
|
||||
Blessed also includes an extremely high-level widget library.
|
||||
|
||||
## Example Usage
|
||||
|
||||
|
@ -29,7 +28,7 @@ $ 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
|
||||
var blessed = require('blessed')
|
||||
|
@ -135,6 +134,10 @@ The base node which everything inherits from.
|
|||
- **adopt** - received when node is added to a parent.
|
||||
- **remove** - received when node is removed from it's current 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:
|
||||
|
||||
|
|
|
@ -122,6 +122,10 @@ Program.prototype.listen = function() {
|
|||
}
|
||||
return;
|
||||
}
|
||||
if (key.name === 'undefined' && key.code === '[M') {
|
||||
// A mouse sequence. The readline module doesn't understand these.
|
||||
return;
|
||||
}
|
||||
self.emit('keypress', ch, key);
|
||||
});
|
||||
|
||||
|
@ -1816,6 +1820,7 @@ Program.prototype.showCursor = function() {
|
|||
|
||||
Program.prototype.alternate =
|
||||
Program.prototype.alternateBuffer = function() {
|
||||
if (this.term('vt') || this.term('linux')) return;
|
||||
//return this.setMode('?47');
|
||||
//return this.setMode('?1047');
|
||||
return this.setMode('?1049');
|
||||
|
@ -1966,6 +1971,9 @@ Program.prototype.setMouse = function(opt) {
|
|||
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-
|
||||
// tion Mouse Tracking.
|
||||
// Ps = 9 -> Don't send Mouse X & Y on button press.
|
||||
|
@ -1985,6 +1993,10 @@ Program.prototype.setMouse = function(opt) {
|
|||
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 -> Don't use Hilite Mouse Tracking.
|
||||
if (opt.hiliteTracking != null) {
|
||||
|
|
237
lib/widget.js
237
lib/widget.js
|
@ -33,6 +33,10 @@ function Node(options) {
|
|||
this.parent.append(this);
|
||||
}
|
||||
|
||||
if (!this.parent) {
|
||||
this._detached = true;
|
||||
}
|
||||
|
||||
(options.children || []).forEach(this.append.bind(this));
|
||||
|
||||
if (this._isScreen && !this.focused) {
|
||||
|
@ -43,6 +47,8 @@ function Node(options) {
|
|||
Node.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
Node.prototype.prepend = function(element) {
|
||||
var old = element.parent;
|
||||
|
||||
element.parent = this;
|
||||
|
||||
if (this._isScreen && !this.focused) {
|
||||
|
@ -55,9 +61,18 @@ Node.prototype.prepend = function(element) {
|
|||
|
||||
element.emit('reparent', this);
|
||||
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) {
|
||||
var old = element.parent;
|
||||
|
||||
element.parent = this;
|
||||
|
||||
if (this._isScreen && !this.focused) {
|
||||
|
@ -70,6 +85,13 @@ Node.prototype.append = function(element) {
|
|||
|
||||
element.emit('reparent', this);
|
||||
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) {
|
||||
|
@ -93,6 +115,12 @@ Node.prototype.remove = function(element) {
|
|||
|
||||
element.emit('reparent', null);
|
||||
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) {
|
||||
|
@ -736,6 +764,28 @@ function Element(options) {
|
|||
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;
|
||||
|
@ -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() {
|
||||
if (this.hidden) return;
|
||||
this.hidden = true;
|
||||
|
@ -800,6 +864,7 @@ Element.prototype.setContent = function(content) {
|
|||
// TODO: Maybe simply set _pcontent with _parseTags result.
|
||||
// text = text.replace(/\x1b(?!\[[\d;]*m)/g, '');
|
||||
this.content = this._parseTags(content || '');
|
||||
this.parseContent();
|
||||
if (ret) {
|
||||
//if (ret && !this.hidden) {
|
||||
this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl);
|
||||
|
@ -833,6 +898,15 @@ Element.prototype.__defineGetter__('visible', function() {
|
|||
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
|
||||
*/
|
||||
|
@ -1120,6 +1194,10 @@ Element.prototype.__defineSetter__('rbottom', function(val) {
|
|||
return this.options.bottom = this.position.bottom = val;
|
||||
});
|
||||
|
||||
Element.prototype.calcShrink = function(xi_, xl, yi_, yl) {
|
||||
return [xi_, xl, yi_, yl];
|
||||
};
|
||||
|
||||
/**
|
||||
* Box
|
||||
*/
|
||||
|
@ -1135,6 +1213,17 @@ Box.prototype.__proto__ = Element.prototype;
|
|||
|
||||
// TODO: Optimize. Move elsewhere.
|
||||
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');
|
||||
return {
|
||||
height: lines.length,
|
||||
|
@ -1195,6 +1284,13 @@ Box.prototype.render = function(stop) {
|
|||
|
||||
// NOTE: Could simply change some offsets around and move this below the
|
||||
// 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') {
|
||||
var ncontent = content.replace(/\x1b\[[\d;]*m/g, '')
|
||||
, 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) {
|
||||
var hw = this._getShrinkSize(content)
|
||||
, 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 = {
|
||||
xi: xi_,
|
||||
xl: xl,
|
||||
|
@ -1785,6 +1936,7 @@ function ScrollableText(options) {
|
|||
});
|
||||
}
|
||||
|
||||
/*
|
||||
this.screen.on('resize', function() {
|
||||
self._recalculateIndex();
|
||||
});
|
||||
|
@ -1806,6 +1958,7 @@ function ScrollableText(options) {
|
|||
self._recalculateIndex();
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
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
|
||||
// and put it in a scrollable text box.
|
||||
if (this.content != null) {
|
||||
w = this.width - (this.border ? 2 : 0);
|
||||
if (this._clines == null || this._clines.width !== w) {
|
||||
this._clines = wrapContent(this.content, w);
|
||||
this._pcontent = this._clines.join('\n');
|
||||
}
|
||||
this._parseContent();
|
||||
//w = this.width - (this.border ? 2 : 0);
|
||||
//if (this._clines == null || this._clines.width !== w) {
|
||||
// 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));
|
||||
if (max < 0) max = 0;
|
||||
|
@ -1854,14 +2009,14 @@ ScrollableText.prototype.scroll = function(offset) {
|
|||
ScrollableText.prototype._setContent = ScrollableText.prototype.setContent;
|
||||
ScrollableText.prototype.setContent = function(content) {
|
||||
var ret = this._setContent(content);
|
||||
this._recalculateIndex();
|
||||
//this._recalculateIndex();
|
||||
return ret;
|
||||
};
|
||||
|
||||
ScrollableText.prototype._recalculateIndex = function() {
|
||||
if (!this.parent) return;
|
||||
this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
|
||||
this._pcontent = this._clines.join('\n');
|
||||
if (this.detached) return;
|
||||
//this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
|
||||
//this._pcontent = this._clines.join('\n');
|
||||
|
||||
var max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0));
|
||||
if (max < 0) max = 0;
|
||||
|
@ -1876,6 +2031,14 @@ ScrollableText.prototype._recalculateIndex = function() {
|
|||
this.contentIndex = t;
|
||||
};
|
||||
|
||||
ScrollableText.prototype._parseContent = ScrollableText.prototype.parseContent;
|
||||
ScrollableText.prototype.parseContent = function(content) {
|
||||
var ret = this._parseContent();
|
||||
this._recalculateIndex();
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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')
|
||||
, out = [];
|
||||
|
||||
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
|
||||
, i
|
||||
, part
|
||||
|
@ -2283,7 +2479,17 @@ function wrapContent(content, width) {
|
|||
while (line[i] && line[i++] !== 'm');
|
||||
}
|
||||
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);
|
||||
|
@ -2292,10 +2498,12 @@ function wrapContent(content, width) {
|
|||
if (esc) {
|
||||
part = part.slice(0, -esc[0].length);
|
||||
line = line.substring(i - 1 - esc[0].length);
|
||||
out.push(part);
|
||||
//out.push(part);
|
||||
out.push(sp(part, width, align));
|
||||
} else {
|
||||
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;
|
||||
return;
|
||||
}
|
||||
out.push(line);
|
||||
//out.push(line);
|
||||
out.push(sp(line, width, align));
|
||||
});
|
||||
|
||||
out.width = width;
|
||||
|
|
Loading…
Reference in New Issue