more events. setItems. predefined keys. docs.

This commit is contained in:
Christopher Jeffrey 2013-06-18 10:52:48 -05:00
parent ef9178f7d0
commit d4ce9322cd
3 changed files with 169 additions and 17 deletions

View File

@ -202,6 +202,8 @@ The screen on which every other node renders.
- **right**, **rright** - right offset, always zero.
- **top**, **rtop** - top offset, always zero.
- **bottom**, **rbottom** - bottom offset, always zero.
- **grabKeys** - whether the focused element grabs all keypresses.
- **lockKeys** - prevent keypresses from being received by any element.
##### Events:
@ -364,6 +366,8 @@ A scrollable list which can display selectable items.
treated like bold and underline.
- **mouse** - whether to automatically enable mouse support for this list (allows
clicking items).
- **keys** - use predefined keys for navigating the list.
- **vi** - use vi keys with the `keys` option.
- **items** - an array of strings which become the list's items.
##### Properties:
@ -374,6 +378,8 @@ A scrollable list which can display selectable items.
- inherits all from ScrollableBox.
- **select** - received when an item is selected.
- **cancel** - list was canceled (when `esc` is pressed with the `keys` option).
- **action** - either a select or a cancel event was received.
##### Methods:
@ -393,6 +399,8 @@ A scrollable text box which can display and scroll text, as well as handle pre-e
- inherits all from ScrollableBox.
- **mouse** - whether to enable automatic mouse support for this element.
- **keys** - use predefined keys for navigating the text.
- **vi** - use vi keys with the `keys` option.
##### Properties:

View File

