mirror of
https://github.com/embarklabs/neo-blessed.git
synced 2025-01-10 19:16:20 +00:00
add filemanager widget. g keys.
This commit is contained in:
parent
28d6b096ab
commit
7414922a76
29
README.md
29
README.md
@ -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.
|
||||
|
226
lib/widget.js
226
lib/widget.js
@ -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
64
test/widget-file.js
Normal 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);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user