add filemanager widget. g keys.

This commit is contained in:
Christopher Jeffrey 2013-07-12 07:48:55 -05:00
parent 28d6b096ab
commit 7414922a76
3 changed files with 316 additions and 3 deletions

View File

@ -583,6 +583,35 @@ A progress bar allowing various styles.
- **reset()** - reset the bar.
#### FileManager (from List)
A very simple file manager for selecting files.
##### Options:
- inherits all from List.
- **cwd** - current working directory.
##### Properties:
- inherits all from List.
- **cwd** - current working directory.
##### Events:
- inherits all from List.
- **cd** - directory was selected and navigated to.
- **file** - file was selected.
##### Methods:
- inherits all from List.
- **refresh([callback])** - refresh the file list (perform a `readdir` on `cwd`
and update the list items).
- **pick(callback)** - pick a single file and return the path in the callback.
- **reset([callback])** - reset back to original cwd.
### Positioning
Offsets may be a number, a percentage (e.g. `50%`), or a keyword (e.g.

View File

@ -8,7 +8,9 @@
* Modules
*/
var EventEmitter = require('events').EventEmitter;
var EventEmitter = require('events').EventEmitter
, path = require('path')
, fs = require('fs');
/**
* Node
@ -1085,7 +1087,10 @@ function Element(options) {
content: options.label,
left: 2,
top: this.border ? 0 : -1,
tags: this.parseTags,
shrink: true
// bg: this.border ? this.border.bg : null,
// fg: this.border ? this.border.fg : null
}));
}
@ -1760,8 +1765,14 @@ outer:
if (this.scrollbar && (yl - yi_) < h) {
xi = xl - 1;
if (this.scrollbar.ignoreBorder && this.border) xi++;
yi = h - (yl - yi_) - (this.border ? 2 : 0);
yi = yi_ + (((yl - yi_) * (this.childBase / yi)) | 0);
if (this.selected == null) {
// TODO: Fix this - doesn't work with lists (and possibly scrollabletext).
yi = h - (yl - yi_) - (this.border ? 2 : 0);
yi = yi_ + (((yl - yi_) * (this.childBase / yi)) | 0);
} else {
yi = this.selected / h;
yi = yi_ + ((yl - yi_) * yi | 0);
}
cell = lines[yi] && lines[yi][xi];
if (cell) {
ch = this.scrollbar.ch || ' ';
@ -2172,6 +2183,16 @@ function List(options) {
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && !key.shift) {
self.select(0);
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && key.shift) {
self.select(self.items.length - 1);
self.screen.render();
return;
}
});
}
@ -2364,6 +2385,16 @@ function ScrollableText(options) {
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && !key.shift) {
self.scroll(-self._clines.length);
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && key.shift) {
self.scroll(self._clines.length);
self.screen.render();
return;
}
});
}
@ -2904,6 +2935,177 @@ ProgressBar.prototype.reset = function() {
this.filled = 0;
};
/**
* FileManager
*/
function FileManager(options) {
if (!(this instanceof FileManager)) {
return new FileManager(options);
}
var self = this;
options.parseTags = true;
List.call(this, options);
this.cwd = options.cwd || process.cwd();
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
if (err) {
return self.emit('error', err, file);
}
if (stat.isDirectory()) {
self.emit('cd', file, self.cwd);
self.cwd = file;
self.refresh();
} else {
self.emit('file', file);
}
});
});
}
FileManager.prototype.__proto__ = List.prototype;
FileManager.prototype.type = 'file-manager';
FileManager.prototype.refresh = function(cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
var self = this
, cwd = cwd || this.cwd;
return fs.readdir(cwd, function(err, list) {
if (err && err.code === 'ENOENT') {
self.cwd = cwd !== process.env.HOME
? process.env.HOME
: '/';
return self.refresh(callback);
}
if (err) {
if (callback) return callback(err);
return self.emit('error', err, cwd);
}
var dirs = []
, files = [];
list.unshift('..');
list.forEach(function(name) {
var f = path.resolve(cwd, name)
, stat;
try {
stat = fs.lstatSync(f);
} catch (e) {
;
}
if ((stat && stat.isDirectory()) || name === '..') {
dirs.push({
name: name,
text: '{light-blue-fg}' + name + '{/light-blue-fg}/',
dir: true
});
} else if (stat && stat.isSymbolicLink()) {
files.push({
name: name,
text: '{light-cyan-fg}' + name + '{/light-cyan-fg}@',
dir: false
});
} else {
files.push({
name: name,
text: name,
dir: false
});
}
});
dirs = asort(dirs);
files = asort(files);
list = dirs.concat(files).map(function(data) {
return data.text;
});
self.setItems(list);
self.select(0);
self.screen.render();
if (callback) callback();
});
};
FileManager.prototype.pick = function(cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
var self = this
, focused = this.screen.focused === this
, hidden = this.hidden;
if (hidden) {
this.show();
}
if (!focused) {
this.screen.saveFocus();
this.focus();
}
var onfile, oncancel;
function resume() {
self.removeListener('file', onfile);
self.removeListener('cancel', oncancel);
if (hidden) {
self.hide();
}
if (!focused) {
self.screen.restoreFocus();
}
self.screen.render();
}
this.on('file', onfile = function(file) {
resume();
return callback(null, file);
});
this.on('cancel', oncancel = function() {
resume();
return callback();
});
this.refresh(cwd, function(err) {
if (err) return callback(err);
self.screen.render();
});
};
FileManager.prototype.reset = function(cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
this.cwd = cwd || this.options.cwd;
this.refresh(callback);
};
/**
* Helpers
*/
@ -3105,6 +3307,23 @@ function wrapContent(content, width, tags, state) {
return out;
}
function asort(obj) {
return obj.sort(function(a, b) {
a = a.name.toLowerCase();
b = b.name.toLowerCase();
if (a[0] === '.' && b[0] === '.') {
a = a[1];
b = b[1];
} else {
a = a[0];
b = b[0];
}
return a > b ? 1 : (a < b ? -1 : 0);
});
}
var colors = {
default: -1,
bg: -1,
@ -3163,3 +3382,4 @@ exports.Textbox = exports.textbox = Textbox;
exports.Textarea = exports.textarea = Textarea;
exports.Button = exports.button = Button;
exports.ProgressBar = exports.progressbar = ProgressBar;
exports.FileManager = exports.filemanager = FileManager;

64
test/widget-file.js Normal file
View File

@ -0,0 +1,64 @@
var blessed = require('blessed');
var screen = blessed.screen({
tput: true
});
var fm = blessed.filemanager({
parent: screen,
border: {
type: 'ascii'
},
selectedBg: 'blue',
height: 'half',
width: 'half',
top: 'center',
left: 'center',
cwd: process.env.HOME,
keys: true,
vi: true,
scrollbar: {
bg: 'white',
ch: ' '
}
});
var box = blessed.box({
parent: screen,
bg: 'green',
border: {
type: 'ascii'
},
height: 'half',
width: 'half',
top: 'center',
left: 'center',
hidden: true
});
fm.refresh();
screen.render();
screen.key('q', function() {
process.exit(0);
});
screen.key(['s', 'p'], function() {
fm.hide();
screen.render();
setTimeout(function() {
fm.pick(function(err, file) {
box.show();
box.setContent(err ? err + '' : file);
screen.render();
setTimeout(function() {
box.hide();
fm.reset(function() {
fm.show();
screen.render();
});
}, 2000);
});
}, 2000);
});