From 2c6b34a86bf5f0e9700b87472175e6110a784482 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 3 Apr 2015 22:55:56 -0700 Subject: [PATCH] add percentage offsets. see #118. --- README.md | 15 +++++++++-- lib/widget.js | 64 +++++++++++++++++++++++++++++++++++---------- test/widget-dock.js | 24 ++++++++--------- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 56538f1..729fb9c 100644 --- a/README.md +++ b/README.md @@ -415,10 +415,12 @@ The base element. - __padding__ - amount of padding on the inside of the element. can be a number or an object containing the properties: `left`, `right`, `top`, and `bottom`. - __width, height__ - width/height of the element, can be a number, percentage - (`0-100%`), or keyword (`half` or `shrink`). + (`0-100%`), or keyword (`half` or `shrink`). percentages can also have + offsets (`50%+1`, `50%-1`). - __left, right, top, bottom__ - offsets of the element __relative to its parent__. can be a number, percentage (`0-100%`), or keyword (`center`). - `right` and `bottom` do not accept keywords. + `right` and `bottom` do not accept keywords. percentages can also have + offsets (`50%+1`, `50%-1`). - __position__ - can contain the above options. - __scrollable__ - whether the element is scrollable or not. - __ch__ - background character (default is whitespace ` `). @@ -1339,6 +1341,15 @@ var box = blessed.box({ This tells blessed to create a box, perfectly centered __relative to its parent__, 50% as wide and 50% as tall as its parent. +Percentages can also have offsets applied to them: + +``` js + ... + height: '50%-1', + left: '45%+1', + ... +``` + To access the calculated offsets, relative to the parent: ``` js diff --git a/lib/widget.js b/lib/widget.js index 44968b1..2af6402 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -2934,12 +2934,17 @@ Element.prototype._getPos = function() { Element.prototype._getWidth = function(get) { var parent = get ? this.parent._getPos() : this.parent , width = this.position.width || 0 - , left; + , left + , expr; if (typeof width === 'string') { if (width === 'half') width = '50%'; + expr = width.split(/(?=\+|-)/); + width = expr[0]; width = +width.slice(0, -1) / 100; - return parent.width * width | 0; + width = parent.width * width | 0; + width += +(expr[1] || 0); + return width; } // This is for if the element is being streched or shrunken. @@ -2952,8 +2957,11 @@ Element.prototype._getWidth = function(get) { left = this.position.left || 0; if (typeof left === 'string') { if (left === 'center') left = '50%'; + expr = left.split(/(?=\+|-)/); + left = expr[0]; left = +left.slice(0, -1) / 100; left = parent.width * left | 0; + left += +(expr[1] || 0); } width = parent.width - (this.position.right || 0) - left; if (this.screen.autoPadding) { @@ -2975,12 +2983,17 @@ Element.prototype.__defineGetter__('width', function() { Element.prototype._getHeight = function(get) { var parent = get ? this.parent._getPos() : this.parent , height = this.position.height || 0 - , top; + , top + , expr; if (typeof height === 'string') { if (height === 'half') height = '50%'; + expr = height.split(/(?=\+|-)/); + height = expr[0]; height = +height.slice(0, -1) / 100; - return parent.height * height | 0; + height = parent.height * height | 0; + height += +(expr[1] || 0); + return height; } // This is for if the element is being streched or shrunken. @@ -2993,8 +3006,11 @@ Element.prototype._getHeight = function(get) { top = this.position.top || 0; if (typeof top === 'string') { if (top === 'center') top = '50%'; + expr = top.split(/(?=\+|-)/); + top = expr[0]; top = +top.slice(0, -1) / 100; top = parent.height * top | 0; + top += +(expr[1] || 0); } height = parent.height - (this.position.bottom || 0) - top; if (this.screen.autoPadding) { @@ -3016,12 +3032,16 @@ Element.prototype.__defineGetter__('height', function() { Element.prototype._getLeft = function(get) { var parent = get ? this.parent._getPos() : this.parent - , left = this.position.left || 0; + , left = this.position.left || 0 + , expr; if (typeof left === 'string') { if (left === 'center') left = '50%'; + expr = left.split(/(?=\+|-)/); + left = expr[0]; left = +left.slice(0, -1) / 100; left = parent.width * left | 0; + left += +(expr[1] || 0); if (this.position.left === 'center') { left -= this._getWidth(get) / 2 | 0; } @@ -3073,12 +3093,16 @@ Element.prototype.__defineGetter__('aright', function() { Element.prototype._getTop = function(get) { var parent = get ? this.parent._getPos() : this.parent - , top = this.position.top || 0; + , top = this.position.top || 0 + , expr; if (typeof top === 'string') { if (top === 'center') top = '50%'; + expr = top.split(/(?=\+|-)/); + top = expr[0]; top = +top.slice(0, -1) / 100; top = parent.height * top | 0; + top += +(expr[1] || 0); if (this.position.top === 'center') { top -= this._getHeight(get) / 2 | 0; } @@ -3155,6 +3179,7 @@ Element.prototype.__defineGetter__('rbottom', function() { // the parent was resized, etc. Element.prototype.__defineSetter__('width', function(val) { if (this.position.width === val) return; + if (/^\d+$/.test(val)) val = +val; this.emit('resize'); this.clearPos(); return this.position.width = val; @@ -3162,19 +3187,24 @@ Element.prototype.__defineSetter__('width', function(val) { Element.prototype.__defineSetter__('height', function(val) { if (this.position.height === val) return; + if (/^\d+$/.test(val)) val = +val; this.emit('resize'); this.clearPos(); return this.position.height = val; }); Element.prototype.__defineSetter__('aleft', function(val) { + var expr; if (typeof val === 'string') { if (val === 'center') { val = this.screen.width / 2 | 0; val -= this.width / 2 | 0; } else { + expr = val.split(/(?=\+|-)/); + val = expr[0]; val = +val.slice(0, -1) / 100; val = this.screen.width * val | 0; + val += +(expr[1] || 0); } } val -= this.parent.aleft; @@ -3193,13 +3223,17 @@ Element.prototype.__defineSetter__('aright', function(val) { }); Element.prototype.__defineSetter__('atop', function(val) { + var expr; if (typeof val === 'string') { if (val === 'center') { val = this.screen.height / 2 | 0; val -= this.height / 2 | 0; } else { + expr = val.split(/(?=\+|-)/); + val = expr[0]; val = +val.slice(0, -1) / 100; val = this.screen.height * val | 0; + val += +(expr[1] || 0); } } val -= this.parent.atop; @@ -3219,6 +3253,7 @@ Element.prototype.__defineSetter__('abottom', function(val) { Element.prototype.__defineSetter__('rleft', function(val) { if (this.position.left === val) return; + if (/^\d+$/.test(val)) val = +val; this.emit('move'); this.clearPos(); return this.position.left = val; @@ -3233,6 +3268,7 @@ Element.prototype.__defineSetter__('rright', function(val) { Element.prototype.__defineSetter__('rtop', function(val) { if (this.position.top === val) return; + if (/^\d+$/.test(val)) val = +val; this.emit('move'); this.clearPos(); return this.position.top = val; @@ -3869,6 +3905,8 @@ Element.prototype.render = function() { if (!lines[y]) break; if (coords.noleft && x === xi) continue; if (coords.noright && x === xl - 1) continue; + cell = lines[y][x]; + if (!cell) break; if (this.border.type === 'line') { if (x === xi) { ch = '┌'; @@ -3930,8 +3968,6 @@ Element.prototype.render = function() { } else if (this.border.type === 'bg') { ch = this.border.ch; } - cell = lines[y][x]; - if (!cell) break; if (battr !== cell[0] || ch !== cell[1]) { lines[y][x][0] = battr; lines[y][x][1] = ch; @@ -3941,6 +3977,8 @@ Element.prototype.render = function() { y = yi + 1; for (; y < yl - 1; y++) { if (!lines[y]) break; + cell = lines[y][xi]; + if (!cell) break; if (this.border.type === 'line') { ch = '│'; if (this.screen.dockBorders || this.dockBorders) { @@ -3953,14 +3991,14 @@ Element.prototype.render = function() { } else if (this.border.type === 'bg') { ch = this.border.ch; } - cell = lines[y][xi]; - if (!cell) break; if (!coords.noleft) if (battr !== cell[0] || ch !== cell[1]) { lines[y][xi][0] = battr; lines[y][xi][1] = ch; lines[y].dirty = true; } + cell = lines[y][xl - 1]; + if (!cell) break; if (this.border.type === 'line') { ch = '│'; if (this.screen.dockBorders || this.dockBorders) { @@ -3986,8 +4024,6 @@ Element.prototype.render = function() { } else if (this.border.type === 'bg') { ch = this.border.ch; } - cell = lines[y][xl - 1]; - if (!cell) break; if (!coords.noright) if (battr !== cell[0] || ch !== cell[1]) { lines[y][xl - 1][0] = battr; @@ -4001,6 +4037,8 @@ Element.prototype.render = function() { if (!lines[y]) break; if (coords.noleft && x === xi) continue; if (coords.noright && x === xl - 1) continue; + cell = lines[y][x]; + if (!cell) break; if (this.border.type === 'line') { if (x === xi) { ch = '└'; @@ -4064,8 +4102,6 @@ Element.prototype.render = function() { } else if (this.border.type === 'bg') { ch = this.border.ch; } - cell = lines[y][x]; - if (!cell) break; if (battr !== cell[0] || ch !== cell[1]) { lines[y][x][0] = battr; lines[y][x][1] = ch; diff --git a/test/widget-dock.js b/test/widget-dock.js index 9a55404..4a47798 100644 --- a/test/widget-dock.js +++ b/test/widget-dock.js @@ -11,18 +11,18 @@ blessed.box({ parent: screen, left: 0, top: 0, - width: 10, - height: 5, + width: '50%', + height: '50%', border: 'line', content: 'Foo' }); blessed.box({ parent: screen, - left: 9, + left: '50%-1', top: 0, - width: 10, - height: 5, + width: '50%+1', + height: '50%', content: 'Bar', border: 'line' }); @@ -30,19 +30,19 @@ blessed.box({ blessed.box({ parent: screen, left: 0, - top: 4, - width: 10, - height: 5, + top: '50%-1', + width: '50%', + height: '50%+1', border: 'line', content: 'Foo' }); blessed.box({ parent: screen, - left: 9, - top: 4, - width: 10, - height: 5, + left: '50%-1', + top: '50%-1', + width: '50%+1', + height: '50%+1', border: 'line', content: 'Bar' });