diff --git a/lib/widget.js b/lib/widget.js index 8a2b76a..e73ab35 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -638,14 +638,25 @@ Element.prototype.setContent = function(content) { ret.yl - (this.border ? 1 : 0)); }; +/** + * Positioning + */ + +// NOTE: When coords are entered in the Element constructor, all of the coords +// are *relative* to their parent, when retrieving them from `.left`, `.right`, +// etc members, the coords are absolute. To see the *relative* coords again, +// use `.rleft`, `.rright`, etc. + Element.prototype.__defineGetter__('left', function() { var left = this.position.left; if (typeof left === 'string') { if (left === 'center') left = '50%'; left = +left.slice(0, -1) / 100; - var len = Math.min(this.width, this.parent.width); - left = (this.parent.width - len) * left | 0; + left = Math.max(this.width, this.parent.width) * left | 0; + if (this.position.left === 'center') { + left -= this.width / 2 | 0; + } } if (this.options.left == null && this.options.right != null) { @@ -656,9 +667,9 @@ Element.prototype.__defineGetter__('left', function() { }); Element.prototype.__defineGetter__('right', function() { - //if (this.options.right == null && this.options.left != null) { - // return this.screen.cols - (this.left + this.width); - //} + if (this.options.right == null && this.options.left != null) { + return this.screen.cols - (this.left + this.width); + } return (this.parent.right || 0) + this.position.right; }); @@ -668,8 +679,10 @@ Element.prototype.__defineGetter__('top', function() { if (typeof top === 'string') { if (top === 'center') top = '50%'; top = +top.slice(0, -1) / 100; - var len = Math.min(this.height, this.parent.height); - top = (this.parent.height - len) * top | 0; + top = Math.max(this.height, this.parent.height) * top | 0; + if (this.position.top === 'center') { + top -= this.height / 2 | 0; + } } if (this.options.top == null && this.options.bottom != null) { @@ -680,9 +693,9 @@ Element.prototype.__defineGetter__('top', function() { }); Element.prototype.__defineGetter__('bottom', function() { - //if (this.options.bottom == null && this.options.top != null) { - // return this.screen.rows - (this.top + this.height); - //} + 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; }); @@ -732,8 +745,10 @@ Element.prototype.__defineGetter__('rleft', function() { if (typeof left === 'string') { if (left === 'center') left = '50%'; left = +left.slice(0, -1) / 100; - var len = Math.min(this.width, this.parent.width); - left = len * left | 0; + left = Math.max(this.width, this.parent.width) * left | 0; + if (this.position.left === 'center') { + left -= this.width / 2 | 0; + } } if (this.options.left == null && this.options.right != null) { @@ -744,6 +759,9 @@ Element.prototype.__defineGetter__('rleft', function() { }); Element.prototype.__defineGetter__('rright', function() { + if (this.options.right == null && this.options.left != null) { + return this.parent.width - (this.rleft + this.width); + } return this.position.right; }); @@ -753,8 +771,10 @@ Element.prototype.__defineGetter__('rtop', function() { if (typeof top === 'string') { if (top === 'center') top = '50%'; top = +top.slice(0, -1) / 100; - var len = Math.min(this.height, this.parent.height); - top = len * top | 0; + top = Math.max(this.height, this.parent.height) * top | 0; + if (this.position.top === 'center') { + top -= this.height / 2 | 0; + } } if (this.options.top == null && this.options.bottom != null) { @@ -765,6 +785,9 @@ Element.prototype.__defineGetter__('rtop', function() { }); Element.prototype.__defineGetter__('rbottom', function() { + if (this.options.bottom == null && this.options.top != null) { + return this.parent.height - (this.rtop + this.height); + } return this.position.bottom; }); @@ -773,10 +796,17 @@ Element.prototype.__defineGetter__('rbottom', function() { // 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 = val - this.parent.left; + } this.emit('move'); this.screen.clearRegion(this.left, this.left + this.width, this.top, this.top + this.height); - var left = this.left; - return this.options.left = this.position.left = left + (val - left); + //var left = this.left; + //return this.options.left = this.position.left = left + (val - left); + return this.options.left = this.position.left = val - this.parent.left; // More efficient clearing: // if (left > this.left) { // this.screen.clearRegion(this.left + this.width, left + this.width, this.top, this.top + this.height); @@ -786,30 +816,51 @@ Element.prototype.__defineSetter__('left', function(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 = val - this.parent.right; + } this.emit('move'); this.screen.clearRegion( this.left, this.left + this.width, this.top, this.top + this.height); - var right = this.right; - return this.options.right = this.position.right = right + (val - right); + //var right = this.right; + //return this.options.right = this.position.right = right + (val - right); + return this.options.right = this.position.right = val - this.parent.right; }); 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 = val - this.parent.top; + } this.emit('move'); this.screen.clearRegion( this.left, this.left + this.width, this.top, this.top + this.height); - var top = this.top; - return this.options.top = this.position.top = top + (val - top); + //var top = this.top; + //return this.options.top = this.position.top = top + (val - top); + return this.options.top = this.position.top = val - this.parent.top; }); 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 = val - this.parent.bottom; + } this.emit('move'); this.screen.clearRegion( this.left, this.left + this.width, this.top, this.top + this.height); - var bottom = this.bottom; - return this.options.bottom = this.position.bottom = bottom + (val - bottom); + //var bottom = this.bottom; + //return this.options.bottom = this.position.bottom = bottom + (val - bottom); + return this.options.bottom = this.position.bottom = val - this.parent.bottom; }); Element.prototype.__defineSetter__('width', function(val) { diff --git a/test/widget-pos.js b/test/widget-pos.js index 708d008..dc9ae57 100644 --- a/test/widget-pos.js +++ b/test/widget-pos.js @@ -1,13 +1,16 @@ var blessed = require('blessed') - , program = blessed(); + , program = blessed() + , assert = require('assert'); var screen = new blessed.Screen({ program: program }); var main = new blessed.Box({ - width: '75%', - height: '75%', + //width: '75%', + //height: '75%', + width: 115, + height: 14, bg: 3, top: 2, left: 2, @@ -19,6 +22,8 @@ screen.append(main); var inner = new blessed.Box({ width: '50%', height: '50%', + //width: 57, + //height: 7, bg: 4, top: 2, left: 2, @@ -40,6 +45,50 @@ inner.setContent(inner.content + '\n' + JSON.stringify({ rbottom: inner.rbottom })); +// NOTE: With 154 cols. +program.cols = 154; +program.rows = 19; + +assert.equal(inner.width, 57); +assert.equal(inner.height, 7); + +assert.equal(inner.left, 4); +assert.equal(inner.right, 93); +assert.equal(inner.top, 4); +assert.equal(inner.bottom, 8); + +assert.equal(inner.rleft, 2); +assert.equal(inner.rright, 56); +assert.equal(inner.rtop, 2); +assert.equal(inner.rbottom, 5); + +// Change left to half of the parent width. +inner.rleft = '50%'; +assert.equal(inner.left, 59); + +// Change left to half of the screen width. +inner.left = '50%'; +assert.equal(inner.left, screen.width / 2 | 0); + +// Test implied height/width. +reset(inner, { + top: 5, + bottom: 5, + left: 5, + right: 5 +}); + +assert.equal(inner.width, 105); +assert.equal(inner.height, 4); + +// Demonstrate the difference between `left: 5`, and `.left = 5` (relative vs. absolute): +inner.top = inner.bottom = inner.left = inner.right = 5; + +assert.equal(inner.width, 144); +assert.equal(inner.height, 9); + +// TODO: Start storing position.left, etc. as absolute? + screen.on('keypress', function(ch, key) { if (key.name === 'escape' || key.name === 'q') { return process.exit(0); @@ -47,3 +96,12 @@ screen.on('keypress', function(ch, key) { }); screen.render(); + +function reset(el, pos) { + el.position.width = el.options.width = pos.width; + el.position.height = el.options.height = pos.height; + el.position.left = el.options.left = pos.left; + el.position.right = el.options.right = pos.right; + el.position.top = el.options.top = pos.top; + el.position.bottom = el.options.bottom = pos.bottom; +} diff --git a/test/widget.js b/test/widget.js index e9909b4..fd53c78 100644 --- a/test/widget.js +++ b/test/widget.js @@ -231,6 +231,3 @@ setInterval(function() { setTimeout(fill, 300); progress.top -= 2; })(); - -setTimeout(function() { -}, 2000);