more scrolling improvements. scrolling form.

This commit is contained in:
Christopher Jeffrey 2013-07-19 09:17:21 -05:00
parent 7af4dbaf1c
commit a98bef4c90
2 changed files with 90 additions and 52 deletions

View File

@ -1614,6 +1614,32 @@ Element.prototype.toggle = function() {
Element.prototype.focus = function() {
var old = this.screen.focused;
this.screen.focused = this;
// Find a scrollable ancestor if we have one.
var el = this;
while (el = el.parent) {
if (el.childBase != null) break;
}
// If we're in a scrollable element,
// automatically scroll to the focused element.
if (el) {
var ryi = this.top - el.top - el.itop
, ryl = this.top + this.height - el.top - el.ibottom
, visible = el.height - el.iheight;
if (ryi < el.childBase) {
el.scrollTo(ryi);
this.screen.render();
} else if (ryi >= el.childBase + visible) {
el.scrollTo(ryi);
this.screen.render();
} else if (ryl >= el.childBase + visible) {
el.scrollTo(ryi);
this.screen.render();
}
}
old.emit('blur', this);
this.emit('focus', old);
this.screen.emit('element blur', old, this);
@ -2374,35 +2400,39 @@ Box.prototype._getCoords = function(get) {
, coords
, v;
// Find a scrollable ancestor if we have one.
var el = this;
while (el = el.parent) {
if (el.childBase != null) break;
}
// Check to make sure we're visible and
// inside of the visible scroll area.
if (this.parent.childBase != null
&& (!this.parent.items
|| ~this.parent.items.indexOf(this))) {
ryi = yi - this.parent._getTop(get) - this.parent.itop;
ryl = yl - this.parent._getTop(get) - this.parent.ibottom;
visible = this.parent._getHeight(get) - this.parent.iheight;
if (el && (!el.items || ~el.items.indexOf(this))) {
ryi = yi - el._getTop(get) - el.itop;
ryl = yl - el._getTop(get) - el.ibottom;
visible = el._getHeight(get) - el.iheight;
if (ryi < this.parent.childBase) {
if (ryl > this.parent.childBase) {
// Is partially covered above. TODO: Improve.
v = ryl - this.parent.childBase;
if (ryi < el.childBase) {
if (ryl > el.childBase) {
// Is partially covered above.
v = ryl - el.childBase;
yi += (ryl - ryi) - v;
} else {
// Is above.
return;
}
} else if (ryi >= this.parent.childBase + visible) {
} else if (ryi >= el.childBase + visible) {
// Is below.
return;
} else if (ryl >= this.parent.childBase + visible) {
// Is partially covered below. TODO: Improve.
v = this.parent.childBase + visible + (yl - yi) - ryl;
} else if (ryl >= el.childBase + visible) {
// Is partially covered below.
v = el.childBase + visible + (yl - yi) - ryl;
yl = yi + v;
}
yi -= this.parent.childBase;
yl -= this.parent.childBase;
yi -= el.childBase;
yl -= el.childBase;
}
// Attempt to shrink the element base on the
@ -2455,11 +2485,12 @@ Box.prototype.render = function() {
// If we're in a scrollable text box, check to
// see which attributes this line starts with.
if (this.contentIndex != null && this.childBase != null) {
if (this._clines.length > this.childBase) {
attr = this._clines.attr[this.childBase];
} else {
attr = this._clines.attr[this._clines.length - 1];
}
// if (this._clines.length > this.childBase) {
// attr = this._clines.attr[this.childBase];
// } else {
// attr = this._clines.attr[this._clines.length - 1];
// }
attr = this._clines.attr[this.childBase];
}
if (this.border) xi++, xl--, yi++, yl--;
@ -3081,22 +3112,22 @@ function List(options) {
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - (self.border ? 2 : 0)) / 2) | 0);
self.move(-((self.height - self.iheight) / 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.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - (self.border ? 2 : 0)));
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - (self.border ? 2 : 0));
self.move(self.height - self.iheight);
self.screen.render();
return;
}
@ -3109,7 +3140,7 @@ function List(options) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - (self.border ? 2 : 0),
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(self.childBase + visible - self.selected);
self.screen.render();
@ -3118,7 +3149,7 @@ function List(options) {
if (options.vi && key.name === 'l' && key.shift) {
// XXX This goes one too far on lists with an odd number of items.
self.down(self.childBase
+ Math.min(self.height - (self.border ? 2 : 0), self.items.length)
+ Math.min(self.height - self.iheight, self.items.length)
- self.selected);
self.screen.render();
return;
@ -3438,7 +3469,7 @@ function Form(options) {
options = options || {};
Box.call(this, options);
ScrollableBox.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
@ -3458,11 +3489,11 @@ function Form(options) {
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
self.screen.grabKeys = false;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
@ -3473,7 +3504,7 @@ function Form(options) {
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
self.screen.grabKeys = false;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
@ -3487,7 +3518,7 @@ function Form(options) {
}
}
Form.prototype.__proto__ = Box.prototype;
Form.prototype.__proto__ = ScrollableBox.prototype;
Form.prototype.type = 'form';
@ -3677,22 +3708,21 @@ function Textbox(options) {
this.secret = options.secret;
this.censor = options.censor;
this.on('resize', updateCursor);
this.on('move', updateCursor);
function updateCursor() {
if (self.screen.focused !== self) return;
self.screen.program.cup(
self.top + (self.border ? 1 : 0),
self.left + (self.border ? 1 : 0)
+ self.value.length);
}
this.__updateCursor = this.updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
}
Textbox.prototype.__proto__ = Input.prototype;
Textbox.prototype.type = 'textbox';
Textbox.prototype.updateCursor = function() {
if (this.screen.focused !== this) return;
this.screen.program.cup(this.top + this.itop,
this.left + this.ileft + this.value.length);
};
Textbox.prototype.input =
Textbox.prototype.readInput =
Textbox.prototype.setInput = function(callback) {
@ -3706,12 +3736,7 @@ Textbox.prototype.setInput = function(callback) {
this.screen.grabKeys = true;
// Could possibly save and restore cursor.
this.screen.program.cup(
this.top + this.itop,
this.left + this.ileft
+ this.value.length);
this.updateCursor();
this.screen.program.showCursor();
this.screen.program.sgr('normal');
@ -3801,6 +3826,7 @@ Textbox.prototype.render = function() {
// setContent is necessary to clear the area in case
// .shrink is being used and the new content is smaller.
// Could technically optimize this.
if (!this._getCoords(true)) return;
if (this.secret) {
this.setContent('');
return this._render();
@ -4134,11 +4160,8 @@ ProgressBar.prototype.type = 'progress-bar';
ProgressBar.prototype._render = ProgressBar.prototype.render;
ProgressBar.prototype.render = function() {
// NOTE: Maybe move this `hidden` check
// down below `stop` check and return `ret`.
if (this.hidden) return;
var ret = this._render();
if (!ret) return;
var xi = ret.xi
, xl = ret.xl
@ -4412,6 +4435,7 @@ Checkbox.prototype.type = 'checkbox';
Checkbox.prototype._render = Checkbox.prototype.render;
Checkbox.prototype.render = function() {
if (!this._getCoords(true)) return;
if (this.type === 'radio-button') {
this.setContent('(' + (this.checked ? '*' : ' ') + ') ' + this.text);
} else {
@ -4981,6 +5005,7 @@ Listbar.prototype.setItems = function(commands) {
Listbar.prototype._render = Listbar.prototype.render;
Listbar.prototype.render = function() {
if (!this._getCoords(true)) return;
var self = this
, drawn = 0;

View File

@ -1,5 +1,5 @@
var blessed = require('../')
, screen = blessed.screen();
, screen = blessed.screen({ dump: __dirname + '/form.log' });
var form = blessed.form({
parent: screen,
@ -84,6 +84,19 @@ var check = blessed.checkbox({
content: 'check'
});
var check2 = blessed.checkbox({
parent: form,
mouse: true,
keys: true,
shrink: true,
bg: 'magenta',
height: 1,
left: 28,
top: 10,
name: 'foooooooo2',
content: 'foooooooo2'
});
var submit = blessed.button({
parent: form,
mouse: true,