mirror of
https://github.com/embarklabs/neo-blessed.git
synced 2025-01-11 03:25:45 +00:00
improve position, attribute parsing. misc.
This commit is contained in:
parent
3ae01237be
commit
df317bb472
@ -257,6 +257,7 @@ The base element.
|
|||||||
(`0-100%`), or keyword (`half` or `shrink`).
|
(`0-100%`), or keyword (`half` or `shrink`).
|
||||||
- **left, right, top, bottom** - offsets of the element **relative to its
|
- **left, right, top, bottom** - offsets of the element **relative to its
|
||||||
parent**. can be a number, percentage (`0-100%`), or keyword (`center`).
|
parent**. can be a number, percentage (`0-100%`), or keyword (`center`).
|
||||||
|
`right` and `bottom` do not accept keywords.
|
||||||
|
|
||||||
##### Properties:
|
##### Properties:
|
||||||
|
|
||||||
|
510
lib/widget.js
510
lib/widget.js
@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter
|
var EventEmitter = require('events').EventEmitter
|
||||||
|
, assert = require('assert')
|
||||||
, path = require('path')
|
, path = require('path')
|
||||||
, fs = require('fs')
|
, fs = require('fs')
|
||||||
, colors = require('./colors')
|
, colors = require('./colors')
|
||||||
@ -150,6 +151,39 @@ Node.prototype.detach = function() {
|
|||||||
if (this.parent) this.parent.remove(this);
|
if (this.parent) this.parent.remove(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Node.prototype.forDescendants = function(iter, s) {
|
||||||
|
if (s) iter(this);
|
||||||
|
el.children.forEach(function emit(el) {
|
||||||
|
iter(el);
|
||||||
|
el.children.forEach(emit);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Node.prototype.forAncestors = function(iter, s) {
|
||||||
|
var el = this;
|
||||||
|
if (s) iter(this);
|
||||||
|
while (el = el.parent) {
|
||||||
|
iter(el);
|
||||||
|
el.emit.apply(el, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Node.prototype.collectDescendants = function(s) {
|
||||||
|
var out = [];
|
||||||
|
this.forDescendants(function(el) {
|
||||||
|
out.push(el);
|
||||||
|
}, s);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
Node.prototype.collectAncestors = function(s) {
|
||||||
|
var out = [];
|
||||||
|
this.forAncestors(function(el) {
|
||||||
|
out.push(el);
|
||||||
|
}, s);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
Node.prototype.emitDescendants = function() {
|
Node.prototype.emitDescendants = function() {
|
||||||
var args = Array.prototype.slice(arguments)
|
var args = Array.prototype.slice(arguments)
|
||||||
, iter;
|
, iter;
|
||||||
@ -158,31 +192,28 @@ Node.prototype.emitDescendants = function() {
|
|||||||
iter = args.pop();
|
iter = args.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
(function emit(el) {
|
return this.forDescendants(function(el) {
|
||||||
if (iter) iter(el);
|
if (iter) iter(el);
|
||||||
el.emit.apply(el, args);
|
el.emit.apply(el, args);
|
||||||
if (el.children) {
|
}, true);
|
||||||
el.children.forEach(emit);
|
|
||||||
}
|
|
||||||
})(this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.emitAncestors = function() {
|
Node.prototype.emitAncestors = function() {
|
||||||
var args = Array.prototype.slice(arguments)
|
var args = Array.prototype.slice(arguments)
|
||||||
, el = this
|
|
||||||
, iter;
|
, iter;
|
||||||
|
|
||||||
if (typeof args[args.length-1] === 'function') {
|
if (typeof args[args.length-1] === 'function') {
|
||||||
iter = args.pop();
|
iter = args.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
return this.forAncestors(function(el) {
|
||||||
if (iter) iter(el);
|
if (iter) iter(el);
|
||||||
el.emit.apply(el, args);
|
el.emit.apply(el, args);
|
||||||
} while (el = el.parent);
|
}, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.hasDescendant = function(target) {
|
Node.prototype.hasDescendant = function(target) {
|
||||||
|
// return this.collectDescendants().indexOf(target) !== -1;
|
||||||
return (function find(el) {
|
return (function find(el) {
|
||||||
for (var i = 0; i < el.children.length; i++) {
|
for (var i = 0; i < el.children.length; i++) {
|
||||||
if (el.children[i] === target) {
|
if (el.children[i] === target) {
|
||||||
@ -197,6 +228,7 @@ Node.prototype.hasDescendant = function(target) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.hasAncestor = function(target) {
|
Node.prototype.hasAncestor = function(target) {
|
||||||
|
// return this.collectAncestors().indexOf(target) !== -1;
|
||||||
var el = this;
|
var el = this;
|
||||||
while (el = el.parent) {
|
while (el = el.parent) {
|
||||||
if (el === target) return true;
|
if (el === target) return true;
|
||||||
@ -1557,7 +1589,6 @@ Element.prototype.hide = function() {
|
|||||||
Element.prototype.show = function() {
|
Element.prototype.show = function() {
|
||||||
if (!this.hidden) return;
|
if (!this.hidden) return;
|
||||||
this.hidden = false;
|
this.hidden = false;
|
||||||
// this.render();
|
|
||||||
this.emit('show');
|
this.emit('show');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1602,11 +1633,14 @@ Element.prototype.parseContent = function() {
|
|||||||
this._clines = this._wrapContent(content, width);
|
this._clines = this._wrapContent(content, width);
|
||||||
this._clines.width = width;
|
this._clines.width = width;
|
||||||
this._clines.content = this.content;
|
this._clines.content = this.content;
|
||||||
|
this._clines.attr = this._parseAttr(this._clines);
|
||||||
this._pcontent = this._clines.join('\n');
|
this._pcontent = this._clines.join('\n');
|
||||||
this.emit('parsed content');
|
this.emit('parsed content');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._clines.attr = this._parseAttr(this._clines) || this._clines.attr;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1629,6 +1663,38 @@ Element.prototype._parseTags = function(text) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Element.prototype._parseAttr = function(lines) {
|
||||||
|
var attr = this.sattr(this.style, this.style.fg, this.style.bg)
|
||||||
|
, attrs = []
|
||||||
|
, line
|
||||||
|
, i
|
||||||
|
, j
|
||||||
|
, c;
|
||||||
|
|
||||||
|
if (lines[0].attr === attr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.contentIndex == null || this.childBase == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < lines.length; j++) {
|
||||||
|
line = lines[j];
|
||||||
|
attrs[j] = attr;
|
||||||
|
for (i = 0; i < line.length; i++) {
|
||||||
|
if (line[i] === '\x1b') {
|
||||||
|
if (c = /^\x1b\[[\d;]*m/.exec(line.substring(i))) {
|
||||||
|
attr = this.screen.attrCode(c[0], attr);
|
||||||
|
i += c[0].length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs;
|
||||||
|
};
|
||||||
|
|
||||||
Element.prototype._align = function(line, width, align) {
|
Element.prototype._align = function(line, width, align) {
|
||||||
if (!align) return line;
|
if (!align) return line;
|
||||||
|
|
||||||
@ -1798,241 +1864,223 @@ Element.prototype.clearPos = function() {
|
|||||||
* Positioning
|
* Positioning
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Help center shrunken nested elements.
|
// The below methods are a bit confusing: basically
|
||||||
Screen.prototype.spos =
|
// whenever Box.render is called `lpos` gets set on
|
||||||
Element.prototype.spos = function() {
|
// the element, an object containing the rendered
|
||||||
|
// coordinates. Since these don't update if the
|
||||||
|
// element is moved somehow, they're unreliable in
|
||||||
|
// that situation. However, if we can guarantee that
|
||||||
|
// lpos is good and up to date, it can be more
|
||||||
|
// accurate than the calculated positions below.
|
||||||
|
// In this case, if the element is being rendered,
|
||||||
|
// it's guaranteed that the parent will have been
|
||||||
|
// rendered first, in which case we can use the
|
||||||
|
// parant's lpos instead of recalculating it's
|
||||||
|
// position (since that might be wrong because
|
||||||
|
// it doesn't handle content shrinkage).
|
||||||
|
|
||||||
|
Screen.prototype._getPos = function() {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype._getPos = function() {
|
||||||
var pos = this.lpos;
|
var pos = this.lpos;
|
||||||
if (!pos || !this.shrink) return this;
|
|
||||||
pos.width = pos.xl - pos.xi;
|
assert.ok(pos && !pos.changed);
|
||||||
pos.height = pos.yl - pos.yi;
|
|
||||||
|
if (pos.left != null) return pos;
|
||||||
|
|
||||||
pos.left = pos.xi;
|
pos.left = pos.xi;
|
||||||
pos.top = pos.yi;
|
pos.top = pos.yi;
|
||||||
pos.right = this.screen.cols - pos.xl;
|
pos.right = this.screen.cols - pos.xl;
|
||||||
pos.bottom = this.screen.rows - pos.yl;
|
pos.bottom = this.screen.rows - pos.yl;
|
||||||
|
pos.width = pos.xl - pos.xi;
|
||||||
|
pos.height = pos.yl - pos.yi;
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: When coords are entered in the Element constructor, all of the coords
|
Element.prototype._bindPosChanged = function() {
|
||||||
// are *relative* to their parent, when retrieving them from `.left`, `.right`,
|
function changed() {
|
||||||
// etc members, the coords are absolute. To see the *relative* coords again,
|
self.forDescendants(function(el) {
|
||||||
// use `.rleft`, `.rright`, etc.
|
if (el.lpos) el.lpos.changed = true;
|
||||||
|
}, true);
|
||||||
Element.prototype.__defineGetter__('left', function() {
|
|
||||||
var left = this.position.left;
|
|
||||||
|
|
||||||
var parent = this.parent.spos();
|
|
||||||
|
|
||||||
if (typeof left === 'string') {
|
|
||||||
if (left === 'center') left = '50%';
|
|
||||||
left = +left.slice(0, -1) / 100;
|
|
||||||
left = this.parent.width * left | 0;
|
|
||||||
if (this.position.left === 'center') {
|
|
||||||
left -= this.width / 2 | 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.left == null && this.options.right != null) {
|
this.on('move', changed);
|
||||||
return this.screen.cols - this.width - this.right;
|
// Resize might be tricky because it's
|
||||||
}
|
// emitted recursively for a screen resize.
|
||||||
|
this.on('resize', changed);
|
||||||
|
this.on('reparent', changed);
|
||||||
|
};
|
||||||
|
|
||||||
return (parent.left || 0) + left;
|
/**
|
||||||
});
|
* Position Getters
|
||||||
|
*/
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('right', function() {
|
Element.prototype._getWidth = function(get) {
|
||||||
if (this.options.right == null && this.options.left != null) {
|
var parent = get ? this.parent._getPos() : this.parent;
|
||||||
return this.screen.cols - (this.left + this.width);
|
|
||||||
}
|
|
||||||
return (this.parent.right || 0) + this.position.right;
|
|
||||||
});
|
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('top', function() {
|
|
||||||
var top = this.position.top;
|
|
||||||
|
|
||||||
var parent = this.parent.spos();
|
|
||||||
|
|
||||||
if (typeof top === 'string') {
|
|
||||||
if (top === 'center') top = '50%';
|
|
||||||
top = +top.slice(0, -1) / 100;
|
|
||||||
top = this.parent.height * top | 0;
|
|
||||||
if (this.position.top === 'center') {
|
|
||||||
top -= this.height / 2 | 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.top == null && this.options.bottom != null) {
|
|
||||||
return this.screen.rows - this.height - this.bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (parent.top || 0) + top;
|
|
||||||
});
|
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('bottom', function() {
|
|
||||||
if (this.options.bottom == null && this.options.top != null) {
|
|
||||||
return this.screen.rows - (this.top + this.height);
|
|
||||||
}
|
|
||||||
return (this.parent.bottom || 0) + this.position.bottom;
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Move _getShrinkSize calculation here. This will in turn fix .left.
|
|
||||||
Element.prototype.__defineGetter__('width', function() {
|
|
||||||
var width = this.position.width;
|
var width = this.position.width;
|
||||||
|
|
||||||
if (typeof width === 'string') {
|
if (typeof width === 'string') {
|
||||||
if (width === 'half') width = '50%';
|
if (width === 'half') width = '50%';
|
||||||
width = +width.slice(0, -1) / 100;
|
width = +width.slice(0, -1) / 100;
|
||||||
return this.parent.width * width | 0;
|
return parent.width * width | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is for if the element is being streched or shrunken.
|
||||||
|
// Although the width for shrunken elements is calculated
|
||||||
|
// in the render function, it may be calculated based on
|
||||||
|
// the content width, and the content width is initially
|
||||||
|
// decided by the width the element, so it needs to be
|
||||||
|
// calculated here.
|
||||||
if (!width) {
|
if (!width) {
|
||||||
// Problem if .left is 'center', we can't calculate the width
|
|
||||||
// NOTE: This assume `right` cannot be a string.
|
|
||||||
var left = this.position.left;
|
var left = this.position.left;
|
||||||
if (typeof left === 'string') {
|
if (typeof left === 'string') {
|
||||||
if (left === 'center') left = '50%';
|
if (left === 'center') left = '50%';
|
||||||
left = +left.slice(0, -1) / 100;
|
left = +left.slice(0, -1) / 100;
|
||||||
left = this.parent.width * left | 0;
|
left = parent.width * left | 0;
|
||||||
}
|
}
|
||||||
width = this.parent.width - this.position.right - left;
|
width = parent.width - this.position.right - left;
|
||||||
}
|
}
|
||||||
|
|
||||||
return width;
|
return width;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('width', function() {
|
||||||
|
return this._getWidth(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Move _getShrinkSize calculation here. This will in turn fix .top.
|
Element.prototype._getHeight = function(get) {
|
||||||
Element.prototype.__defineGetter__('height', function() {
|
var parent = get ? this.parent._getPos() : this.parent;
|
||||||
var height = this.position.height;
|
var height = this.position.height;
|
||||||
|
|
||||||
if (typeof height === 'string') {
|
if (typeof height === 'string') {
|
||||||
if (height === 'half') height = '50%';
|
if (height === 'half') height = '50%';
|
||||||
height = +height.slice(0, -1) / 100;
|
height = +height.slice(0, -1) / 100;
|
||||||
return this.parent.height * height | 0;
|
return parent.height * height | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is for if the element is being streched or shrunken.
|
||||||
|
// Although the width for shrunken elements is calculated
|
||||||
|
// in the render function, it may be calculated based on
|
||||||
|
// the content width, and the content width is initially
|
||||||
|
// decided by the width the element, so it needs to be
|
||||||
|
// calculated here.
|
||||||
if (!height) {
|
if (!height) {
|
||||||
// Problem if .top is 'center', we can't calculate the height
|
|
||||||
// NOTE: This assume `bottom` cannot be a string.
|
|
||||||
var top = this.position.top;
|
var top = this.position.top;
|
||||||
if (typeof top === 'string') {
|
if (typeof top === 'string') {
|
||||||
if (top === 'center') top = '50%';
|
if (top === 'center') top = '50%';
|
||||||
top = +top.slice(0, -1) / 100;
|
top = +top.slice(0, -1) / 100;
|
||||||
top = this.parent.height * top | 0;
|
top = parent.height * top | 0;
|
||||||
}
|
}
|
||||||
height = this.parent.height - this.position.bottom - top;
|
height = parent.height - this.position.bottom - top;
|
||||||
}
|
}
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('height', function() {
|
||||||
|
return this._getHeight(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('rleft', function() {
|
Element.prototype._getLeft = function(get) {
|
||||||
|
var parent = get ? this.parent._getPos() : this.parent;
|
||||||
var left = this.position.left;
|
var left = this.position.left;
|
||||||
|
|
||||||
if (typeof left === 'string') {
|
if (typeof left === 'string') {
|
||||||
if (left === 'center') left = '50%';
|
if (left === 'center') left = '50%';
|
||||||
left = +left.slice(0, -1) / 100;
|
left = +left.slice(0, -1) / 100;
|
||||||
left = this.parent.width * left | 0;
|
left = parent.width * left | 0;
|
||||||
if (this.position.left === 'center') {
|
if (this.position.left === 'center') {
|
||||||
left -= this.width / 2 | 0;
|
left -= this._getWidth(get) / 2 | 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.left == null && this.options.right != null) {
|
if (this.options.left == null && this.options.right != null) {
|
||||||
return this.parent.width - this.width - this.right;
|
return this.screen.cols - this._getWidth(get) - this._getRight(get);
|
||||||
}
|
}
|
||||||
|
|
||||||
return left;
|
return (parent.left || 0) + left;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('left', function() {
|
||||||
|
return this._getLeft(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('rright', function() {
|
Element.prototype._getRight = function(get) {
|
||||||
|
var parent = get ? this.parent._getPos() : this.parent;
|
||||||
if (this.options.right == null && this.options.left != null) {
|
if (this.options.right == null && this.options.left != null) {
|
||||||
return this.parent.width - (this.rleft + this.width);
|
return this.screen.cols - (this._getLeft(get) + this._getWidth(get));
|
||||||
}
|
}
|
||||||
return this.position.right;
|
return (parent.right || 0) + this.position.right;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('right', function() {
|
||||||
|
return this._getRight(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('rtop', function() {
|
Element.prototype._getTop = function(get) {
|
||||||
|
var parent = get ? this.parent._getPos() : this.parent;
|
||||||
var top = this.position.top;
|
var top = this.position.top;
|
||||||
|
|
||||||
if (typeof top === 'string') {
|
if (typeof top === 'string') {
|
||||||
if (top === 'center') top = '50%';
|
if (top === 'center') top = '50%';
|
||||||
top = +top.slice(0, -1) / 100;
|
top = +top.slice(0, -1) / 100;
|
||||||
top = this.parent.height * top | 0;
|
top = parent.height * top | 0;
|
||||||
if (this.position.top === 'center') {
|
if (this.position.top === 'center') {
|
||||||
top -= this.height / 2 | 0;
|
top -= this._getHeight(get) / 2 | 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.top == null && this.options.bottom != null) {
|
if (this.options.top == null && this.options.bottom != null) {
|
||||||
return this.parent.height - this.height - this.bottom;
|
return this.screen.rows - this._getHeight(get) - this._getBottom(get);
|
||||||
}
|
}
|
||||||
|
|
||||||
return top;
|
return (parent.top || 0) + top;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('top', function() {
|
||||||
|
return this._getTop(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype._getBottom = function(get) {
|
||||||
|
var parent = get ? this.parent._getPos() : this.parent;
|
||||||
|
if (this.options.bottom == null && this.options.top != null) {
|
||||||
|
return this.screen.rows - (this._getTop(get) + this._getHeight(get));
|
||||||
|
}
|
||||||
|
return (parent.bottom || 0) + this.position.bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('bottom', function() {
|
||||||
|
return this._getBottom(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('rleft', function() {
|
||||||
|
return this.left - this.parent.left;
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('rright', function() {
|
||||||
|
return this.right - this.parent.right;
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineGetter__('rtop', function() {
|
||||||
|
return this.top - this.parent.top;
|
||||||
});
|
});
|
||||||
|
|
||||||
Element.prototype.__defineGetter__('rbottom', function() {
|
Element.prototype.__defineGetter__('rbottom', function() {
|
||||||
if (this.options.bottom == null && this.options.top != null) {
|
return this.bottom - this.parent.bottom;
|
||||||
return this.parent.height - (this.rtop + this.height);
|
|
||||||
}
|
|
||||||
return this.position.bottom;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Reconcile the fact the `position.left` is actually `.rleft`. etc.
|
/**
|
||||||
// TODO: Allow string values for absolute coords below.
|
* Position Setters
|
||||||
// TODO: Optimize clearing to only clear what is necessary.
|
*/
|
||||||
|
|
||||||
Element.prototype.__defineSetter__('left', function(val) {
|
|
||||||
if (typeof val === 'string') {
|
|
||||||
if (val === 'center') val = '50%';
|
|
||||||
val = +val.slice(0, -1) / 100;
|
|
||||||
val = this.screen.width * val | 0;
|
|
||||||
}
|
|
||||||
val -= this.parent.left;
|
|
||||||
if (this.position.left === val) return;
|
|
||||||
this.emit('move');
|
|
||||||
this.clearPos();
|
|
||||||
return this.options.left = this.position.left = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
Element.prototype.__defineSetter__('right', function(val) {
|
|
||||||
if (typeof val === 'string') {
|
|
||||||
if (val === 'center') val = '50%';
|
|
||||||
val = +val.slice(0, -1) / 100;
|
|
||||||
val = this.screen.width * val | 0;
|
|
||||||
}
|
|
||||||
val -= this.parent.right;
|
|
||||||
if (this.position.right === val) return;
|
|
||||||
this.emit('move');
|
|
||||||
this.clearPos();
|
|
||||||
//if (this.options.right == null) {
|
|
||||||
// return this.options.left = this.position.left =
|
|
||||||
// this.screen.width - 1 - val;
|
|
||||||
//}
|
|
||||||
return this.options.right = this.position.right = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
Element.prototype.__defineSetter__('top', function(val) {
|
|
||||||
if (typeof val === 'string') {
|
|
||||||
if (val === 'center') val = '50%';
|
|
||||||
val = +val.slice(0, -1) / 100;
|
|
||||||
val = this.screen.height * val | 0;
|
|
||||||
}
|
|
||||||
val -= this.parent.top;
|
|
||||||
if (this.position.top === val) return;
|
|
||||||
this.emit('move');
|
|
||||||
this.clearPos();
|
|
||||||
return this.options.top = this.position.top = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
Element.prototype.__defineSetter__('bottom', function(val) {
|
|
||||||
if (typeof val === 'string') {
|
|
||||||
if (val === 'center') val = '50%';
|
|
||||||
val = +val.slice(0, -1) / 100;
|
|
||||||
val = this.screen.height * val | 0;
|
|
||||||
}
|
|
||||||
val -= this.parent.bottom;
|
|
||||||
if (this.position.bottom === val) return;
|
|
||||||
this.emit('move');
|
|
||||||
this.clearPos();
|
|
||||||
//if (this.options.bottom == null) {
|
|
||||||
// return this.options.top = this.position.top =
|
|
||||||
// this.screen.height - 1 - val;
|
|
||||||
//}
|
|
||||||
return this.options.bottom = this.position.bottom = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// For right, bottom, rright, and rbottom:
|
||||||
|
// If position.bottom is null, we could simply set top instead.
|
||||||
|
// But it wouldn't replicate bottom behavior appropriately if
|
||||||
|
// the parent was resized, etc.
|
||||||
Element.prototype.__defineSetter__('width', function(val) {
|
Element.prototype.__defineSetter__('width', function(val) {
|
||||||
if (this.position.width === val) return;
|
if (this.position.width === val) return;
|
||||||
this.emit('resize');
|
this.emit('resize');
|
||||||
@ -2047,6 +2095,56 @@ Element.prototype.__defineSetter__('height', function(val) {
|
|||||||
return this.options.height = this.position.height = val;
|
return this.options.height = this.position.height = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineSetter__('left', function(val) {
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
if (val === 'center') {
|
||||||
|
val = this.screen.width / 2 | 0;
|
||||||
|
val -= this.width / 2 | 0;
|
||||||
|
} else {
|
||||||
|
val = +val.slice(0, -1) / 100;
|
||||||
|
val = this.screen.width * val | 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val -= this.parent.left;
|
||||||
|
if (this.position.left === val) return;
|
||||||
|
this.emit('move');
|
||||||
|
this.clearPos();
|
||||||
|
return this.options.left = this.position.left = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineSetter__('right', function(val) {
|
||||||
|
val -= this.parent.right;
|
||||||
|
if (this.position.right === val) return;
|
||||||
|
this.emit('move');
|
||||||
|
this.clearPos();
|
||||||
|
return this.options.right = this.position.right = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineSetter__('top', function(val) {
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
if (val === 'center') {
|
||||||
|
val = this.screen.height / 2 | 0;
|
||||||
|
val -= this.height / 2 | 0;
|
||||||
|
} else {
|
||||||
|
val = +val.slice(0, -1) / 100;
|
||||||
|
val = this.screen.height * val | 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val -= this.parent.top;
|
||||||
|
if (this.position.top === val) return;
|
||||||
|
this.emit('move');
|
||||||
|
this.clearPos();
|
||||||
|
return this.options.top = this.position.top = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
Element.prototype.__defineSetter__('bottom', function(val) {
|
||||||
|
val -= this.parent.bottom;
|
||||||
|
if (this.position.bottom === val) return;
|
||||||
|
this.emit('move');
|
||||||
|
this.clearPos();
|
||||||
|
return this.options.bottom = this.position.bottom = val;
|
||||||
|
});
|
||||||
|
|
||||||
Element.prototype.__defineSetter__('rleft', function(val) {
|
Element.prototype.__defineSetter__('rleft', function(val) {
|
||||||
if (this.position.left === val) return;
|
if (this.position.left === val) return;
|
||||||
this.emit('move');
|
this.emit('move');
|
||||||
@ -2058,10 +2156,6 @@ Element.prototype.__defineSetter__('rright', function(val) {
|
|||||||
if (this.position.right === val) return;
|
if (this.position.right === val) return;
|
||||||
this.emit('move');
|
this.emit('move');
|
||||||
this.clearPos();
|
this.clearPos();
|
||||||
//if (this.options.right == null) {
|
|
||||||
// return this.options.left = this.position.left =
|
|
||||||
// this.parent.width - 1 - val;
|
|
||||||
//}
|
|
||||||
return this.options.right = this.position.right = val;
|
return this.options.right = this.position.right = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2076,10 +2170,6 @@ Element.prototype.__defineSetter__('rbottom', function(val) {
|
|||||||
if (this.position.bottom === val) return;
|
if (this.position.bottom === val) return;
|
||||||
this.emit('move');
|
this.emit('move');
|
||||||
this.clearPos();
|
this.clearPos();
|
||||||
//if (this.options.bottom == null) {
|
|
||||||
// return this.options.top = this.position.top =
|
|
||||||
// this.parent.height - 1 - val;
|
|
||||||
//}
|
|
||||||
return this.options.bottom = this.position.bottom = val;
|
return this.options.bottom = this.position.bottom = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2123,7 +2213,7 @@ Box.prototype.__proto__ = Element.prototype;
|
|||||||
|
|
||||||
Box.prototype.type = 'box';
|
Box.prototype.type = 'box';
|
||||||
|
|
||||||
Box.prototype._getShrinkSize = function(content) {
|
Box.prototype._getShrinkSize = function() {
|
||||||
// TODO: Possibly move this to parseContent.
|
// TODO: Possibly move this to parseContent.
|
||||||
return {
|
return {
|
||||||
height: this._clines.length,
|
height: this._clines.length,
|
||||||
@ -2146,7 +2236,7 @@ Box.prototype._getShrinkBox = function(xi, xl, yi, yl) {
|
|||||||
for (i = 0; i < this.children.length; i++) {
|
for (i = 0; i < this.children.length; i++) {
|
||||||
el = this.children[i];
|
el = this.children[i];
|
||||||
|
|
||||||
ret = el.getCoords();
|
ret = el._getCoords();
|
||||||
if (!ret) continue;
|
if (!ret) continue;
|
||||||
|
|
||||||
if (ret.xi < mxi) mxi = ret.xi;
|
if (ret.xi < mxi) mxi = ret.xi;
|
||||||
@ -2183,8 +2273,8 @@ Box.prototype._getShrinkBox = function(xi, xl, yi, yl) {
|
|||||||
return { xi: xi, xl: xl, yi: yi, yl: yl };
|
return { xi: xi, xl: xl, yi: yi, yl: yl };
|
||||||
};
|
};
|
||||||
|
|
||||||
Box.prototype._getShrinkContent = function(xi, xl, yi, yl, content) {
|
Box.prototype._getShrinkContent = function(xi, xl, yi, yl) {
|
||||||
var hw = this._getShrinkSize(content)
|
var hw = this._getShrinkSize()
|
||||||
, h = hw.height
|
, h = hw.height
|
||||||
, w = hw.width;
|
, w = hw.width;
|
||||||
|
|
||||||
@ -2212,9 +2302,9 @@ Box.prototype._getShrinkContent = function(xi, xl, yi, yl, content) {
|
|||||||
return { xi: xi, xl: xl, yi: yi, yl: yl };
|
return { xi: xi, xl: xl, yi: yi, yl: yl };
|
||||||
};
|
};
|
||||||
|
|
||||||
Box.prototype._getShrink = function(xi, xl, yi, yl, content) {
|
Box.prototype._getShrink = function(xi, xl, yi, yl) {
|
||||||
var shrinkBox = this._getShrinkBox(xi, xl, yi, yl)
|
var shrinkBox = this._getShrinkBox(xi, xl, yi, yl)
|
||||||
, shrinkContent = this._getShrinkContent(xi, xl, yi, yl, content)
|
, shrinkContent = this._getShrinkContent(xi, xl, yi, yl)
|
||||||
, xll = xl
|
, xll = xl
|
||||||
, yll = yl;
|
, yll = yl;
|
||||||
|
|
||||||
@ -2251,48 +2341,30 @@ Box.prototype._getShrink = function(xi, xl, yi, yl, content) {
|
|||||||
return { xi: xi, xl: xl, yi: yi, yl: yl };
|
return { xi: xi, xl: xl, yi: yi, yl: yl };
|
||||||
};
|
};
|
||||||
|
|
||||||
Box.prototype.getCoords = function() {
|
Box.prototype._getCoords = function(get) {
|
||||||
if (this.hidden) return;
|
if (this.hidden) return;
|
||||||
|
|
||||||
var lines = this.screen.lines
|
var xi = this._getLeft(get)
|
||||||
, xi = this.left
|
, xl = xi + this._getWidth(get)
|
||||||
//, xl = this.screen.cols - this.right
|
, yi = this._getTop(get)
|
||||||
, xl = xi + this.width
|
, yl = yi + this._getHeight(get)
|
||||||
, yi = this.top
|
|
||||||
//, yl = this.screen.rows - this.bottom
|
|
||||||
, yl = yi + this.height
|
|
||||||
, rtop
|
, rtop
|
||||||
, visible
|
, visible
|
||||||
, coords;
|
, coords;
|
||||||
|
|
||||||
//if (this.position.width) {
|
|
||||||
// xl = xi + this.width;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (this.position.height) {
|
|
||||||
// yl = yi + this.height;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Check to make sure we're visible and
|
// Check to make sure we're visible and
|
||||||
// inside of the visible scroll area.
|
// inside of the visible scroll area.
|
||||||
if (this.parent.childBase != null
|
if (this.parent.childBase != null
|
||||||
&& (!this.parent.items
|
&& (!this.parent.items
|
||||||
|| ~this.parent.items.indexOf(this))) {
|
|| ~this.parent.items.indexOf(this))) {
|
||||||
rtop = this.rtop
|
rtop = yi - this.parent._getTop(get)
|
||||||
- (this.parent.border ? 1 : 0)
|
- (this.parent.border ? 1 : 0)
|
||||||
- this.parent.padding;
|
- this.parent.padding;
|
||||||
|
|
||||||
visible = this.parent.height
|
visible = this.parent._getHeight(get)
|
||||||
- (this.parent.border ? 2 : 0)
|
- (this.parent.border ? 2 : 0)
|
||||||
- this.parent.padding * 2;
|
- this.parent.padding * 2;
|
||||||
|
|
||||||
yi -= this.parent.childBase;
|
|
||||||
|
|
||||||
yl = Math.min(yl, this.screen.rows
|
|
||||||
- this.parent.bottom
|
|
||||||
- (this.parent.border ? 1 : 0)
|
|
||||||
- this.parent.padding);
|
|
||||||
|
|
||||||
if (rtop - this.parent.childBase < 0) {
|
if (rtop - this.parent.childBase < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2300,6 +2372,9 @@ Box.prototype.getCoords = function() {
|
|||||||
if (rtop - this.parent.childBase >= visible) {
|
if (rtop - this.parent.childBase >= visible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yi -= this.parent.childBase;
|
||||||
|
yl -= this.parent.childBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to shrink the element base on the
|
// Attempt to shrink the element base on the
|
||||||
@ -2319,12 +2394,10 @@ Box.prototype.getCoords = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Here be dragons.
|
// Here be dragons.
|
||||||
// TODO: Potentially move all calculations performed on
|
|
||||||
// xi/xl/yi/yl here to Element offset and size getters.
|
|
||||||
Box.prototype.render = function() {
|
Box.prototype.render = function() {
|
||||||
this.parseContent();
|
this.parseContent();
|
||||||
|
|
||||||
var coords = this.getCoords();
|
var coords = this._getCoords(true);
|
||||||
if (!coords) return;
|
if (!coords) return;
|
||||||
|
|
||||||
var lines = this.screen.lines
|
var lines = this.screen.lines
|
||||||
@ -2351,17 +2424,10 @@ Box.prototype.render = function() {
|
|||||||
dattr = this.sattr(this.style, this.style.fg, this.style.bg);
|
dattr = this.sattr(this.style, this.style.fg, this.style.bg);
|
||||||
attr = dattr;
|
attr = dattr;
|
||||||
|
|
||||||
// Check previous line for escape codes.
|
// If we're in a scrollable text box, check to
|
||||||
if (this.contentIndex != null && this.childBase > 0 && this._clines) {
|
// see which attributes this line starts with.
|
||||||
i = ci - (this._clines[this.childBase - 1].length + 1);
|
if (this.contentIndex != null && this.childBase != null) {
|
||||||
for (; i < ci; i++) {
|
attr = this._clines.attr[this.childBase];
|
||||||
if (content[i] === '\x1b') {
|
|
||||||
if (c = /^\x1b\[[\d;]*m/.exec(content.substring(i))) {
|
|
||||||
attr = this.screen.attrCode(c[0], attr);
|
|
||||||
i += c[0].length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.border) xi++, xl--, yi++, yl--;
|
if (this.border) xi++, xl--, yi++, yl--;
|
||||||
@ -2564,7 +2630,7 @@ Box.prototype.render = function() {
|
|||||||
// screen.render();
|
// screen.render();
|
||||||
// box.left++;
|
// box.left++;
|
||||||
// box.insertTop('foobar');
|
// box.insertTop('foobar');
|
||||||
// Things will break because we're using lpos instead of getCoords().
|
// Things will break because we're using lpos instead of _getCoords().
|
||||||
// Maybe lpos could be updated on .left, .right, etc setters?
|
// Maybe lpos could be updated on .left, .right, etc setters?
|
||||||
|
|
||||||
Box.prototype.insertLine = function(i, line) {
|
Box.prototype.insertLine = function(i, line) {
|
||||||
@ -4241,9 +4307,9 @@ function Checkbox(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.on('focus', function(old) {
|
this.on('focus', function(old) {
|
||||||
if (!this.lpos) return;
|
if (!self.lpos) return;
|
||||||
self.screen.program.saveCursor();
|
self.screen.program.saveCursor();
|
||||||
self.screen.program.cup(this.lpos.yi, this.lpos.xi + 1);
|
self.screen.program.cup(self.lpos.yi, self.lpos.xi + 1);
|
||||||
self.screen.program.showCursor();
|
self.screen.program.showCursor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user