From 29cd1a376f92588f3b428720eac10f9ee159d2bb Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 17 Jul 2013 04:20:22 -0500 Subject: [PATCH] more Box.render refactoring. docs. misc. --- README.md | 10 ++-- lib/widget.js | 137 ++++++++++++++++++++++---------------------------- 2 files changed, 68 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index a116241..6a8ed4d 100644 --- a/README.md +++ b/README.md @@ -250,13 +250,18 @@ The base element. - **label** - a simple text label for the element. - **align** - text alignment: `left`, `center`, or `right`. - **valign** - vertical text alignment: `top`, `middle`, or `bottom`. -- **shrink** - shrink/flex/grow to content width/height during render. -- **shrinkBox** - shrink/flex/grow to combined coordinates of all child boxes. +- **shrink** - shrink/flex/grow to content and child elements. width/height + during render. - **padding** - amount of padding on the inside of the element. +- **width, height** - width/height of the element, can be a number, percentage + (`0-100%`), or keyword (`half` or `shrink`). +- **left, right, top, bottom** - offsets of the element **relative to its + parent**. can be a number, percentage (`0-100%`), or keyword (`center`). ##### Properties: - inherits all from Node. +- **name** - name of the element. useful for form submission. - **border** - border object. - **type** - type of border (`line` or `bg`). `bg` by default. - **ch** - character to use if `bg` type, default is space. @@ -278,7 +283,6 @@ The base element. - **rright** - calculated relative right offset. - **rtop** - calculated relative top offset. - **rbottom** - calculated relative bottom offset. -- **name** - name of the element. useful for form submission. ##### Events: diff --git a/lib/widget.js b/lib/widget.js index b349d2e..21ac853 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -1364,6 +1364,12 @@ function Element(options) { this.name = options.name; + if (options.width === 'shrink' || options.height === 'shrink') { + if (options.width === 'shrink') delete options.width; + if (options.height === 'shrink') delete options.height; + options.shrink = true; + } + this.position = { left: options.left || 0, right: options.right || 0, @@ -1393,6 +1399,7 @@ function Element(options) { this.hidden = options.hidden || false; this.fixed = options.fixed || false; this.align = options.align || 'left'; + this.valign = options.valign || 'top'; this.shrink = options.shrink; this.padding = options.padding || 0; @@ -1942,6 +1949,7 @@ Box.prototype.__proto__ = Element.prototype; Box.prototype.type = 'box'; Box.prototype._getShrinkSize = function(content) { + // TODO: Possibly move this to parseContent. return { height: this._clines.length, width: this._clines.reduce(function(current, line) { @@ -1955,8 +1963,8 @@ Box.prototype._getShrinkSize = function(content) { Box.prototype._getShrinkBox = function(xi, xl, yi, yl) { if (!this.children.length) { - return { xi: xi, xl: xi, yi: yi, yl: yi }; - //return { xi: Infinity, xl: 0, yi: Infinity, yl: 0 }; + // return { xi: xi, xl: xi, yi: yi, yl: yi }; + return { xi: Infinity, xl: 0, yi: Infinity, yl: 0 }; } var i, el, mxi = 0, mxl = 0, myi = 0, myl = 0; @@ -1965,6 +1973,7 @@ Box.prototype._getShrinkBox = function(xi, xl, yi, yl) { el = this.children[i]; var ret = el.render(true); + if (!ret) continue; if (ret.xi < mxi) mxi = ret.xi; if (ret.xl > mxl) mxl = ret.xl; @@ -2083,7 +2092,6 @@ Box.prototype._getShrink = function(xi, xl, yi, yl, content) { // TODO: Potentially move all calculations performed on // xi/xl/yi/yl here to Element offset and size getters. Box.prototype.render = function(stop) { - // NOTE: Maybe move this `hidden` check down below `stop` check and return `ret`. if (this.hidden) return; this.parseContent(); @@ -2100,15 +2108,13 @@ Box.prototype.render = function(stop) { , ch , content = this._pcontent , ci = this.contentIndex || 0 - , cl = content.length , battr , dattr , c , rtop , visible - , h , ret - , cci; + , i; if (this.position.width) { xl = xi + this.width; @@ -2118,13 +2124,25 @@ Box.prototype.render = function(stop) { yl = yi + this.height; } - // 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 = this.rtop - (this.parent.border ? 1 : 0); - visible = this.parent.height - (this.parent.border ? 2 : 0); + // 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 = this.rtop + - (this.parent.border ? 1 : 0) + - this.parent.padding; + + visible = this.parent.height + - (this.parent.border ? 2 : 0) + - this.parent.padding * 2; yi -= this.parent.childBase; - yl = Math.min(yl, this.screen.rows - this.parent.bottom - (this.parent.border ? 1 : 0)); + + yl = Math.min(yl, this.screen.rows + - this.parent.bottom + - (this.parent.border ? 1 : 0) + - this.parent.padding); if (rtop - this.parent.childBase < 0) { return; @@ -2135,18 +2153,13 @@ Box.prototype.render = function(stop) { } } - // TODO: Possibly do both shrinkBox and shrink - // and use whichever values are higher. - // TODO: Check for 'center', recalculate yi, and xi. Better - // yet, simply move this check into this.left/width/etc. + // Attempt to shrink the element base on the + // size of the content and child elements. if (this.shrink) { ret = this._getShrink(xi, xl, yi, yl, content); xi = ret.xi, xl = ret.xl, yi = ret.yi, yl = ret.yl; } - // TODO: - // Calculate whether we moved/resized by checking the previous _lastPos. - // Maybe clear based on that. Possibly emit events here. ret = { xi: xi, xl: xl, @@ -2154,6 +2167,9 @@ Box.prototype.render = function(stop) { yl: yl }; + // If we just wanted to calculate the coords, return. + // Possibly move the coord calculation into its own + // method. if (stop) return ret; this._lastPos = ret; @@ -2167,43 +2183,47 @@ Box.prototype.render = function(stop) { // Check previous line for escape codes. if (this.contentIndex != null && this.childBase > 0 && this._clines) { - cci = ci - (this._clines[this.childBase - 1].length + 1); - for (; cci < ci; cci++) { - if (content[cci] === '\x1b') { - if (c = /^\x1b\[[\d;]*m/.exec(content.substring(cci))) { + i = ci - (this._clines[this.childBase - 1].length + 1); + for (; i < ci; i++) { + if (content[i] === '\x1b') { + if (c = /^\x1b\[[\d;]*m/.exec(content.substring(i))) { attr = this.screen.attrCode(c[0], attr); - cci += c[0].length - 1; + i += c[0].length - 1; } } } } - if (this.border) yi++, yl--, xi++, xl--; + if (this.border) xi++, xl--, yi++, yl--; - if (this.padding || this.options.valign) { + // If we have padding/valign, that means the + // content-drawing loop will skip a few cells/lines. + // To deal with this, we can just fill the whole thing + // ahead of time. This could be optimized. + if (this.padding || (this.valign && this.valign !== 'top')) { this.screen.fillRegion(dattr, ' ', xi, xl, yi, yl); } if (this.padding) { - yi += this.padding, yl -= this.padding; xi += this.padding, xl -= this.padding; + yi += this.padding, yl -= this.padding; } - if (this.options.valign === 'middle' || this.options.valign === 'bottom') { - //visible = (yl - yi) - (this.border ? 2 : 0) - this.padding; + // Determine where to place the text if it's vertically aligned. + if (this.valign === 'middle' || this.valign === 'bottom') { visible = yl - yi; if (this._clines.length < visible) { - if (this.options.valign === 'middle') { + if (this.valign === 'middle') { visible = visible / 2 | 0; visible -= this._clines.length / 2 | 0; - } else if (this.options.valign === 'bottom') { + } else if (this.valign === 'bottom') { visible -= this._clines.length; } yi += visible; } } -outer: + // Draw the content and background. for (y = yi; y < yl; y++) { if (!lines[y]) break; for (x = xi; x < xl; x++) { @@ -2234,8 +2254,8 @@ outer: x--; continue; } - // this.screen.fillRegion(attr, ' ', x, xl, y, y + 1); - // continue outer; + // We could use fillRegion here, name the + // outer loop, and continue to it instead. ch = ' '; for (; x < xl; x++) { cell = lines[y][x]; @@ -2248,7 +2268,6 @@ outer: } continue; } - // if (ch < ' ') ch = ' '; if (attr !== cell[0] || ch !== cell[1]) { lines[y][x][0] = attr; @@ -2258,24 +2277,22 @@ outer: } } - h = this.items ? this.items.length : this._clines.length; - if (this.scrollbar && (yl - yi) < h) { + // Draw the scrollbar. + i = this.items ? this.items.length : this._clines.length; + if (this.scrollbar && (yl - yi) < i) { x = xl - 1; if (this.scrollbar.ignoreBorder && this.border) x++; if (this.selected == null) { // TODO: Fix this - doesn't work with lists (and possibly scrollabletext). - y = h - (yl - yi) - (this.border ? 2 : 0) - this.padding * 2; + y = i - (yl - yi) - (this.border ? 2 : 0) - this.padding * 2; y = yi + (((yl - yi) * (this.childBase / y)) | 0); } else { - y = this.selected / h; + y = this.selected / i; y = yi + ((yl - yi) * y | 0); } cell = lines[y] && lines[y][x]; if (cell) { ch = this.scrollbar.ch || ' '; - //attr = this.sattr(this.style, - // this.scrollbar.style.fg || this.style.fg, - // this.scrollbar.style.bg || this.style.bg); attr = this.sattr(this.style, this.style.scrollbar.fg || this.style.fg, this.style.scrollbar.bg || this.style.bg); @@ -2287,46 +2304,14 @@ outer: } } - // This seems redundant, but we need to draw the - // border second because of the `shrink` option. - if (this.border) yi--, yl++, xi--, xl++; + if (this.border) xi--, xl++, yi--, yl++; if (this.padding) { - yi -= this.padding, yl += this.padding; xi -= this.padding, xl += this.padding; + yi -= this.padding, yl += this.padding; } -/* - if (this.padding) { - // Set padding to green for debugging: - // dattr = (dattr & ~0x1ff) | colors.convert('green'); - // top - this.screen.fillRegion(dattr, ' ', - xi + (this.border ? 1 : 0), - xl - (this.border ? 1 : 0), - yi + (this.border ? 1 : 0), - yi + 0 + (this.border ? 1 : 0) + this.padding); - // bottom - this.screen.fillRegion(dattr, ' ', - xi + (this.border ? 1 : 0), - xl - (this.border ? 1 : 0), - yl - 0 - (this.border ? 1 : 0) - this.padding, - yl - (this.border ? 1 : 0)); - // left - this.screen.fillRegion(dattr, ' ', - xi + (this.border ? 1 : 0), - xi + 0 + (this.border ? 1 : 0) + this.padding, - yi + (this.border ? 1 : 0), - yl - (this.border ? 1 : 0)); - // right - this.screen.fillRegion(dattr, ' ', - xl - 0 - (this.border ? 1 : 0) - this.padding, - xl - (this.border ? 1 : 0), - yi + (this.border ? 1 : 0), - yl - (this.border ? 1 : 0)); - } -*/ - + // Draw the border. if (this.border) { y = yi; for (x = xi; x < xl; x++) {