add multiline textareas. remove old hoverBg code.

This commit is contained in:
Christopher Jeffrey 2013-07-04 00:10:01 -05:00
parent 6355b1259f
commit 06400d089f
3 changed files with 267 additions and 165 deletions

View File

@ -221,6 +221,7 @@ The screen on which every other node renders.
blessed app after exit.
- **exec(file, args, options, callback)** - spawn a process in the foreground,
return to blessed app after exit. executes callback on error or exit.
- **readEditor([options], callback)** - read data from text editor.
#### Element (from Node)
@ -447,6 +448,31 @@ A box which allows text input.
the resulting file. takes a callback which receives the final value.
#### Textarea (from Input/ScrollableText)
A box which allows multiline text input.
##### Options:
- inherits all from Input/ScrollableText.
##### Properties:
- inherits all from Input/ScrollableText.
##### Events:
- inherits all from Input/ScrollableText.
##### Methods:
- inherits all from Input/ScrollableText.
- **readInput(callback)** - grab key events and start reading text from the
keyboard. takes a callback which receives the final value.
- **readEditor(callback)** - open text editor in `$EDITOR`, read the output from
the resulting file. takes a callback which receives the final value.
#### Button (from Input)
A button which can be focused and allows key and mouse input.

View File

@ -880,9 +880,21 @@ Screen.prototype.exec = function(file, args, options, callback) {
return ps;
};
Screen.prototype.readEditor = function(callback) {
var fs = require('fs')
, editor = process.env.EDITOR || 'vi'
Screen.prototype.readEditor = function(options, callback) {
if (typeof options === 'string') {
options = { editor: options };
}
if (!callback) {
callback = options;
options = null;
}
options = options || {};
var self = this
, fs = require('fs')
, editor = options.editor || process.env.EDITOR || 'vi'
, file = '/tmp/blessed.' + Math.random().toString(36)
, args = [file]
, opt;
@ -893,12 +905,19 @@ Screen.prototype.readEditor = function(callback) {
cwd: process.env.HOME
};
return this.exec(editor, args, opt, function(err, success) {
if (err) return callback(err);
return fs.readFile(file, 'utf8', function(err, data) {
return fs.unlink(file, function() {
if (err) return callback(err);
return callback(null, data);
function writeFile(callback) {
if (!options.value) return callback();
return fs.writeFile(file, options.value, callback);
}
return writeFile(function() {
return self.exec(editor, args, opt, function(err, success) {
if (err) return callback(err);
return fs.readFile(file, 'utf8', function(err, data) {
return fs.unlink(file, function() {
if (err) return callback(err);
return callback(null, data);
});
});
});
});
@ -992,9 +1011,9 @@ function Element(options) {
}
});
//this.onScreenEvent('resize', function() {
// self.parseContent();
//});
// this.onScreenEvent('resize', function() {
// self.parseContent();
// });
this.on('resize', function() {
self.parseContent();
@ -1004,27 +1023,32 @@ function Element(options) {
self.parseContent();
});
// if (this.options.hoverBg != null) {
// var hoverBg = convert(this.options.hoverBg);
//
// this.on('mouseover', function() {
// // XXX Possibly a better alternative for the below workaround.
// self._bg = self.bg;
// //if (self._bg == null) self._bg = self.bg;
// self.bg = hoverBg;
// self.screen.render();
// });
//
// this.on('mouseout', function() {
// // XXX Workaround
// // if (self.parent.type === 'list'
// // && self === self.parent.items[self.parent.selected]
// // && self.bg === self.parent.selectedBg) {
// // return;
// // }
// if (self._bg != null) self.bg = self._bg;
// self.screen.render();
// });
// }
if (this.options.hoverBg != null) {
var hoverBg = convert(this.options.hoverBg);
this.on('mouseover', function() {
// XXX Possibly a better alternative for the below workaround.
self._bg = self.bg;
//if (self._bg == null) self._bg = self.bg;
self.bg = hoverBg;
self.screen.render();
});
this.on('mouseout', function() {
// XXX Workaround
// if (self.parent.type === 'list'
// && self === self.parent.items[self.parent.selected]
// && self.bg === self.parent.selectedBg) {
// return;
// }
if (self._bg != null) self.bg = self._bg;
self.screen.render();
});
this.options.hoverEffects = this.options.hoverEffects || {};
this.options.hoverEffects.bg = this.options.hoverBg;
}
if (this.options.hoverEffects) {
@ -1036,13 +1060,13 @@ function Element(options) {
}
});
this.__h = {};
this._htemp = {};
this.on('mouseover', function() {
Object.keys(effects).forEach(function(key) {
var val = effects[key];
if (self.__h[key] == null) {
self.__h[key] = self[key];
if (self._htemp[key] == null) {
self._htemp[key] = self[key];
}
self[key] = val;
});
@ -1051,8 +1075,8 @@ function Element(options) {
this.on('mouseout', function() {
Object.keys(effects).forEach(function(key) {
if (self.__h[key] != null) {
self[key] = self.__h[key];
if (self._htemp[key] != null) {
self[key] = self._htemp[key];
}
});
self.screen.render();
@ -2534,86 +2558,7 @@ Textbox.prototype.setEditor = function(callback) {
this.focus();
self.screen.program.normalBuffer();
self.screen.program.showCursor();
var _listenedMouse = self.screen._listenedMouse;
if (self.screen._listenedMouse) {
self.screen.program.disableMouse();
}
return readEditor(function(err, value) {
self.screen.program.alternateBuffer();
self.screen.program.hideCursor();
if (_listenedMouse) {
self.screen.program.enableMouse();
}
self.screen.alloc();
self.screen.render();
if (err) return callback(err);
value = value.replace(/[\r\n]/g, '');
self.value = value;
////if (self.censor) {
//// self.setContent(Array(self.value.length + 1).join('*'));
////} else
//if (!self.secret) {
// self.setContent(value);
//}
//return callback(null, value);
return self.setInput(callback);
});
};
Textbox.prototype.editor =
Textbox.prototype.readEditor =
Textbox.prototype.setEditor = function(callback) {
var self = this;
this.focus();
var fs = require('fs')
, editor = process.env.EDITOR || 'vi'
, file = '/tmp/blessed.' + Math.random().toString(36)
, args = [file]
, opt;
opt = {
stdio: 'inherit',
env: process.env,
cwd: process.env.HOME
};
return this.screen.exec(editor, args, opt, function(err, success) {
if (err) return callback(err);
return fs.readFile(file, 'utf8', function(err, data) {
fs.unlink(file);
if (err) return callback(err);
value = value.replace(/[\r\n]/g, '');
self.value = value;
////if (self.censor) {
//// self.setContent(Array(self.value.length + 1).join('*'));
////} else
//if (!self.secret) {
// self.setContent(value);
//}
//return callback(null, value);
return self.setInput(callback);
});
});
};
Textbox.prototype.editor =
Textbox.prototype.readEditor =
Textbox.prototype.setEditor = function(callback) {
var self = this;
this.focus();
return this.screen.readEditor(function(err, value) {
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) return callback(err);
value = value.replace(/[\r\n]/g, '');
self.value = value;
@ -2638,13 +2583,160 @@ function Textarea(options) {
if (!(this instanceof Textarea)) {
return new Textarea(options);
}
Input.call(this, options);
ScrollableText.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
this.__updateCursor = this.updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
}
Textarea.prototype.__proto__ = Input.prototype;
Textarea.prototype.__proto__ = ScrollableText.prototype;
Textarea.prototype.type = 'textarea';
Textarea.prototype.updateCursor = function() {
if (this.screen.focused !== this) return;
var clen = this._clines.length;
var last = this._clines[this._clines.length-1];
if (last.length === this.width - (this.border ? 2 : 0)) {
last = '';
clen++;
}
var line = Math.min(
clen - 1 - this.childBase,
this.height - (this.border ? 2 : 0));
this.screen.program.cup(
this.top + 1 + (this.border ? 1 : 0) + line,
this.left + 1 + (this.border ? 1 : 0) + last.length);
};
Textarea.prototype.input =
Textarea.prototype.readInput =
Textarea.prototype.setInput = function(callback) {
var self = this;
if (this._timeout != null) {
clearTimeout(this._timeout);
delete this._timeout;
}
this.focus();
this.screen.grabKeys = true;
this.updateCursor();
this.screen.program.showCursor();
this.screen.program.sgr('normal');
this._callback = function(err, value) {
self.screen.program.hideCursor();
self._timeout = setTimeout(function() {
self.screen.grabKeys = false;
}, 1);
return err
? callback(err)
: callback(null, value);
};
this.__listener = this._listener.bind(this);
this.on('keypress', this.__listener);
};
Textarea.prototype._listener = function(ch, key) {
var callback = this._callback
, value = this.value;
if (key.name === 'enter') {
ch = '\n';
}
// TODO: Handle directional keys.
if (key.name === 'left' || key.name === 'right'
|| key.name === 'up' || key.name === 'down') {
;
}
// if (key.name === 'escape' || key.name === 'enter') {
if (key.name === 'escape') {
delete this._callback;
this.removeListener('keypress', this.__listener);
delete this.__listener;
callback(null, key.name === 'enter' ? value : null);
} else if (key.name === 'backspace') {
if (this.value.length) {
this.value = this.value.slice(0, -1);
/*
var last = this._clines[this._clines.length-1];
if (last.length === this.width - (this.border ? 2 : 0)) {
last = '';
}
if (last.length === 0) {
this.screen.program.cuu();
this.screen.program.cuf(this.width - (this.border ? 2 : 0));
} else {
this.screen.program.cub();
}
*/
}
} else {
if (ch) {
this.value += ch;
/*
var last = this._clines[this._clines.length-1];
if (last.length === this.width - (this.border ? 2 : 0)) {
last = '';
}
if (last.length < this.width - (this.border ? 2 : 0) - 1) {
this.screen.program.cuf();
} else {
this.screen.program.cud();
this.screen.program.cub(this.width - (this.border ? 2 : 0));
}
*/
}
}
if (this.value !== value) {
this.setContent(this.value);
this.updateCursor();
this.screen.render();
}
};
Textarea.prototype.submit = function() {
// return this._listener(null, { name: 'enter' });
return this._listener('\x1b', { name: 'escape' });
};
Textarea.prototype.cancel = function() {
return this._listener('\x1b', { name: 'escape' });
};
Textarea.prototype.editor =
Textarea.prototype.readEditor =
Textarea.prototype.setEditor = function(callback) {
var self = this;
this.focus();
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) return callback(err);
self.value = value;
self.setContent(self.value);
self.screen.render();
return self.setInput(callback);
});
};
/**
* Button
*/
@ -2859,52 +2951,6 @@ function attrCode(code, cur) {
return (flags << 18) | (fg << 9) | bg;
}
function readEditor(callback) {
var spawn = require('child_process').spawn
, fs = require('fs')
, editor = process.env.EDITOR || 'vi'
, file = '/tmp/blessed.' + Math.random().toString(36);
var write = process.stdout.write;
process.stdout.write = function() {};
try {
process.stdin.pause();
} catch (e) {
;
}
var resume = function() {
try {
process.stdin.resume();
} catch (e) {
;
}
process.stdout.write = write;
};
var ps = spawn(editor, [file], {
stdio: 'inherit',
env: process.env,
cwd: process.env.HOME
});
ps.on('error', function(err) {
resume();
return callback(err);
});
ps.on('exit', function(code) {
resume();
return fs.readFile(file, 'utf8', function(err, data) {
return fs.unlink(file, function() {
if (err) return callback(err);
return callback(null, data);
});
});
});
}
function sp(line, width, align) {
if (!align) return line;

30
test/widget-textarea.js Normal file
View File

@ -0,0 +1,30 @@
var blessed = require('blessed');
var screen = blessed.screen({
tput: true
});
var box = blessed.textarea({
parent: screen,
// Possibly support:
// align: 'center',
bg: 'blue',
height: 'half',
width: 'half',
top: 'center',
left: 'center'
});
screen.render();
screen.key('q', function() {
process.exit(0);
});
screen.key('i', function() {
box.readInput(function() {});
});
screen.key('e', function() {
box.readEditor(function() {});
});