diff --git a/README.md b/README.md index f0228f7..f2eff9d 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ The screen on which every other node renders. - **resizeTimeout** - amount of time (in ms) to redraw the screen after the terminal is resized (default: 300). - **tabSize** - the width of tabs within an element's content. +- **autoPadding** - automatically position child elements with border and + padding in mind. ##### Properties: diff --git a/lib/widget.js b/lib/widget.js index 138fa9d..4b01182 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -282,6 +282,8 @@ function Screen(options) { this.tput = this.program.tput; this.output = this.program.output; + this.autoPadding = options.autoPadding; + this.tabc = Array((options.tabSize || 4) + 1).join(' '); this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff; @@ -292,6 +294,20 @@ function Screen(options) { bottom: this.bottom = this.rbottom = 0 }; + this.ileft = 0; + this.itop = 0; + this.iright = 0; + this.ibottom = 0; + this.iheight = 0; + this.iwidth = 0; + + this.padding = { + left: 0, + top: 0, + right: 0, + bottom: 0 + }; + this.hover = null; this.history = []; this.clickable = []; @@ -1703,6 +1719,13 @@ function Element(options) { height: options.height }; + // if (options.position.left == null && options.position.right == null) { + // options.position.left = 0; + // } + // if (options.position.top == null && options.position.bottom == null) { + // options.position.top = 0; + // } + if (options.position.width === 'shrink' || options.position.height === 'shrink') { if (options.position.width === 'shrink') { @@ -1792,6 +1815,10 @@ function Element(options) { style: this.style.label, fixed: true })); + if (this.screen.autoPadding) { + this.children[this.children.length-1].rleft = -this.ileft + 2; + this.children[this.children.length-1].rtop = -this.itop; + } } // TODO: Possibly move this to Node for screen.on('mouse', ...). @@ -2286,7 +2313,7 @@ Screen.prototype._getPos = function() { Element.prototype._getPos = function() { var pos = this.lpos; - assert.ok(pos && !pos.changed); + assert.ok(pos); if (pos.left != null) return pos; @@ -2300,20 +2327,6 @@ Element.prototype._getPos = function() { return pos; }; -Element.prototype._bindPosChanged = function() { - function changed() { - self.forDescendants(function(el) { - if (el.lpos) el.lpos.changed = true; - }, true); - } - - this.on('move', changed); - // Resize might be tricky because it's - // emitted recursively for a screen resize. - this.on('resize', changed); - this.on('reparent', changed); -}; - /** * Position Getters */ @@ -2342,6 +2355,11 @@ Element.prototype._getWidth = function(get) { left = parent.width * left | 0; } width = parent.width - (this.position.right || 0) - left; + if (this.screen.autoPadding) { + width -= ((this.position.left != null || this.position.right == null) + && this.position.left !== 'center' ? this.parent.ileft : 0); + width -= (this.position.right != null ? this.parent.iright : 0); + } } return width; @@ -2375,6 +2393,11 @@ Element.prototype._getHeight = function(get) { top = parent.height * top | 0; } height = parent.height - (this.position.bottom || 0) - top; + if (this.screen.autoPadding) { + height -= ((this.position.top != null || this.position.bottom == null) + && this.position.top !== 'center' ? this.parent.itop : 0); + height -= (this.position.bottom != null ? this.parent.ibottom : 0); + } } return height; @@ -2401,6 +2424,13 @@ Element.prototype._getLeft = function(get) { return this.screen.cols - this._getWidth(get) - this._getRight(get); } + if (this.screen.autoPadding) { + if ((this.position.left != null || this.position.right == null) + && this.position.left !== 'center') { + left += this.parent.ileft; + } + } + return (parent.left || 0) + left; }; @@ -2411,9 +2441,17 @@ Element.prototype.__defineGetter__('left', function() { Element.prototype._getRight = function(get) { var parent = get ? this.parent._getPos() : this.parent; if (this.position.right == null && this.position.left != null) { - return this.screen.cols - (this._getLeft(get) + this._getWidth(get)); + var right = this.screen.cols - (this._getLeft(get) + this._getWidth(get)); + if (this.screen.autoPadding) { + right += (this.position.right != null ? this.parent.iright : 0); + } + return right; } - return (parent.right || 0) + (this.position.right || 0); + var right = (parent.right || 0) + (this.position.right || 0); + if (this.screen.autoPadding) { + right += (this.position.right != null ? this.parent.iright : 0); + } + return right; }; Element.prototype.__defineGetter__('right', function() { @@ -2437,6 +2475,13 @@ Element.prototype._getTop = function(get) { return this.screen.rows - this._getHeight(get) - this._getBottom(get); } + if (this.screen.autoPadding) { + if ((this.position.top != null || this.position.bottom == null) + && this.position.top !== 'center') { + top += this.parent.itop; + } + } + return (parent.top || 0) + top; }; @@ -2447,9 +2492,17 @@ Element.prototype.__defineGetter__('top', function() { Element.prototype._getBottom = function(get) { var parent = get ? this.parent._getPos() : this.parent; if (this.position.bottom == null && this.position.top != null) { - return this.screen.rows - (this._getTop(get) + this._getHeight(get)); + var bottom = this.screen.rows - (this._getTop(get) + this._getHeight(get)); + if (this.screen.autoPadding) { + bottom += (this.position.bottom != null ? this.parent.ibottom : 0); + } + return bottom; } - return (parent.bottom || 0) + (this.position.bottom || 0); + var bottom = (parent.bottom || 0) + (this.position.bottom || 0); + if (this.screen.autoPadding) { + bottom += (this.position.bottom != null ? this.parent.ibottom : 0); + } + return bottom; }; Element.prototype.__defineGetter__('bottom', function() { @@ -2630,10 +2683,18 @@ Element.prototype._getShrinkBox = function(xi, xl, yi, yl) { || this.position.right == null)) { if (this.position.left == null && this.position.right != null) { xi = xl - (mxl - mxi); - xi -= this.padding.left + this.padding.right; + if (!this.screen.autoPadding) { + xi -= this.padding.left + this.padding.right; + } else { + xi -= this.padding.left; + } } else { xl = mxl; - xl += this.padding.left + this.padding.right; + if (!this.screen.autoPadding) { + xl += this.padding.left + this.padding.right; + } else { + xl += this.padding.right; + } } } @@ -2643,10 +2704,18 @@ Element.prototype._getShrinkBox = function(xi, xl, yi, yl) { && !this.scrollable) { if (this.position.top == null && this.position.bottom != null) { yi = yl - (myl - myi); - yi -= this.padding.top + this.padding.bottom; + if (!this.screen.autoPadding) { + yi -= this.padding.top + this.padding.bottom; + } else { + yi -= this.padding.top; + } } else { yl = myl; - yl += this.padding.top + this.padding.bottom; + if (!this.screen.autoPadding) { + yl += this.padding.top + this.padding.bottom; + } else { + yl += this.padding.bottom; + } } } @@ -3822,6 +3891,12 @@ List.prototype.add = function(item) { focusEffects: this.mouse ? this.style.item.focus : null }; + if (this.screen.autoPadding) { + options.top = this.items.length; + options.left = 1; + options.right = 1; + } + ['bg', 'fg', 'bold', 'underline', 'blink', 'inverse', 'invisible'].forEach(function(name) { options[name] = function() { diff --git a/test/widget.js b/test/widget.js index 0b0f7d3..9ebb643 100644 --- a/test/widget.js +++ b/test/widget.js @@ -103,6 +103,11 @@ list.prepend(blessed.text({ content: ' My list ' })); +if (screen.autoPadding) { + list.children[0].rleft = -list.ileft + 2; + list.children[0].rtop = -list.itop; +} + list.on('keypress', function(ch, key) { if (key.name === 'up' || key.name === 'k') { list.up();