From f0b107978bbb2283352f6d0089e9f8d3d1acb053 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 16 Jun 2013 04:21:57 -0500 Subject: [PATCH] button and shrink work. --- README.md | 38 ++++++++++++++ lib/widget.js | 139 +++++++++++++++++++++++++++++++++++++++++++------ test/widget.js | 25 ++++++++- 3 files changed, 184 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8b39142..eb6b17e 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ screen.render(); Blessed comes with a number of high-level widgets so you can avoid all the nasty low-level terminal stuff. + #### Node (from EventEmitter) The base node which everything inherits from. @@ -173,6 +174,7 @@ The base node which everything inherits from. - **remove(node)** - remove child node from node. - **detach()** - remove node from its parent. + #### Screen (from Node) The screen on which every other node renders. @@ -218,6 +220,7 @@ The screen on which every other node renders. - **focusPush(element)** - push element on the focus stack (equivalent to `screen.focused = el`). - **focusPop()/focusLast()** - pop element off the focus stack. + #### Element (from Node) The base element. @@ -277,6 +280,7 @@ The base element. - **toggle()** - toggle hidden/shown. - **focus()** - focus element. + #### Box (from Element) A box element which draws a simple box containing `content` or other elements. @@ -296,6 +300,7 @@ An element similar to Box, but geared towards rendering simple text elements. Inherits all options, properties, events, and methods from Element. + #### Line (from Box) A simple line which can be `ascii` or `bg` styled. @@ -308,6 +313,7 @@ A simple line which can be `ascii` or `bg` styled. Inherits all options, properties, events, and methods from Box. + #### ScrollableBox (from Box) A box with scrollable content. @@ -334,6 +340,7 @@ A box with scrollable content. - **scroll(offset)** - scroll the content by an offset. + #### List (from ScrollableBox) A scrollable list which can display selectable items. @@ -367,6 +374,7 @@ A scrollable list which can display selectable items. - **up(amount)** - select item above selected. - **down(amount)** - select item below selected. + #### ScrollableText (from ScrollableBox) A scrollable text box which can display and scroll text, as well as handle pre-existing newlines and escape codes. @@ -392,6 +400,7 @@ A scrollable text box which can display and scroll text, as well as handle pre-e A form input. + #### Textbox (from Input) A box which allows text input. @@ -416,6 +425,34 @@ A box which allows text input. - **setEditor(callback)** - open text editor in $EDITOR, read the output from the resulting file. takes a callback which receives the final value. + +#### Button (from Input) + +A button which can be focused and allows key and mouse input. + +##### Options: + +- inherits all from Input. + +##### Properties: + +- inherits all from Input. + +##### Events: + +- inherits all from ScrollableBox. +- **press** - received when the button is clicked/pressed. + +##### Methods: + +- inherits all from ScrollableBox. +- **add(text)** - add an item based on a string. +- **select(index)** - select an index of an item. +- **move(offset)** - select item based on current offset. +- **up(amount)** - select item above selected. +- **down(amount)** - select item below selected. + + #### ProgressBar (from Input) A progress bar allowing various styles. @@ -443,6 +480,7 @@ A progress bar allowing various styles. - **progress(amount)** - progress the bar by a fill amount. - **reset()** - reset the bar. + ### Positioning Offsets may be a number, a percentage (e.g. `50%`), or a keyword (e.g. diff --git a/lib/widget.js b/lib/widget.js index 462f55a..9855d2d 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -438,7 +438,10 @@ Screen.prototype.draw = function(start, end) { } if (bgColor !== 0x1ff) { - if (bgColor < 16 || (this.tput && this.tput.colors <= 16)) { + if (this.tput) { + bgColor = this._reduceColor(bgColor); + } + if (bgColor < 16) { if (bgColor < 8) { bgColor += 40; } else if (bgColor < 16) { @@ -452,7 +455,10 @@ Screen.prototype.draw = function(start, end) { } if (fgColor !== 0x1ff) { - if (fgColor < 16 || (this.tput && this.tput.colors <= 16)) { + if (this.tput) { + fgColor = this._reduceColor(fgColor); + } + if (fgColor < 16) { if (fgColor < 8) { fgColor += 30; } else if (fgColor < 16) { @@ -489,6 +495,22 @@ Screen.prototype.draw = function(start, end) { this.program.restoreCursor(); }; +Screen.prototype._reduceColor = function(col) { + if (this.tput) { + if (col >= 16 && this.tput.colors <= 16) { + //col = Screen.ccolors[col]; + if (col >= 244) col = colors.white; + else if (col >= 232) col = colors.black; + else col = colors.blue; + } else if (col >= 8 && this.tput.colors <= 8) { + col -= 8; + } else if (col >= 2 && this.tput.colors <= 2) { + col %= 2; + } + } + return col; +}; + Screen.prototype.focus = function(offset) { if (!this.input.length || !offset) return; var i = this.input.indexOf(this.focused); @@ -632,6 +654,8 @@ function Element(options) { if (type === 'mouse' || type === 'click' || type === 'hover' + || type === 'mouseover' + || type === 'mouseout' || type === 'mousedown' || type === 'mouseup' || type === 'mousewheel' @@ -697,11 +721,7 @@ Element.prototype.setContent = function(content) { var ret = this.render(true); // TODO: Maybe simply set _pcontent with _parseTags result. this.content = this._parseTags(content || ''); - this.screen.clearRegion( - ret.xi + (this.border ? 1 : 0), - ret.xl - (this.border ? 1 : 0), - ret.yi + (this.border ? 1 : 0), - ret.yl - (this.border ? 1 : 0)); + this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl); }; // Convert `{red-fg}foo{/red-fg}` to `\x1b[31mfoo\x1b[39m`. @@ -1021,6 +1041,19 @@ function Box(options) { Box.prototype.__proto__ = Element.prototype; +Box.prototype._getShrinkSize = function(content) { + var lines = content.replace(/\x1b\[[\d;]*m/g, '').split('\n'); + return { + height: lines.length, + width: lines.reduce(function(current, line) { + return line.length > current + ? line.length //+ (lines.length > 1 ? 1 : 0) // for newlines + : current; + }, 0) + }; +}; + +// Here be dragons. Box.prototype.render = function(stop) { // NOTE: Maybe move this `hidden` check down below `stop` check and return `ret`. if (this.hidden) return; @@ -1065,19 +1098,48 @@ Box.prototype.render = function(stop) { } } + // NOTE: Could simply change some offsets around and move this below the + // shrink block. Could also stop passing a string to _getShrinkSize. if (this.align === 'center' || this.align === 'right') { - var ncontent = content.split('\n')[0].replace(/\x1b\[[\d;]*m/g, '') - , width = this.width; + var ncontent = content.replace(/\x1b\[[\d;]*m/g, '') + , width = this.width - (this.border ? 2 : 0) - this.padding * 2; if (ncontent.length < width) { var s = width - ncontent.length; if (this.align === 'center') { - content = Array(((s / 2 | 0) - (this.border ? 1 : 0)) + 1).join(' ') + content; + s = Array(((s / 2 | 0) - (this.border ? 1 : 0)) + 1).join(' '); + content = s + content + (this.shrink ? s : ''); + //s = (s / 2 | 0) - (this.border ? 1 : 0); + //ci -= s; + //xl += s * 2; } else { - content = Array((s - (this.border ? 2 : 0)) + 1).join(' ') + content; + s -= this.left; // this shouldn't be necessary + s = Array((s - (this.border ? 2 : 0)) + 1).join(' '); + content = s + content; + //s = s - (this.border ? 2 : 0); + //ci -= s; + //xl += s; } } } + if (this.shrink) { + var hw = this._getShrinkSize(content) + , h = hw.height + , w = hw.width; + if (this.options.left == null && this.options.right != null) { + xi_ = xl - w - (this.border ? 2 : 0) - this.padding; + //xi_--; // make it one cell wider for newlines + } else { + xl = xi_ + w + (this.border ? 2 : 0) + this.padding; + //xl++; // make it one cell wider for newlines + } + if (this.options.top == null && this.options.bottom != null) { + yi_ = yl - h - (this.border ? 2 : 0) - this.padding; + } else { + yl = yi_ + h + (this.border ? 2 : 0) + this.padding; + } + } + var ret = { xi: xi_, xl: xl, @@ -1109,6 +1171,7 @@ Box.prototype.render = function(stop) { if (this.border) yi_++, yl--, xi_++, xl--; + // TODO: Fix padding. if (this.padding) { yi_ += this.padding, yl -= this.padding; xi_ += this.padding, xl -= this.padding; @@ -1121,12 +1184,6 @@ outer: cell = lines[yi][xi]; if (!cell) break; - if (this.shrink && !content[ci] && yi === yi_) { - // Need to subtract 1 and padding for below. - xl = xi + 1 - 1 - this.padding; - break outer; - } - ch = content[ci++] || ' '; // Handle escape codes. @@ -1144,6 +1201,15 @@ outer: // Handle newlines. if (ch === '\t') ch = ' '; if (ch === '\n' || ch === '\r') { + // If we're on the first cell and we find a newline and the last cell + // of the last line was not a newline, let's just treat this like the + // newline was already "counted". + if (xi === xi_ && yi !== yi_ && content[ci-2] !== '\n') { + xi--; + continue; + } + // this.screen.fillRegion(attr, ' ', xi, xl, yi, yi + 1); + // continue outer; ch = ' '; for (; xi < xl; xi++) { cell = lines[yi][xi]; @@ -1722,14 +1788,51 @@ Textarea.prototype.__proto__ = Input.prototype; */ function Button(options) { + var self = this; + if (!(this instanceof Button)) { return new Button(options); } + Input.call(this, options); + + this.on('keypress', function(ch, key) { + if (key.name === 'enter' || key.name === 'space') { + self.press(); + } + }); + + this.on('click', function() { + self.press(); + }); + + this.on('mouseover', function() { + self.inverse = !self.options.inverse; + self.screen.render(); + }); + + this.on('mouseout', function() { + self.inverse = self.options.inverse; + self.screen.render(); + }); } Button.prototype.__proto__ = Input.prototype; +Button.prototype.press = function() { + var self = this; + this.emit('press'); + if (this.border) { + var color = this.border.color; + this.border.color = 2; + this.screen.render(); + setTimeout(function() { + self.border.color = color; + self.screen.render(); + }, 300); + } +}; + /** * ProgressBar */ @@ -2002,6 +2105,7 @@ function convert(color) { var val = colors[color]; if (val == null) val = color; if (val == null) val = -1; + //if (typeof val === 'string') val = Screen._findColor(val); if (val === -1) return 0x1ff; return val; } @@ -2029,5 +2133,6 @@ exports.List = List; exports.ScrollableText = ScrollableText; exports.Input = Input; exports.Textbox = Textbox; +exports.Textarea = Textarea; exports.Button = Button; exports.ProgressBar = ProgressBar; diff --git a/test/widget.js b/test/widget.js index 1d6a33d..e855bc5 100644 --- a/test/widget.js +++ b/test/widget.js @@ -9,7 +9,8 @@ screen.append(new blessed.Text({ top: 0, left: 2, width: '100%', - content: '{green-fg}Welcome{/green-fg} to my {red-bg,ul}program{/}', + //bg: 'blue', + content: '{green-fg}Welcome{/green-fg} to my {red-fg,ul}program{/red-fg,ul}', tags: true, align: 'center' })); @@ -196,6 +197,28 @@ var input = new blessed.Textbox({ screen.append(input); +var button = blessed.Button({ + //content: 'Click me!', + content: 'Click\nme!', + shrink: true, + border: { + type: 'ascii' + }, + fg: 'red', + bg: 'blue', + //height: 3, + right: 4, + //bottom: 6, + bottom: 2, + padding: 0 +}); + +button.on('press', function() { + button.setContent('Clicked!'); +}); + +screen.append(button); + screen.on('keypress', function(ch, key) { if (key.name === 'tab') { return key.shift