@ -29,6 +29,10 @@ function Node(options) {
this.children = [];
this.$ = this._ = this.data = {};
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
if (this._isScreen && !this.focused) {
@ -76,6 +80,13 @@ Node.prototype.remove = function(element) {
this.children.splice(i, 1);
}
if (!this._isScreen) {
i = this.screen.clickable.indexOf(element);
if (~i) this.screen.clickable.splice(i, 1);
i = this.screen.input.indexOf(element);
if (~i) this.screen.input.splice(i, 1);
}
if (this._isScreen && this.focused === element) {
this.focused = this.children[0];
}
@ -131,6 +142,7 @@ function Screen(options) {
this.clickable = [];
this.input = [];
this.grabKeys = false;
this.lockKeys = false;
this.alloc();
@ -195,6 +207,8 @@ Screen.prototype._listenMouse = function(el) {
//});
this.program.on('mouse', function(data) {
if (self.lockKeys) return;
var i = 0
, left
, top
@ -279,6 +293,7 @@ Screen.prototype._listenKeys = function(el) {
this._listenedKeys = true;
this.program.on('keypress', function(ch, key) {
if (self.lockKeys) return;
if (~self.input.indexOf(self.focused)) {
self.focused.emit('keypress', ch, key);
}
@ -751,6 +766,11 @@ Element.prototype.hide = function() {
this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl);
}
this.emit('hide');
//if (this.screen.focused === this) {
// this.screen.focusPop();
// var el = this.screen.focusPop();
// if (el) el.focus();
//}
};
Element.prototype.show = function() {
@ -765,6 +785,7 @@ Element.prototype.toggle = function() {
};
Element.prototype.focus = function() {
//if (this.screen.grabKeys || this.screen.lockKeys) return;
var old = this.screen.focused;
this.screen.focused = this;
old.emit('blur', this);
@ -774,11 +795,15 @@ Element.prototype.focus = function() {
};
Element.prototype.setContent = function(content) {
var ret = this.render(true);
//var ret = this.render(true);
var ret = this._lastPos;
// TODO: Maybe simply set _pcontent with _parseTags result.
// text = text.replace(/\x1b(?!\[[\d;]*m)/g, '');
this.content = this._parseTags(content || '');
this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl);
if (ret) {
//if (ret && !this.hidden) {
this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl);
}
};
// Convert `{red-fg}foo{/red-fg}` to `\x1b[31mfoo\x1b[39m`.
@ -1446,6 +1471,8 @@ ScrollableBox.prototype.__proto__ = Box.prototype;
ScrollableBox.prototype.scroll = function(offset) {
var visible = this.height - (this.border ? 2 : 0);
// Maybe do for lists:
//if (this.items) visible = Math.min(this.items.length, visible);
if (this.alwaysScroll) {
// Semi-workaround
this.childOffset = offset > 0
@ -1505,22 +1532,81 @@ function List(options) {
}
if (this.mouse) {
self.on('wheeldown', function(data) {
this.on('wheeldown', function(data) {
self.select(self.selected + 2);
self.screen.render();
});
self.on('wheelup', function(data) {
this.on('wheelup', function(data) {
self.select(self.selected - 2);
self.screen.render();
});
}
// self.on('keypress', function(ch, key) {
// if (key.name === 'enter') {
// self.emit('select', self.items[self.selected], self.selected);
// }
// });
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
return;
}
if (key.name === 'enter' || (options.vi && key.name === 'j')) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - (self.border ? 2 : 0)) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - (self.border ? 2 : 0)) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - (self.border ? 2 : 0)));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - (self.border ? 2 : 0));
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length, ... for calculating visible items elsewhere.
self.move(self.childBase
+ (Math.min(self.height - (self.border ? 2 : 0), this.items.length) / 2 | 0)
- self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'l' && key.shift) {
self.down(self.childBase
+ Math.min(self.height - (self.border ? 2 : 0), this.items.length)
- self.selected);
self.screen.render();
return;
}
});
}
function resize() {
var visible = self.height - (self.border ? 2 : 0);
@ -1546,7 +1632,6 @@ List.prototype.add = function(item) {
var item = new Box({
screen: this.screen,
parent: this,
fg: this.fg,
bg: this.bg,
content: item.content || item,
@ -1579,6 +1664,25 @@ List.prototype.remove = function(child) {
this._remove(child);
};
List.prototype.setItems = function(items) {
var i = 0
, original = this.items.slice();
this.select(0);
for (; i < items.length; i++) {
if (this.items[i]) {
this.items[i].setContent(items[i]);
} else {
this.add(items[i]);
}
}
for (; i < original.length; i++) {
this.remove(original[i]);
}
};
List.prototype.select = function(index) {
if (!this.items.length) return;
@ -1594,7 +1698,9 @@ List.prototype.select = function(index) {
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
this.items[this.selected][name] = this[name];
if (this.items[this.selected]) {
this.items[this.selected][name] = this[name];
}
this.items[index][name] = this['selected'
+ name.substring(0, 1).toUpperCase()
+ name.substring(1)];
@ -1644,6 +1750,41 @@ function ScrollableText(options) {
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.scroll(-self.height || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.scroll(self.height || 1);
self.screen.render();
return;
}
});
}
this.screen.on('resize', function() {
self._recalculateIndex();
});
@ -1770,13 +1911,15 @@ Textbox.prototype.setInput = function(callback) {
delete this._timeout;
}
this.focus();
this.screen.grabKeys = true;
this.focus();
// this.screen.program.saveCursor();
this.screen.program.cup(
this.top + 1 + (this.border ? 1 : 0),
this.left + 1 + (this.border ? 1 : 0));
this.left + 1 + (this.border ? 1 : 0)
+ this.content.length);
this.screen.program.showCursor();
this.screen.program.sgr('normal');
@ -1787,6 +1930,11 @@ Textbox.prototype.setInput = function(callback) {
self._timeout = setTimeout(function() {
self.screen.grabKeys = false;
}, 1);
//self.screen.focusPop();
//var el = self.screen.focusPop();
//if (el) el.focus();
return err
? callback(err)
: callback(null, value);

View File

@ -20,8 +20,6 @@ screen.append(new blessed.Line({
/*
screen.append(new blessed.Box({
screen: screen,
parent: screen,
fg: 4,
bg: -1,
border: {
@ -37,8 +35,6 @@ screen.append(new blessed.Box({
}));
screen.children[0].append(new blessed.Box({
screen: screen,
parent: screen.children[0],
fg: 4,
bg: 3,
border: {