scrollable elements.
This commit is contained in:
parent
0f4b60314b
commit
e33838804b
124
lib/widget.js
124
lib/widget.js
|
@ -2348,29 +2348,43 @@ Box.prototype._getCoords = function(get) {
|
|||
, xl = xi + this._getWidth(get)
|
||||
, yi = this._getTop(get)
|
||||
, yl = yi + this._getHeight(get)
|
||||
, rtop
|
||||
, ryi
|
||||
, ryl
|
||||
, visible
|
||||
, coords;
|
||||
, coords
|
||||
, v;
|
||||
|
||||
// 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))) {
|
||||
rtop = yi - this.parent._getTop(get)
|
||||
ryi = yi - this.parent._getTop(get)
|
||||
- (this.parent.border ? 1 : 0)
|
||||
- this.parent.padding;
|
||||
ryl = yl - this.parent._getTop(get)
|
||||
- (this.parent.border ? 1 : 0)
|
||||
- this.parent.padding;
|
||||
|
||||
visible = this.parent._getHeight(get)
|
||||
- (this.parent.border ? 2 : 0)
|
||||
- this.parent.padding * 2;
|
||||
|
||||
if (rtop - this.parent.childBase < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rtop - this.parent.childBase >= visible) {
|
||||
if (ryi < this.parent.childBase) {
|
||||
if (ryl > this.parent.childBase) {
|
||||
// Is partially covered above. TODO: Improve.
|
||||
v = ryl - this.parent.childBase;
|
||||
yi += (ryl - ryi) - v;
|
||||
} else {
|
||||
// Is above.
|
||||
return;
|
||||
}
|
||||
} else if (ryi >= this.parent.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;
|
||||
yl = yi + v;
|
||||
}
|
||||
|
||||
yi -= this.parent.childBase;
|
||||
|
@ -2427,7 +2441,11 @@ 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) {
|
||||
attr = this._clines.attr[this.childBase];
|
||||
if (this._clines.length > this.childBase) {
|
||||
attr = this._clines.attr[this.childBase];
|
||||
} else {
|
||||
attr = this._clines.attr[this._clines.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.border) xi++, xl--, yi++, yl--;
|
||||
|
@ -2514,19 +2532,21 @@ Box.prototype.render = function() {
|
|||
}
|
||||
|
||||
// Draw the scrollbar.
|
||||
i = this.items ? this.items.length : this._clines.length;
|
||||
if (this.scrollbar) {
|
||||
i = this.type === 'scrollable-text'
|
||||
? this._clines.length + 1
|
||||
: this._scrollBottom() + (yl - yi) + 1;
|
||||
//i = Math.max(this._clines.length + 1, this._scrollBottom() + (yl - yi) + 1);
|
||||
}
|
||||
if (this.scrollbar && (yl - yi) < i) {
|
||||
i -= yl - yi;
|
||||
x = xl - 1;
|
||||
if (this.scrollbar.ignoreBorder && this.border) x++;
|
||||
if (this.selected == null) {
|
||||
// TODO: The scrollbar seems to disappear at
|
||||
// the bottom of a scrollable text box. Fix.
|
||||
i = i - (yl - yi);
|
||||
y = yi + (yl - yi) * (this.childBase / i) | 0;
|
||||
} else {
|
||||
y = this.selected / i;
|
||||
y = yi + ((yl - yi) * y | 0);
|
||||
}
|
||||
y = this.selected == null
|
||||
? this.childBase
|
||||
: this.selected;
|
||||
y = y / i;
|
||||
y = yi + ((yl - yi) * y | 0);
|
||||
cell = lines[y] && lines[y][x];
|
||||
if (cell) {
|
||||
ch = this.scrollbar.ch || ' ';
|
||||
|
@ -2819,12 +2839,17 @@ function ScrollableBox(options) {
|
|||
this.scrollbar = options.scrollbar;
|
||||
if (this.scrollbar) {
|
||||
this.scrollbar.ch = this.scrollbar.ch || ' ';
|
||||
if (!this.scrollbar.style) {
|
||||
this.scrollbar.style = this.style.scrollbar || {};
|
||||
this.scrollbar.style.fg = this.scrollbar.fg;
|
||||
this.scrollbar.style.bg = this.scrollbar.bg;
|
||||
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
|
||||
if (!this.style.scrollbar) {
|
||||
this.style.scrollbar = {};
|
||||
this.style.scrollbar.fg = this.scrollbar.fg;
|
||||
this.style.scrollbar.bg = this.scrollbar.bg;
|
||||
this.style.scrollbar.bold = this.scrollbar.bold;
|
||||
this.style.scrollbar.underline = this.scrollbar.underline;
|
||||
this.style.scrollbar.inverse = this.scrollbar.inverse;
|
||||
this.style.scrollbar.invisible = this.scrollbar.invisible;
|
||||
}
|
||||
this.style.scrollbar = this.scrollbar.style;
|
||||
this.scrollbar.style = this.style.scrollbar;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2832,6 +2857,30 @@ ScrollableBox.prototype.__proto__ = Box.prototype;
|
|||
|
||||
ScrollableBox.prototype.type = 'scrollable-box';
|
||||
|
||||
ScrollableBox.prototype._scrollBottom = function() {
|
||||
// We could just calculate the children, but we can
|
||||
// optimize for lists by just returning the items.length.
|
||||
if (this.type === 'list') {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
if (this.lpos && this.lpos._scrollBottom) {
|
||||
return this.lpos._scrollBottom;
|
||||
}
|
||||
|
||||
var bottom = this.children.reduce(function(current, el) {
|
||||
return Math.max(current, el.rtop + el.height);
|
||||
}, 0);
|
||||
|
||||
if (this.lpos) this.lpos._scrollBottom = bottom;
|
||||
|
||||
return bottom;
|
||||
};
|
||||
|
||||
ScrollableBox.prototype.scrollTo = function(offset) {
|
||||
return this.scroll(offset - (this.childBase + this.childOffset));
|
||||
};
|
||||
|
||||
ScrollableBox.prototype.scroll = function(offset) {
|
||||
var visible = this.height - (this.border ? 2 : 0) - this.padding * 2
|
||||
, base = this.childBase
|
||||
|
@ -2867,6 +2916,15 @@ ScrollableBox.prototype.scroll = function(offset) {
|
|||
this.childBase = this.baseLimit;
|
||||
}
|
||||
|
||||
// This code works for scrollable box and list, but it
|
||||
// makes scrollable text choke because it can't diff properly.
|
||||
if (this.type !== 'scrollable-text') {
|
||||
var bottom = this._scrollBottom();
|
||||
if (this.childBase > bottom) {
|
||||
this.childBase = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
// Optimize scrolling with CSR + IL/DL.
|
||||
p = this.lpos;
|
||||
if (this.childBase !== base && this.screen.cleanSides(this)) {
|
||||
|
@ -3065,7 +3123,7 @@ function List(options) {
|
|||
}
|
||||
|
||||
this.on('resize', function() {
|
||||
var visible = self.height - (self.border ? 2 : 0);
|
||||
var visible = self.height - (self.border ? 2 : 0) - self.padding * 2;
|
||||
if (visible >= self.selected + 1) {
|
||||
//if (self.selected < visible - 1) {
|
||||
self.childBase = 0;
|
||||
|
@ -3182,10 +3240,11 @@ List.prototype.select = function(index) {
|
|||
if (this.selected === index && this._listInitialized) return;
|
||||
this._listInitialized = true;
|
||||
|
||||
var diff = index - this.selected;
|
||||
//var diff = index - this.selected;
|
||||
this.selected = index;
|
||||
this.value = this.ritems[this.selected];
|
||||
this.scroll(diff);
|
||||
//this.scroll(diff);
|
||||
this.scrollTo(this.selected);
|
||||
};
|
||||
|
||||
List.prototype.move = function(offset) {
|
||||
|
@ -3313,12 +3372,18 @@ ScrollableText.prototype.scroll = function(offset) {
|
|||
diff = cb - base;
|
||||
}
|
||||
|
||||
// this.childBase = Math.max(this.childBase, max);
|
||||
// cb = this.childBase;
|
||||
// diff = cb - base;
|
||||
|
||||
if (diff > 0) {
|
||||
for (i = base; i < cb; i++) {
|
||||
// if (!this._clines[i]) continue;
|
||||
this.contentIndex += this._clines[i].length + 1;
|
||||
}
|
||||
} else {
|
||||
for (i = base - 1; i >= cb; i--) {
|
||||
// if (!this._clines[i]) continue;
|
||||
this.contentIndex -= this._clines[i].length + 1;
|
||||
}
|
||||
}
|
||||
|
@ -3338,7 +3403,10 @@ ScrollableText.prototype._recalculateIndex = function() {
|
|||
this.childBase = max;
|
||||
}
|
||||
|
||||
//this.childBase = Math.max(this.childBase, max);
|
||||
|
||||
for (var i = 0, t = 0; i < this.childBase; i++) {
|
||||
// if (!this._clines[i]) continue;
|
||||
t += this._clines[i].length + 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
var blessed = require('../')
|
||||
, screen = blessed.screen();
|
||||
|
||||
var box = blessed.scrollablebox({
|
||||
//var box = blessed.scrollabletext({
|
||||
parent: screen,
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
width: '80%',
|
||||
height: '80%',
|
||||
bg: 'green',
|
||||
border: {
|
||||
type: 'ascii'
|
||||
},
|
||||
content: 'foobar',
|
||||
keys: true,
|
||||
vi: true,
|
||||
alwaysScroll: true,
|
||||
scrollbar: {
|
||||
ch: ' ',
|
||||
inverse: true
|
||||
}
|
||||
});
|
||||
|
||||
var text = blessed.box({
|
||||
parent: box,
|
||||
content: 'hello',
|
||||
style: {
|
||||
bg: 'red'
|
||||
},
|
||||
left: 2,
|
||||
top: 30,
|
||||
width: '50%',
|
||||
height: 4
|
||||
});
|
||||
|
||||
var text2 = blessed.box({
|
||||
parent: box,
|
||||
content: 'world',
|
||||
style: {
|
||||
bg: 'red'
|
||||
},
|
||||
left: 2,
|
||||
top: 50,
|
||||
width: '50%',
|
||||
height: 3
|
||||
});
|
||||
|
||||
screen.key('q', function() {
|
||||
return process.exit(0);
|
||||
});
|
||||
|
||||
box.on('keypress', function(ch, key) {
|
||||
if (key.name === 'up' || key.name === 'k') {
|
||||
box.scroll(-1);
|
||||
screen.render();
|
||||
return;
|
||||
}
|
||||
if (key.name === 'down' || key.name === 'j') {
|
||||
box.scroll(1);
|
||||
screen.render();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
box.focus();
|
||||
|
||||
screen.render();
|
|
@ -79,7 +79,11 @@ var list = blessed.list({
|
|||
'eight',
|
||||
'nine',
|
||||
'ten'
|
||||
]
|
||||
],
|
||||
scrollbar: {
|
||||
ch: ' ',
|
||||
inverse: true
|
||||
}
|
||||
});
|
||||
|
||||
screen.append(list);
|
||||
|
|
Loading…
Reference in New Issue